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
@@ -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;
@@ -834,8 +834,9 @@ tinymce.create('tinymce.util.Dispatcher', {
834
834
  // Default settings
835
835
  s = t.settings = s || {};
836
836
 
837
- // Strange app protocol or local anchor
838
- if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) {
837
+ // Strange app protocol that isn't http/https or local anchor
838
+ // For example: mailto,skype,tel etc.
839
+ if (/^([\w\-]+):([^\/]{2})/i.test(u) || /^\s*#/.test(u)) {
839
840
  t.source = u;
840
841
  return;
841
842
  }
@@ -1320,31 +1321,89 @@ tinymce.create('static tinymce.util.XHR', {
1320
1321
  })(tinymce);
1321
1322
 
1322
1323
  (function(tinymce) {
1324
+ var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;
1325
+
1323
1326
  function cleanupStylesWhenDeleting(ed) {
1324
- var dom = ed.dom, selection = ed.selection, VK= tinymce.VK;
1325
- ed.onKeyUp.add(function(ed, e) {
1326
- if (e.keyCode == VK.DELETE ||e.keyCode == VK.BACKSPACE) {
1327
- var startContainer = selection.getRng().startContainer;
1328
- var blockElement = startContainer;
1329
- while (!dom.isBlock(blockElement)) {
1330
- blockElement = blockElement.parentNode;
1327
+ var dom = ed.dom, selection = ed.selection;
1328
+
1329
+ ed.onKeyDown.add(function(ed, e) {
1330
+ var rng, blockElm, node, clonedSpan, isDelete;
1331
+
1332
+ isDelete = e.keyCode == DELETE;
1333
+ if (isDelete || e.keyCode == BACKSPACE) {
1334
+ e.preventDefault();
1335
+ rng = selection.getRng();
1336
+
1337
+ // Find root block
1338
+ blockElm = dom.getParent(rng.startContainer, dom.isBlock);
1339
+
1340
+ // On delete clone the root span of the next block element
1341
+ if (isDelete)
1342
+ blockElm = dom.getNext(blockElm, dom.isBlock);
1343
+
1344
+ // Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace
1345
+ if (blockElm) {
1346
+ node = blockElm.firstChild;
1347
+
1348
+ if (node && node.nodeName === 'SPAN') {
1349
+ clonedSpan = node.cloneNode(false);
1331
1350
  }
1332
- var spans = dom.select("span.Apple-style-span", blockElement);
1333
- dom.remove(spans, true);
1334
1351
  }
1335
- });
1336
- }
1337
1352
 
1353
+ // Do the backspace/delete actiopn
1354
+ ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
1355
+
1356
+ // Find all odd apple-style-spans
1357
+ blockElm = dom.getParent(rng.startContainer, dom.isBlock);
1358
+ tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {
1359
+ var rng = dom.createRng();
1360
+
1361
+ // Set range selection before the span we are about to remove
1362
+ rng.setStartBefore(span);
1363
+ rng.setEndBefore(span);
1364
+
1365
+ if (clonedSpan) {
1366
+ dom.replace(clonedSpan.cloneNode(false), span, true);
1367
+ } else {
1368
+ dom.remove(span, true);
1369
+ }
1370
+
1371
+ // Restore the selection
1372
+ selection.setRng(rng);
1373
+ });
1374
+ }
1375
+ });
1376
+ };
1377
+
1378
+ function emptyEditorWhenDeleting(ed) {
1379
+ ed.onKeyUp.add(function(ed, e) {
1380
+ var keyCode = e.keyCode;
1381
+
1382
+ if (keyCode == DELETE || keyCode == BACKSPACE) {
1383
+ if (ed.dom.isEmpty(ed.getBody())) {
1384
+ ed.setContent('', {format : 'raw'});
1385
+ ed.nodeChanged();
1386
+ return;
1387
+ }
1388
+ }
1389
+ });
1390
+ };
1391
+
1338
1392
  tinymce.create('tinymce.util.Quirks', {
1339
1393
  Quirks: function(ed) {
1394
+ // Load WebKit specific fixed
1340
1395
  if (tinymce.isWebKit) {
1341
1396
  cleanupStylesWhenDeleting(ed);
1397
+ emptyEditorWhenDeleting(ed);
1398
+ }
1399
+
1400
+ // Load IE specific fixes
1401
+ if (tinymce.isIE) {
1402
+ emptyEditorWhenDeleting(ed);
1342
1403
  }
1343
-
1344
1404
  }
1345
1405
  });
1346
- })(tinymce);
1347
-
1406
+ })(tinymce);
1348
1407
  (function(tinymce) {
1349
1408
  var namedEntities, baseEntities, reverseEntities,
1350
1409
  attrsCharsRegExp = /[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
@@ -1756,7 +1815,7 @@ tinymce.html.Styles = function(settings, schema) {
1756
1815
 
1757
1816
  (function(tinymce) {
1758
1817
  var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap, customElementsMap = {},
1759
- whiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
1818
+ defaultWhiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
1760
1819
 
1761
1820
  function split(str, delim) {
1762
1821
  return str.split(delim || ',');
@@ -1920,11 +1979,11 @@ tinymce.html.Styles = function(settings, schema) {
1920
1979
  boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls');
1921
1980
  shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');
1922
1981
  nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,audio,object'), shortEndedElementsMap);
1923
- whiteSpaceElementsMap = makeMap('pre,script,style,textarea');
1982
+ defaultWhiteSpaceElementsMap = makeMap('pre,script,style,textarea');
1924
1983
  selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
1925
1984
 
1926
1985
  tinymce.html.Schema = function(settings) {
1927
- var self = this, elements = {}, children = {}, patternElements = [], validStyles;
1986
+ var self = this, elements = {}, children = {}, patternElements = [], validStyles, whiteSpaceElementsMap;
1928
1987
 
1929
1988
  settings = settings || {};
1930
1989
 
@@ -1942,6 +2001,8 @@ tinymce.html.Styles = function(settings, schema) {
1942
2001
  });
1943
2002
  }
1944
2003
 
2004
+ whiteSpaceElementsMap = settings.whitespace_elements ? makeMap(settings.whitespace_elements) : defaultWhiteSpaceElementsMap;
2005
+
1945
2006
  // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
1946
2007
  function patternToRegExp(str) {
1947
2008
  return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
@@ -2296,9 +2357,9 @@ tinymce.html.Styles = function(settings, schema) {
2296
2357
 
2297
2358
  self.parse = function(html) {
2298
2359
  var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name, isInternalElement, removeInternalElements,
2299
- shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue,
2360
+ shortEndedElements, fillAttrsMap, isShortEnded, validate, elementRule, isValidElement, attr, attribsValue, invalidPrefixRegExp,
2300
2361
  validAttributesMap, validAttributePatterns, attributesRequired, attributesDefault, attributesForced, selfClosing,
2301
- tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing;
2362
+ tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0, decode = tinymce.html.Entities.decode, fixSelfClosing, isIE;
2302
2363
 
2303
2364
  function processEndTag(name) {
2304
2365
  var pos, i;
@@ -2349,6 +2410,8 @@ tinymce.html.Styles = function(settings, schema) {
2349
2410
  validate = settings.validate;
2350
2411
  removeInternalElements = settings.remove_internals;
2351
2412
  fixSelfClosing = settings.fix_self_closing;
2413
+ isIE = tinymce.isIE;
2414
+ invalidPrefixRegExp = /^:/;
2352
2415
 
2353
2416
  while (matches = tokenRegExp.exec(html)) {
2354
2417
  // Text
@@ -2356,9 +2419,20 @@ tinymce.html.Styles = function(settings, schema) {
2356
2419
  self.text(decode(html.substr(index, matches.index - index)));
2357
2420
 
2358
2421
  if (value = matches[6]) { // End element
2359
- processEndTag(value.toLowerCase());
2422
+ value = value.toLowerCase();
2423
+
2424
+ // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
2425
+ if (isIE && invalidPrefixRegExp.test(value))
2426
+ value = value.substr(1);
2427
+
2428
+ processEndTag(value);
2360
2429
  } else if (value = matches[7]) { // Start element
2361
2430
  value = value.toLowerCase();
2431
+
2432
+ // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
2433
+ if (isIE && invalidPrefixRegExp.test(value))
2434
+ value = value.substr(1);
2435
+
2362
2436
  isShortEnded = value in shortEndedElements;
2363
2437
 
2364
2438
  // Is self closing tag for example an <li> after an open <li>
@@ -3372,11 +3446,13 @@ tinymce.html.Styles = function(settings, schema) {
3372
3446
  elementRule = schema.getElementRule(parent.name);
3373
3447
 
3374
3448
  // Remove or padd the element depending on schema rule
3375
- if (elementRule.removeEmpty)
3376
- parent.remove();
3377
- else if (elementRule.paddEmpty)
3378
- parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
3379
- }
3449
+ if (elementRule) {
3450
+ if (elementRule.removeEmpty)
3451
+ parent.remove();
3452
+ else if (elementRule.paddEmpty)
3453
+ parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
3454
+ }
3455
+ }
3380
3456
  }
3381
3457
  }
3382
3458
  }
@@ -4641,7 +4717,7 @@ tinymce.html.Writer = function(settings) {
4641
4717
  },
4642
4718
 
4643
4719
  isEmpty : function(node, elements) {
4644
- var self = this, i, attributes, type, walker, name;
4720
+ var self = this, i, attributes, type, walker, name, parentNode;
4645
4721
 
4646
4722
  node = node.firstChild;
4647
4723
  if (node) {
@@ -4657,8 +4733,16 @@ tinymce.html.Writer = function(settings) {
4657
4733
  continue;
4658
4734
 
4659
4735
  // Keep empty elements like <img />
4660
- if (elements && elements[node.nodeName.toLowerCase()])
4736
+ name = node.nodeName.toLowerCase();
4737
+ if (elements && elements[name]) {
4738
+ // Ignore single BR elements in blocks like <p><br /></p>
4739
+ parentNode = node.parentNode;
4740
+ if (name === 'br' && self.isBlock(parentNode) && parentNode.firstChild === node && parentNode.lastChild === node) {
4741
+ continue;
4742
+ }
4743
+
4661
4744
  return false;
4745
+ }
4662
4746
 
4663
4747
  // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
4664
4748
  attributes = self.getAttribs(node);
@@ -6443,9 +6527,13 @@ tinymce.html.Writer = function(settings) {
6443
6527
 
6444
6528
  if (n)
6445
6529
  e.appendChild(n);
6446
- } else if (is(r.item) || is(r.htmlText))
6447
- e.innerHTML = r.item ? r.item(0).outerHTML : r.htmlText;
6448
- else
6530
+ } else if (is(r.item) || is(r.htmlText)) {
6531
+ // IE will produce invalid markup if elements are present that
6532
+ // it doesn't understand like custom elements or HTML5 elements.
6533
+ // Adding a BR in front of the contents and then remoiving it seems to fix it though.
6534
+ e.innerHTML = '<br>' + (r.item ? r.item(0).outerHTML : r.htmlText);
6535
+ e.removeChild(e.firstChild);
6536
+ } else
6449
6537
  e.innerHTML = r.toString();
6450
6538
 
6451
6539
  // Keep whitespace before and after
@@ -6530,7 +6618,12 @@ tinymce.html.Writer = function(settings) {
6530
6618
  rng = self.getRng();
6531
6619
  }
6532
6620
 
6533
- rng.pasteHTML(content);
6621
+ // Explorer removes spaces from the beginning of pasted contents
6622
+ if (/^\s+/.test(content)) {
6623
+ rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
6624
+ self.dom.remove('__mce_tmp');
6625
+ } else
6626
+ rng.pasteHTML(content);
6534
6627
  }
6535
6628
 
6536
6629
  // Dispatch set content event
@@ -7137,26 +7230,29 @@ tinymce.html.Writer = function(settings) {
7137
7230
  container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];
7138
7231
  offset = 0;
7139
7232
 
7140
- // Walk the DOM to find a text node to place the caret at or a BR
7141
- node = container;
7142
- walker = new tinymce.dom.TreeWalker(container, body);
7143
- do {
7144
- // Found a text node use that position
7145
- if (node.nodeType === 3) {
7146
- offset = start ? 0 : node.nodeValue.length - 1;
7147
- container = node;
7148
- break;
7149
- }
7233
+ // Don't walk into elements that doesn't have any child nodes like a IMG
7234
+ if (container.hasChildNodes()) {
7235
+ // Walk the DOM to find a text node to place the caret at or a BR
7236
+ node = container;
7237
+ walker = new tinymce.dom.TreeWalker(container, body);
7238
+ do {
7239
+ // Found a text node use that position
7240
+ if (node.nodeType === 3) {
7241
+ offset = start ? 0 : node.nodeValue.length - 1;
7242
+ container = node;
7243
+ break;
7244
+ }
7150
7245
 
7151
- // Found a BR element that we can place the caret before
7152
- if (node.nodeName === 'BR') {
7153
- offset = dom.nodeIndex(node);
7154
- container = node.parentNode;
7155
- break;
7156
- }
7157
- } while (node = (start ? walker.next() : walker.prev()));
7246
+ // Found a BR element that we can place the caret before
7247
+ if (node.nodeName === 'BR') {
7248
+ offset = dom.nodeIndex(node);
7249
+ container = node.parentNode;
7250
+ break;
7251
+ }
7252
+ } while (node = (start ? walker.next() : walker.prev()));
7158
7253
 
7159
- normalized = true;
7254
+ normalized = true;
7255
+ }
7160
7256
  }
7161
7257
  }
7162
7258
 
@@ -7450,7 +7546,7 @@ tinymce.html.Writer = function(settings) {
7450
7546
 
7451
7547
  // Explorer won't clone contents of script and style and the
7452
7548
  // selected index of select elements are cleared on a clone operation.
7453
- if (isIE && dom.select('script,style,select').length > 0) {
7549
+ if (isIE && dom.select('script,style,select,map').length > 0) {
7454
7550
  content = node.innerHTML;
7455
7551
  node = node.cloneNode(false);
7456
7552
  dom.setHTML(node, content);
@@ -9151,6 +9247,11 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9151
9247
  Event.remove(t.id, 'blur', bf);
9152
9248
  });
9153
9249
 
9250
+ //prevent default left and right keys on chrome - so that the keyboard navigation is used.
9251
+ if (tinymce.isWebKit && (e.keyCode==37 ||e.keyCode==39)) {
9252
+ return Event.prevent(e);
9253
+ }
9254
+
9154
9255
  if (e.keyCode == 13 || e.keyCode == 32) {
9155
9256
  onChange(e);
9156
9257
  return Event.cancel(e);
@@ -9161,6 +9262,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9161
9262
  }
9162
9263
  });
9163
9264
  })(tinymce);
9265
+
9164
9266
  (function(tinymce) {
9165
9267
  var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each;
9166
9268
 
@@ -9416,6 +9518,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
9416
9518
  }
9417
9519
 
9418
9520
  t.isMenuVisible = 0;
9521
+ t.onHideMenu.dispatch();
9419
9522
  }
9420
9523
  },
9421
9524
 
@@ -10504,14 +10607,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10504
10607
 
10505
10608
  t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
10506
10609
 
10507
- // Firefox 2 doesn't load stylesheets correctly this way
10508
- if (!isGecko || !/Firefox\/2/.test(navigator.userAgent)) {
10509
- for (i = 0; i < t.contentCSS.length; i++)
10510
- t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';
10511
-
10512
- t.contentCSS = [];
10513
- }
10514
-
10515
10610
  bi = s.body_id || 'tinymce';
10516
10611
  if (bi.indexOf('=') != -1) {
10517
10612
  bi = t.getParam('body_id', '', 'hash');
@@ -10524,7 +10619,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10524
10619
  bc = bc[t.id] || '';
10525
10620
  }
10526
10621
 
10527
- t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"></body></html>';
10622
+ t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';
10528
10623
 
10529
10624
  // Domain relaxing enabled, then set document domain
10530
10625
  if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
@@ -10542,7 +10637,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10542
10637
  title : s.aria_label,
10543
10638
  style : {
10544
10639
  width : '100%',
10545
- height : h
10640
+ height : h,
10641
+ display : 'block' // Important for Gecko to render the iframe correctly
10546
10642
  }
10547
10643
  });
10548
10644
 
@@ -10557,50 +10653,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10557
10653
  e = n = o = null; // Cleanup
10558
10654
  },
10559
10655
 
10560
- setupIframe : function(filled) {
10656
+ setupIframe : function() {
10561
10657
  var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
10562
10658
 
10563
10659
  // Setup iframe body
10564
- if ((!isIE || !tinymce.relaxedDomain) && !filled) {
10565
- // We need to wait for the load event on Gecko
10566
- if (isGecko && !s.readonly) {
10567
- t.getWin().addEventListener("DOMContentLoaded", function() {
10568
- window.setTimeout(function() {
10569
- var b = t.getBody(), undef;
10570
-
10571
- // Editable element needs to have some contents or backspace/delete won't work properly for some odd reason on FF 3.6 or older
10572
- b.innerHTML = '<br>';
10573
-
10574
- // Check if Gecko supports contentEditable mode FF2 doesn't
10575
- if (b.contentEditable !== undef) {
10576
- // Setting the contentEditable off/on seems to force caret mode in the editor and enabled auto focus
10577
- b.contentEditable = false;
10578
- b.contentEditable = true;
10579
-
10580
- // Caret doesn't get rendered when you mousedown on the HTML element on FF 3.x
10581
- t.onMouseDown.add(function(ed, e) {
10582
- if (e.target.nodeName === "HTML") {
10583
- // Setting the contentEditable off/on seems to force caret mode in the editor and enabled auto focus
10584
- b.contentEditable = false;
10585
- b.contentEditable = true;
10586
-
10587
- d.designMode = 'on'; // Render the caret
10588
-
10589
- // Remove design mode again after a while so it has some time to execute
10590
- window.setTimeout(function() {
10591
- d.designMode = 'off';
10592
- t.getBody().focus();
10593
- }, 1);
10594
- }
10595
- });
10596
- } else
10597
- d.designMode = 'on';
10598
-
10599
- // Call setup frame once the contentEditable/designMode has been initialized
10600
- // since the caret won't be rendered some times otherwise.
10601
- t.setupIframe(true);
10602
- }, 1);
10603
- }, false);
10660
+ if (!isIE || !tinymce.relaxedDomain) {
10661
+ // Fix for a focus bug in FF 3.x where the body element
10662
+ // wouldn't get proper focus if the user clicked on the HTML element
10663
+ if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
10664
+ t.onMouseDown.add(function(ed, e) {
10665
+ if (e.target.nodeName === "HTML") {
10666
+ var body = t.getBody();
10667
+
10668
+ // Blur the body it's focused but not correctly focused
10669
+ body.blur();
10670
+
10671
+ // Refocus the body after a little while
10672
+ setTimeout(function() {
10673
+ body.focus();
10674
+ }, 0);
10675
+ }
10676
+ });
10604
10677
  }
10605
10678
 
10606
10679
  d.open();
@@ -10609,17 +10682,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10609
10682
 
10610
10683
  if (tinymce.relaxedDomain)
10611
10684
  d.domain = tinymce.relaxedDomain;
10612
-
10613
- // Wait for iframe onload event on Gecko
10614
- if (isGecko && !s.readonly)
10615
- return;
10616
10685
  }
10617
10686
 
10618
10687
  // It will not steal focus while setting contentEditable
10619
10688
  b = t.getBody();
10620
10689
  b.disabled = true;
10621
10690
 
10622
- if (!isGecko && !s.readonly)
10691
+ if (!s.readonly)
10623
10692
  b.contentEditable = true;
10624
10693
 
10625
10694
  b.disabled = false;
@@ -10769,6 +10838,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
10769
10838
  subscript : {inline : 'sub'},
10770
10839
  superscript : {inline : 'sup'},
10771
10840
 
10841
+ link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
10842
+ onmatch : function(node) {
10843
+ return true;
10844
+ },
10845
+
10846
+ onformat : function(elm, fmt, vars) {
10847
+ each(vars, function(value, key) {
10848
+ t.dom.setAttrib(elm, key, value);
10849
+ });
10850
+ }
10851
+ },
10852
+
10772
10853
  removeformat : [
10773
10854
  {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
10774
10855
  {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
@@ -11021,6 +11102,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11021
11102
  controlElm = ieRng.item(0);
11022
11103
  }
11023
11104
 
11105
+ t._refreshContentEditable();
11024
11106
  selection.normalize();
11025
11107
 
11026
11108
  // Is not content editable
@@ -11757,16 +11839,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
11757
11839
  var t = this, d = t.getDoc(), s = t.settings;
11758
11840
 
11759
11841
  if (isGecko && !s.readonly) {
11760
- if (t._isHidden()) {
11761
- try {
11762
- if (!s.content_editable) {
11763
- d.body.contentEditable = false;
11764
- d.body.contentEditable = true;
11765
- }
11766
- } catch (ex) {
11767
- // Fails if it's hidden
11768
- }
11769
- }
11842
+ t._refreshContentEditable();
11770
11843
 
11771
11844
  try {
11772
11845
  // Try new Gecko method
@@ -12042,7 +12115,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12042
12115
  if (tinymce.isWebKit) {
12043
12116
  dom.bind(t.getDoc(), 'selectionchange', function() {
12044
12117
  if (t.selectionTimer) {
12045
- window.clearTimeout(t.selectionTimer);
12118
+ clearTimeout(t.selectionTimer);
12046
12119
  t.selectionTimer = 0;
12047
12120
  }
12048
12121
 
@@ -12104,6 +12177,21 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12104
12177
  }
12105
12178
  },
12106
12179
 
12180
+ _refreshContentEditable : function() {
12181
+ var self = this, body, parent;
12182
+
12183
+ // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
12184
+ if (self._isHidden()) {
12185
+ body = self.getBody();
12186
+ parent = body.parentNode;
12187
+
12188
+ parent.removeChild(body);
12189
+ parent.appendChild(body);
12190
+
12191
+ body.focus();
12192
+ }
12193
+ },
12194
+
12107
12195
  _isHidden : function() {
12108
12196
  var s;
12109
12197
 
@@ -12126,6 +12214,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12126
12214
  selection = editor.selection,
12127
12215
  commands = {state: {}, exec : {}, value : {}},
12128
12216
  settings = editor.settings,
12217
+ formatter = editor.formatter,
12129
12218
  bookmark;
12130
12219
 
12131
12220
  function execCommand(command, ui, value) {
@@ -12191,11 +12280,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12191
12280
  };
12192
12281
 
12193
12282
  function isFormatMatch(name) {
12194
- return editor.formatter.match(name);
12283
+ return formatter.match(name);
12195
12284
  };
12196
12285
 
12197
12286
  function toggleFormat(name, value) {
12198
- editor.formatter.toggle(name, value ? {value : value} : undefined);
12287
+ formatter.toggle(name, value ? {value : value} : undefined);
12199
12288
  };
12200
12289
 
12201
12290
  function storeSelection(type) {
@@ -12255,7 +12344,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12255
12344
  // Remove all other alignments first
12256
12345
  each('left,center,right,full'.split(','), function(name) {
12257
12346
  if (align != name)
12258
- editor.formatter.remove('align' + name);
12347
+ formatter.remove('align' + name);
12259
12348
  });
12260
12349
 
12261
12350
  toggleFormat('align' + align);
@@ -12312,7 +12401,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12312
12401
  },
12313
12402
 
12314
12403
  RemoveFormat : function(command) {
12315
- editor.formatter.remove(command);
12404
+ formatter.remove(command);
12316
12405
  },
12317
12406
 
12318
12407
  mceBlockQuote : function(command) {
@@ -12535,7 +12624,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12535
12624
  },
12536
12625
 
12537
12626
  mceToggleFormat : function(command, ui, value) {
12538
- editor.formatter.toggle(value);
12627
+ formatter.toggle(value);
12539
12628
  },
12540
12629
 
12541
12630
  InsertHorizontalRule : function() {
@@ -12552,47 +12641,27 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
12552
12641
  },
12553
12642
 
12554
12643
  mceInsertLink : function(command, ui, value) {
12555
- var link = dom.getParent(selection.getNode(), 'a'), img, style, cls;
12644
+ var anchor;
12556
12645
 
12557
- if (tinymce.is(value, 'string'))
12646
+ if (typeof(value) == 'string')
12558
12647
  value = {href : value};
12559
12648
 
12649
+ anchor = dom.getParent(selection.getNode(), 'a');
12650
+
12560
12651
  // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here.
12561
12652
  value.href = value.href.replace(' ', '%20');
12562
12653
 
12563
- if (!link) {
12564
- // WebKit can't create links on floated images for some odd reason
12565
- // So, just remove styles and restore it later
12566
- if (tinymce.isWebKit) {
12567
- img = dom.getParent(selection.getNode(), 'img');
12568
-
12569
- if (img) {
12570
- style = img.style.cssText;
12571
- cls = img.className;
12572
- img.style.cssText = null;
12573
- img.className = null;
12574
- }
12575
- }
12576
-
12577
- execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);');
12654
+ // Remove existing links if there could be child links or that the href isn't specified
12655
+ if (!anchor || !value.href) {
12656
+ formatter.remove('link');
12657
+ }
12578
12658
 
12579
- // Restore styles
12580
- if (style)
12581
- img.style.cssText = style;
12582
- if (cls)
12583
- img.className = cls;
12584
-
12585
- each(dom.select("a[href='javascript:mctmp(0);']"), function(link) {
12586
- dom.setAttribs(link, value);
12587
- });
12588
- } else {
12589
- if (value.href)
12590
- dom.setAttribs(link, value);
12591
- else
12592
- editor.dom.remove(link, TRUE);
12659
+ // Apply new link to selection
12660
+ if (value.href) {
12661
+ formatter.apply('link', value, anchor);
12593
12662
  }
12594
12663
  },
12595
-
12664
+
12596
12665
  selectAll : function() {
12597
12666
  var root = dom.getRoot(), rng = dom.createRng();
12598
12667
 
@@ -13164,6 +13233,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
13164
13233
  ra.setStart(en, 0);
13165
13234
  }
13166
13235
 
13236
+ // If the body is totally empty add a BR element this might happen on webkit
13237
+ if (!d.body.hasChildNodes()) {
13238
+ d.body.appendChild(dom.create('br'));
13239
+ }
13240
+
13167
13241
  // Never use body as start or end node
13168
13242
  sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
13169
13243
  sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
@@ -14023,6 +14097,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14023
14097
  fmt = fmt || format;
14024
14098
 
14025
14099
  if (elm) {
14100
+ if (fmt.onformat) {
14101
+ fmt.onformat(elm, fmt, vars, node);
14102
+ }
14103
+
14026
14104
  each(fmt.styles, function(value, name) {
14027
14105
  dom.setStyle(elm, name, replaceVars(value, vars));
14028
14106
  });
@@ -14040,7 +14118,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14040
14118
  }
14041
14119
  };
14042
14120
  function adjustSelectionToVisibleSelection() {
14043
-
14044
14121
  function findSelectionEnd(start, end) {
14045
14122
  var walker = new TreeWalker(end);
14046
14123
  for (node = walker.current(); node; node = walker.prev()) {
@@ -14048,37 +14125,49 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14048
14125
  return node;
14049
14126
  }
14050
14127
  }
14051
- }
14128
+ };
14052
14129
 
14053
14130
  // Adjust selection so that a end container with a end offset of zero is not included in the selection
14054
14131
  // as this isn't visible to the user.
14055
14132
  var rng = ed.selection.getRng();
14056
14133
  var start = rng.startContainer;
14057
14134
  var end = rng.endContainer;
14135
+
14058
14136
  if (start != end && rng.endOffset == 0) {
14059
14137
  var newEnd = findSelectionEnd(start, end);
14060
14138
  var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
14139
+
14061
14140
  rng.setEnd(newEnd, endOffset);
14062
14141
  }
14142
+
14063
14143
  return rng;
14064
14144
  }
14065
14145
 
14066
14146
  function applyStyleToList(node, bookmark, wrapElm, newWrappers, process){
14067
- var nodes =[], listIndex =-1, list, startIndex = -1, endIndex = -1, currentWrapElm;
14147
+ var nodes = [], listIndex = -1, list, startIndex = -1, endIndex = -1, currentWrapElm;
14068
14148
 
14069
14149
  // find the index of the first child list.
14070
14150
  each(node.childNodes, function(n, index) {
14071
- if (n.nodeName==="UL"||n.nodeName==="OL") {listIndex = index; list=n; return false; }
14151
+ if (n.nodeName === "UL" || n.nodeName === "OL") {
14152
+ listIndex = index;
14153
+ list = n;
14154
+ return false;
14155
+ }
14072
14156
  });
14073
14157
 
14074
14158
  // get the index of the bookmarks
14075
14159
  each(node.childNodes, function(n, index) {
14076
- if (n.nodeName==="SPAN" &&dom.getAttrib(n, "data-mce-type")=="bookmark" && n.id==bookmark.id+"_start") {startIndex=index}
14077
- if (n.nodeName==="SPAN" &&dom.getAttrib(n, "data-mce-type")=="bookmark" && n.id==bookmark.id+"_end") {endIndex=index}
14160
+ if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
14161
+ if (n.id == bookmark.id + "_start") {
14162
+ startIndex = index;
14163
+ } else if (n.id == bookmark.id + "_end") {
14164
+ endIndex = index;
14165
+ }
14166
+ }
14078
14167
  });
14079
14168
 
14080
14169
  // if the selection spans across an embedded list, or there isn't an embedded list - handle processing normally
14081
- if (listIndex<=0 || (startIndex<listIndex&&endIndex>listIndex)) {
14170
+ if (listIndex <= 0 || (startIndex < listIndex && endIndex > listIndex)) {
14082
14171
  each(tinymce.grep(node.childNodes), process);
14083
14172
  return 0;
14084
14173
  } else {
@@ -14086,22 +14175,26 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14086
14175
 
14087
14176
  // create a list of the nodes on the same side of the list as the selection
14088
14177
  each(tinymce.grep(node.childNodes), function(n, index) {
14089
- if ((startIndex<listIndex && index <listIndex) || (startIndex>listIndex && index >listIndex)) {
14178
+ if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
14090
14179
  nodes.push(n);
14091
- n.parentNode.removeChild(n);
14180
+ n.parentNode.removeChild(n);
14092
14181
  }
14093
14182
  });
14094
14183
 
14095
14184
  // insert the wrapping element either before or after the list.
14096
- if (startIndex<listIndex) {
14185
+ if (startIndex < listIndex) {
14097
14186
  node.insertBefore(currentWrapElm, list);
14098
- } else if (startIndex>listIndex) {
14187
+ } else if (startIndex > listIndex) {
14099
14188
  node.insertBefore(currentWrapElm, list.nextSibling);
14100
14189
  }
14101
14190
 
14102
14191
  // add the new nodes to the list.
14103
14192
  newWrappers.push(currentWrapElm);
14104
- each(nodes, function(node){currentWrapElm.appendChild(node)});
14193
+
14194
+ each(nodes, function(node) {
14195
+ currentWrapElm.appendChild(node);
14196
+ });
14197
+
14105
14198
  return currentWrapElm;
14106
14199
  }
14107
14200
  };
@@ -14315,7 +14408,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14315
14408
  }
14316
14409
 
14317
14410
  // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
14318
- if (node) {
14411
+ if (node && format.merge_siblings !== false) {
14319
14412
  node = mergeSiblings(getNonWhiteSpaceSibling(node), node);
14320
14413
  node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE));
14321
14414
  }
@@ -14581,6 +14674,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
14581
14674
  function matchItems(node, format, item_name) {
14582
14675
  var key, value, items = format[item_name], i;
14583
14676
 
14677
+ // Custom match
14678
+ if (format.onmatch) {
14679
+ return format.onmatch(node, format, item_name);
14680
+ }
14681
+
14584
14682
  // Check all items
14585
14683
  if (items) {
14586
14684
  // Non indexed object