tinymce-rails 4.0.26 → 4.0.28

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.
@@ -1,4 +1,4 @@
1
- // 4.0.26 (2014-05-06)
1
+ // 4.0.28 (2014-05-27)
2
2
 
3
3
  /**
4
4
  * Compiled inline version. (Library mode)
@@ -97,6 +97,11 @@
97
97
  /*jshint loopfunc:true*/
98
98
  /*eslint no-loop-func:0 */
99
99
 
100
+ /**
101
+ * This class wraps the browsers native event logic with more convenient methods.
102
+ *
103
+ * @class tinymce.dom.EventUtils
104
+ */
100
105
  define("tinymce/dom/EventUtils", [], function() {
101
106
  "use strict";
102
107
 
@@ -657,7 +662,7 @@ define("tinymce/dom/EventUtils", [], function() {
657
662
  */
658
663
 
659
664
  /*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */
660
- /*eslint dot-notation:0, no-empty:0, no-cond-assign:0, no-unused-expressions:0, new-cap:0, no-nested-ternary:0, func-style:0, no-bitwise: 0 */
665
+ /*eslint dot-notation:0, no-empty:0, no-cond-assign:0, no-unused-expressions:0, new-cap:0, no-nested-ternary:0, func-style:0, no-bitwise:0, max-len:0, brace-style:0 */
661
666
 
662
667
  /*
663
668
  * Sizzle CSS Selector Engine
@@ -3344,10 +3349,15 @@ define("tinymce/html/Styles", [], function() {
3344
3349
  urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
3345
3350
  styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
3346
3351
  trimRightRegExp = /\s+$/,
3347
- undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF';
3352
+ undef, i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
3348
3353
 
3349
3354
  settings = settings || {};
3350
3355
 
3356
+ if (schema) {
3357
+ validStyles = schema.getValidStyles();
3358
+ invalidStyles = schema.getInvalidStyles();
3359
+ }
3360
+
3351
3361
  encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
3352
3362
  for (i = 0; i < encodingItems.length; i++) {
3353
3363
  encodingLookup[encodingItems[i]] = invisibleChar + i;
@@ -3605,16 +3615,16 @@ define("tinymce/html/Styles", [], function() {
3605
3615
  *
3606
3616
  * @method serialize
3607
3617
  * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
3608
- * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
3618
+ * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
3609
3619
  * @return {String} String representation of the style object for example: border: 1px solid red.
3610
3620
  */
3611
- serialize: function(styles, element_name) {
3621
+ serialize: function(styles, elementName) {
3612
3622
  var css = '', name, value;
3613
3623
 
3614
3624
  function serializeStyles(name) {
3615
3625
  var styleList, i, l, value;
3616
3626
 
3617
- styleList = schema.styles[name];
3627
+ styleList = validStyles[name];
3618
3628
  if (styleList) {
3619
3629
  for (i = 0, l = styleList.length; i < l; i++) {
3620
3630
  name = styleList[i];
@@ -3627,18 +3637,36 @@ define("tinymce/html/Styles", [], function() {
3627
3637
  }
3628
3638
  }
3629
3639
 
3640
+ function isValid(name, elementName) {
3641
+ var styleMap;
3642
+
3643
+ styleMap = invalidStyles['*'];
3644
+ if (styleMap && styleMap[name]) {
3645
+ return false;
3646
+ }
3647
+
3648
+ styleMap = invalidStyles[elementName];
3649
+ if (styleMap && styleMap[name]) {
3650
+ return false;
3651
+ }
3652
+
3653
+ return true;
3654
+ }
3655
+
3630
3656
  // Serialize styles according to schema
3631
- if (element_name && schema && schema.styles) {
3657
+ if (elementName && validStyles) {
3632
3658
  // Serialize global styles and element specific styles
3633
3659
  serializeStyles('*');
3634
- serializeStyles(element_name);
3660
+ serializeStyles(elementName);
3635
3661
  } else {
3636
3662
  // Output the styles in the order they are inside the object
3637
3663
  for (name in styles) {
3638
3664
  value = styles[name];
3639
3665
 
3640
3666
  if (value !== undef && value.length > 0) {
3641
- css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
3667
+ if (!invalidStyles || isValid(name, elementName)) {
3668
+ css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
3669
+ }
3642
3670
  }
3643
3671
  }
3644
3672
  }
@@ -5912,7 +5940,9 @@ define("tinymce/dom/DOMUtils", [
5912
5940
  selectorVal = selector;
5913
5941
 
5914
5942
  if (selector === '*') {
5915
- selector = function(node) {return node.nodeType == 1;};
5943
+ selector = function(node) {
5944
+ return node.nodeType == 1;
5945
+ };
5916
5946
  } else {
5917
5947
  selector = function(node) {
5918
5948
  return self.is(node, selectorVal);
@@ -6229,7 +6259,7 @@ define("tinymce/dom/DOMUtils", [
6229
6259
  });
6230
6260
 
6231
6261
  // Default px suffix on these
6232
- if (typeof(value) === 'number' && !numericCssMap[name]) {
6262
+ if (((typeof(value) === 'number') || /^[\-0-9\.]+$/.test(value)) && !numericCssMap[name]) {
6233
6263
  value += 'px';
6234
6264
  }
6235
6265
 
@@ -8990,7 +9020,8 @@ define("tinymce/html/Schema", [
8990
9020
  if (type != "html5-strict") {
8991
9021
  addAttrs("script", "language xml:space");
8992
9022
  addAttrs("style", "xml:space");
8993
- addAttrs("object", "declare classid codebase codetype archive standby align border hspace vspace");
9023
+ addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
9024
+ addAttrs("embed", "align name hspace vspace");
8994
9025
  addAttrs("param", "valuetype type");
8995
9026
  addAttrs("a", "charset name rev shape coords");
8996
9027
  addAttrs("br", "clear");
@@ -9059,6 +9090,27 @@ define("tinymce/html/Schema", [
9059
9090
  return schema;
9060
9091
  }
9061
9092
 
9093
+ function compileElementMap(value, mode) {
9094
+ var styles;
9095
+
9096
+ if (value) {
9097
+ styles = {};
9098
+
9099
+ if (typeof value == 'string') {
9100
+ value = {
9101
+ '*': value
9102
+ };
9103
+ }
9104
+
9105
+ // Convert styles into a rule list
9106
+ each(value, function(value, key) {
9107
+ styles[key] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/);
9108
+ });
9109
+ }
9110
+
9111
+ return styles;
9112
+ }
9113
+
9062
9114
  /**
9063
9115
  * Constructs a new Schema instance.
9064
9116
  *
@@ -9067,9 +9119,10 @@ define("tinymce/html/Schema", [
9067
9119
  * @param {Object} settings Name/value settings object.
9068
9120
  */
9069
9121
  return function(settings) {
9070
- var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
9071
- var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap;
9072
- var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, customElementsMap = {}, specialElements = {};
9122
+ var self = this, elements = {}, children = {}, patternElements = [], validStyles, invalidStyles, schemaItems;
9123
+ var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses;
9124
+ var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, textInlineElementsMap;
9125
+ var customElementsMap = {}, specialElements = {};
9073
9126
 
9074
9127
  // Creates an lookup table map object for the specified option or the default value
9075
9128
  function createLookupTable(option, default_value, extendWith) {
@@ -9101,15 +9154,9 @@ define("tinymce/html/Schema", [
9101
9154
  settings.valid_elements = '*[*]';
9102
9155
  }
9103
9156
 
9104
- // Build styles list
9105
- if (settings.valid_styles) {
9106
- validStyles = {};
9107
-
9108
- // Convert styles into a rule list
9109
- each(settings.valid_styles, function(value, key) {
9110
- validStyles[key] = explode(value);
9111
- });
9112
- }
9157
+ validStyles = compileElementMap(settings.valid_styles);
9158
+ invalidStyles = compileElementMap(settings.invalid_styles, 'map');
9159
+ validClasses = compileElementMap(settings.valid_classes, 'map');
9113
9160
 
9114
9161
  // Setup map objects
9115
9162
  whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
@@ -9124,6 +9171,8 @@ define("tinymce/html/Schema", [
9124
9171
  blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
9125
9172
  'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
9126
9173
  'datalist select optgroup', textBlockElementsMap);
9174
+ textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
9175
+ 'dfn code mark q sup sub samp');
9127
9176
 
9128
9177
  each((settings.special || 'script noscript style textarea').split(' '), function(name) {
9129
9178
  specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
@@ -9480,10 +9529,32 @@ define("tinymce/html/Schema", [
9480
9529
  /**
9481
9530
  * Name/value map object with valid styles for each element.
9482
9531
  *
9483
- * @field styles
9532
+ * @method getValidStyles
9533
+ * @type Object
9534
+ */
9535
+ self.getValidStyles = function() {
9536
+ return validStyles;
9537
+ };
9538
+
9539
+ /**
9540
+ * Name/value map object with valid styles for each element.
9541
+ *
9542
+ * @method getInvalidStyles
9484
9543
  * @type Object
9485
9544
  */
9486
- self.styles = validStyles;
9545
+ self.getInvalidStyles = function() {
9546
+ return invalidStyles;
9547
+ };
9548
+
9549
+ /**
9550
+ * Name/value map object with valid classes for each element.
9551
+ *
9552
+ * @method getValidClasses
9553
+ * @type Object
9554
+ */
9555
+ self.getValidClasses = function() {
9556
+ return validClasses;
9557
+ };
9487
9558
 
9488
9559
  /**
9489
9560
  * Returns a map with boolean attributes.
@@ -9515,6 +9586,16 @@ define("tinymce/html/Schema", [
9515
9586
  return textBlockElementsMap;
9516
9587
  };
9517
9588
 
9589
+ /**
9590
+ * Returns a map of inline text format nodes for example strong/span or ins.
9591
+ *
9592
+ * @method getTextInlineElements
9593
+ * @return {Object} Name/value lookup map for text format elements.
9594
+ */
9595
+ self.getTextInlineElements = function() {
9596
+ return textInlineElementsMap;
9597
+ };
9598
+
9518
9599
  /**
9519
9600
  * Returns a map with short ended elements such as BR or IMG.
9520
9601
  *
@@ -9744,6 +9825,36 @@ define("tinymce/html/SaxParser", [
9744
9825
  ], function(Schema, Entities, Tools) {
9745
9826
  var each = Tools.each;
9746
9827
 
9828
+ /**
9829
+ * Returns the index of the end tag for a specific start tag. This can be
9830
+ * used to skip all children of a parent element from being processed.
9831
+ */
9832
+ function skipUntilEndTag(schema, html, startIndex) {
9833
+ var count = 1, matches, tokenRegExp, shortEndedElements;
9834
+
9835
+ shortEndedElements = schema.getShortEndedElements();
9836
+ tokenRegExp = /<([!?\/])?([A-Za-z0-9\-\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
9837
+ tokenRegExp.lastIndex = startIndex;
9838
+
9839
+ while ((matches = tokenRegExp.exec(html))) {
9840
+ if (matches[1] === '/') { // End element
9841
+ count--;
9842
+ } else if (!matches[1]) { // Start element
9843
+ if (matches[2] in shortEndedElements) {
9844
+ continue;
9845
+ }
9846
+
9847
+ count++;
9848
+ }
9849
+
9850
+ if (count === 0) {
9851
+ break;
9852
+ }
9853
+ }
9854
+
9855
+ return tokenRegExp.lastIndex;
9856
+ }
9857
+
9747
9858
  /**
9748
9859
  * Constructs a new SaxParser instance.
9749
9860
  *
@@ -10025,7 +10136,13 @@ define("tinymce/html/SaxParser", [
10025
10136
  }
10026
10137
 
10027
10138
  // Invalidate element if it's marked as bogus
10028
- if (attrList.map['data-mce-bogus']) {
10139
+ if ((attr = attrList.map['data-mce-bogus'])) {
10140
+ if (attr === 'all') {
10141
+ index = skipUntilEndTag(schema, html, tokenRegExp.lastIndex);
10142
+ tokenRegExp.lastIndex = index;
10143
+ continue;
10144
+ }
10145
+
10029
10146
  isValidElement = false;
10030
10147
  }
10031
10148
  }
@@ -10688,7 +10805,7 @@ define("tinymce/html/DomParser", [
10688
10805
  // Leave nodes that have a name like <a name="name">
10689
10806
  if (!node.attributes.map.name && !node.attributes.map.id) {
10690
10807
  tempNode = node.parent;
10691
- node.empty().remove();
10808
+ node.unwrap();
10692
10809
  node = tempNode;
10693
10810
  return;
10694
10811
  }
@@ -10867,6 +10984,48 @@ define("tinymce/html/DomParser", [
10867
10984
  }
10868
10985
  });
10869
10986
  }
10987
+
10988
+ if (settings.validate && schema.getValidClasses()) {
10989
+ self.addAttributeFilter('class', function(nodes) {
10990
+ var i = nodes.length, node, classList, ci, className, classValue;
10991
+ var validClasses = schema.getValidClasses(), validClassesMap, valid;
10992
+
10993
+ while (i--) {
10994
+ node = nodes[i];
10995
+ classList = node.attr('class').split(' ');
10996
+ classValue = '';
10997
+
10998
+ for (ci = 0; ci < classList.length; ci++) {
10999
+ className = classList[ci];
11000
+ valid = false;
11001
+
11002
+ validClassesMap = validClasses['*'];
11003
+ if (validClassesMap && validClassesMap[className]) {
11004
+ valid = true;
11005
+ }
11006
+
11007
+ validClassesMap = validClasses[node.name];
11008
+ if (!valid && validClassesMap && !validClassesMap[className]) {
11009
+ valid = true;
11010
+ }
11011
+
11012
+ if (valid) {
11013
+ if (classValue) {
11014
+ classValue += ' ';
11015
+ }
11016
+
11017
+ classValue += className;
11018
+ }
11019
+ }
11020
+
11021
+ if (!classValue.length) {
11022
+ classValue = null;
11023
+ }
11024
+
11025
+ node.attr('class', classValue);
11026
+ }
11027
+ });
11028
+ }
10870
11029
  };
10871
11030
  });
10872
11031
 
@@ -11349,15 +11508,6 @@ define("tinymce/dom/Serializer", [
11349
11508
  }
11350
11509
  });
11351
11510
 
11352
- // Remove expando attributes
11353
- htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name) {
11354
- var i = nodes.length;
11355
-
11356
- while (i--) {
11357
- nodes[i].attr(name, null);
11358
- }
11359
- });
11360
-
11361
11511
  htmlParser.addNodeFilter('noscript', function(nodes) {
11362
11512
  var i = nodes.length, node;
11363
11513
 
@@ -11372,7 +11522,7 @@ define("tinymce/dom/Serializer", [
11372
11522
 
11373
11523
  // Force script into CDATA sections and remove the mce- prefix also add comments around styles
11374
11524
  htmlParser.addNodeFilter('script,style', function(nodes, name) {
11375
- var i = nodes.length, node, value;
11525
+ var i = nodes.length, node, value, type;
11376
11526
 
11377
11527
  function trim(value) {
11378
11528
  /*jshint maxlen:255 */
@@ -11388,9 +11538,12 @@ define("tinymce/dom/Serializer", [
11388
11538
  value = node.firstChild ? node.firstChild.value : '';
11389
11539
 
11390
11540
  if (name === "script") {
11391
- // Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
11392
- var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
11393
- node.attr('type', type === 'text/javascript' ? null : type);
11541
+ // Remove mce- prefix from script elements and remove default type since the user specified
11542
+ // a script element without type attribute
11543
+ type = node.attr('type');
11544
+ if (type) {
11545
+ node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, ''));
11546
+ }
11394
11547
 
11395
11548
  if (value.length > 0) {
11396
11549
  node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
@@ -11457,13 +11610,19 @@ define("tinymce/dom/Serializer", [
11457
11610
  }
11458
11611
 
11459
11612
  // Remove internal data attributes
11460
- htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,data-mce-selected', function(nodes, name) {
11461
- var i = nodes.length;
11613
+ htmlParser.addAttributeFilter(
11614
+ 'data-mce-src,data-mce-href,data-mce-style,' +
11615
+ 'data-mce-selected,data-mce-expando,' +
11616
+ 'data-mce-type,data-mce-resize',
11462
11617
 
11463
- while (i--) {
11464
- nodes[i].attr(name, null);
11618
+ function(nodes, name) {
11619
+ var i = nodes.length;
11620
+
11621
+ while (i--) {
11622
+ nodes[i].attr(name, null);
11623
+ }
11465
11624
  }
11466
- });
11625
+ );
11467
11626
 
11468
11627
  // Return public methods
11469
11628
  return {
@@ -11799,15 +11958,17 @@ define("tinymce/dom/TridentSelection", [], function() {
11799
11958
 
11800
11959
  // Find the text node and offset
11801
11960
  while (sibling) {
11802
- nodeValue = sibling.nodeValue;
11803
- textNodeOffset += nodeValue.length;
11804
-
11805
- // We are at or passed the position we where looking for
11806
- if (textNodeOffset >= offset) {
11807
- container = sibling;
11808
- textNodeOffset -= offset;
11809
- textNodeOffset = nodeValue.length - textNodeOffset;
11810
- break;
11961
+ if (sibling.nodeType == 3) {
11962
+ nodeValue = sibling.nodeValue;
11963
+ textNodeOffset += nodeValue.length;
11964
+
11965
+ // We are at or passed the position we where looking for
11966
+ if (textNodeOffset >= offset) {
11967
+ container = sibling;
11968
+ textNodeOffset -= offset;
11969
+ textNodeOffset = nodeValue.length - textNodeOffset;
11970
+ break;
11971
+ }
11811
11972
  }
11812
11973
 
11813
11974
  sibling = sibling.nextSibling;
@@ -11832,13 +11993,15 @@ define("tinymce/dom/TridentSelection", [], function() {
11832
11993
  }
11833
11994
 
11834
11995
  while (sibling) {
11835
- textNodeOffset += sibling.nodeValue.length;
11996
+ if (sibling.nodeType == 3) {
11997
+ textNodeOffset += sibling.nodeValue.length;
11836
11998
 
11837
- // We are at or passed the position we where looking for
11838
- if (textNodeOffset >= offset) {
11839
- container = sibling;
11840
- textNodeOffset -= offset;
11841
- break;
11999
+ // We are at or passed the position we where looking for
12000
+ if (textNodeOffset >= offset) {
12001
+ container = sibling;
12002
+ textNodeOffset -= offset;
12003
+ break;
12004
+ }
11842
12005
  }
11843
12006
 
11844
12007
  sibling = sibling.previousSibling;
@@ -12171,8 +12334,8 @@ define("tinymce/util/VK", [
12171
12334
  },
12172
12335
 
12173
12336
  metaKeyPressed: function(e) {
12174
- // Check if ctrl or meta key is pressed also check if alt is false for Polish users
12175
- return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
12337
+ // Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states
12338
+ return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey);
12176
12339
  }
12177
12340
  };
12178
12341
  });
@@ -12704,7 +12867,7 @@ define("tinymce/dom/ControlSelection", [
12704
12867
  // Included from: js/tinymce/classes/dom/RangeUtils.js
12705
12868
 
12706
12869
  /**
12707
- * Range.js
12870
+ * RangeUtils.js
12708
12871
  *
12709
12872
  * Copyright, Moxiecode Systems AB
12710
12873
  * Released under LGPL License.
@@ -12714,7 +12877,7 @@ define("tinymce/dom/ControlSelection", [
12714
12877
  */
12715
12878
 
12716
12879
  /**
12717
- * RangeUtils
12880
+ * This class contains a few utility methods for ranges.
12718
12881
  *
12719
12882
  * @class tinymce.dom.RangeUtils
12720
12883
  * @private
@@ -12725,6 +12888,20 @@ define("tinymce/dom/RangeUtils", [
12725
12888
  ], function(Tools, TreeWalker) {
12726
12889
  var each = Tools.each;
12727
12890
 
12891
+ function getEndChild(container, index) {
12892
+ var childNodes = container.childNodes;
12893
+
12894
+ index--;
12895
+
12896
+ if (index > childNodes.length - 1) {
12897
+ index = childNodes.length - 1;
12898
+ } else if (index < 0) {
12899
+ index = 0;
12900
+ }
12901
+
12902
+ return childNodes[index] || container;
12903
+ }
12904
+
12728
12905
  function RangeUtils(dom) {
12729
12906
  /**
12730
12907
  * Walks the specified range like object and executes the callback for each sibling collection it finds.
@@ -12837,7 +13014,7 @@ define("tinymce/dom/RangeUtils", [
12837
13014
 
12838
13015
  // If index based end position then resolve it
12839
13016
  if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
12840
- endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
13017
+ endContainer = getEndChild(endContainer, endOffset);
12841
13018
  }
12842
13019
 
12843
13020
  // Same container
@@ -13184,6 +13361,398 @@ define("tinymce/dom/RangeUtils", [
13184
13361
  return RangeUtils;
13185
13362
  });
13186
13363
 
13364
+ // Included from: js/tinymce/classes/dom/BookmarkManager.js
13365
+
13366
+ /**
13367
+ * BookmarkManager.js
13368
+ *
13369
+ * Copyright, Moxiecode Systems AB
13370
+ * Released under LGPL License.
13371
+ *
13372
+ * License: http://www.tinymce.com/license
13373
+ * Contributing: http://www.tinymce.com/contributing
13374
+ */
13375
+
13376
+ /**
13377
+ * This class handles selection bookmarks.
13378
+ *
13379
+ * @class tinymce.dom.BookmarkManager
13380
+ */
13381
+ define("tinymce/dom/BookmarkManager", [
13382
+ "tinymce/Env",
13383
+ "tinymce/util/Tools"
13384
+ ], function(Env, Tools) {
13385
+ /**
13386
+ * Constructs a new BookmarkManager instance for a specific selection instance.
13387
+ *
13388
+ * @constructor
13389
+ * @method BookmarkManager
13390
+ * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for.
13391
+ */
13392
+ function BookmarkManager(selection) {
13393
+ var dom = selection.dom;
13394
+
13395
+ /**
13396
+ * Returns a bookmark location for the current selection. This bookmark object
13397
+ * can then be used to restore the selection after some content modification to the document.
13398
+ *
13399
+ * @method getBookmark
13400
+ * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
13401
+ * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
13402
+ * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
13403
+ * @example
13404
+ * // Stores a bookmark of the current selection
13405
+ * var bm = tinymce.activeEditor.selection.getBookmark();
13406
+ *
13407
+ * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
13408
+ *
13409
+ * // Restore the selection bookmark
13410
+ * tinymce.activeEditor.selection.moveToBookmark(bm);
13411
+ */
13412
+ this.getBookmark = function(type, normalized) {
13413
+ var rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
13414
+
13415
+ function findIndex(name, element) {
13416
+ var index = 0;
13417
+
13418
+ Tools.each(dom.select(name), function(node, i) {
13419
+ if (node == element) {
13420
+ index = i;
13421
+ }
13422
+ });
13423
+
13424
+ return index;
13425
+ }
13426
+
13427
+ function normalizeTableCellSelection(rng) {
13428
+ function moveEndPoint(start) {
13429
+ var container, offset, childNodes, prefix = start ? 'start' : 'end';
13430
+
13431
+ container = rng[prefix + 'Container'];
13432
+ offset = rng[prefix + 'Offset'];
13433
+
13434
+ if (container.nodeType == 1 && container.nodeName == "TR") {
13435
+ childNodes = container.childNodes;
13436
+ container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
13437
+ if (container) {
13438
+ offset = start ? 0 : container.childNodes.length;
13439
+ rng['set' + (start ? 'Start' : 'End')](container, offset);
13440
+ }
13441
+ }
13442
+ }
13443
+
13444
+ moveEndPoint(true);
13445
+ moveEndPoint();
13446
+
13447
+ return rng;
13448
+ }
13449
+
13450
+ function getLocation() {
13451
+ var rng = selection.getRng(true), root = dom.getRoot(), bookmark = {};
13452
+
13453
+ function getPoint(rng, start) {
13454
+ var container = rng[start ? 'startContainer' : 'endContainer'],
13455
+ offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
13456
+
13457
+ if (container.nodeType == 3) {
13458
+ if (normalized) {
13459
+ for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
13460
+ offset += node.nodeValue.length;
13461
+ }
13462
+ }
13463
+
13464
+ point.push(offset);
13465
+ } else {
13466
+ childNodes = container.childNodes;
13467
+
13468
+ if (offset >= childNodes.length && childNodes.length) {
13469
+ after = 1;
13470
+ offset = Math.max(0, childNodes.length - 1);
13471
+ }
13472
+
13473
+ point.push(dom.nodeIndex(childNodes[offset], normalized) + after);
13474
+ }
13475
+
13476
+ for (; container && container != root; container = container.parentNode) {
13477
+ point.push(dom.nodeIndex(container, normalized));
13478
+ }
13479
+
13480
+ return point;
13481
+ }
13482
+
13483
+ bookmark.start = getPoint(rng, true);
13484
+
13485
+ if (!selection.isCollapsed()) {
13486
+ bookmark.end = getPoint(rng);
13487
+ }
13488
+
13489
+ return bookmark;
13490
+ }
13491
+
13492
+ if (type == 2) {
13493
+ element = selection.getNode();
13494
+ name = element ? element.nodeName : null;
13495
+
13496
+ if (name == 'IMG') {
13497
+ return {name: name, index: findIndex(name, element)};
13498
+ }
13499
+
13500
+ if (selection.tridentSel) {
13501
+ return selection.tridentSel.getBookmark(type);
13502
+ }
13503
+
13504
+ return getLocation();
13505
+ }
13506
+
13507
+ // Handle simple range
13508
+ if (type) {
13509
+ return {rng: selection.getRng()};
13510
+ }
13511
+
13512
+ rng = selection.getRng();
13513
+ id = dom.uniqueId();
13514
+ collapsed = selection.isCollapsed();
13515
+ styles = 'overflow:hidden;line-height:0px';
13516
+
13517
+ // Explorer method
13518
+ if (rng.duplicate || rng.item) {
13519
+ // Text selection
13520
+ if (!rng.item) {
13521
+ rng2 = rng.duplicate();
13522
+
13523
+ try {
13524
+ // Insert start marker
13525
+ rng.collapse();
13526
+ rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
13527
+
13528
+ // Insert end marker
13529
+ if (!collapsed) {
13530
+ rng2.collapse(false);
13531
+
13532
+ // Detect the empty space after block elements in IE and move the
13533
+ // end back one character <p></p>] becomes <p>]</p>
13534
+ rng.moveToElementText(rng2.parentElement());
13535
+ if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
13536
+ rng2.move('character', -1);
13537
+ }
13538
+
13539
+ rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
13540
+ }
13541
+ } catch (ex) {
13542
+ // IE might throw unspecified error so lets ignore it
13543
+ return null;
13544
+ }
13545
+ } else {
13546
+ // Control selection
13547
+ element = rng.item(0);
13548
+ name = element.nodeName;
13549
+
13550
+ return {name: name, index: findIndex(name, element)};
13551
+ }
13552
+ } else {
13553
+ element = selection.getNode();
13554
+ name = element.nodeName;
13555
+ if (name == 'IMG') {
13556
+ return {name: name, index: findIndex(name, element)};
13557
+ }
13558
+
13559
+ // W3C method
13560
+ rng2 = normalizeTableCellSelection(rng.cloneRange());
13561
+
13562
+ // Insert end marker
13563
+ if (!collapsed) {
13564
+ rng2.collapse(false);
13565
+ rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
13566
+ }
13567
+
13568
+ rng = normalizeTableCellSelection(rng);
13569
+ rng.collapse(true);
13570
+ rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
13571
+ }
13572
+
13573
+ selection.moveToBookmark({id: id, keep: 1});
13574
+
13575
+ return {id: id};
13576
+ };
13577
+
13578
+ /**
13579
+ * Restores the selection to the specified bookmark.
13580
+ *
13581
+ * @method moveToBookmark
13582
+ * @param {Object} bookmark Bookmark to restore selection from.
13583
+ * @return {Boolean} true/false if it was successful or not.
13584
+ * @example
13585
+ * // Stores a bookmark of the current selection
13586
+ * var bm = tinymce.activeEditor.selection.getBookmark();
13587
+ *
13588
+ * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
13589
+ *
13590
+ * // Restore the selection bookmark
13591
+ * tinymce.activeEditor.selection.moveToBookmark(bm);
13592
+ */
13593
+ this.moveToBookmark = function(bookmark) {
13594
+ var rng, root, startContainer, endContainer, startOffset, endOffset;
13595
+
13596
+ function setEndPoint(start) {
13597
+ var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
13598
+
13599
+ if (point) {
13600
+ offset = point[0];
13601
+
13602
+ // Find container node
13603
+ for (node = root, i = point.length - 1; i >= 1; i--) {
13604
+ children = node.childNodes;
13605
+
13606
+ if (point[i] > children.length - 1) {
13607
+ return;
13608
+ }
13609
+
13610
+ node = children[point[i]];
13611
+ }
13612
+
13613
+ // Move text offset to best suitable location
13614
+ if (node.nodeType === 3) {
13615
+ offset = Math.min(point[0], node.nodeValue.length);
13616
+ }
13617
+
13618
+ // Move element offset to best suitable location
13619
+ if (node.nodeType === 1) {
13620
+ offset = Math.min(point[0], node.childNodes.length);
13621
+ }
13622
+
13623
+ // Set offset within container node
13624
+ if (start) {
13625
+ rng.setStart(node, offset);
13626
+ } else {
13627
+ rng.setEnd(node, offset);
13628
+ }
13629
+ }
13630
+
13631
+ return true;
13632
+ }
13633
+
13634
+ function restoreEndPoint(suffix) {
13635
+ var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
13636
+
13637
+ if (marker) {
13638
+ node = marker.parentNode;
13639
+
13640
+ if (suffix == 'start') {
13641
+ if (!keep) {
13642
+ idx = dom.nodeIndex(marker);
13643
+ } else {
13644
+ node = marker.firstChild;
13645
+ idx = 1;
13646
+ }
13647
+
13648
+ startContainer = endContainer = node;
13649
+ startOffset = endOffset = idx;
13650
+ } else {
13651
+ if (!keep) {
13652
+ idx = dom.nodeIndex(marker);
13653
+ } else {
13654
+ node = marker.firstChild;
13655
+ idx = 1;
13656
+ }
13657
+
13658
+ endContainer = node;
13659
+ endOffset = idx;
13660
+ }
13661
+
13662
+ if (!keep) {
13663
+ prev = marker.previousSibling;
13664
+ next = marker.nextSibling;
13665
+
13666
+ // Remove all marker text nodes
13667
+ Tools.each(Tools.grep(marker.childNodes), function(node) {
13668
+ if (node.nodeType == 3) {
13669
+ node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
13670
+ }
13671
+ });
13672
+
13673
+ // Remove marker but keep children if for example contents where inserted into the marker
13674
+ // Also remove duplicated instances of the marker for example by a
13675
+ // split operation or by WebKit auto split on paste feature
13676
+ while ((marker = dom.get(bookmark.id + '_' + suffix))) {
13677
+ dom.remove(marker, 1);
13678
+ }
13679
+
13680
+ // If siblings are text nodes then merge them unless it's Opera since it some how removes the node
13681
+ // and we are sniffing since adding a lot of detection code for a browser with 3% of the market
13682
+ // isn't worth the effort. Sorry, Opera but it's just a fact
13683
+ if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) {
13684
+ idx = prev.nodeValue.length;
13685
+ prev.appendData(next.nodeValue);
13686
+ dom.remove(next);
13687
+
13688
+ if (suffix == 'start') {
13689
+ startContainer = endContainer = prev;
13690
+ startOffset = endOffset = idx;
13691
+ } else {
13692
+ endContainer = prev;
13693
+ endOffset = idx;
13694
+ }
13695
+ }
13696
+ }
13697
+ }
13698
+ }
13699
+
13700
+ function addBogus(node) {
13701
+ // Adds a bogus BR element for empty block elements
13702
+ if (dom.isBlock(node) && !node.innerHTML && !Env.ie) {
13703
+ node.innerHTML = '<br data-mce-bogus="1" />';
13704
+ }
13705
+
13706
+ return node;
13707
+ }
13708
+
13709
+ if (bookmark) {
13710
+ if (bookmark.start) {
13711
+ rng = dom.createRng();
13712
+ root = dom.getRoot();
13713
+
13714
+ if (selection.tridentSel) {
13715
+ return selection.tridentSel.moveToBookmark(bookmark);
13716
+ }
13717
+
13718
+ if (setEndPoint(true) && setEndPoint()) {
13719
+ selection.setRng(rng);
13720
+ }
13721
+ } else if (bookmark.id) {
13722
+ // Restore start/end points
13723
+ restoreEndPoint('start');
13724
+ restoreEndPoint('end');
13725
+
13726
+ if (startContainer) {
13727
+ rng = dom.createRng();
13728
+ rng.setStart(addBogus(startContainer), startOffset);
13729
+ rng.setEnd(addBogus(endContainer), endOffset);
13730
+ selection.setRng(rng);
13731
+ }
13732
+ } else if (bookmark.name) {
13733
+ selection.select(dom.select(bookmark.name)[bookmark.index]);
13734
+ } else if (bookmark.rng) {
13735
+ selection.setRng(bookmark.rng);
13736
+ }
13737
+ }
13738
+ };
13739
+ }
13740
+
13741
+ /**
13742
+ * Returns true/false if the specified node is a bookmark node or not.
13743
+ *
13744
+ * @static
13745
+ * @method isBookmarkNode
13746
+ * @param {DOMNode} node DOM Node to check if it's a bookmark node or not.
13747
+ * @return {Boolean} true/false if the node is a bookmark node or not.
13748
+ */
13749
+ BookmarkManager.isBookmarkNode = function(node) {
13750
+ return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark';
13751
+ };
13752
+
13753
+ return BookmarkManager;
13754
+ });
13755
+
13187
13756
  // Included from: js/tinymce/classes/dom/Selection.js
13188
13757
 
13189
13758
  /**
@@ -13210,11 +13779,12 @@ define("tinymce/dom/Selection", [
13210
13779
  "tinymce/dom/TridentSelection",
13211
13780
  "tinymce/dom/ControlSelection",
13212
13781
  "tinymce/dom/RangeUtils",
13782
+ "tinymce/dom/BookmarkManager",
13213
13783
  "tinymce/Env",
13214
13784
  "tinymce/util/Tools"
13215
- ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, Env, Tools) {
13216
- var each = Tools.each, grep = Tools.grep, trim = Tools.trim;
13217
- var isIE = Env.ie, isOpera = Env.opera;
13785
+ ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, Env, Tools) {
13786
+ var each = Tools.each, trim = Tools.trim;
13787
+ var isIE = Env.ie;
13218
13788
 
13219
13789
  /**
13220
13790
  * Constructs a new selection instance.
@@ -13232,7 +13802,7 @@ define("tinymce/dom/Selection", [
13232
13802
  self.win = win;
13233
13803
  self.serializer = serializer;
13234
13804
  self.editor = editor;
13235
-
13805
+ self.bookmarkManager = new BookmarkManager(self);
13236
13806
  self.controlSelection = new ControlSelection(self, editor);
13237
13807
 
13238
13808
  // No W3C Range support
@@ -13532,169 +14102,7 @@ define("tinymce/dom/Selection", [
13532
14102
  * tinymce.activeEditor.selection.moveToBookmark(bm);
13533
14103
  */
13534
14104
  getBookmark: function(type, normalized) {
13535
- var self = this, dom = self.dom, rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
13536
-
13537
- function findIndex(name, element) {
13538
- var index = 0;
13539
-
13540
- each(dom.select(name), function(node, i) {
13541
- if (node == element) {
13542
- index = i;
13543
- }
13544
- });
13545
-
13546
- return index;
13547
- }
13548
-
13549
- function normalizeTableCellSelection(rng) {
13550
- function moveEndPoint(start) {
13551
- var container, offset, childNodes, prefix = start ? 'start' : 'end';
13552
-
13553
- container = rng[prefix + 'Container'];
13554
- offset = rng[prefix + 'Offset'];
13555
-
13556
- if (container.nodeType == 1 && container.nodeName == "TR") {
13557
- childNodes = container.childNodes;
13558
- container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
13559
- if (container) {
13560
- offset = start ? 0 : container.childNodes.length;
13561
- rng['set' + (start ? 'Start' : 'End')](container, offset);
13562
- }
13563
- }
13564
- }
13565
-
13566
- moveEndPoint(true);
13567
- moveEndPoint();
13568
-
13569
- return rng;
13570
- }
13571
-
13572
- function getLocation() {
13573
- var rng = self.getRng(true), root = dom.getRoot(), bookmark = {};
13574
-
13575
- function getPoint(rng, start) {
13576
- var container = rng[start ? 'startContainer' : 'endContainer'],
13577
- offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
13578
-
13579
- if (container.nodeType == 3) {
13580
- if (normalized) {
13581
- for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
13582
- offset += node.nodeValue.length;
13583
- }
13584
- }
13585
-
13586
- point.push(offset);
13587
- } else {
13588
- childNodes = container.childNodes;
13589
-
13590
- if (offset >= childNodes.length && childNodes.length) {
13591
- after = 1;
13592
- offset = Math.max(0, childNodes.length - 1);
13593
- }
13594
-
13595
- point.push(self.dom.nodeIndex(childNodes[offset], normalized) + after);
13596
- }
13597
-
13598
- for (; container && container != root; container = container.parentNode) {
13599
- point.push(self.dom.nodeIndex(container, normalized));
13600
- }
13601
-
13602
- return point;
13603
- }
13604
-
13605
- bookmark.start = getPoint(rng, true);
13606
-
13607
- if (!self.isCollapsed()) {
13608
- bookmark.end = getPoint(rng);
13609
- }
13610
-
13611
- return bookmark;
13612
- }
13613
-
13614
- if (type == 2) {
13615
- element = self.getNode();
13616
- name = element ? element.nodeName : null;
13617
-
13618
- if (name == 'IMG') {
13619
- return {name: name, index: findIndex(name, element)};
13620
- }
13621
-
13622
- if (self.tridentSel) {
13623
- return self.tridentSel.getBookmark(type);
13624
- }
13625
-
13626
- return getLocation();
13627
- }
13628
-
13629
- // Handle simple range
13630
- if (type) {
13631
- return {rng: self.getRng()};
13632
- }
13633
-
13634
- rng = self.getRng();
13635
- id = dom.uniqueId();
13636
- collapsed = self.isCollapsed();
13637
- styles = 'overflow:hidden;line-height:0px';
13638
-
13639
- // Explorer method
13640
- if (rng.duplicate || rng.item) {
13641
- // Text selection
13642
- if (!rng.item) {
13643
- rng2 = rng.duplicate();
13644
-
13645
- try {
13646
- // Insert start marker
13647
- rng.collapse();
13648
- rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
13649
-
13650
- // Insert end marker
13651
- if (!collapsed) {
13652
- rng2.collapse(false);
13653
-
13654
- // Detect the empty space after block elements in IE and move the
13655
- // end back one character <p></p>] becomes <p>]</p>
13656
- rng.moveToElementText(rng2.parentElement());
13657
- if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
13658
- rng2.move('character', -1);
13659
- }
13660
-
13661
- rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
13662
- }
13663
- } catch (ex) {
13664
- // IE might throw unspecified error so lets ignore it
13665
- return null;
13666
- }
13667
- } else {
13668
- // Control selection
13669
- element = rng.item(0);
13670
- name = element.nodeName;
13671
-
13672
- return {name: name, index: findIndex(name, element)};
13673
- }
13674
- } else {
13675
- element = self.getNode();
13676
- name = element.nodeName;
13677
- if (name == 'IMG') {
13678
- return {name: name, index: findIndex(name, element)};
13679
- }
13680
-
13681
- // W3C method
13682
- rng2 = normalizeTableCellSelection(rng.cloneRange());
13683
-
13684
- // Insert end marker
13685
- if (!collapsed) {
13686
- rng2.collapse(false);
13687
- rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
13688
- }
13689
-
13690
- rng = normalizeTableCellSelection(rng);
13691
- rng.collapse(true);
13692
- rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
13693
- }
13694
-
13695
- self.moveToBookmark({id: id, keep: 1});
13696
-
13697
- return {id: id};
14105
+ return this.bookmarkManager.getBookmark(type, normalized);
13698
14106
  },
13699
14107
 
13700
14108
  /**
@@ -13713,150 +14121,7 @@ define("tinymce/dom/Selection", [
13713
14121
  * tinymce.activeEditor.selection.moveToBookmark(bm);
13714
14122
  */
13715
14123
  moveToBookmark: function(bookmark) {
13716
- var self = this, dom = self.dom, rng, root, startContainer, endContainer, startOffset, endOffset;
13717
-
13718
- function setEndPoint(start) {
13719
- var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
13720
-
13721
- if (point) {
13722
- offset = point[0];
13723
-
13724
- // Find container node
13725
- for (node = root, i = point.length - 1; i >= 1; i--) {
13726
- children = node.childNodes;
13727
-
13728
- if (point[i] > children.length - 1) {
13729
- return;
13730
- }
13731
-
13732
- node = children[point[i]];
13733
- }
13734
-
13735
- // Move text offset to best suitable location
13736
- if (node.nodeType === 3) {
13737
- offset = Math.min(point[0], node.nodeValue.length);
13738
- }
13739
-
13740
- // Move element offset to best suitable location
13741
- if (node.nodeType === 1) {
13742
- offset = Math.min(point[0], node.childNodes.length);
13743
- }
13744
-
13745
- // Set offset within container node
13746
- if (start) {
13747
- rng.setStart(node, offset);
13748
- } else {
13749
- rng.setEnd(node, offset);
13750
- }
13751
- }
13752
-
13753
- return true;
13754
- }
13755
-
13756
- function restoreEndPoint(suffix) {
13757
- var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
13758
-
13759
- if (marker) {
13760
- node = marker.parentNode;
13761
-
13762
- if (suffix == 'start') {
13763
- if (!keep) {
13764
- idx = dom.nodeIndex(marker);
13765
- } else {
13766
- node = marker.firstChild;
13767
- idx = 1;
13768
- }
13769
-
13770
- startContainer = endContainer = node;
13771
- startOffset = endOffset = idx;
13772
- } else {
13773
- if (!keep) {
13774
- idx = dom.nodeIndex(marker);
13775
- } else {
13776
- node = marker.firstChild;
13777
- idx = 1;
13778
- }
13779
-
13780
- endContainer = node;
13781
- endOffset = idx;
13782
- }
13783
-
13784
- if (!keep) {
13785
- prev = marker.previousSibling;
13786
- next = marker.nextSibling;
13787
-
13788
- // Remove all marker text nodes
13789
- each(grep(marker.childNodes), function(node) {
13790
- if (node.nodeType == 3) {
13791
- node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
13792
- }
13793
- });
13794
-
13795
- // Remove marker but keep children if for example contents where inserted into the marker
13796
- // Also remove duplicated instances of the marker for example by a
13797
- // split operation or by WebKit auto split on paste feature
13798
- while ((marker = dom.get(bookmark.id + '_' + suffix))) {
13799
- dom.remove(marker, 1);
13800
- }
13801
-
13802
- // If siblings are text nodes then merge them unless it's Opera since it some how removes the node
13803
- // and we are sniffing since adding a lot of detection code for a browser with 3% of the market
13804
- // isn't worth the effort. Sorry, Opera but it's just a fact
13805
- if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !isOpera) {
13806
- idx = prev.nodeValue.length;
13807
- prev.appendData(next.nodeValue);
13808
- dom.remove(next);
13809
-
13810
- if (suffix == 'start') {
13811
- startContainer = endContainer = prev;
13812
- startOffset = endOffset = idx;
13813
- } else {
13814
- endContainer = prev;
13815
- endOffset = idx;
13816
- }
13817
- }
13818
- }
13819
- }
13820
- }
13821
-
13822
- function addBogus(node) {
13823
- // Adds a bogus BR element for empty block elements
13824
- if (dom.isBlock(node) && !node.innerHTML && !isIE) {
13825
- node.innerHTML = '<br data-mce-bogus="1" />';
13826
- }
13827
-
13828
- return node;
13829
- }
13830
-
13831
- if (bookmark) {
13832
- if (bookmark.start) {
13833
- rng = dom.createRng();
13834
- root = dom.getRoot();
13835
-
13836
- if (self.tridentSel) {
13837
- return self.tridentSel.moveToBookmark(bookmark);
13838
- }
13839
-
13840
- if (setEndPoint(true) && setEndPoint()) {
13841
- self.setRng(rng);
13842
- }
13843
- } else if (bookmark.id) {
13844
- // Restore start/end points
13845
- restoreEndPoint('start');
13846
- restoreEndPoint('end');
13847
-
13848
- if (startContainer) {
13849
- rng = dom.createRng();
13850
- rng.setStart(addBogus(startContainer), startOffset);
13851
- rng.setEnd(addBogus(endContainer), endOffset);
13852
- self.setRng(rng);
13853
- }
13854
- } else if (bookmark.name) {
13855
- self.select(dom.select(bookmark.name)[bookmark.index]);
13856
- } else if (bookmark.rng) {
13857
- self.setRng(bookmark.rng);
13858
- }
13859
- }
14124
+ return this.bookmarkManager.moveToBookmark(bookmark);
13860
14125
  },
13861
14126
 
13862
14127
  /**
@@ -14446,6 +14711,126 @@ define("tinymce/dom/Selection", [
14446
14711
  return Selection;
14447
14712
  });
14448
14713
 
14714
+ // Included from: js/tinymce/classes/dom/ElementUtils.js
14715
+
14716
+ /**
14717
+ * ElementUtils.js
14718
+ *
14719
+ * Copyright, Moxiecode Systems AB
14720
+ * Released under LGPL License.
14721
+ *
14722
+ * License: http://www.tinymce.com/license
14723
+ * Contributing: http://www.tinymce.com/contributing
14724
+ */
14725
+
14726
+ /**
14727
+ * Utility class for various element specific functions.
14728
+ *
14729
+ * @private
14730
+ */
14731
+ define("tinymce/dom/ElementUtils", [
14732
+ "tinymce/dom/BookmarkManager",
14733
+ "tinymce/util/Tools"
14734
+ ], function(BookmarkManager, Tools) {
14735
+ var each = Tools.each;
14736
+
14737
+ function ElementUtils(dom) {
14738
+ /**
14739
+ * Compares two nodes and checks if it's attributes and styles matches.
14740
+ * This doesn't compare classes as items since their order is significant.
14741
+ *
14742
+ * @method compare
14743
+ * @param {Node} node1 First node to compare with.
14744
+ * @param {Node} node2 Second node to compare with.
14745
+ * @return {boolean} True/false if the nodes are the same or not.
14746
+ */
14747
+ this.compare = function(node1, node2) {
14748
+ // Not the same name
14749
+ if (node1.nodeName != node2.nodeName) {
14750
+ return false;
14751
+ }
14752
+
14753
+ /**
14754
+ * Returns all the nodes attributes excluding internal ones, styles and classes.
14755
+ *
14756
+ * @private
14757
+ * @param {Node} node Node to get attributes from.
14758
+ * @return {Object} Name/value object with attributes and attribute values.
14759
+ */
14760
+ function getAttribs(node) {
14761
+ var attribs = {};
14762
+
14763
+ each(dom.getAttribs(node), function(attr) {
14764
+ var name = attr.nodeName.toLowerCase();
14765
+
14766
+ // Don't compare internal attributes or style
14767
+ if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
14768
+ attribs[name] = dom.getAttrib(node, name);
14769
+ }
14770
+ });
14771
+
14772
+ return attribs;
14773
+ }
14774
+
14775
+ /**
14776
+ * Compares two objects checks if it's key + value exists in the other one.
14777
+ *
14778
+ * @private
14779
+ * @param {Object} obj1 First object to compare.
14780
+ * @param {Object} obj2 Second object to compare.
14781
+ * @return {boolean} True/false if the objects matches or not.
14782
+ */
14783
+ function compareObjects(obj1, obj2) {
14784
+ var value, name;
14785
+
14786
+ for (name in obj1) {
14787
+ // Obj1 has item obj2 doesn't have
14788
+ if (obj1.hasOwnProperty(name)) {
14789
+ value = obj2[name];
14790
+
14791
+ // Obj2 doesn't have obj1 item
14792
+ if (typeof value == "undefined") {
14793
+ return false;
14794
+ }
14795
+
14796
+ // Obj2 item has a different value
14797
+ if (obj1[name] != value) {
14798
+ return false;
14799
+ }
14800
+
14801
+ // Delete similar value
14802
+ delete obj2[name];
14803
+ }
14804
+ }
14805
+
14806
+ // Check if obj 2 has something obj 1 doesn't have
14807
+ for (name in obj2) {
14808
+ // Obj2 has item obj1 doesn't have
14809
+ if (obj2.hasOwnProperty(name)) {
14810
+ return false;
14811
+ }
14812
+ }
14813
+
14814
+ return true;
14815
+ }
14816
+
14817
+ // Attribs are not the same
14818
+ if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
14819
+ return false;
14820
+ }
14821
+
14822
+ // Styles are not the same
14823
+ if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
14824
+ return false;
14825
+ }
14826
+
14827
+ return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2);
14828
+ };
14829
+ }
14830
+
14831
+ return ElementUtils;
14832
+ });
14833
+
14449
14834
  // Included from: js/tinymce/classes/fmt/Preview.js
14450
14835
 
14451
14836
  /**
@@ -14629,9 +15014,11 @@ define("tinymce/fmt/Preview", [
14629
15014
  define("tinymce/Formatter", [
14630
15015
  "tinymce/dom/TreeWalker",
14631
15016
  "tinymce/dom/RangeUtils",
15017
+ "tinymce/dom/BookmarkManager",
15018
+ "tinymce/dom/ElementUtils",
14632
15019
  "tinymce/util/Tools",
14633
15020
  "tinymce/fmt/Preview"
14634
- ], function(TreeWalker, RangeUtils, Tools, Preview) {
15021
+ ], function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, Tools, Preview) {
14635
15022
  /**
14636
15023
  * Constructs a new formatter instance.
14637
15024
  *
@@ -14655,7 +15042,8 @@ define("tinymce/Formatter", [
14655
15042
  undef,
14656
15043
  getContentEditable = dom.getContentEditable,
14657
15044
  disableCaretContainer,
14658
- markCaretContainersBogus;
15045
+ markCaretContainersBogus,
15046
+ isBookmarkNode = BookmarkManager.isBookmarkNode;
14659
15047
 
14660
15048
  var each = Tools.each,
14661
15049
  grep = Tools.grep,
@@ -14680,7 +15068,6 @@ define("tinymce/Formatter", [
14680
15068
 
14681
15069
  function defaultFormats() {
14682
15070
  register({
14683
-
14684
15071
  valigntop: [
14685
15072
  {selector: 'td,th', styles: {'verticalAlign': 'top'}}
14686
15073
  ],
@@ -14971,7 +15358,7 @@ define("tinymce/Formatter", [
14971
15358
 
14972
15359
  // get the index of the bookmarks
14973
15360
  each(node.childNodes, function(n, index) {
14974
- if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
15361
+ if (isBookmarkNode(n)) {
14975
15362
  if (n.id == bookmark.id + "_start") {
14976
15363
  startIndex = index;
14977
15364
  } else if (n.id == bookmark.id + "_end") {
@@ -16547,17 +16934,6 @@ define("tinymce/Formatter", [
16547
16934
  }
16548
16935
  }
16549
16936
 
16550
- /**
16551
- * Checks if the specified node is a bookmark node or not.
16552
- *
16553
- * @private
16554
- * @param {Node} node Node to check if it's a bookmark node or not.
16555
- * @return {Boolean} true/false if the node is a bookmark node.
16556
- */
16557
- function isBookmarkNode(node) {
16558
- return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';
16559
- }
16560
-
16561
16937
  /**
16562
16938
  * Merges the next/previous sibling element if they match.
16563
16939
  *
@@ -16567,99 +16943,7 @@ define("tinymce/Formatter", [
16567
16943
  * @return {Node} Next node if we didn't merge and prev node if we did.
16568
16944
  */
16569
16945
  function mergeSiblings(prev, next) {
16570
- var sibling, tmpSibling;
16571
-
16572
- /**
16573
- * Compares two nodes and checks if it's attributes and styles matches.
16574
- * This doesn't compare classes as items since their order is significant.
16575
- *
16576
- * @private
16577
- * @param {Node} node1 First node to compare with.
16578
- * @param {Node} node2 Second node to compare with.
16579
- * @return {boolean} True/false if the nodes are the same or not.
16580
- */
16581
- function compareElements(node1, node2) {
16582
- // Not the same name
16583
- if (node1.nodeName != node2.nodeName) {
16584
- return FALSE;
16585
- }
16586
-
16587
- /**
16588
- * Returns all the nodes attributes excluding internal ones, styles and classes.
16589
- *
16590
- * @private
16591
- * @param {Node} node Node to get attributes from.
16592
- * @return {Object} Name/value object with attributes and attribute values.
16593
- */
16594
- function getAttribs(node) {
16595
- var attribs = {};
16596
-
16597
- each(dom.getAttribs(node), function(attr) {
16598
- var name = attr.nodeName.toLowerCase();
16599
-
16600
- // Don't compare internal attributes or style
16601
- if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
16602
- attribs[name] = dom.getAttrib(node, name);
16603
- }
16604
- });
16605
-
16606
- return attribs;
16607
- }
16608
-
16609
- /**
16610
- * Compares two objects checks if it's key + value exists in the other one.
16611
- *
16612
- * @private
16613
- * @param {Object} obj1 First object to compare.
16614
- * @param {Object} obj2 Second object to compare.
16615
- * @return {boolean} True/false if the objects matches or not.
16616
- */
16617
- function compareObjects(obj1, obj2) {
16618
- var value, name;
16619
-
16620
- for (name in obj1) {
16621
- // Obj1 has item obj2 doesn't have
16622
- if (obj1.hasOwnProperty(name)) {
16623
- value = obj2[name];
16624
-
16625
- // Obj2 doesn't have obj1 item
16626
- if (value === undef) {
16627
- return FALSE;
16628
- }
16629
-
16630
- // Obj2 item has a different value
16631
- if (obj1[name] != value) {
16632
- return FALSE;
16633
- }
16634
-
16635
- // Delete similar value
16636
- delete obj2[name];
16637
- }
16638
- }
16639
-
16640
- // Check if obj 2 has something obj 1 doesn't have
16641
- for (name in obj2) {
16642
- // Obj2 has item obj1 doesn't have
16643
- if (obj2.hasOwnProperty(name)) {
16644
- return FALSE;
16645
- }
16646
- }
16647
-
16648
- return TRUE;
16649
- }
16650
-
16651
- // Attribs are not the same
16652
- if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
16653
- return FALSE;
16654
- }
16655
-
16656
- // Styles are not the same
16657
- if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
16658
- return FALSE;
16659
- }
16660
-
16661
- return !isBookmarkNode(node1) && !isBookmarkNode(node2);
16662
- }
16946
+ var sibling, tmpSibling, elementUtils = new ElementUtils(dom);
16663
16947
 
16664
16948
  function findElementSibling(node, sibling_name) {
16665
16949
  for (sibling = node; sibling; sibling = sibling[sibling_name]) {
@@ -16682,7 +16966,7 @@ define("tinymce/Formatter", [
16682
16966
  next = findElementSibling(next, 'nextSibling');
16683
16967
 
16684
16968
  // Compare next and previous nodes
16685
- if (compareElements(prev, next)) {
16969
+ if (elementUtils.compare(prev, next)) {
16686
16970
  // Append nodes between
16687
16971
  for (sibling = prev.nextSibling; sibling && sibling != next;) {
16688
16972
  tmpSibling = sibling;
@@ -16885,7 +17169,7 @@ define("tinymce/Formatter", [
16885
17169
  node = container;
16886
17170
 
16887
17171
  if (container.nodeType == 3) {
16888
- if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
17172
+ if (offset != container.nodeValue.length) {
16889
17173
  hasContentAfter = true;
16890
17174
  }
16891
17175
 
@@ -17467,6 +17751,10 @@ define("tinymce/EnterKey", [
17467
17751
  function trimInlineElementsOnLeftSideOfBlock(block) {
17468
17752
  var node = block, firstChilds = [], i;
17469
17753
 
17754
+ if (!node) {
17755
+ return;
17756
+ }
17757
+
17470
17758
  // Find inner most first child ex: <p><i><b>*</b></i></p>
17471
17759
  while ((node = node.firstChild)) {
17472
17760
  if (dom.isBlock(node)) {
@@ -17507,6 +17795,10 @@ define("tinymce/EnterKey", [
17507
17795
  }
17508
17796
  }
17509
17797
 
17798
+ if (!root) {
17799
+ return;
17800
+ }
17801
+
17510
17802
  // Old IE versions doesn't properly render blocks with br elements in them
17511
17803
  // For example <p><br></p> wont be rendered correctly in a contentEditable area
17512
17804
  // until you remove the br producing <p></p>
@@ -17516,16 +17808,23 @@ define("tinymce/EnterKey", [
17516
17808
  }
17517
17809
  }
17518
17810
 
17519
- if (root.nodeName == 'LI') {
17811
+ if (/^(LI|DT|DD)$/.test(root.nodeName)) {
17520
17812
  var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
17521
17813
 
17522
- if (firstChild && /^(UL|OL)$/.test(firstChild.nodeName)) {
17814
+ if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
17523
17815
  root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
17524
17816
  }
17525
17817
  }
17526
17818
 
17527
17819
  rng = dom.createRng();
17528
17820
 
17821
+ // Normalize whitespace to remove empty text nodes. Fix for: #6904
17822
+ // Gecko will be able to place the caret in empty text nodes but it won't render propery
17823
+ // Older IE versions will sometimes crash so for now ignore all IE versions
17824
+ if (!Env.ie) {
17825
+ root.normalize();
17826
+ }
17827
+
17529
17828
  if (root.hasChildNodes()) {
17530
17829
  walker = new TreeWalker(root, root);
17531
17830
 
@@ -17589,7 +17888,7 @@ define("tinymce/EnterKey", [
17589
17888
  // Creates a new block element by cloning the current one or creating a new one if the name is specified
17590
17889
  // This function will also copy any text formatting from the parent block and add it to the new one
17591
17890
  function createNewBlock(name) {
17592
- var node = container, block, clonedNode, caretNode;
17891
+ var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements();
17593
17892
 
17594
17893
  if (name || parentBlockName == "TABLE") {
17595
17894
  block = dom.create(name || newBlockName);
@@ -17603,7 +17902,7 @@ define("tinymce/EnterKey", [
17603
17902
  // Clone any parent styles
17604
17903
  if (settings.keep_styles !== false) {
17605
17904
  do {
17606
- if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U|VAR|CITE|DFN|CODE|MARK|Q|SUP|SUB|SAMP)$/.test(node.nodeName)) {
17905
+ if (textInlineElements[node.nodeName]) {
17607
17906
  // Never clone a caret containers
17608
17907
  if (node.id == '_mce_caret') {
17609
17908
  continue;
@@ -17764,7 +18063,7 @@ define("tinymce/EnterKey", [
17764
18063
  function getContainerBlock() {
17765
18064
  var containerBlockParent = containerBlock.parentNode;
17766
18065
 
17767
- if (containerBlockParent.nodeName == 'LI') {
18066
+ if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) {
17768
18067
  return containerBlockParent;
17769
18068
  }
17770
18069
 
@@ -17994,8 +18293,8 @@ define("tinymce/EnterKey", [
17994
18293
  parentBlockName = containerBlockName;
17995
18294
  }
17996
18295
 
17997
- // Handle enter in LI
17998
- if (parentBlockName == 'LI') {
18296
+ // Handle enter in list item
18297
+ if (/^(LI|DT|DD)$/.test(parentBlockName)) {
17999
18298
  if (!newBlockName && shiftKey) {
18000
18299
  insertBr();
18001
18300
  return;
@@ -18240,8 +18539,9 @@ define("tinymce/ForceBlocks", [], function() {
18240
18539
  define("tinymce/EditorCommands", [
18241
18540
  "tinymce/html/Serializer",
18242
18541
  "tinymce/Env",
18243
- "tinymce/util/Tools"
18244
- ], function(Serializer, Env, Tools) {
18542
+ "tinymce/util/Tools",
18543
+ "tinymce/dom/ElementUtils"
18544
+ ], function(Serializer, Env, Tools, ElementUtils) {
18245
18545
  // Added for compression purposes
18246
18546
  var each = Tools.each, extend = Tools.extend;
18247
18547
  var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
@@ -18536,7 +18836,8 @@ define("tinymce/EditorCommands", [
18536
18836
 
18537
18837
  mceInsertContent: function(command, ui, value) {
18538
18838
  var parser, serializer, parentNode, rootNode, fragment, args;
18539
- var marker, rng, node, node2, bookmarkHtml;
18839
+ var marker, rng, node, node2, bookmarkHtml, merge;
18840
+ var textInlineElements = editor.schema.getTextInlineElements();
18540
18841
 
18541
18842
  function trimOrPaddLeftRight(html) {
18542
18843
  var rng, container, offset;
@@ -18566,6 +18867,37 @@ define("tinymce/EditorCommands", [
18566
18867
  return html;
18567
18868
  }
18568
18869
 
18870
+ function markInlineFormatElements(fragment) {
18871
+ if (merge) {
18872
+ for (node = fragment.firstChild; node; node = node.walk(true)) {
18873
+ if (textInlineElements[node.name]) {
18874
+ node.attr('data-mce-new', "true");
18875
+ }
18876
+ }
18877
+ }
18878
+ }
18879
+
18880
+ function reduceInlineTextElements() {
18881
+ if (merge) {
18882
+ var root = editor.getBody(), elementUtils = new ElementUtils(dom);
18883
+
18884
+ each(dom.select('*[data-mce-new]'), function(node) {
18885
+ node.removeAttribute('data-mce-new');
18886
+
18887
+ for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
18888
+ if (elementUtils.compare(testNode, node)) {
18889
+ dom.remove(node, true);
18890
+ }
18891
+ }
18892
+ });
18893
+ }
18894
+ }
18895
+
18896
+ if (typeof(value) != 'string') {
18897
+ merge = value.merge;
18898
+ value = value.content;
18899
+ }
18900
+
18569
18901
  // Check for whitespace before/after value
18570
18902
  if (/^ | $/.test(value)) {
18571
18903
  value = trimOrPaddLeftRight(value);
@@ -18613,6 +18945,8 @@ define("tinymce/EditorCommands", [
18613
18945
  var parserArgs = {context: parentNode.nodeName.toLowerCase()};
18614
18946
  fragment = parser.parse(value, parserArgs);
18615
18947
 
18948
+ markInlineFormatElements(fragment);
18949
+
18616
18950
  // Move the caret to a more suitable location
18617
18951
  node = fragment.lastChild;
18618
18952
  if (node.attr('id') == 'mce_marker') {
@@ -18679,6 +19013,8 @@ define("tinymce/EditorCommands", [
18679
19013
  }
18680
19014
  }
18681
19015
 
19016
+ reduceInlineTextElements();
19017
+
18682
19018
  marker = dom.get('mce_marker');
18683
19019
  selection.scrollIntoView(marker);
18684
19020
 
@@ -18981,11 +19317,9 @@ define("tinymce/util/URI", [
18981
19317
  function URI(url, settings) {
18982
19318
  var self = this, baseUri, base_url;
18983
19319
 
18984
- // Trim whitespace
18985
19320
  url = trim(url);
18986
-
18987
- // Default settings
18988
19321
  settings = self.settings = settings || {};
19322
+ baseUri = settings.base_uri;
18989
19323
 
18990
19324
  // Strange app protocol that isn't http/https or local anchor
18991
19325
  // For example: mailto,skype,tel etc.
@@ -18998,7 +19332,7 @@ define("tinymce/util/URI", [
18998
19332
 
18999
19333
  // Absolute path with no host, fake host and protocol
19000
19334
  if (url.indexOf('/') === 0 && !isProtocolRelative) {
19001
- url = (settings.base_uri ? settings.base_uri.protocol || 'http' : 'http') + '://mce_host' + url;
19335
+ url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url;
19002
19336
  }
19003
19337
 
19004
19338
  // Relative path http:// or protocol relative //path
@@ -19007,7 +19341,8 @@ define("tinymce/util/URI", [
19007
19341
  if (settings.base_uri.protocol === "") {
19008
19342
  url = '//mce_host' + self.toAbsPath(base_url, url);
19009
19343
  } else {
19010
- url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
19344
+ url = /([^#?]*)([#?]?.*)/.exec(url);
19345
+ url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url[1]) + url[2];
19011
19346
  }
19012
19347
  }
19013
19348
 
@@ -19029,7 +19364,6 @@ define("tinymce/util/URI", [
19029
19364
  self[v] = part;
19030
19365
  });
19031
19366
 
19032
- baseUri = settings.base_uri;
19033
19367
  if (baseUri) {
19034
19368
  if (!self.protocol) {
19035
19369
  self.protocol = baseUri.protocol;
@@ -19539,7 +19873,8 @@ define("tinymce/util/EventDispatcher", [
19539
19873
  var nativeEvents = Tools.makeMap(
19540
19874
  "focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
19541
19875
  "mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
19542
- "draggesture dragdrop drop drag submit",
19876
+ "draggesture dragdrop drop drag submit " +
19877
+ "compositionstart compositionend compositionupdate",
19543
19878
  ' '
19544
19879
  );
19545
19880
 
@@ -20596,9 +20931,11 @@ define("tinymce/ui/DomUtils", [
20596
20931
  ], function(Tools, DOMUtils) {
20597
20932
  "use strict";
20598
20933
 
20934
+ var count = 0;
20935
+
20599
20936
  return {
20600
20937
  id: function() {
20601
- return DOMUtils.DOM.uniqueId();
20938
+ return 'mceu_' + (count++);
20602
20939
  },
20603
20940
 
20604
20941
  createFragment: function(html) {
@@ -23832,9 +24169,117 @@ define("tinymce/ui/FloatPanel", [
23832
24169
  ], function(Panel, Movable, Resizable, DomUtils) {
23833
24170
  "use strict";
23834
24171
 
23835
- var documentClickHandler, documentScrollHandler, visiblePanels = [];
24172
+ var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = [];
23836
24173
  var zOrder = [], hasModal;
23837
24174
 
24175
+ function bindDocumentClickHandler() {
24176
+ function isChildOf(ctrl, parent) {
24177
+ while (ctrl) {
24178
+ if (ctrl == parent) {
24179
+ return true;
24180
+ }
24181
+
24182
+ ctrl = ctrl.parent();
24183
+ }
24184
+ }
24185
+
24186
+ if (!documentClickHandler) {
24187
+ documentClickHandler = function(e) {
24188
+ // Gecko fires click event and in the wrong order on Mac so lets normalize
24189
+ if (e.button == 2) {
24190
+ return;
24191
+ }
24192
+
24193
+ // Hide any float panel when a click is out side that float panel and the
24194
+ // float panels direct parent for example a click on a menu button
24195
+ var i = visiblePanels.length;
24196
+ while (i--) {
24197
+ var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
24198
+
24199
+ if (panel.settings.autohide) {
24200
+ if (clickCtrl) {
24201
+ if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
24202
+ continue;
24203
+ }
24204
+ }
24205
+
24206
+ e = panel.fire('autohide', {target: e.target});
24207
+ if (!e.isDefaultPrevented()) {
24208
+ panel.hide();
24209
+ }
24210
+ }
24211
+ }
24212
+ };
24213
+
24214
+ DomUtils.on(document, 'click', documentClickHandler);
24215
+ }
24216
+ }
24217
+
24218
+ function bindDocumentScrollHandler() {
24219
+ if (!documentScrollHandler) {
24220
+ documentScrollHandler = function() {
24221
+ var i;
24222
+
24223
+ i = visiblePanels.length;
24224
+ while (i--) {
24225
+ repositionPanel(visiblePanels[i]);
24226
+ }
24227
+ };
24228
+
24229
+ DomUtils.on(window, 'scroll', documentScrollHandler);
24230
+ }
24231
+ }
24232
+
24233
+ function bindWindowResizeHandler() {
24234
+ if (!windowResizeHandler) {
24235
+ windowResizeHandler = function() {
24236
+ FloatPanel.hideAll();
24237
+ };
24238
+
24239
+ DomUtils.on(window, 'resize', windowResizeHandler);
24240
+ }
24241
+ }
24242
+
24243
+ /**
24244
+ * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
24245
+ * also reposition all child panels of the current panel.
24246
+ */
24247
+ function repositionPanel(panel) {
24248
+ var scrollY = DomUtils.getViewPort().y;
24249
+
24250
+ function toggleFixedChildPanels(fixed, deltaY) {
24251
+ var parent;
24252
+
24253
+ for (var i = 0; i < visiblePanels.length; i++) {
24254
+ if (visiblePanels[i] != panel) {
24255
+ parent = visiblePanels[i].parent();
24256
+
24257
+ while (parent && (parent = parent.parent())) {
24258
+ if (parent == panel) {
24259
+ visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
24260
+ }
24261
+ }
24262
+ }
24263
+ }
24264
+ }
24265
+
24266
+ if (panel.settings.autofix) {
24267
+ if (!panel._fixed) {
24268
+ panel._autoFixY = panel.layoutRect().y;
24269
+
24270
+ if (panel._autoFixY < scrollY) {
24271
+ panel.fixed(true).layoutRect({y: 0}).repaint();
24272
+ toggleFixedChildPanels(true, scrollY - panel._autoFixY);
24273
+ }
24274
+ } else {
24275
+ if (panel._autoFixY > scrollY) {
24276
+ panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
24277
+ toggleFixedChildPanels(false, panel._autoFixY - scrollY);
24278
+ }
24279
+ }
24280
+ }
24281
+ }
24282
+
23838
24283
  var FloatPanel = Panel.extend({
23839
24284
  Mixins: [Movable, Resizable],
23840
24285
 
@@ -23876,56 +24321,6 @@ define("tinymce/ui/FloatPanel", [
23876
24321
  FloatPanel.currentZIndex = zIndex;
23877
24322
  }
23878
24323
 
23879
- function isChildOf(ctrl, parent) {
23880
- while (ctrl) {
23881
- if (ctrl == parent) {
23882
- return true;
23883
- }
23884
-
23885
- ctrl = ctrl.parent();
23886
- }
23887
- }
23888
-
23889
- /**
23890
- * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
23891
- * also reposition all child panels of the current panel.
23892
- */
23893
- function repositionPanel(panel) {
23894
- var scrollY = DomUtils.getViewPort().y;
23895
-
23896
- function toggleFixedChildPanels(fixed, deltaY) {
23897
- var parent;
23898
-
23899
- for (var i = 0; i < visiblePanels.length; i++) {
23900
- if (visiblePanels[i] != panel) {
23901
- parent = visiblePanels[i].parent();
23902
-
23903
- while (parent && (parent = parent.parent())) {
23904
- if (parent == panel) {
23905
- visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
23906
- }
23907
- }
23908
- }
23909
- }
23910
- }
23911
-
23912
- if (panel.settings.autofix) {
23913
- if (!panel._fixed) {
23914
- panel._autoFixY = panel.layoutRect().y;
23915
-
23916
- if (panel._autoFixY < scrollY) {
23917
- panel.fixed(true).layoutRect({y: 0}).repaint();
23918
- toggleFixedChildPanels(true, scrollY - panel._autoFixY);
23919
- }
23920
- } else {
23921
- if (panel._autoFixY > scrollY) {
23922
- panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
23923
- toggleFixedChildPanels(false, panel._autoFixY - scrollY);
23924
- }
23925
- }
23926
- }
23927
- }
23928
-
23929
24324
  self._super(settings);
23930
24325
  self._eventsRoot = self;
23931
24326
 
@@ -23933,48 +24328,13 @@ define("tinymce/ui/FloatPanel", [
23933
24328
 
23934
24329
  // Hide floatpanes on click out side the root button
23935
24330
  if (settings.autohide) {
23936
- if (!documentClickHandler) {
23937
- documentClickHandler = function(e) {
23938
- // Hide any float panel when a click is out side that float panel and the
23939
- // float panels direct parent for example a click on a menu button
23940
- var i = visiblePanels.length;
23941
- while (i--) {
23942
- var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
23943
-
23944
- if (panel.settings.autohide) {
23945
- if (clickCtrl) {
23946
- if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
23947
- continue;
23948
- }
23949
- }
23950
-
23951
- e = panel.fire('autohide', {target: e.target});
23952
- if (!e.isDefaultPrevented()) {
23953
- panel.hide();
23954
- }
23955
- }
23956
- }
23957
- };
23958
-
23959
- DomUtils.on(document, 'click', documentClickHandler);
23960
- }
23961
-
24331
+ bindDocumentClickHandler();
24332
+ bindWindowResizeHandler();
23962
24333
  visiblePanels.push(self);
23963
24334
  }
23964
24335
 
23965
24336
  if (settings.autofix) {
23966
- if (!documentScrollHandler) {
23967
- documentScrollHandler = function() {
23968
- var i;
23969
-
23970
- i = visiblePanels.length;
23971
- while (i--) {
23972
- repositionPanel(visiblePanels[i]);
23973
- }
23974
- };
23975
-
23976
- DomUtils.on(window, 'scroll', documentScrollHandler);
23977
- }
24337
+ bindDocumentScrollHandler();
23978
24338
 
23979
24339
  self.on('move', function() {
23980
24340
  repositionPanel(this);
@@ -24090,7 +24450,8 @@ define("tinymce/ui/FloatPanel", [
24090
24450
  },
24091
24451
 
24092
24452
  /**
24093
- * Hides all visible the float panels.
24453
+ * Hide all visible float panels with he autohide setting enabled. This is for
24454
+ * manually hiding floating menus or panels.
24094
24455
  *
24095
24456
  * @method hideAll
24096
24457
  */
@@ -24133,7 +24494,8 @@ define("tinymce/ui/FloatPanel", [
24133
24494
  });
24134
24495
 
24135
24496
  /**
24136
- * Hides all visible the float panels.
24497
+ * Hide all visible float panels with he autohide setting enabled. This is for
24498
+ * manually hiding floating menus or panels.
24137
24499
  *
24138
24500
  * @static
24139
24501
  * @method hideAll
@@ -26539,8 +26901,13 @@ define("tinymce/Shortcuts", [
26539
26901
  break;
26540
26902
 
26541
26903
  default:
26542
- shortcut.charCode = value.charCodeAt(0);
26543
- shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
26904
+ // Allow numeric keycodes like ctrl+219 for ctrl+[
26905
+ if (/^[0-9]{2,}$/.test(value)) {
26906
+ shortcut.keyCode = parseInt(value, 10);
26907
+ } else {
26908
+ shortcut.charCode = value.charCodeAt(0);
26909
+ shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
26910
+ }
26544
26911
  }
26545
26912
  });
26546
26913
 
@@ -27347,7 +27714,14 @@ define("tinymce/Editor", [
27347
27714
  // Add internal attribute if we need to we don't on a refresh of the document
27348
27715
  if (!node.attributes.map[internalName]) {
27349
27716
  if (name === "style") {
27350
- node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
27717
+ value = dom.serializeStyle(dom.parseStyle(value), node.name);
27718
+
27719
+ if (!value.length) {
27720
+ value = null;
27721
+ }
27722
+
27723
+ node.attr(internalName, value);
27724
+ node.attr(name, value);
27351
27725
  } else if (name === "tabindex") {
27352
27726
  node.attr(internalName, value);
27353
27727
  node.attr(name, null);
@@ -27364,7 +27738,7 @@ define("tinymce/Editor", [
27364
27738
 
27365
27739
  while (i--) {
27366
27740
  node = nodes[i];
27367
- node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
27741
+ node.attr('type', 'mce-' + (node.attr('type') || 'no/type'));
27368
27742
  }
27369
27743
  });
27370
27744
 
@@ -27978,8 +28352,18 @@ define("tinymce/Editor", [
27978
28352
  }
27979
28353
 
27980
28354
  // Browser commands
27981
- self.getDoc().execCommand(cmd, ui, value);
27982
- self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
28355
+ try {
28356
+ state = self.getDoc().execCommand(cmd, ui, value);
28357
+ } catch (ex) {
28358
+ // Ignore old IE errors
28359
+ }
28360
+
28361
+ if (state) {
28362
+ self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
28363
+ return true;
28364
+ }
28365
+
28366
+ return false;
27983
28367
  },
27984
28368
 
27985
28369
  /**
@@ -28001,8 +28385,8 @@ define("tinymce/Editor", [
28001
28385
  if ((queryItem = self.queryStateCommands[cmd])) {
28002
28386
  returnVal = queryItem.func.call(queryItem.scope);
28003
28387
 
28004
- // Fall though on true
28005
- if (returnVal !== true) {
28388
+ // Fall though on non boolean returns
28389
+ if (returnVal === true || returnVal === false) {
28006
28390
  return returnVal;
28007
28391
  }
28008
28392
  }
@@ -28092,8 +28476,6 @@ define("tinymce/Editor", [
28092
28476
  var self = this, doc = self.getDoc();
28093
28477
 
28094
28478
  if (!self.hidden) {
28095
- self.hidden = true;
28096
-
28097
28479
  // Fixed bug where IE has a blinking cursor left from the editor
28098
28480
  if (ie && doc && !self.inline) {
28099
28481
  doc.execCommand('SelectAll');
@@ -28114,6 +28496,7 @@ define("tinymce/Editor", [
28114
28496
  DOM.setStyle(self.id, 'display', self.orgDisplay);
28115
28497
  }
28116
28498
 
28499
+ self.hidden = true;
28117
28500
  self.fire('hide');
28118
28501
  }
28119
28502
  },
@@ -28377,8 +28760,13 @@ define("tinymce/Editor", [
28377
28760
  *
28378
28761
  * @method insertContent
28379
28762
  * @param {String} content Content to insert.
28763
+ * @param {Object} args Optional args to pass to insert call.
28380
28764
  */
28381
- insertContent: function(content) {
28765
+ insertContent: function(content, args) {
28766
+ if (args) {
28767
+ content = extend({content: content}, args);
28768
+ }
28769
+
28382
28770
  this.execCommand('mceInsertContent', false, content);
28383
28771
  },
28384
28772
 
@@ -29133,7 +29521,7 @@ define("tinymce/EditorManager", [
29133
29521
  * @property minorVersion
29134
29522
  * @type String
29135
29523
  */
29136
- minorVersion : '0.26',
29524
+ minorVersion : '0.28',
29137
29525
 
29138
29526
  /**
29139
29527
  * Release date of TinyMCE build.
@@ -29141,7 +29529,7 @@ define("tinymce/EditorManager", [
29141
29529
  * @property releaseDate
29142
29530
  * @type String
29143
29531
  */
29144
- releaseDate: '2014-05-06',
29532
+ releaseDate: '2014-05-27',
29145
29533
 
29146
29534
  /**
29147
29535
  * Collection of editor instances.
@@ -29177,9 +29565,16 @@ define("tinymce/EditorManager", [
29177
29565
  var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
29178
29566
 
29179
29567
  // Get base URL for the current document
29180
- documentBaseURL = document.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
29181
- if (!/[\/\\]$/.test(documentBaseURL)) {
29182
- documentBaseURL += '/';
29568
+ documentBaseURL = document.location.href;
29569
+
29570
+ // Check if the URL is a document based format like: http://site/dir/file
29571
+ // leave other formats like applewebdata://... intact
29572
+ if (/^[^:]+:\/\/[^\/]+\//.test(documentBaseURL)) {
29573
+ documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
29574
+
29575
+ if (!/[\/\\]$/.test(documentBaseURL)) {
29576
+ documentBaseURL += '/';
29577
+ }
29183
29578
  }
29184
29579
 
29185
29580
  // If tinymce is defined and has a base use that or use the old tinyMCEPreInit
@@ -32113,13 +32508,19 @@ define("tinymce/ui/Form", [
32113
32508
  * @private
32114
32509
  */
32115
32510
  recalcLabels: function() {
32116
- var self = this, maxLabelWidth = 0, labels = [], i, labelGap;
32511
+ var self = this, maxLabelWidth = 0, labels = [], i, labelGap, items;
32117
32512
 
32118
32513
  if (self.settings.labelGapCalc === false) {
32119
32514
  return;
32120
32515
  }
32121
32516
 
32122
- self.items().filter('formitem').each(function(item) {
32517
+ if (self.settings.labelGapCalc == "children") {
32518
+ items = self.find('formitem');
32519
+ } else {
32520
+ items = self.items();
32521
+ }
32522
+
32523
+ items.filter('formitem').each(function(item) {
32123
32524
  var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
32124
32525
 
32125
32526
  maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
@@ -32260,8 +32661,9 @@ define("tinymce/ui/FieldSet", [
32260
32661
  * @extends tinymce.ui.ComboBox
32261
32662
  */
32262
32663
  define("tinymce/ui/FilePicker", [
32263
- "tinymce/ui/ComboBox"
32264
- ], function(ComboBox) {
32664
+ "tinymce/ui/ComboBox",
32665
+ "tinymce/util/Tools"
32666
+ ], function(ComboBox, Tools) {
32265
32667
  "use strict";
32266
32668
 
32267
32669
  return ComboBox.extend({
@@ -32272,12 +32674,17 @@ define("tinymce/ui/FilePicker", [
32272
32674
  * @param {Object} settings Name/value object with settings.
32273
32675
  */
32274
32676
  init: function(settings) {
32275
- var self = this, editor = tinymce.activeEditor, fileBrowserCallback;
32677
+ var self = this, editor = tinymce.activeEditor, fileBrowserCallback, fileBrowserCallbackTypes;
32276
32678
 
32277
32679
  settings.spellcheck = false;
32278
32680
 
32681
+ fileBrowserCallbackTypes = editor.settings.file_browser_callback_types;
32682
+ if (fileBrowserCallbackTypes) {
32683
+ fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
32684
+ }
32685
+
32279
32686
  fileBrowserCallback = editor.settings.file_browser_callback;
32280
- if (fileBrowserCallback) {
32687
+ if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
32281
32688
  settings.icon = 'browse';
32282
32689
 
32283
32690
  settings.onaction = function() {
@@ -32793,6 +33200,7 @@ define("tinymce/ui/FormatControls", [
32793
33200
  }
32794
33201
 
32795
33202
  menuItem.format = formatName;
33203
+ menuItem.cmd = format.cmd;
32796
33204
  }
32797
33205
 
32798
33206
  menu.push(menuItem);
@@ -32839,20 +33247,32 @@ define("tinymce/ui/FormatControls", [
32839
33247
  },
32840
33248
 
32841
33249
  onPostRender: function() {
32842
- var self = this, formatName = this.settings.format;
33250
+ var self = this;
33251
+
33252
+ self.parent().on('show', function() {
33253
+ var formatName, command;
32843
33254
 
32844
- if (formatName) {
32845
- self.parent().on('show', function() {
33255
+ formatName = self.settings.format;
33256
+ if (formatName) {
32846
33257
  self.disabled(!editor.formatter.canApply(formatName));
32847
33258
  self.active(editor.formatter.match(formatName));
32848
- });
32849
- }
33259
+ }
33260
+
33261
+ command = self.settings.cmd;
33262
+ if (command) {
33263
+ self.active(editor.queryCommandState(command));
33264
+ }
33265
+ });
32850
33266
  },
32851
33267
 
32852
33268
  onclick: function() {
32853
33269
  if (this.settings.format) {
32854
33270
  toggleFormat(this.settings.format);
32855
33271
  }
33272
+
33273
+ if (this.settings.cmd) {
33274
+ editor.execCommand(this.settings.cmd);
33275
+ }
32856
33276
  }
32857
33277
  }
32858
33278
  };
@@ -32947,30 +33367,21 @@ define("tinymce/ui/FormatControls", [
32947
33367
  });
32948
33368
  });
32949
33369
 
32950
- function hasUndo() {
32951
- return editor.undoManager ? editor.undoManager.hasUndo() : false;
32952
- }
32953
-
32954
- function hasRedo() {
32955
- return editor.undoManager ? editor.undoManager.hasRedo() : false;
32956
- }
32957
-
32958
- function toggleUndoState() {
32959
- var self = this;
33370
+ function toggleUndoRedoState(type) {
33371
+ return function() {
33372
+ var self = this;
32960
33373
 
32961
- self.disabled(!hasUndo());
32962
- editor.on('Undo Redo AddUndo TypingUndo', function() {
32963
- self.disabled(!hasUndo());
32964
- });
32965
- }
33374
+ type = type == 'redo' ? 'hasRedo' : 'hasUndo';
32966
33375
 
32967
- function toggleRedoState() {
32968
- var self = this;
33376
+ function checkState() {
33377
+ return editor.undoManager ? editor.undoManager[type]() : false;
33378
+ }
32969
33379
 
32970
- self.disabled(!hasRedo());
32971
- editor.on('Undo Redo AddUndo TypingUndo', function() {
32972
- self.disabled(!hasRedo());
32973
- });
33380
+ self.disabled(!checkState());
33381
+ editor.on('Undo Redo AddUndo TypingUndo ClearUndos', function() {
33382
+ self.disabled(!checkState());
33383
+ });
33384
+ };
32974
33385
  }
32975
33386
 
32976
33387
  function toggleVisualAidState() {
@@ -32985,13 +33396,13 @@ define("tinymce/ui/FormatControls", [
32985
33396
 
32986
33397
  editor.addButton('undo', {
32987
33398
  tooltip: 'Undo',
32988
- onPostRender: toggleUndoState,
33399
+ onPostRender: toggleUndoRedoState('undo'),
32989
33400
  cmd: 'undo'
32990
33401
  });
32991
33402
 
32992
33403
  editor.addButton('redo', {
32993
33404
  tooltip: 'Redo',
32994
- onPostRender: toggleRedoState,
33405
+ onPostRender: toggleUndoRedoState('redo'),
32995
33406
  cmd: 'redo'
32996
33407
  });
32997
33408
 
@@ -33006,7 +33417,7 @@ define("tinymce/ui/FormatControls", [
33006
33417
  text: 'Undo',
33007
33418
  icon: 'undo',
33008
33419
  shortcut: 'Ctrl+Z',
33009
- onPostRender: toggleUndoState,
33420
+ onPostRender: toggleUndoRedoState('undo'),
33010
33421
  cmd: 'undo'
33011
33422
  });
33012
33423
 
@@ -33014,7 +33425,7 @@ define("tinymce/ui/FormatControls", [
33014
33425
  text: 'Redo',
33015
33426
  icon: 'redo',
33016
33427
  shortcut: 'Ctrl+Y',
33017
- onPostRender: toggleRedoState,
33428
+ onPostRender: toggleUndoRedoState('redo'),
33018
33429
  cmd: 'redo'
33019
33430
  });
33020
33431
 
@@ -33149,7 +33560,14 @@ define("tinymce/ui/FormatControls", [
33149
33560
  var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
33150
33561
 
33151
33562
  each(fontsize_formats.split(' '), function(item) {
33152
- items.push({text: item, value: item});
33563
+ var text = item, value = item;
33564
+ // Allow text=value font sizes.
33565
+ var values = item.split('=');
33566
+ if (values.length > 1) {
33567
+ text = values[0];
33568
+ value = values[1];
33569
+ }
33570
+ items.push({text: text, value: value});
33153
33571
  });
33154
33572
 
33155
33573
  return {
@@ -33991,19 +34409,29 @@ define("tinymce/ui/ListBox", [
33991
34409
  * @setting {Array} values Array with values to add to list box.
33992
34410
  */
33993
34411
  init: function(settings) {
33994
- var self = this, values, i, selected, selectedText, lastItemCtrl;
34412
+ var self = this, values, selected, selectedText, lastItemCtrl;
33995
34413
 
33996
- self._values = values = settings.values;
33997
- if (values) {
33998
- for (i = 0; i < values.length; i++) {
33999
- selected = values[i].selected || settings.value === values[i].value;
34414
+ function setSelected(menuValues) {
34415
+ // Try to find a selected value
34416
+ for (var i = 0; i < menuValues.length; i++) {
34417
+ selected = menuValues[i].selected || settings.value === menuValues[i].value;
34000
34418
 
34001
34419
  if (selected) {
34002
- selectedText = selectedText || values[i].text;
34003
- self._value = values[i].value;
34420
+ selectedText = selectedText || menuValues[i].text;
34421
+ self._value = menuValues[i].value;
34004
34422
  break;
34005
34423
  }
34424
+
34425
+ // If the value has a submenu, try to find the selected values in that menu
34426
+ if (menuValues[i].menu) {
34427
+ setSelected(menuValues[i].menu);
34428
+ }
34006
34429
  }
34430
+ }
34431
+
34432
+ self._values = values = settings.values;
34433
+ if (values) {
34434
+ setSelected(values);
34007
34435
 
34008
34436
  // Default with first item
34009
34437
  if (!selected && values.length > 0) {
@@ -34044,7 +34472,7 @@ define("tinymce/ui/ListBox", [
34044
34472
  * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
34045
34473
  */
34046
34474
  value: function(value) {
34047
- var self = this, active, selectedText, menu, i;
34475
+ var self = this, active, selectedText, menu;
34048
34476
 
34049
34477
  function activateByValue(menu, value) {
34050
34478
  menu.items().each(function(ctrl) {
@@ -34062,20 +34490,28 @@ define("tinymce/ui/ListBox", [
34062
34490
  });
34063
34491
  }
34064
34492
 
34493
+ function setActiveValues(menuValues) {
34494
+ for (var i = 0; i < menuValues.length; i++) {
34495
+ active = menuValues[i].value == value;
34496
+
34497
+ if (active) {
34498
+ selectedText = selectedText || menuValues[i].text;
34499
+ }
34500
+
34501
+ menuValues[i].active = active;
34502
+
34503
+ if (menuValues[i].menu) {
34504
+ setActiveValues(menuValues[i].menu);
34505
+ }
34506
+ }
34507
+ }
34508
+
34065
34509
  if (typeof(value) != "undefined") {
34066
34510
  if (self.menu) {
34067
34511
  activateByValue(self.menu, value);
34068
34512
  } else {
34069
34513
  menu = self.settings.menu;
34070
- for (i = 0; i < menu.length; i++) {
34071
- active = menu[i].value == value;
34072
-
34073
- if (active) {
34074
- selectedText = selectedText || menu[i].text;
34075
- }
34076
-
34077
- menu[i].active = active;
34078
- }
34514
+ setActiveValues(menu);
34079
34515
  }
34080
34516
 
34081
34517
  self.text(selectedText || this.settings.text);
@@ -34217,12 +34653,16 @@ define("tinymce/ui/MenuItem", [
34217
34653
 
34218
34654
  menu = self.menu = Factory.create(menu).parent(self).renderTo();
34219
34655
  menu.reflow();
34220
- menu.fire('show');
34221
34656
  menu.on('cancel', function(e) {
34222
34657
  e.stopPropagation();
34223
34658
  self.focus();
34224
34659
  menu.hide();
34225
34660
  });
34661
+ menu.on('show hide', function(e) {
34662
+ e.control.items().each(function(ctrl) {
34663
+ ctrl.active(ctrl.settings.selected);
34664
+ });
34665
+ }).fire('show');
34226
34666
 
34227
34667
  menu.on('hide', function(e) {
34228
34668
  if (e.control === menu) {
@@ -35319,5 +35759,5 @@ define("tinymce/ui/Throbber", [
35319
35759
  };
35320
35760
  });
35321
35761
 
35322
- expose(["tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/util/Tools","tinymce/dom/Range","tinymce/html/Entities","tinymce/Env","tinymce/dom/StyleSheetLoader","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/dom/TridentSelection","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/RangeUtils","tinymce/dom/Selection","tinymce/fmt/Preview","tinymce/Formatter","tinymce/UndoManager","tinymce/EnterKey","tinymce/ForceBlocks","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/DomUtils","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/util/Quirks","tinymce/util/Observable","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/LegacyInput","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/ui/ComboBox","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/ListBox","tinymce/ui/MenuItem","tinymce/ui/Menu","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox","tinymce/ui/Throbber"]);
35762
+ expose(["tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/util/Tools","tinymce/dom/Range","tinymce/html/Entities","tinymce/Env","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/dom/TridentSelection","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/BookmarkManager","tinymce/dom/Selection","tinymce/dom/ElementUtils","tinymce/Formatter","tinymce/UndoManager","tinymce/EnterKey","tinymce/ForceBlocks","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/DomUtils","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/util/Quirks","tinymce/util/Observable","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/LegacyInput","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/ui/ComboBox","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/ListBox","tinymce/ui/MenuItem","tinymce/ui/Menu","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox","tinymce/ui/Throbber"]);
35323
35763
  })(this);