tinymce-rails 4.0.26 → 4.0.28

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 98010f91779772edeb4ccd7b425414a2715f5d21
4
- data.tar.gz: cf3ffd85b9a52c9ec7a6bae0cc07cafe5b9de8de
3
+ metadata.gz: 3d61de30fa04cae313a1896b6cb9326acb089427
4
+ data.tar.gz: c505df76105015340cc701707fda4801201756ee
5
5
  SHA512:
6
- metadata.gz: 46b1d81eda6b817413d6620ec69693c45c080f44eeb8be4e629cfd7ef05fbd6810e19e343c5a778ed5b5189f4addf28246bc308d87c9d3cddd8ec5f001be5144
7
- data.tar.gz: c4fce036672158aa0a41f7c664030e011e6a6bf7bf300141a1fdf7a0c0133c29c829b570eca9ea154b4d0efb7000244fa29eb4be48b552d246b85c2c8b86892d
6
+ metadata.gz: 43bd86134162aa09da12d021d390f0795a1eeaa064b4eaa7b9a3daa5ce07cca60a777ac350c9cde96d495acd094a4a116f7020067c1b71a4cf2aec8513659399
7
+ data.tar.gz: cdf88d954d3b887f44a2a4f4e9834c5657a87659e9e9440a44d748dc52150957bc5d63dcb4a5cac3fe2d5543c4e930890d2870917e4e30435e46209d7c08ee0e
data/README.md CHANGED
@@ -25,8 +25,9 @@ Be sure to add to the global group, not the `assets` group. Then run `bundle ins
25
25
  **2. Create a `config/tinymce.yml` file with your global configuration options:**
26
26
 
27
27
  ```yml
28
- toolbar1: styleselect | bold italic | link image | undo redo
29
- toolbar2: table | fullscreen
28
+ toolbar:
29
+ - styleselect | bold italic | link image | undo redo
30
+ - table | fullscreen
30
31
  plugins:
31
32
  - table
32
33
  - fullscreen
@@ -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)
@@ -146,10 +146,15 @@ define("tinymce/html/Styles", [], function() {
146
146
  urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
147
147
  styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
148
148
  trimRightRegExp = /\s+$/,
149
- undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF';
149
+ undef, i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
150
150
 
151
151
  settings = settings || {};
152
152
 
153
+ if (schema) {
154
+ validStyles = schema.getValidStyles();
155
+ invalidStyles = schema.getInvalidStyles();
156
+ }
157
+
153
158
  encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
154
159
  for (i = 0; i < encodingItems.length; i++) {
155
160
  encodingLookup[encodingItems[i]] = invisibleChar + i;
@@ -407,16 +412,16 @@ define("tinymce/html/Styles", [], function() {
407
412
  *
408
413
  * @method serialize
409
414
  * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
410
- * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
415
+ * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
411
416
  * @return {String} String representation of the style object for example: border: 1px solid red.
412
417
  */
413
- serialize: function(styles, element_name) {
418
+ serialize: function(styles, elementName) {
414
419
  var css = '', name, value;
415
420
 
416
421
  function serializeStyles(name) {
417
422
  var styleList, i, l, value;
418
423
 
419
- styleList = schema.styles[name];
424
+ styleList = validStyles[name];
420
425
  if (styleList) {
421
426
  for (i = 0, l = styleList.length; i < l; i++) {
422
427
  name = styleList[i];
@@ -429,18 +434,36 @@ define("tinymce/html/Styles", [], function() {
429
434
  }
430
435
  }
431
436
 
437
+ function isValid(name, elementName) {
438
+ var styleMap;
439
+
440
+ styleMap = invalidStyles['*'];
441
+ if (styleMap && styleMap[name]) {
442
+ return false;
443
+ }
444
+
445
+ styleMap = invalidStyles[elementName];
446
+ if (styleMap && styleMap[name]) {
447
+ return false;
448
+ }
449
+
450
+ return true;
451
+ }
452
+
432
453
  // Serialize styles according to schema
433
- if (element_name && schema && schema.styles) {
454
+ if (elementName && validStyles) {
434
455
  // Serialize global styles and element specific styles
435
456
  serializeStyles('*');
436
- serializeStyles(element_name);
457
+ serializeStyles(elementName);
437
458
  } else {
438
459
  // Output the styles in the order they are inside the object
439
460
  for (name in styles) {
440
461
  value = styles[name];
441
462
 
442
463
  if (value !== undef && value.length > 0) {
443
- css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
464
+ if (!invalidStyles || isValid(name, elementName)) {
465
+ css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
466
+ }
444
467
  }
445
468
  }
446
469
  }
@@ -466,6 +489,11 @@ define("tinymce/html/Styles", [], function() {
466
489
  /*jshint loopfunc:true*/
467
490
  /*eslint no-loop-func:0 */
468
491
 
492
+ /**
493
+ * This class wraps the browsers native event logic with more convenient methods.
494
+ *
495
+ * @class tinymce.dom.EventUtils
496
+ */
469
497
  define("tinymce/dom/EventUtils", [], function() {
470
498
  "use strict";
471
499
 
@@ -3274,7 +3302,9 @@ define("tinymce/dom/DOMUtils", [
3274
3302
  selectorVal = selector;
3275
3303
 
3276
3304
  if (selector === '*') {
3277
- selector = function(node) {return node.nodeType == 1;};
3305
+ selector = function(node) {
3306
+ return node.nodeType == 1;
3307
+ };
3278
3308
  } else {
3279
3309
  selector = function(node) {
3280
3310
  return self.is(node, selectorVal);
@@ -3591,7 +3621,7 @@ define("tinymce/dom/DOMUtils", [
3591
3621
  });
3592
3622
 
3593
3623
  // Default px suffix on these
3594
- if (typeof(value) === 'number' && !numericCssMap[name]) {
3624
+ if (((typeof(value) === 'number') || /^[\-0-9\.]+$/.test(value)) && !numericCssMap[name]) {
3595
3625
  value += 'px';
3596
3626
  }
3597
3627
 
@@ -6352,7 +6382,8 @@ define("tinymce/html/Schema", [
6352
6382
  if (type != "html5-strict") {
6353
6383
  addAttrs("script", "language xml:space");
6354
6384
  addAttrs("style", "xml:space");
6355
- addAttrs("object", "declare classid codebase codetype archive standby align border hspace vspace");
6385
+ addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
6386
+ addAttrs("embed", "align name hspace vspace");
6356
6387
  addAttrs("param", "valuetype type");
6357
6388
  addAttrs("a", "charset name rev shape coords");
6358
6389
  addAttrs("br", "clear");
@@ -6421,6 +6452,27 @@ define("tinymce/html/Schema", [
6421
6452
  return schema;
6422
6453
  }
6423
6454
 
6455
+ function compileElementMap(value, mode) {
6456
+ var styles;
6457
+
6458
+ if (value) {
6459
+ styles = {};
6460
+
6461
+ if (typeof value == 'string') {
6462
+ value = {
6463
+ '*': value
6464
+ };
6465
+ }
6466
+
6467
+ // Convert styles into a rule list
6468
+ each(value, function(value, key) {
6469
+ styles[key] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/);
6470
+ });
6471
+ }
6472
+
6473
+ return styles;
6474
+ }
6475
+
6424
6476
  /**
6425
6477
  * Constructs a new Schema instance.
6426
6478
  *
@@ -6429,9 +6481,10 @@ define("tinymce/html/Schema", [
6429
6481
  * @param {Object} settings Name/value settings object.
6430
6482
  */
6431
6483
  return function(settings) {
6432
- var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
6433
- var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap;
6434
- var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, customElementsMap = {}, specialElements = {};
6484
+ var self = this, elements = {}, children = {}, patternElements = [], validStyles, invalidStyles, schemaItems;
6485
+ var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses;
6486
+ var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, textInlineElementsMap;
6487
+ var customElementsMap = {}, specialElements = {};
6435
6488
 
6436
6489
  // Creates an lookup table map object for the specified option or the default value
6437
6490
  function createLookupTable(option, default_value, extendWith) {
@@ -6463,15 +6516,9 @@ define("tinymce/html/Schema", [
6463
6516
  settings.valid_elements = '*[*]';
6464
6517
  }
6465
6518
 
6466
- // Build styles list
6467
- if (settings.valid_styles) {
6468
- validStyles = {};
6469
-
6470
- // Convert styles into a rule list
6471
- each(settings.valid_styles, function(value, key) {
6472
- validStyles[key] = explode(value);
6473
- });
6474
- }
6519
+ validStyles = compileElementMap(settings.valid_styles);
6520
+ invalidStyles = compileElementMap(settings.invalid_styles, 'map');
6521
+ validClasses = compileElementMap(settings.valid_classes, 'map');
6475
6522
 
6476
6523
  // Setup map objects
6477
6524
  whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
@@ -6486,6 +6533,8 @@ define("tinymce/html/Schema", [
6486
6533
  blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
6487
6534
  'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
6488
6535
  'datalist select optgroup', textBlockElementsMap);
6536
+ textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
6537
+ 'dfn code mark q sup sub samp');
6489
6538
 
6490
6539
  each((settings.special || 'script noscript style textarea').split(' '), function(name) {
6491
6540
  specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
@@ -6842,10 +6891,32 @@ define("tinymce/html/Schema", [
6842
6891
  /**
6843
6892
  * Name/value map object with valid styles for each element.
6844
6893
  *
6845
- * @field styles
6894
+ * @method getValidStyles
6895
+ * @type Object
6896
+ */
6897
+ self.getValidStyles = function() {
6898
+ return validStyles;
6899
+ };
6900
+
6901
+ /**
6902
+ * Name/value map object with valid styles for each element.
6903
+ *
6904
+ * @method getInvalidStyles
6905
+ * @type Object
6906
+ */
6907
+ self.getInvalidStyles = function() {
6908
+ return invalidStyles;
6909
+ };
6910
+
6911
+ /**
6912
+ * Name/value map object with valid classes for each element.
6913
+ *
6914
+ * @method getValidClasses
6846
6915
  * @type Object
6847
6916
  */
6848
- self.styles = validStyles;
6917
+ self.getValidClasses = function() {
6918
+ return validClasses;
6919
+ };
6849
6920
 
6850
6921
  /**
6851
6922
  * Returns a map with boolean attributes.
@@ -6877,6 +6948,16 @@ define("tinymce/html/Schema", [
6877
6948
  return textBlockElementsMap;
6878
6949
  };
6879
6950
 
6951
+ /**
6952
+ * Returns a map of inline text format nodes for example strong/span or ins.
6953
+ *
6954
+ * @method getTextInlineElements
6955
+ * @return {Object} Name/value lookup map for text format elements.
6956
+ */
6957
+ self.getTextInlineElements = function() {
6958
+ return textInlineElementsMap;
6959
+ };
6960
+
6880
6961
  /**
6881
6962
  * Returns a map with short ended elements such as BR or IMG.
6882
6963
  *
@@ -7106,6 +7187,36 @@ define("tinymce/html/SaxParser", [
7106
7187
  ], function(Schema, Entities, Tools) {
7107
7188
  var each = Tools.each;
7108
7189
 
7190
+ /**
7191
+ * Returns the index of the end tag for a specific start tag. This can be
7192
+ * used to skip all children of a parent element from being processed.
7193
+ */
7194
+ function skipUntilEndTag(schema, html, startIndex) {
7195
+ var count = 1, matches, tokenRegExp, shortEndedElements;
7196
+
7197
+ shortEndedElements = schema.getShortEndedElements();
7198
+ tokenRegExp = /<([!?\/])?([A-Za-z0-9\-\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
7199
+ tokenRegExp.lastIndex = startIndex;
7200
+
7201
+ while ((matches = tokenRegExp.exec(html))) {
7202
+ if (matches[1] === '/') { // End element
7203
+ count--;
7204
+ } else if (!matches[1]) { // Start element
7205
+ if (matches[2] in shortEndedElements) {
7206
+ continue;
7207
+ }
7208
+
7209
+ count++;
7210
+ }
7211
+
7212
+ if (count === 0) {
7213
+ break;
7214
+ }
7215
+ }
7216
+
7217
+ return tokenRegExp.lastIndex;
7218
+ }
7219
+
7109
7220
  /**
7110
7221
  * Constructs a new SaxParser instance.
7111
7222
  *
@@ -7387,7 +7498,13 @@ define("tinymce/html/SaxParser", [
7387
7498
  }
7388
7499
 
7389
7500
  // Invalidate element if it's marked as bogus
7390
- if (attrList.map['data-mce-bogus']) {
7501
+ if ((attr = attrList.map['data-mce-bogus'])) {
7502
+ if (attr === 'all') {
7503
+ index = skipUntilEndTag(schema, html, tokenRegExp.lastIndex);
7504
+ tokenRegExp.lastIndex = index;
7505
+ continue;
7506
+ }
7507
+
7391
7508
  isValidElement = false;
7392
7509
  }
7393
7510
  }
@@ -8050,7 +8167,7 @@ define("tinymce/html/DomParser", [
8050
8167
  // Leave nodes that have a name like <a name="name">
8051
8168
  if (!node.attributes.map.name && !node.attributes.map.id) {
8052
8169
  tempNode = node.parent;
8053
- node.empty().remove();
8170
+ node.unwrap();
8054
8171
  node = tempNode;
8055
8172
  return;
8056
8173
  }
@@ -8229,6 +8346,48 @@ define("tinymce/html/DomParser", [
8229
8346
  }
8230
8347
  });
8231
8348
  }
8349
+
8350
+ if (settings.validate && schema.getValidClasses()) {
8351
+ self.addAttributeFilter('class', function(nodes) {
8352
+ var i = nodes.length, node, classList, ci, className, classValue;
8353
+ var validClasses = schema.getValidClasses(), validClassesMap, valid;
8354
+
8355
+ while (i--) {
8356
+ node = nodes[i];
8357
+ classList = node.attr('class').split(' ');
8358
+ classValue = '';
8359
+
8360
+ for (ci = 0; ci < classList.length; ci++) {
8361
+ className = classList[ci];
8362
+ valid = false;
8363
+
8364
+ validClassesMap = validClasses['*'];
8365
+ if (validClassesMap && validClassesMap[className]) {
8366
+ valid = true;
8367
+ }
8368
+
8369
+ validClassesMap = validClasses[node.name];
8370
+ if (!valid && validClassesMap && !validClassesMap[className]) {
8371
+ valid = true;
8372
+ }
8373
+
8374
+ if (valid) {
8375
+ if (classValue) {
8376
+ classValue += ' ';
8377
+ }
8378
+
8379
+ classValue += className;
8380
+ }
8381
+ }
8382
+
8383
+ if (!classValue.length) {
8384
+ classValue = null;
8385
+ }
8386
+
8387
+ node.attr('class', classValue);
8388
+ }
8389
+ });
8390
+ }
8232
8391
  };
8233
8392
  });
8234
8393
 
@@ -8711,15 +8870,6 @@ define("tinymce/dom/Serializer", [
8711
8870
  }
8712
8871
  });
8713
8872
 
8714
- // Remove expando attributes
8715
- htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name) {
8716
- var i = nodes.length;
8717
-
8718
- while (i--) {
8719
- nodes[i].attr(name, null);
8720
- }
8721
- });
8722
-
8723
8873
  htmlParser.addNodeFilter('noscript', function(nodes) {
8724
8874
  var i = nodes.length, node;
8725
8875
 
@@ -8734,7 +8884,7 @@ define("tinymce/dom/Serializer", [
8734
8884
 
8735
8885
  // Force script into CDATA sections and remove the mce- prefix also add comments around styles
8736
8886
  htmlParser.addNodeFilter('script,style', function(nodes, name) {
8737
- var i = nodes.length, node, value;
8887
+ var i = nodes.length, node, value, type;
8738
8888
 
8739
8889
  function trim(value) {
8740
8890
  /*jshint maxlen:255 */
@@ -8750,9 +8900,12 @@ define("tinymce/dom/Serializer", [
8750
8900
  value = node.firstChild ? node.firstChild.value : '';
8751
8901
 
8752
8902
  if (name === "script") {
8753
- // Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
8754
- var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
8755
- node.attr('type', type === 'text/javascript' ? null : type);
8903
+ // Remove mce- prefix from script elements and remove default type since the user specified
8904
+ // a script element without type attribute
8905
+ type = node.attr('type');
8906
+ if (type) {
8907
+ node.attr('type', type == 'mce-no/type' ? null : type.replace(/^mce\-/, ''));
8908
+ }
8756
8909
 
8757
8910
  if (value.length > 0) {
8758
8911
  node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
@@ -8819,13 +8972,19 @@ define("tinymce/dom/Serializer", [
8819
8972
  }
8820
8973
 
8821
8974
  // Remove internal data attributes
8822
- htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,data-mce-selected', function(nodes, name) {
8823
- var i = nodes.length;
8975
+ htmlParser.addAttributeFilter(
8976
+ 'data-mce-src,data-mce-href,data-mce-style,' +
8977
+ 'data-mce-selected,data-mce-expando,' +
8978
+ 'data-mce-type,data-mce-resize',
8824
8979
 
8825
- while (i--) {
8826
- nodes[i].attr(name, null);
8980
+ function(nodes, name) {
8981
+ var i = nodes.length;
8982
+
8983
+ while (i--) {
8984
+ nodes[i].attr(name, null);
8985
+ }
8827
8986
  }
8828
- });
8987
+ );
8829
8988
 
8830
8989
  // Return public methods
8831
8990
  return {
@@ -9161,15 +9320,17 @@ define("tinymce/dom/TridentSelection", [], function() {
9161
9320
 
9162
9321
  // Find the text node and offset
9163
9322
  while (sibling) {
9164
- nodeValue = sibling.nodeValue;
9165
- textNodeOffset += nodeValue.length;
9166
-
9167
- // We are at or passed the position we where looking for
9168
- if (textNodeOffset >= offset) {
9169
- container = sibling;
9170
- textNodeOffset -= offset;
9171
- textNodeOffset = nodeValue.length - textNodeOffset;
9172
- break;
9323
+ if (sibling.nodeType == 3) {
9324
+ nodeValue = sibling.nodeValue;
9325
+ textNodeOffset += nodeValue.length;
9326
+
9327
+ // We are at or passed the position we where looking for
9328
+ if (textNodeOffset >= offset) {
9329
+ container = sibling;
9330
+ textNodeOffset -= offset;
9331
+ textNodeOffset = nodeValue.length - textNodeOffset;
9332
+ break;
9333
+ }
9173
9334
  }
9174
9335
 
9175
9336
  sibling = sibling.nextSibling;
@@ -9194,13 +9355,15 @@ define("tinymce/dom/TridentSelection", [], function() {
9194
9355
  }
9195
9356
 
9196
9357
  while (sibling) {
9197
- textNodeOffset += sibling.nodeValue.length;
9358
+ if (sibling.nodeType == 3) {
9359
+ textNodeOffset += sibling.nodeValue.length;
9198
9360
 
9199
- // We are at or passed the position we where looking for
9200
- if (textNodeOffset >= offset) {
9201
- container = sibling;
9202
- textNodeOffset -= offset;
9203
- break;
9361
+ // We are at or passed the position we where looking for
9362
+ if (textNodeOffset >= offset) {
9363
+ container = sibling;
9364
+ textNodeOffset -= offset;
9365
+ break;
9366
+ }
9204
9367
  }
9205
9368
 
9206
9369
  sibling = sibling.previousSibling;
@@ -9533,8 +9696,8 @@ define("tinymce/util/VK", [
9533
9696
  },
9534
9697
 
9535
9698
  metaKeyPressed: function(e) {
9536
- // Check if ctrl or meta key is pressed also check if alt is false for Polish users
9537
- return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
9699
+ // Check if ctrl or meta key is pressed. Edge case for AltGr on Windows where it produces ctrlKey+altKey states
9700
+ return (Env.mac ? e.metaKey : e.ctrlKey && !e.altKey);
9538
9701
  }
9539
9702
  };
9540
9703
  });
@@ -10066,7 +10229,7 @@ define("tinymce/dom/ControlSelection", [
10066
10229
  // Included from: js/tinymce/classes/dom/RangeUtils.js
10067
10230
 
10068
10231
  /**
10069
- * Range.js
10232
+ * RangeUtils.js
10070
10233
  *
10071
10234
  * Copyright, Moxiecode Systems AB
10072
10235
  * Released under LGPL License.
@@ -10076,7 +10239,7 @@ define("tinymce/dom/ControlSelection", [
10076
10239
  */
10077
10240
 
10078
10241
  /**
10079
- * RangeUtils
10242
+ * This class contains a few utility methods for ranges.
10080
10243
  *
10081
10244
  * @class tinymce.dom.RangeUtils
10082
10245
  * @private
@@ -10087,6 +10250,20 @@ define("tinymce/dom/RangeUtils", [
10087
10250
  ], function(Tools, TreeWalker) {
10088
10251
  var each = Tools.each;
10089
10252
 
10253
+ function getEndChild(container, index) {
10254
+ var childNodes = container.childNodes;
10255
+
10256
+ index--;
10257
+
10258
+ if (index > childNodes.length - 1) {
10259
+ index = childNodes.length - 1;
10260
+ } else if (index < 0) {
10261
+ index = 0;
10262
+ }
10263
+
10264
+ return childNodes[index] || container;
10265
+ }
10266
+
10090
10267
  function RangeUtils(dom) {
10091
10268
  /**
10092
10269
  * Walks the specified range like object and executes the callback for each sibling collection it finds.
@@ -10199,7 +10376,7 @@ define("tinymce/dom/RangeUtils", [
10199
10376
 
10200
10377
  // If index based end position then resolve it
10201
10378
  if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
10202
- endContainer = endContainer.childNodes[Math.min(endOffset - 1, endContainer.childNodes.length - 1)];
10379
+ endContainer = getEndChild(endContainer, endOffset);
10203
10380
  }
10204
10381
 
10205
10382
  // Same container
@@ -10546,6 +10723,398 @@ define("tinymce/dom/RangeUtils", [
10546
10723
  return RangeUtils;
10547
10724
  });
10548
10725
 
10726
+ // Included from: js/tinymce/classes/dom/BookmarkManager.js
10727
+
10728
+ /**
10729
+ * BookmarkManager.js
10730
+ *
10731
+ * Copyright, Moxiecode Systems AB
10732
+ * Released under LGPL License.
10733
+ *
10734
+ * License: http://www.tinymce.com/license
10735
+ * Contributing: http://www.tinymce.com/contributing
10736
+ */
10737
+
10738
+ /**
10739
+ * This class handles selection bookmarks.
10740
+ *
10741
+ * @class tinymce.dom.BookmarkManager
10742
+ */
10743
+ define("tinymce/dom/BookmarkManager", [
10744
+ "tinymce/Env",
10745
+ "tinymce/util/Tools"
10746
+ ], function(Env, Tools) {
10747
+ /**
10748
+ * Constructs a new BookmarkManager instance for a specific selection instance.
10749
+ *
10750
+ * @constructor
10751
+ * @method BookmarkManager
10752
+ * @param {tinymce.dom.Selection} selection Selection instance to handle bookmarks for.
10753
+ */
10754
+ function BookmarkManager(selection) {
10755
+ var dom = selection.dom;
10756
+
10757
+ /**
10758
+ * Returns a bookmark location for the current selection. This bookmark object
10759
+ * can then be used to restore the selection after some content modification to the document.
10760
+ *
10761
+ * @method getBookmark
10762
+ * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
10763
+ * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
10764
+ * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
10765
+ * @example
10766
+ * // Stores a bookmark of the current selection
10767
+ * var bm = tinymce.activeEditor.selection.getBookmark();
10768
+ *
10769
+ * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
10770
+ *
10771
+ * // Restore the selection bookmark
10772
+ * tinymce.activeEditor.selection.moveToBookmark(bm);
10773
+ */
10774
+ this.getBookmark = function(type, normalized) {
10775
+ var rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
10776
+
10777
+ function findIndex(name, element) {
10778
+ var index = 0;
10779
+
10780
+ Tools.each(dom.select(name), function(node, i) {
10781
+ if (node == element) {
10782
+ index = i;
10783
+ }
10784
+ });
10785
+
10786
+ return index;
10787
+ }
10788
+
10789
+ function normalizeTableCellSelection(rng) {
10790
+ function moveEndPoint(start) {
10791
+ var container, offset, childNodes, prefix = start ? 'start' : 'end';
10792
+
10793
+ container = rng[prefix + 'Container'];
10794
+ offset = rng[prefix + 'Offset'];
10795
+
10796
+ if (container.nodeType == 1 && container.nodeName == "TR") {
10797
+ childNodes = container.childNodes;
10798
+ container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
10799
+ if (container) {
10800
+ offset = start ? 0 : container.childNodes.length;
10801
+ rng['set' + (start ? 'Start' : 'End')](container, offset);
10802
+ }
10803
+ }
10804
+ }
10805
+
10806
+ moveEndPoint(true);
10807
+ moveEndPoint();
10808
+
10809
+ return rng;
10810
+ }
10811
+
10812
+ function getLocation() {
10813
+ var rng = selection.getRng(true), root = dom.getRoot(), bookmark = {};
10814
+
10815
+ function getPoint(rng, start) {
10816
+ var container = rng[start ? 'startContainer' : 'endContainer'],
10817
+ offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
10818
+
10819
+ if (container.nodeType == 3) {
10820
+ if (normalized) {
10821
+ for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
10822
+ offset += node.nodeValue.length;
10823
+ }
10824
+ }
10825
+
10826
+ point.push(offset);
10827
+ } else {
10828
+ childNodes = container.childNodes;
10829
+
10830
+ if (offset >= childNodes.length && childNodes.length) {
10831
+ after = 1;
10832
+ offset = Math.max(0, childNodes.length - 1);
10833
+ }
10834
+
10835
+ point.push(dom.nodeIndex(childNodes[offset], normalized) + after);
10836
+ }
10837
+
10838
+ for (; container && container != root; container = container.parentNode) {
10839
+ point.push(dom.nodeIndex(container, normalized));
10840
+ }
10841
+
10842
+ return point;
10843
+ }
10844
+
10845
+ bookmark.start = getPoint(rng, true);
10846
+
10847
+ if (!selection.isCollapsed()) {
10848
+ bookmark.end = getPoint(rng);
10849
+ }
10850
+
10851
+ return bookmark;
10852
+ }
10853
+
10854
+ if (type == 2) {
10855
+ element = selection.getNode();
10856
+ name = element ? element.nodeName : null;
10857
+
10858
+ if (name == 'IMG') {
10859
+ return {name: name, index: findIndex(name, element)};
10860
+ }
10861
+
10862
+ if (selection.tridentSel) {
10863
+ return selection.tridentSel.getBookmark(type);
10864
+ }
10865
+
10866
+ return getLocation();
10867
+ }
10868
+
10869
+ // Handle simple range
10870
+ if (type) {
10871
+ return {rng: selection.getRng()};
10872
+ }
10873
+
10874
+ rng = selection.getRng();
10875
+ id = dom.uniqueId();
10876
+ collapsed = selection.isCollapsed();
10877
+ styles = 'overflow:hidden;line-height:0px';
10878
+
10879
+ // Explorer method
10880
+ if (rng.duplicate || rng.item) {
10881
+ // Text selection
10882
+ if (!rng.item) {
10883
+ rng2 = rng.duplicate();
10884
+
10885
+ try {
10886
+ // Insert start marker
10887
+ rng.collapse();
10888
+ rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
10889
+
10890
+ // Insert end marker
10891
+ if (!collapsed) {
10892
+ rng2.collapse(false);
10893
+
10894
+ // Detect the empty space after block elements in IE and move the
10895
+ // end back one character <p></p>] becomes <p>]</p>
10896
+ rng.moveToElementText(rng2.parentElement());
10897
+ if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
10898
+ rng2.move('character', -1);
10899
+ }
10900
+
10901
+ rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
10902
+ }
10903
+ } catch (ex) {
10904
+ // IE might throw unspecified error so lets ignore it
10905
+ return null;
10906
+ }
10907
+ } else {
10908
+ // Control selection
10909
+ element = rng.item(0);
10910
+ name = element.nodeName;
10911
+
10912
+ return {name: name, index: findIndex(name, element)};
10913
+ }
10914
+ } else {
10915
+ element = selection.getNode();
10916
+ name = element.nodeName;
10917
+ if (name == 'IMG') {
10918
+ return {name: name, index: findIndex(name, element)};
10919
+ }
10920
+
10921
+ // W3C method
10922
+ rng2 = normalizeTableCellSelection(rng.cloneRange());
10923
+
10924
+ // Insert end marker
10925
+ if (!collapsed) {
10926
+ rng2.collapse(false);
10927
+ rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
10928
+ }
10929
+
10930
+ rng = normalizeTableCellSelection(rng);
10931
+ rng.collapse(true);
10932
+ rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
10933
+ }
10934
+
10935
+ selection.moveToBookmark({id: id, keep: 1});
10936
+
10937
+ return {id: id};
10938
+ };
10939
+
10940
+ /**
10941
+ * Restores the selection to the specified bookmark.
10942
+ *
10943
+ * @method moveToBookmark
10944
+ * @param {Object} bookmark Bookmark to restore selection from.
10945
+ * @return {Boolean} true/false if it was successful or not.
10946
+ * @example
10947
+ * // Stores a bookmark of the current selection
10948
+ * var bm = tinymce.activeEditor.selection.getBookmark();
10949
+ *
10950
+ * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
10951
+ *
10952
+ * // Restore the selection bookmark
10953
+ * tinymce.activeEditor.selection.moveToBookmark(bm);
10954
+ */
10955
+ this.moveToBookmark = function(bookmark) {
10956
+ var rng, root, startContainer, endContainer, startOffset, endOffset;
10957
+
10958
+ function setEndPoint(start) {
10959
+ var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
10960
+
10961
+ if (point) {
10962
+ offset = point[0];
10963
+
10964
+ // Find container node
10965
+ for (node = root, i = point.length - 1; i >= 1; i--) {
10966
+ children = node.childNodes;
10967
+
10968
+ if (point[i] > children.length - 1) {
10969
+ return;
10970
+ }
10971
+
10972
+ node = children[point[i]];
10973
+ }
10974
+
10975
+ // Move text offset to best suitable location
10976
+ if (node.nodeType === 3) {
10977
+ offset = Math.min(point[0], node.nodeValue.length);
10978
+ }
10979
+
10980
+ // Move element offset to best suitable location
10981
+ if (node.nodeType === 1) {
10982
+ offset = Math.min(point[0], node.childNodes.length);
10983
+ }
10984
+
10985
+ // Set offset within container node
10986
+ if (start) {
10987
+ rng.setStart(node, offset);
10988
+ } else {
10989
+ rng.setEnd(node, offset);
10990
+ }
10991
+ }
10992
+
10993
+ return true;
10994
+ }
10995
+
10996
+ function restoreEndPoint(suffix) {
10997
+ var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
10998
+
10999
+ if (marker) {
11000
+ node = marker.parentNode;
11001
+
11002
+ if (suffix == 'start') {
11003
+ if (!keep) {
11004
+ idx = dom.nodeIndex(marker);
11005
+ } else {
11006
+ node = marker.firstChild;
11007
+ idx = 1;
11008
+ }
11009
+
11010
+ startContainer = endContainer = node;
11011
+ startOffset = endOffset = idx;
11012
+ } else {
11013
+ if (!keep) {
11014
+ idx = dom.nodeIndex(marker);
11015
+ } else {
11016
+ node = marker.firstChild;
11017
+ idx = 1;
11018
+ }
11019
+
11020
+ endContainer = node;
11021
+ endOffset = idx;
11022
+ }
11023
+
11024
+ if (!keep) {
11025
+ prev = marker.previousSibling;
11026
+ next = marker.nextSibling;
11027
+
11028
+ // Remove all marker text nodes
11029
+ Tools.each(Tools.grep(marker.childNodes), function(node) {
11030
+ if (node.nodeType == 3) {
11031
+ node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
11032
+ }
11033
+ });
11034
+
11035
+ // Remove marker but keep children if for example contents where inserted into the marker
11036
+ // Also remove duplicated instances of the marker for example by a
11037
+ // split operation or by WebKit auto split on paste feature
11038
+ while ((marker = dom.get(bookmark.id + '_' + suffix))) {
11039
+ dom.remove(marker, 1);
11040
+ }
11041
+
11042
+ // If siblings are text nodes then merge them unless it's Opera since it some how removes the node
11043
+ // and we are sniffing since adding a lot of detection code for a browser with 3% of the market
11044
+ // isn't worth the effort. Sorry, Opera but it's just a fact
11045
+ if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !Env.opera) {
11046
+ idx = prev.nodeValue.length;
11047
+ prev.appendData(next.nodeValue);
11048
+ dom.remove(next);
11049
+
11050
+ if (suffix == 'start') {
11051
+ startContainer = endContainer = prev;
11052
+ startOffset = endOffset = idx;
11053
+ } else {
11054
+ endContainer = prev;
11055
+ endOffset = idx;
11056
+ }
11057
+ }
11058
+ }
11059
+ }
11060
+ }
11061
+
11062
+ function addBogus(node) {
11063
+ // Adds a bogus BR element for empty block elements
11064
+ if (dom.isBlock(node) && !node.innerHTML && !Env.ie) {
11065
+ node.innerHTML = '<br data-mce-bogus="1" />';
11066
+ }
11067
+
11068
+ return node;
11069
+ }
11070
+
11071
+ if (bookmark) {
11072
+ if (bookmark.start) {
11073
+ rng = dom.createRng();
11074
+ root = dom.getRoot();
11075
+
11076
+ if (selection.tridentSel) {
11077
+ return selection.tridentSel.moveToBookmark(bookmark);
11078
+ }
11079
+
11080
+ if (setEndPoint(true) && setEndPoint()) {
11081
+ selection.setRng(rng);
11082
+ }
11083
+ } else if (bookmark.id) {
11084
+ // Restore start/end points
11085
+ restoreEndPoint('start');
11086
+ restoreEndPoint('end');
11087
+
11088
+ if (startContainer) {
11089
+ rng = dom.createRng();
11090
+ rng.setStart(addBogus(startContainer), startOffset);
11091
+ rng.setEnd(addBogus(endContainer), endOffset);
11092
+ selection.setRng(rng);
11093
+ }
11094
+ } else if (bookmark.name) {
11095
+ selection.select(dom.select(bookmark.name)[bookmark.index]);
11096
+ } else if (bookmark.rng) {
11097
+ selection.setRng(bookmark.rng);
11098
+ }
11099
+ }
11100
+ };
11101
+ }
11102
+
11103
+ /**
11104
+ * Returns true/false if the specified node is a bookmark node or not.
11105
+ *
11106
+ * @static
11107
+ * @method isBookmarkNode
11108
+ * @param {DOMNode} node DOM Node to check if it's a bookmark node or not.
11109
+ * @return {Boolean} true/false if the node is a bookmark node or not.
11110
+ */
11111
+ BookmarkManager.isBookmarkNode = function(node) {
11112
+ return node && node.tagName === 'SPAN' && node.getAttribute('data-mce-type') === 'bookmark';
11113
+ };
11114
+
11115
+ return BookmarkManager;
11116
+ });
11117
+
10549
11118
  // Included from: js/tinymce/classes/dom/Selection.js
10550
11119
 
10551
11120
  /**
@@ -10572,11 +11141,12 @@ define("tinymce/dom/Selection", [
10572
11141
  "tinymce/dom/TridentSelection",
10573
11142
  "tinymce/dom/ControlSelection",
10574
11143
  "tinymce/dom/RangeUtils",
11144
+ "tinymce/dom/BookmarkManager",
10575
11145
  "tinymce/Env",
10576
11146
  "tinymce/util/Tools"
10577
- ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, Env, Tools) {
10578
- var each = Tools.each, grep = Tools.grep, trim = Tools.trim;
10579
- var isIE = Env.ie, isOpera = Env.opera;
11147
+ ], function(TreeWalker, TridentSelection, ControlSelection, RangeUtils, BookmarkManager, Env, Tools) {
11148
+ var each = Tools.each, trim = Tools.trim;
11149
+ var isIE = Env.ie;
10580
11150
 
10581
11151
  /**
10582
11152
  * Constructs a new selection instance.
@@ -10594,7 +11164,7 @@ define("tinymce/dom/Selection", [
10594
11164
  self.win = win;
10595
11165
  self.serializer = serializer;
10596
11166
  self.editor = editor;
10597
-
11167
+ self.bookmarkManager = new BookmarkManager(self);
10598
11168
  self.controlSelection = new ControlSelection(self, editor);
10599
11169
 
10600
11170
  // No W3C Range support
@@ -10894,169 +11464,7 @@ define("tinymce/dom/Selection", [
10894
11464
  * tinymce.activeEditor.selection.moveToBookmark(bm);
10895
11465
  */
10896
11466
  getBookmark: function(type, normalized) {
10897
- var self = this, dom = self.dom, rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
10898
-
10899
- function findIndex(name, element) {
10900
- var index = 0;
10901
-
10902
- each(dom.select(name), function(node, i) {
10903
- if (node == element) {
10904
- index = i;
10905
- }
10906
- });
10907
-
10908
- return index;
10909
- }
10910
-
10911
- function normalizeTableCellSelection(rng) {
10912
- function moveEndPoint(start) {
10913
- var container, offset, childNodes, prefix = start ? 'start' : 'end';
10914
-
10915
- container = rng[prefix + 'Container'];
10916
- offset = rng[prefix + 'Offset'];
10917
-
10918
- if (container.nodeType == 1 && container.nodeName == "TR") {
10919
- childNodes = container.childNodes;
10920
- container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
10921
- if (container) {
10922
- offset = start ? 0 : container.childNodes.length;
10923
- rng['set' + (start ? 'Start' : 'End')](container, offset);
10924
- }
10925
- }
10926
- }
10927
-
10928
- moveEndPoint(true);
10929
- moveEndPoint();
10930
-
10931
- return rng;
10932
- }
10933
-
10934
- function getLocation() {
10935
- var rng = self.getRng(true), root = dom.getRoot(), bookmark = {};
10936
-
10937
- function getPoint(rng, start) {
10938
- var container = rng[start ? 'startContainer' : 'endContainer'],
10939
- offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
10940
-
10941
- if (container.nodeType == 3) {
10942
- if (normalized) {
10943
- for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
10944
- offset += node.nodeValue.length;
10945
- }
10946
- }
10947
-
10948
- point.push(offset);
10949
- } else {
10950
- childNodes = container.childNodes;
10951
-
10952
- if (offset >= childNodes.length && childNodes.length) {
10953
- after = 1;
10954
- offset = Math.max(0, childNodes.length - 1);
10955
- }
10956
-
10957
- point.push(self.dom.nodeIndex(childNodes[offset], normalized) + after);
10958
- }
10959
-
10960
- for (; container && container != root; container = container.parentNode) {
10961
- point.push(self.dom.nodeIndex(container, normalized));
10962
- }
10963
-
10964
- return point;
10965
- }
10966
-
10967
- bookmark.start = getPoint(rng, true);
10968
-
10969
- if (!self.isCollapsed()) {
10970
- bookmark.end = getPoint(rng);
10971
- }
10972
-
10973
- return bookmark;
10974
- }
10975
-
10976
- if (type == 2) {
10977
- element = self.getNode();
10978
- name = element ? element.nodeName : null;
10979
-
10980
- if (name == 'IMG') {
10981
- return {name: name, index: findIndex(name, element)};
10982
- }
10983
-
10984
- if (self.tridentSel) {
10985
- return self.tridentSel.getBookmark(type);
10986
- }
10987
-
10988
- return getLocation();
10989
- }
10990
-
10991
- // Handle simple range
10992
- if (type) {
10993
- return {rng: self.getRng()};
10994
- }
10995
-
10996
- rng = self.getRng();
10997
- id = dom.uniqueId();
10998
- collapsed = self.isCollapsed();
10999
- styles = 'overflow:hidden;line-height:0px';
11000
-
11001
- // Explorer method
11002
- if (rng.duplicate || rng.item) {
11003
- // Text selection
11004
- if (!rng.item) {
11005
- rng2 = rng.duplicate();
11006
-
11007
- try {
11008
- // Insert start marker
11009
- rng.collapse();
11010
- rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
11011
-
11012
- // Insert end marker
11013
- if (!collapsed) {
11014
- rng2.collapse(false);
11015
-
11016
- // Detect the empty space after block elements in IE and move the
11017
- // end back one character <p></p>] becomes <p>]</p>
11018
- rng.moveToElementText(rng2.parentElement());
11019
- if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
11020
- rng2.move('character', -1);
11021
- }
11022
-
11023
- rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
11024
- }
11025
- } catch (ex) {
11026
- // IE might throw unspecified error so lets ignore it
11027
- return null;
11028
- }
11029
- } else {
11030
- // Control selection
11031
- element = rng.item(0);
11032
- name = element.nodeName;
11033
-
11034
- return {name: name, index: findIndex(name, element)};
11035
- }
11036
- } else {
11037
- element = self.getNode();
11038
- name = element.nodeName;
11039
- if (name == 'IMG') {
11040
- return {name: name, index: findIndex(name, element)};
11041
- }
11042
-
11043
- // W3C method
11044
- rng2 = normalizeTableCellSelection(rng.cloneRange());
11045
-
11046
- // Insert end marker
11047
- if (!collapsed) {
11048
- rng2.collapse(false);
11049
- rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
11050
- }
11051
-
11052
- rng = normalizeTableCellSelection(rng);
11053
- rng.collapse(true);
11054
- rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
11055
- }
11056
-
11057
- self.moveToBookmark({id: id, keep: 1});
11058
-
11059
- return {id: id};
11467
+ return this.bookmarkManager.getBookmark(type, normalized);
11060
11468
  },
11061
11469
 
11062
11470
  /**
@@ -11075,150 +11483,7 @@ define("tinymce/dom/Selection", [
11075
11483
  * tinymce.activeEditor.selection.moveToBookmark(bm);
11076
11484
  */
11077
11485
  moveToBookmark: function(bookmark) {
11078
- var self = this, dom = self.dom, rng, root, startContainer, endContainer, startOffset, endOffset;
11079
-
11080
- function setEndPoint(start) {
11081
- var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
11082
-
11083
- if (point) {
11084
- offset = point[0];
11085
-
11086
- // Find container node
11087
- for (node = root, i = point.length - 1; i >= 1; i--) {
11088
- children = node.childNodes;
11089
-
11090
- if (point[i] > children.length - 1) {
11091
- return;
11092
- }
11093
-
11094
- node = children[point[i]];
11095
- }
11096
-
11097
- // Move text offset to best suitable location
11098
- if (node.nodeType === 3) {
11099
- offset = Math.min(point[0], node.nodeValue.length);
11100
- }
11101
-
11102
- // Move element offset to best suitable location
11103
- if (node.nodeType === 1) {
11104
- offset = Math.min(point[0], node.childNodes.length);
11105
- }
11106
-
11107
- // Set offset within container node
11108
- if (start) {
11109
- rng.setStart(node, offset);
11110
- } else {
11111
- rng.setEnd(node, offset);
11112
- }
11113
- }
11114
-
11115
- return true;
11116
- }
11117
-
11118
- function restoreEndPoint(suffix) {
11119
- var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
11120
-
11121
- if (marker) {
11122
- node = marker.parentNode;
11123
-
11124
- if (suffix == 'start') {
11125
- if (!keep) {
11126
- idx = dom.nodeIndex(marker);
11127
- } else {
11128
- node = marker.firstChild;
11129
- idx = 1;
11130
- }
11131
-
11132
- startContainer = endContainer = node;
11133
- startOffset = endOffset = idx;
11134
- } else {
11135
- if (!keep) {
11136
- idx = dom.nodeIndex(marker);
11137
- } else {
11138
- node = marker.firstChild;
11139
- idx = 1;
11140
- }
11141
-
11142
- endContainer = node;
11143
- endOffset = idx;
11144
- }
11145
-
11146
- if (!keep) {
11147
- prev = marker.previousSibling;
11148
- next = marker.nextSibling;
11149
-
11150
- // Remove all marker text nodes
11151
- each(grep(marker.childNodes), function(node) {
11152
- if (node.nodeType == 3) {
11153
- node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
11154
- }
11155
- });
11156
-
11157
- // Remove marker but keep children if for example contents where inserted into the marker
11158
- // Also remove duplicated instances of the marker for example by a
11159
- // split operation or by WebKit auto split on paste feature
11160
- while ((marker = dom.get(bookmark.id + '_' + suffix))) {
11161
- dom.remove(marker, 1);
11162
- }
11163
-
11164
- // If siblings are text nodes then merge them unless it's Opera since it some how removes the node
11165
- // and we are sniffing since adding a lot of detection code for a browser with 3% of the market
11166
- // isn't worth the effort. Sorry, Opera but it's just a fact
11167
- if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !isOpera) {
11168
- idx = prev.nodeValue.length;
11169
- prev.appendData(next.nodeValue);
11170
- dom.remove(next);
11171
-
11172
- if (suffix == 'start') {
11173
- startContainer = endContainer = prev;
11174
- startOffset = endOffset = idx;
11175
- } else {
11176
- endContainer = prev;
11177
- endOffset = idx;
11178
- }
11179
- }
11180
- }
11181
- }
11182
- }
11183
-
11184
- function addBogus(node) {
11185
- // Adds a bogus BR element for empty block elements
11186
- if (dom.isBlock(node) && !node.innerHTML && !isIE) {
11187
- node.innerHTML = '<br data-mce-bogus="1" />';
11188
- }
11189
-
11190
- return node;
11191
- }
11192
-
11193
- if (bookmark) {
11194
- if (bookmark.start) {
11195
- rng = dom.createRng();
11196
- root = dom.getRoot();
11197
-
11198
- if (self.tridentSel) {
11199
- return self.tridentSel.moveToBookmark(bookmark);
11200
- }
11201
-
11202
- if (setEndPoint(true) && setEndPoint()) {
11203
- self.setRng(rng);
11204
- }
11205
- } else if (bookmark.id) {
11206
- // Restore start/end points
11207
- restoreEndPoint('start');
11208
- restoreEndPoint('end');
11209
-
11210
- if (startContainer) {
11211
- rng = dom.createRng();
11212
- rng.setStart(addBogus(startContainer), startOffset);
11213
- rng.setEnd(addBogus(endContainer), endOffset);
11214
- self.setRng(rng);
11215
- }
11216
- } else if (bookmark.name) {
11217
- self.select(dom.select(bookmark.name)[bookmark.index]);
11218
- } else if (bookmark.rng) {
11219
- self.setRng(bookmark.rng);
11220
- }
11221
- }
11486
+ return this.bookmarkManager.moveToBookmark(bookmark);
11222
11487
  },
11223
11488
 
11224
11489
  /**
@@ -11808,6 +12073,126 @@ define("tinymce/dom/Selection", [
11808
12073
  return Selection;
11809
12074
  });
11810
12075
 
12076
+ // Included from: js/tinymce/classes/dom/ElementUtils.js
12077
+
12078
+ /**
12079
+ * ElementUtils.js
12080
+ *
12081
+ * Copyright, Moxiecode Systems AB
12082
+ * Released under LGPL License.
12083
+ *
12084
+ * License: http://www.tinymce.com/license
12085
+ * Contributing: http://www.tinymce.com/contributing
12086
+ */
12087
+
12088
+ /**
12089
+ * Utility class for various element specific functions.
12090
+ *
12091
+ * @private
12092
+ */
12093
+ define("tinymce/dom/ElementUtils", [
12094
+ "tinymce/dom/BookmarkManager",
12095
+ "tinymce/util/Tools"
12096
+ ], function(BookmarkManager, Tools) {
12097
+ var each = Tools.each;
12098
+
12099
+ function ElementUtils(dom) {
12100
+ /**
12101
+ * Compares two nodes and checks if it's attributes and styles matches.
12102
+ * This doesn't compare classes as items since their order is significant.
12103
+ *
12104
+ * @method compare
12105
+ * @param {Node} node1 First node to compare with.
12106
+ * @param {Node} node2 Second node to compare with.
12107
+ * @return {boolean} True/false if the nodes are the same or not.
12108
+ */
12109
+ this.compare = function(node1, node2) {
12110
+ // Not the same name
12111
+ if (node1.nodeName != node2.nodeName) {
12112
+ return false;
12113
+ }
12114
+
12115
+ /**
12116
+ * Returns all the nodes attributes excluding internal ones, styles and classes.
12117
+ *
12118
+ * @private
12119
+ * @param {Node} node Node to get attributes from.
12120
+ * @return {Object} Name/value object with attributes and attribute values.
12121
+ */
12122
+ function getAttribs(node) {
12123
+ var attribs = {};
12124
+
12125
+ each(dom.getAttribs(node), function(attr) {
12126
+ var name = attr.nodeName.toLowerCase();
12127
+
12128
+ // Don't compare internal attributes or style
12129
+ if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
12130
+ attribs[name] = dom.getAttrib(node, name);
12131
+ }
12132
+ });
12133
+
12134
+ return attribs;
12135
+ }
12136
+
12137
+ /**
12138
+ * Compares two objects checks if it's key + value exists in the other one.
12139
+ *
12140
+ * @private
12141
+ * @param {Object} obj1 First object to compare.
12142
+ * @param {Object} obj2 Second object to compare.
12143
+ * @return {boolean} True/false if the objects matches or not.
12144
+ */
12145
+ function compareObjects(obj1, obj2) {
12146
+ var value, name;
12147
+
12148
+ for (name in obj1) {
12149
+ // Obj1 has item obj2 doesn't have
12150
+ if (obj1.hasOwnProperty(name)) {
12151
+ value = obj2[name];
12152
+
12153
+ // Obj2 doesn't have obj1 item
12154
+ if (typeof value == "undefined") {
12155
+ return false;
12156
+ }
12157
+
12158
+ // Obj2 item has a different value
12159
+ if (obj1[name] != value) {
12160
+ return false;
12161
+ }
12162
+
12163
+ // Delete similar value
12164
+ delete obj2[name];
12165
+ }
12166
+ }
12167
+
12168
+ // Check if obj 2 has something obj 1 doesn't have
12169
+ for (name in obj2) {
12170
+ // Obj2 has item obj1 doesn't have
12171
+ if (obj2.hasOwnProperty(name)) {
12172
+ return false;
12173
+ }
12174
+ }
12175
+
12176
+ return true;
12177
+ }
12178
+
12179
+ // Attribs are not the same
12180
+ if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
12181
+ return false;
12182
+ }
12183
+
12184
+ // Styles are not the same
12185
+ if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
12186
+ return false;
12187
+ }
12188
+
12189
+ return !BookmarkManager.isBookmarkNode(node1) && !BookmarkManager.isBookmarkNode(node2);
12190
+ };
12191
+ }
12192
+
12193
+ return ElementUtils;
12194
+ });
12195
+
11811
12196
  // Included from: js/tinymce/classes/fmt/Preview.js
11812
12197
 
11813
12198
  /**
@@ -11991,9 +12376,11 @@ define("tinymce/fmt/Preview", [
11991
12376
  define("tinymce/Formatter", [
11992
12377
  "tinymce/dom/TreeWalker",
11993
12378
  "tinymce/dom/RangeUtils",
12379
+ "tinymce/dom/BookmarkManager",
12380
+ "tinymce/dom/ElementUtils",
11994
12381
  "tinymce/util/Tools",
11995
12382
  "tinymce/fmt/Preview"
11996
- ], function(TreeWalker, RangeUtils, Tools, Preview) {
12383
+ ], function(TreeWalker, RangeUtils, BookmarkManager, ElementUtils, Tools, Preview) {
11997
12384
  /**
11998
12385
  * Constructs a new formatter instance.
11999
12386
  *
@@ -12017,7 +12404,8 @@ define("tinymce/Formatter", [
12017
12404
  undef,
12018
12405
  getContentEditable = dom.getContentEditable,
12019
12406
  disableCaretContainer,
12020
- markCaretContainersBogus;
12407
+ markCaretContainersBogus,
12408
+ isBookmarkNode = BookmarkManager.isBookmarkNode;
12021
12409
 
12022
12410
  var each = Tools.each,
12023
12411
  grep = Tools.grep,
@@ -12042,7 +12430,6 @@ define("tinymce/Formatter", [
12042
12430
 
12043
12431
  function defaultFormats() {
12044
12432
  register({
12045
-
12046
12433
  valigntop: [
12047
12434
  {selector: 'td,th', styles: {'verticalAlign': 'top'}}
12048
12435
  ],
@@ -12333,7 +12720,7 @@ define("tinymce/Formatter", [
12333
12720
 
12334
12721
  // get the index of the bookmarks
12335
12722
  each(node.childNodes, function(n, index) {
12336
- if (n.nodeName === "SPAN" && dom.getAttrib(n, "data-mce-type") == "bookmark") {
12723
+ if (isBookmarkNode(n)) {
12337
12724
  if (n.id == bookmark.id + "_start") {
12338
12725
  startIndex = index;
12339
12726
  } else if (n.id == bookmark.id + "_end") {
@@ -13903,21 +14290,10 @@ define("tinymce/Formatter", [
13903
14290
 
13904
14291
  for (node = inc ? node : node[next]; node; node = node[next]) {
13905
14292
  if (node.nodeType == 1 || !isWhiteSpaceNode(node)) {
13906
- return node;
13907
- }
13908
- }
13909
- }
13910
- }
13911
-
13912
- /**
13913
- * Checks if the specified node is a bookmark node or not.
13914
- *
13915
- * @private
13916
- * @param {Node} node Node to check if it's a bookmark node or not.
13917
- * @return {Boolean} true/false if the node is a bookmark node.
13918
- */
13919
- function isBookmarkNode(node) {
13920
- return node && node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark';
14293
+ return node;
14294
+ }
14295
+ }
14296
+ }
13921
14297
  }
13922
14298
 
13923
14299
  /**
@@ -13929,99 +14305,7 @@ define("tinymce/Formatter", [
13929
14305
  * @return {Node} Next node if we didn't merge and prev node if we did.
13930
14306
  */
13931
14307
  function mergeSiblings(prev, next) {
13932
- var sibling, tmpSibling;
13933
-
13934
- /**
13935
- * Compares two nodes and checks if it's attributes and styles matches.
13936
- * This doesn't compare classes as items since their order is significant.
13937
- *
13938
- * @private
13939
- * @param {Node} node1 First node to compare with.
13940
- * @param {Node} node2 Second node to compare with.
13941
- * @return {boolean} True/false if the nodes are the same or not.
13942
- */
13943
- function compareElements(node1, node2) {
13944
- // Not the same name
13945
- if (node1.nodeName != node2.nodeName) {
13946
- return FALSE;
13947
- }
13948
-
13949
- /**
13950
- * Returns all the nodes attributes excluding internal ones, styles and classes.
13951
- *
13952
- * @private
13953
- * @param {Node} node Node to get attributes from.
13954
- * @return {Object} Name/value object with attributes and attribute values.
13955
- */
13956
- function getAttribs(node) {
13957
- var attribs = {};
13958
-
13959
- each(dom.getAttribs(node), function(attr) {
13960
- var name = attr.nodeName.toLowerCase();
13961
-
13962
- // Don't compare internal attributes or style
13963
- if (name.indexOf('_') !== 0 && name !== 'style' && name !== 'data-mce-style') {
13964
- attribs[name] = dom.getAttrib(node, name);
13965
- }
13966
- });
13967
-
13968
- return attribs;
13969
- }
13970
-
13971
- /**
13972
- * Compares two objects checks if it's key + value exists in the other one.
13973
- *
13974
- * @private
13975
- * @param {Object} obj1 First object to compare.
13976
- * @param {Object} obj2 Second object to compare.
13977
- * @return {boolean} True/false if the objects matches or not.
13978
- */
13979
- function compareObjects(obj1, obj2) {
13980
- var value, name;
13981
-
13982
- for (name in obj1) {
13983
- // Obj1 has item obj2 doesn't have
13984
- if (obj1.hasOwnProperty(name)) {
13985
- value = obj2[name];
13986
-
13987
- // Obj2 doesn't have obj1 item
13988
- if (value === undef) {
13989
- return FALSE;
13990
- }
13991
-
13992
- // Obj2 item has a different value
13993
- if (obj1[name] != value) {
13994
- return FALSE;
13995
- }
13996
-
13997
- // Delete similar value
13998
- delete obj2[name];
13999
- }
14000
- }
14001
-
14002
- // Check if obj 2 has something obj 1 doesn't have
14003
- for (name in obj2) {
14004
- // Obj2 has item obj1 doesn't have
14005
- if (obj2.hasOwnProperty(name)) {
14006
- return FALSE;
14007
- }
14008
- }
14009
-
14010
- return TRUE;
14011
- }
14012
-
14013
- // Attribs are not the same
14014
- if (!compareObjects(getAttribs(node1), getAttribs(node2))) {
14015
- return FALSE;
14016
- }
14017
-
14018
- // Styles are not the same
14019
- if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) {
14020
- return FALSE;
14021
- }
14022
-
14023
- return !isBookmarkNode(node1) && !isBookmarkNode(node2);
14024
- }
14308
+ var sibling, tmpSibling, elementUtils = new ElementUtils(dom);
14025
14309
 
14026
14310
  function findElementSibling(node, sibling_name) {
14027
14311
  for (sibling = node; sibling; sibling = sibling[sibling_name]) {
@@ -14044,7 +14328,7 @@ define("tinymce/Formatter", [
14044
14328
  next = findElementSibling(next, 'nextSibling');
14045
14329
 
14046
14330
  // Compare next and previous nodes
14047
- if (compareElements(prev, next)) {
14331
+ if (elementUtils.compare(prev, next)) {
14048
14332
  // Append nodes between
14049
14333
  for (sibling = prev.nextSibling; sibling && sibling != next;) {
14050
14334
  tmpSibling = sibling;
@@ -14247,7 +14531,7 @@ define("tinymce/Formatter", [
14247
14531
  node = container;
14248
14532
 
14249
14533
  if (container.nodeType == 3) {
14250
- if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
14534
+ if (offset != container.nodeValue.length) {
14251
14535
  hasContentAfter = true;
14252
14536
  }
14253
14537
 
@@ -14829,6 +15113,10 @@ define("tinymce/EnterKey", [
14829
15113
  function trimInlineElementsOnLeftSideOfBlock(block) {
14830
15114
  var node = block, firstChilds = [], i;
14831
15115
 
15116
+ if (!node) {
15117
+ return;
15118
+ }
15119
+
14832
15120
  // Find inner most first child ex: <p><i><b>*</b></i></p>
14833
15121
  while ((node = node.firstChild)) {
14834
15122
  if (dom.isBlock(node)) {
@@ -14869,6 +15157,10 @@ define("tinymce/EnterKey", [
14869
15157
  }
14870
15158
  }
14871
15159
 
15160
+ if (!root) {
15161
+ return;
15162
+ }
15163
+
14872
15164
  // Old IE versions doesn't properly render blocks with br elements in them
14873
15165
  // For example <p><br></p> wont be rendered correctly in a contentEditable area
14874
15166
  // until you remove the br producing <p></p>
@@ -14878,16 +15170,23 @@ define("tinymce/EnterKey", [
14878
15170
  }
14879
15171
  }
14880
15172
 
14881
- if (root.nodeName == 'LI') {
15173
+ if (/^(LI|DT|DD)$/.test(root.nodeName)) {
14882
15174
  var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
14883
15175
 
14884
- if (firstChild && /^(UL|OL)$/.test(firstChild.nodeName)) {
15176
+ if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
14885
15177
  root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
14886
15178
  }
14887
15179
  }
14888
15180
 
14889
15181
  rng = dom.createRng();
14890
15182
 
15183
+ // Normalize whitespace to remove empty text nodes. Fix for: #6904
15184
+ // Gecko will be able to place the caret in empty text nodes but it won't render propery
15185
+ // Older IE versions will sometimes crash so for now ignore all IE versions
15186
+ if (!Env.ie) {
15187
+ root.normalize();
15188
+ }
15189
+
14891
15190
  if (root.hasChildNodes()) {
14892
15191
  walker = new TreeWalker(root, root);
14893
15192
 
@@ -14951,7 +15250,7 @@ define("tinymce/EnterKey", [
14951
15250
  // Creates a new block element by cloning the current one or creating a new one if the name is specified
14952
15251
  // This function will also copy any text formatting from the parent block and add it to the new one
14953
15252
  function createNewBlock(name) {
14954
- var node = container, block, clonedNode, caretNode;
15253
+ var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements();
14955
15254
 
14956
15255
  if (name || parentBlockName == "TABLE") {
14957
15256
  block = dom.create(name || newBlockName);
@@ -14965,7 +15264,7 @@ define("tinymce/EnterKey", [
14965
15264
  // Clone any parent styles
14966
15265
  if (settings.keep_styles !== false) {
14967
15266
  do {
14968
- if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U|VAR|CITE|DFN|CODE|MARK|Q|SUP|SUB|SAMP)$/.test(node.nodeName)) {
15267
+ if (textInlineElements[node.nodeName]) {
14969
15268
  // Never clone a caret containers
14970
15269
  if (node.id == '_mce_caret') {
14971
15270
  continue;
@@ -15126,7 +15425,7 @@ define("tinymce/EnterKey", [
15126
15425
  function getContainerBlock() {
15127
15426
  var containerBlockParent = containerBlock.parentNode;
15128
15427
 
15129
- if (containerBlockParent.nodeName == 'LI') {
15428
+ if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) {
15130
15429
  return containerBlockParent;
15131
15430
  }
15132
15431
 
@@ -15356,8 +15655,8 @@ define("tinymce/EnterKey", [
15356
15655
  parentBlockName = containerBlockName;
15357
15656
  }
15358
15657
 
15359
- // Handle enter in LI
15360
- if (parentBlockName == 'LI') {
15658
+ // Handle enter in list item
15659
+ if (/^(LI|DT|DD)$/.test(parentBlockName)) {
15361
15660
  if (!newBlockName && shiftKey) {
15362
15661
  insertBr();
15363
15662
  return;
@@ -15602,8 +15901,9 @@ define("tinymce/ForceBlocks", [], function() {
15602
15901
  define("tinymce/EditorCommands", [
15603
15902
  "tinymce/html/Serializer",
15604
15903
  "tinymce/Env",
15605
- "tinymce/util/Tools"
15606
- ], function(Serializer, Env, Tools) {
15904
+ "tinymce/util/Tools",
15905
+ "tinymce/dom/ElementUtils"
15906
+ ], function(Serializer, Env, Tools, ElementUtils) {
15607
15907
  // Added for compression purposes
15608
15908
  var each = Tools.each, extend = Tools.extend;
15609
15909
  var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode;
@@ -15898,7 +16198,8 @@ define("tinymce/EditorCommands", [
15898
16198
 
15899
16199
  mceInsertContent: function(command, ui, value) {
15900
16200
  var parser, serializer, parentNode, rootNode, fragment, args;
15901
- var marker, rng, node, node2, bookmarkHtml;
16201
+ var marker, rng, node, node2, bookmarkHtml, merge;
16202
+ var textInlineElements = editor.schema.getTextInlineElements();
15902
16203
 
15903
16204
  function trimOrPaddLeftRight(html) {
15904
16205
  var rng, container, offset;
@@ -15928,6 +16229,37 @@ define("tinymce/EditorCommands", [
15928
16229
  return html;
15929
16230
  }
15930
16231
 
16232
+ function markInlineFormatElements(fragment) {
16233
+ if (merge) {
16234
+ for (node = fragment.firstChild; node; node = node.walk(true)) {
16235
+ if (textInlineElements[node.name]) {
16236
+ node.attr('data-mce-new', "true");
16237
+ }
16238
+ }
16239
+ }
16240
+ }
16241
+
16242
+ function reduceInlineTextElements() {
16243
+ if (merge) {
16244
+ var root = editor.getBody(), elementUtils = new ElementUtils(dom);
16245
+
16246
+ each(dom.select('*[data-mce-new]'), function(node) {
16247
+ node.removeAttribute('data-mce-new');
16248
+
16249
+ for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) {
16250
+ if (elementUtils.compare(testNode, node)) {
16251
+ dom.remove(node, true);
16252
+ }
16253
+ }
16254
+ });
16255
+ }
16256
+ }
16257
+
16258
+ if (typeof(value) != 'string') {
16259
+ merge = value.merge;
16260
+ value = value.content;
16261
+ }
16262
+
15931
16263
  // Check for whitespace before/after value
15932
16264
  if (/^ | $/.test(value)) {
15933
16265
  value = trimOrPaddLeftRight(value);
@@ -15975,6 +16307,8 @@ define("tinymce/EditorCommands", [
15975
16307
  var parserArgs = {context: parentNode.nodeName.toLowerCase()};
15976
16308
  fragment = parser.parse(value, parserArgs);
15977
16309
 
16310
+ markInlineFormatElements(fragment);
16311
+
15978
16312
  // Move the caret to a more suitable location
15979
16313
  node = fragment.lastChild;
15980
16314
  if (node.attr('id') == 'mce_marker') {
@@ -16041,6 +16375,8 @@ define("tinymce/EditorCommands", [
16041
16375
  }
16042
16376
  }
16043
16377
 
16378
+ reduceInlineTextElements();
16379
+
16044
16380
  marker = dom.get('mce_marker');
16045
16381
  selection.scrollIntoView(marker);
16046
16382
 
@@ -16343,11 +16679,9 @@ define("tinymce/util/URI", [
16343
16679
  function URI(url, settings) {
16344
16680
  var self = this, baseUri, base_url;
16345
16681
 
16346
- // Trim whitespace
16347
16682
  url = trim(url);
16348
-
16349
- // Default settings
16350
16683
  settings = self.settings = settings || {};
16684
+ baseUri = settings.base_uri;
16351
16685
 
16352
16686
  // Strange app protocol that isn't http/https or local anchor
16353
16687
  // For example: mailto,skype,tel etc.
@@ -16360,7 +16694,7 @@ define("tinymce/util/URI", [
16360
16694
 
16361
16695
  // Absolute path with no host, fake host and protocol
16362
16696
  if (url.indexOf('/') === 0 && !isProtocolRelative) {
16363
- url = (settings.base_uri ? settings.base_uri.protocol || 'http' : 'http') + '://mce_host' + url;
16697
+ url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url;
16364
16698
  }
16365
16699
 
16366
16700
  // Relative path http:// or protocol relative //path
@@ -16369,7 +16703,8 @@ define("tinymce/util/URI", [
16369
16703
  if (settings.base_uri.protocol === "") {
16370
16704
  url = '//mce_host' + self.toAbsPath(base_url, url);
16371
16705
  } else {
16372
- url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
16706
+ url = /([^#?]*)([#?]?.*)/.exec(url);
16707
+ url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url[1]) + url[2];
16373
16708
  }
16374
16709
  }
16375
16710
 
@@ -16391,7 +16726,6 @@ define("tinymce/util/URI", [
16391
16726
  self[v] = part;
16392
16727
  });
16393
16728
 
16394
- baseUri = settings.base_uri;
16395
16729
  if (baseUri) {
16396
16730
  if (!self.protocol) {
16397
16731
  self.protocol = baseUri.protocol;
@@ -16901,7 +17235,8 @@ define("tinymce/util/EventDispatcher", [
16901
17235
  var nativeEvents = Tools.makeMap(
16902
17236
  "focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
16903
17237
  "mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
16904
- "draggesture dragdrop drop drag submit",
17238
+ "draggesture dragdrop drop drag submit " +
17239
+ "compositionstart compositionend compositionupdate",
16905
17240
  ' '
16906
17241
  );
16907
17242
 
@@ -17958,9 +18293,11 @@ define("tinymce/ui/DomUtils", [
17958
18293
  ], function(Tools, DOMUtils) {
17959
18294
  "use strict";
17960
18295
 
18296
+ var count = 0;
18297
+
17961
18298
  return {
17962
18299
  id: function() {
17963
- return DOMUtils.DOM.uniqueId();
18300
+ return 'mceu_' + (count++);
17964
18301
  },
17965
18302
 
17966
18303
  createFragment: function(html) {
@@ -21194,9 +21531,117 @@ define("tinymce/ui/FloatPanel", [
21194
21531
  ], function(Panel, Movable, Resizable, DomUtils) {
21195
21532
  "use strict";
21196
21533
 
21197
- var documentClickHandler, documentScrollHandler, visiblePanels = [];
21534
+ var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = [];
21198
21535
  var zOrder = [], hasModal;
21199
21536
 
21537
+ function bindDocumentClickHandler() {
21538
+ function isChildOf(ctrl, parent) {
21539
+ while (ctrl) {
21540
+ if (ctrl == parent) {
21541
+ return true;
21542
+ }
21543
+
21544
+ ctrl = ctrl.parent();
21545
+ }
21546
+ }
21547
+
21548
+ if (!documentClickHandler) {
21549
+ documentClickHandler = function(e) {
21550
+ // Gecko fires click event and in the wrong order on Mac so lets normalize
21551
+ if (e.button == 2) {
21552
+ return;
21553
+ }
21554
+
21555
+ // Hide any float panel when a click is out side that float panel and the
21556
+ // float panels direct parent for example a click on a menu button
21557
+ var i = visiblePanels.length;
21558
+ while (i--) {
21559
+ var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
21560
+
21561
+ if (panel.settings.autohide) {
21562
+ if (clickCtrl) {
21563
+ if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
21564
+ continue;
21565
+ }
21566
+ }
21567
+
21568
+ e = panel.fire('autohide', {target: e.target});
21569
+ if (!e.isDefaultPrevented()) {
21570
+ panel.hide();
21571
+ }
21572
+ }
21573
+ }
21574
+ };
21575
+
21576
+ DomUtils.on(document, 'click', documentClickHandler);
21577
+ }
21578
+ }
21579
+
21580
+ function bindDocumentScrollHandler() {
21581
+ if (!documentScrollHandler) {
21582
+ documentScrollHandler = function() {
21583
+ var i;
21584
+
21585
+ i = visiblePanels.length;
21586
+ while (i--) {
21587
+ repositionPanel(visiblePanels[i]);
21588
+ }
21589
+ };
21590
+
21591
+ DomUtils.on(window, 'scroll', documentScrollHandler);
21592
+ }
21593
+ }
21594
+
21595
+ function bindWindowResizeHandler() {
21596
+ if (!windowResizeHandler) {
21597
+ windowResizeHandler = function() {
21598
+ FloatPanel.hideAll();
21599
+ };
21600
+
21601
+ DomUtils.on(window, 'resize', windowResizeHandler);
21602
+ }
21603
+ }
21604
+
21605
+ /**
21606
+ * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
21607
+ * also reposition all child panels of the current panel.
21608
+ */
21609
+ function repositionPanel(panel) {
21610
+ var scrollY = DomUtils.getViewPort().y;
21611
+
21612
+ function toggleFixedChildPanels(fixed, deltaY) {
21613
+ var parent;
21614
+
21615
+ for (var i = 0; i < visiblePanels.length; i++) {
21616
+ if (visiblePanels[i] != panel) {
21617
+ parent = visiblePanels[i].parent();
21618
+
21619
+ while (parent && (parent = parent.parent())) {
21620
+ if (parent == panel) {
21621
+ visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
21622
+ }
21623
+ }
21624
+ }
21625
+ }
21626
+ }
21627
+
21628
+ if (panel.settings.autofix) {
21629
+ if (!panel._fixed) {
21630
+ panel._autoFixY = panel.layoutRect().y;
21631
+
21632
+ if (panel._autoFixY < scrollY) {
21633
+ panel.fixed(true).layoutRect({y: 0}).repaint();
21634
+ toggleFixedChildPanels(true, scrollY - panel._autoFixY);
21635
+ }
21636
+ } else {
21637
+ if (panel._autoFixY > scrollY) {
21638
+ panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
21639
+ toggleFixedChildPanels(false, panel._autoFixY - scrollY);
21640
+ }
21641
+ }
21642
+ }
21643
+ }
21644
+
21200
21645
  var FloatPanel = Panel.extend({
21201
21646
  Mixins: [Movable, Resizable],
21202
21647
 
@@ -21238,56 +21683,6 @@ define("tinymce/ui/FloatPanel", [
21238
21683
  FloatPanel.currentZIndex = zIndex;
21239
21684
  }
21240
21685
 
21241
- function isChildOf(ctrl, parent) {
21242
- while (ctrl) {
21243
- if (ctrl == parent) {
21244
- return true;
21245
- }
21246
-
21247
- ctrl = ctrl.parent();
21248
- }
21249
- }
21250
-
21251
- /**
21252
- * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will
21253
- * also reposition all child panels of the current panel.
21254
- */
21255
- function repositionPanel(panel) {
21256
- var scrollY = DomUtils.getViewPort().y;
21257
-
21258
- function toggleFixedChildPanels(fixed, deltaY) {
21259
- var parent;
21260
-
21261
- for (var i = 0; i < visiblePanels.length; i++) {
21262
- if (visiblePanels[i] != panel) {
21263
- parent = visiblePanels[i].parent();
21264
-
21265
- while (parent && (parent = parent.parent())) {
21266
- if (parent == panel) {
21267
- visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint();
21268
- }
21269
- }
21270
- }
21271
- }
21272
- }
21273
-
21274
- if (panel.settings.autofix) {
21275
- if (!panel._fixed) {
21276
- panel._autoFixY = panel.layoutRect().y;
21277
-
21278
- if (panel._autoFixY < scrollY) {
21279
- panel.fixed(true).layoutRect({y: 0}).repaint();
21280
- toggleFixedChildPanels(true, scrollY - panel._autoFixY);
21281
- }
21282
- } else {
21283
- if (panel._autoFixY > scrollY) {
21284
- panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint();
21285
- toggleFixedChildPanels(false, panel._autoFixY - scrollY);
21286
- }
21287
- }
21288
- }
21289
- }
21290
-
21291
21686
  self._super(settings);
21292
21687
  self._eventsRoot = self;
21293
21688
 
@@ -21295,48 +21690,13 @@ define("tinymce/ui/FloatPanel", [
21295
21690
 
21296
21691
  // Hide floatpanes on click out side the root button
21297
21692
  if (settings.autohide) {
21298
- if (!documentClickHandler) {
21299
- documentClickHandler = function(e) {
21300
- // Hide any float panel when a click is out side that float panel and the
21301
- // float panels direct parent for example a click on a menu button
21302
- var i = visiblePanels.length;
21303
- while (i--) {
21304
- var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target);
21305
-
21306
- if (panel.settings.autohide) {
21307
- if (clickCtrl) {
21308
- if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) {
21309
- continue;
21310
- }
21311
- }
21312
-
21313
- e = panel.fire('autohide', {target: e.target});
21314
- if (!e.isDefaultPrevented()) {
21315
- panel.hide();
21316
- }
21317
- }
21318
- }
21319
- };
21320
-
21321
- DomUtils.on(document, 'click', documentClickHandler);
21322
- }
21323
-
21693
+ bindDocumentClickHandler();
21694
+ bindWindowResizeHandler();
21324
21695
  visiblePanels.push(self);
21325
21696
  }
21326
21697
 
21327
21698
  if (settings.autofix) {
21328
- if (!documentScrollHandler) {
21329
- documentScrollHandler = function() {
21330
- var i;
21331
-
21332
- i = visiblePanels.length;
21333
- while (i--) {
21334
- repositionPanel(visiblePanels[i]);
21335
- }
21336
- };
21337
-
21338
- DomUtils.on(window, 'scroll', documentScrollHandler);
21339
- }
21699
+ bindDocumentScrollHandler();
21340
21700
 
21341
21701
  self.on('move', function() {
21342
21702
  repositionPanel(this);
@@ -21452,7 +21812,8 @@ define("tinymce/ui/FloatPanel", [
21452
21812
  },
21453
21813
 
21454
21814
  /**
21455
- * Hides all visible the float panels.
21815
+ * Hide all visible float panels with he autohide setting enabled. This is for
21816
+ * manually hiding floating menus or panels.
21456
21817
  *
21457
21818
  * @method hideAll
21458
21819
  */
@@ -21495,7 +21856,8 @@ define("tinymce/ui/FloatPanel", [
21495
21856
  });
21496
21857
 
21497
21858
  /**
21498
- * Hides all visible the float panels.
21859
+ * Hide all visible float panels with he autohide setting enabled. This is for
21860
+ * manually hiding floating menus or panels.
21499
21861
  *
21500
21862
  * @static
21501
21863
  * @method hideAll
@@ -23901,8 +24263,13 @@ define("tinymce/Shortcuts", [
23901
24263
  break;
23902
24264
 
23903
24265
  default:
23904
- shortcut.charCode = value.charCodeAt(0);
23905
- shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
24266
+ // Allow numeric keycodes like ctrl+219 for ctrl+[
24267
+ if (/^[0-9]{2,}$/.test(value)) {
24268
+ shortcut.keyCode = parseInt(value, 10);
24269
+ } else {
24270
+ shortcut.charCode = value.charCodeAt(0);
24271
+ shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
24272
+ }
23906
24273
  }
23907
24274
  });
23908
24275
 
@@ -24709,7 +25076,14 @@ define("tinymce/Editor", [
24709
25076
  // Add internal attribute if we need to we don't on a refresh of the document
24710
25077
  if (!node.attributes.map[internalName]) {
24711
25078
  if (name === "style") {
24712
- node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
25079
+ value = dom.serializeStyle(dom.parseStyle(value), node.name);
25080
+
25081
+ if (!value.length) {
25082
+ value = null;
25083
+ }
25084
+
25085
+ node.attr(internalName, value);
25086
+ node.attr(name, value);
24713
25087
  } else if (name === "tabindex") {
24714
25088
  node.attr(internalName, value);
24715
25089
  node.attr(name, null);
@@ -24726,7 +25100,7 @@ define("tinymce/Editor", [
24726
25100
 
24727
25101
  while (i--) {
24728
25102
  node = nodes[i];
24729
- node.attr('type', 'mce-' + (node.attr('type') || 'text/javascript'));
25103
+ node.attr('type', 'mce-' + (node.attr('type') || 'no/type'));
24730
25104
  }
24731
25105
  });
24732
25106
 
@@ -25340,8 +25714,18 @@ define("tinymce/Editor", [
25340
25714
  }
25341
25715
 
25342
25716
  // Browser commands
25343
- self.getDoc().execCommand(cmd, ui, value);
25344
- self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25717
+ try {
25718
+ state = self.getDoc().execCommand(cmd, ui, value);
25719
+ } catch (ex) {
25720
+ // Ignore old IE errors
25721
+ }
25722
+
25723
+ if (state) {
25724
+ self.fire('ExecCommand', {command: cmd, ui: ui, value: value});
25725
+ return true;
25726
+ }
25727
+
25728
+ return false;
25345
25729
  },
25346
25730
 
25347
25731
  /**
@@ -25363,8 +25747,8 @@ define("tinymce/Editor", [
25363
25747
  if ((queryItem = self.queryStateCommands[cmd])) {
25364
25748
  returnVal = queryItem.func.call(queryItem.scope);
25365
25749
 
25366
- // Fall though on true
25367
- if (returnVal !== true) {
25750
+ // Fall though on non boolean returns
25751
+ if (returnVal === true || returnVal === false) {
25368
25752
  return returnVal;
25369
25753
  }
25370
25754
  }
@@ -25454,8 +25838,6 @@ define("tinymce/Editor", [
25454
25838
  var self = this, doc = self.getDoc();
25455
25839
 
25456
25840
  if (!self.hidden) {
25457
- self.hidden = true;
25458
-
25459
25841
  // Fixed bug where IE has a blinking cursor left from the editor
25460
25842
  if (ie && doc && !self.inline) {
25461
25843
  doc.execCommand('SelectAll');
@@ -25476,6 +25858,7 @@ define("tinymce/Editor", [
25476
25858
  DOM.setStyle(self.id, 'display', self.orgDisplay);
25477
25859
  }
25478
25860
 
25861
+ self.hidden = true;
25479
25862
  self.fire('hide');
25480
25863
  }
25481
25864
  },
@@ -25739,8 +26122,13 @@ define("tinymce/Editor", [
25739
26122
  *
25740
26123
  * @method insertContent
25741
26124
  * @param {String} content Content to insert.
26125
+ * @param {Object} args Optional args to pass to insert call.
25742
26126
  */
25743
- insertContent: function(content) {
26127
+ insertContent: function(content, args) {
26128
+ if (args) {
26129
+ content = extend({content: content}, args);
26130
+ }
26131
+
25744
26132
  this.execCommand('mceInsertContent', false, content);
25745
26133
  },
25746
26134
 
@@ -26495,7 +26883,7 @@ define("tinymce/EditorManager", [
26495
26883
  * @property minorVersion
26496
26884
  * @type String
26497
26885
  */
26498
- minorVersion : '0.26',
26886
+ minorVersion : '0.28',
26499
26887
 
26500
26888
  /**
26501
26889
  * Release date of TinyMCE build.
@@ -26503,7 +26891,7 @@ define("tinymce/EditorManager", [
26503
26891
  * @property releaseDate
26504
26892
  * @type String
26505
26893
  */
26506
- releaseDate: '2014-05-06',
26894
+ releaseDate: '2014-05-27',
26507
26895
 
26508
26896
  /**
26509
26897
  * Collection of editor instances.
@@ -26539,9 +26927,16 @@ define("tinymce/EditorManager", [
26539
26927
  var self = this, baseURL, documentBaseURL, suffix = "", preInit, src;
26540
26928
 
26541
26929
  // Get base URL for the current document
26542
- documentBaseURL = document.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
26543
- if (!/[\/\\]$/.test(documentBaseURL)) {
26544
- documentBaseURL += '/';
26930
+ documentBaseURL = document.location.href;
26931
+
26932
+ // Check if the URL is a document based format like: http://site/dir/file
26933
+ // leave other formats like applewebdata://... intact
26934
+ if (/^[^:]+:\/\/[^\/]+\//.test(documentBaseURL)) {
26935
+ documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
26936
+
26937
+ if (!/[\/\\]$/.test(documentBaseURL)) {
26938
+ documentBaseURL += '/';
26939
+ }
26545
26940
  }
26546
26941
 
26547
26942
  // If tinymce is defined and has a base use that or use the old tinyMCEPreInit
@@ -29475,13 +29870,19 @@ define("tinymce/ui/Form", [
29475
29870
  * @private
29476
29871
  */
29477
29872
  recalcLabels: function() {
29478
- var self = this, maxLabelWidth = 0, labels = [], i, labelGap;
29873
+ var self = this, maxLabelWidth = 0, labels = [], i, labelGap, items;
29479
29874
 
29480
29875
  if (self.settings.labelGapCalc === false) {
29481
29876
  return;
29482
29877
  }
29483
29878
 
29484
- self.items().filter('formitem').each(function(item) {
29879
+ if (self.settings.labelGapCalc == "children") {
29880
+ items = self.find('formitem');
29881
+ } else {
29882
+ items = self.items();
29883
+ }
29884
+
29885
+ items.filter('formitem').each(function(item) {
29485
29886
  var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth;
29486
29887
 
29487
29888
  maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth;
@@ -29622,8 +30023,9 @@ define("tinymce/ui/FieldSet", [
29622
30023
  * @extends tinymce.ui.ComboBox
29623
30024
  */
29624
30025
  define("tinymce/ui/FilePicker", [
29625
- "tinymce/ui/ComboBox"
29626
- ], function(ComboBox) {
30026
+ "tinymce/ui/ComboBox",
30027
+ "tinymce/util/Tools"
30028
+ ], function(ComboBox, Tools) {
29627
30029
  "use strict";
29628
30030
 
29629
30031
  return ComboBox.extend({
@@ -29634,12 +30036,17 @@ define("tinymce/ui/FilePicker", [
29634
30036
  * @param {Object} settings Name/value object with settings.
29635
30037
  */
29636
30038
  init: function(settings) {
29637
- var self = this, editor = tinymce.activeEditor, fileBrowserCallback;
30039
+ var self = this, editor = tinymce.activeEditor, fileBrowserCallback, fileBrowserCallbackTypes;
29638
30040
 
29639
30041
  settings.spellcheck = false;
29640
30042
 
30043
+ fileBrowserCallbackTypes = editor.settings.file_browser_callback_types;
30044
+ if (fileBrowserCallbackTypes) {
30045
+ fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
30046
+ }
30047
+
29641
30048
  fileBrowserCallback = editor.settings.file_browser_callback;
29642
- if (fileBrowserCallback) {
30049
+ if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
29643
30050
  settings.icon = 'browse';
29644
30051
 
29645
30052
  settings.onaction = function() {
@@ -30155,6 +30562,7 @@ define("tinymce/ui/FormatControls", [
30155
30562
  }
30156
30563
 
30157
30564
  menuItem.format = formatName;
30565
+ menuItem.cmd = format.cmd;
30158
30566
  }
30159
30567
 
30160
30568
  menu.push(menuItem);
@@ -30201,20 +30609,32 @@ define("tinymce/ui/FormatControls", [
30201
30609
  },
30202
30610
 
30203
30611
  onPostRender: function() {
30204
- var self = this, formatName = this.settings.format;
30612
+ var self = this;
30205
30613
 
30206
- if (formatName) {
30207
- self.parent().on('show', function() {
30614
+ self.parent().on('show', function() {
30615
+ var formatName, command;
30616
+
30617
+ formatName = self.settings.format;
30618
+ if (formatName) {
30208
30619
  self.disabled(!editor.formatter.canApply(formatName));
30209
30620
  self.active(editor.formatter.match(formatName));
30210
- });
30211
- }
30621
+ }
30622
+
30623
+ command = self.settings.cmd;
30624
+ if (command) {
30625
+ self.active(editor.queryCommandState(command));
30626
+ }
30627
+ });
30212
30628
  },
30213
30629
 
30214
30630
  onclick: function() {
30215
30631
  if (this.settings.format) {
30216
30632
  toggleFormat(this.settings.format);
30217
30633
  }
30634
+
30635
+ if (this.settings.cmd) {
30636
+ editor.execCommand(this.settings.cmd);
30637
+ }
30218
30638
  }
30219
30639
  }
30220
30640
  };
@@ -30309,30 +30729,21 @@ define("tinymce/ui/FormatControls", [
30309
30729
  });
30310
30730
  });
30311
30731
 
30312
- function hasUndo() {
30313
- return editor.undoManager ? editor.undoManager.hasUndo() : false;
30314
- }
30315
-
30316
- function hasRedo() {
30317
- return editor.undoManager ? editor.undoManager.hasRedo() : false;
30318
- }
30319
-
30320
- function toggleUndoState() {
30321
- var self = this;
30732
+ function toggleUndoRedoState(type) {
30733
+ return function() {
30734
+ var self = this;
30322
30735
 
30323
- self.disabled(!hasUndo());
30324
- editor.on('Undo Redo AddUndo TypingUndo', function() {
30325
- self.disabled(!hasUndo());
30326
- });
30327
- }
30736
+ type = type == 'redo' ? 'hasRedo' : 'hasUndo';
30328
30737
 
30329
- function toggleRedoState() {
30330
- var self = this;
30738
+ function checkState() {
30739
+ return editor.undoManager ? editor.undoManager[type]() : false;
30740
+ }
30331
30741
 
30332
- self.disabled(!hasRedo());
30333
- editor.on('Undo Redo AddUndo TypingUndo', function() {
30334
- self.disabled(!hasRedo());
30335
- });
30742
+ self.disabled(!checkState());
30743
+ editor.on('Undo Redo AddUndo TypingUndo ClearUndos', function() {
30744
+ self.disabled(!checkState());
30745
+ });
30746
+ };
30336
30747
  }
30337
30748
 
30338
30749
  function toggleVisualAidState() {
@@ -30347,13 +30758,13 @@ define("tinymce/ui/FormatControls", [
30347
30758
 
30348
30759
  editor.addButton('undo', {
30349
30760
  tooltip: 'Undo',
30350
- onPostRender: toggleUndoState,
30761
+ onPostRender: toggleUndoRedoState('undo'),
30351
30762
  cmd: 'undo'
30352
30763
  });
30353
30764
 
30354
30765
  editor.addButton('redo', {
30355
30766
  tooltip: 'Redo',
30356
- onPostRender: toggleRedoState,
30767
+ onPostRender: toggleUndoRedoState('redo'),
30357
30768
  cmd: 'redo'
30358
30769
  });
30359
30770
 
@@ -30368,7 +30779,7 @@ define("tinymce/ui/FormatControls", [
30368
30779
  text: 'Undo',
30369
30780
  icon: 'undo',
30370
30781
  shortcut: 'Ctrl+Z',
30371
- onPostRender: toggleUndoState,
30782
+ onPostRender: toggleUndoRedoState('undo'),
30372
30783
  cmd: 'undo'
30373
30784
  });
30374
30785
 
@@ -30376,7 +30787,7 @@ define("tinymce/ui/FormatControls", [
30376
30787
  text: 'Redo',
30377
30788
  icon: 'redo',
30378
30789
  shortcut: 'Ctrl+Y',
30379
- onPostRender: toggleRedoState,
30790
+ onPostRender: toggleUndoRedoState('redo'),
30380
30791
  cmd: 'redo'
30381
30792
  });
30382
30793
 
@@ -30511,7 +30922,14 @@ define("tinymce/ui/FormatControls", [
30511
30922
  var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
30512
30923
 
30513
30924
  each(fontsize_formats.split(' '), function(item) {
30514
- items.push({text: item, value: item});
30925
+ var text = item, value = item;
30926
+ // Allow text=value font sizes.
30927
+ var values = item.split('=');
30928
+ if (values.length > 1) {
30929
+ text = values[0];
30930
+ value = values[1];
30931
+ }
30932
+ items.push({text: text, value: value});
30515
30933
  });
30516
30934
 
30517
30935
  return {
@@ -31353,19 +31771,29 @@ define("tinymce/ui/ListBox", [
31353
31771
  * @setting {Array} values Array with values to add to list box.
31354
31772
  */
31355
31773
  init: function(settings) {
31356
- var self = this, values, i, selected, selectedText, lastItemCtrl;
31774
+ var self = this, values, selected, selectedText, lastItemCtrl;
31357
31775
 
31358
- self._values = values = settings.values;
31359
- if (values) {
31360
- for (i = 0; i < values.length; i++) {
31361
- selected = values[i].selected || settings.value === values[i].value;
31776
+ function setSelected(menuValues) {
31777
+ // Try to find a selected value
31778
+ for (var i = 0; i < menuValues.length; i++) {
31779
+ selected = menuValues[i].selected || settings.value === menuValues[i].value;
31362
31780
 
31363
31781
  if (selected) {
31364
- selectedText = selectedText || values[i].text;
31365
- self._value = values[i].value;
31782
+ selectedText = selectedText || menuValues[i].text;
31783
+ self._value = menuValues[i].value;
31366
31784
  break;
31367
31785
  }
31786
+
31787
+ // If the value has a submenu, try to find the selected values in that menu
31788
+ if (menuValues[i].menu) {
31789
+ setSelected(menuValues[i].menu);
31790
+ }
31368
31791
  }
31792
+ }
31793
+
31794
+ self._values = values = settings.values;
31795
+ if (values) {
31796
+ setSelected(values);
31369
31797
 
31370
31798
  // Default with first item
31371
31799
  if (!selected && values.length > 0) {
@@ -31406,7 +31834,7 @@ define("tinymce/ui/ListBox", [
31406
31834
  * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation.
31407
31835
  */
31408
31836
  value: function(value) {
31409
- var self = this, active, selectedText, menu, i;
31837
+ var self = this, active, selectedText, menu;
31410
31838
 
31411
31839
  function activateByValue(menu, value) {
31412
31840
  menu.items().each(function(ctrl) {
@@ -31424,20 +31852,28 @@ define("tinymce/ui/ListBox", [
31424
31852
  });
31425
31853
  }
31426
31854
 
31855
+ function setActiveValues(menuValues) {
31856
+ for (var i = 0; i < menuValues.length; i++) {
31857
+ active = menuValues[i].value == value;
31858
+
31859
+ if (active) {
31860
+ selectedText = selectedText || menuValues[i].text;
31861
+ }
31862
+
31863
+ menuValues[i].active = active;
31864
+
31865
+ if (menuValues[i].menu) {
31866
+ setActiveValues(menuValues[i].menu);
31867
+ }
31868
+ }
31869
+ }
31870
+
31427
31871
  if (typeof(value) != "undefined") {
31428
31872
  if (self.menu) {
31429
31873
  activateByValue(self.menu, value);
31430
31874
  } else {
31431
31875
  menu = self.settings.menu;
31432
- for (i = 0; i < menu.length; i++) {
31433
- active = menu[i].value == value;
31434
-
31435
- if (active) {
31436
- selectedText = selectedText || menu[i].text;
31437
- }
31438
-
31439
- menu[i].active = active;
31440
- }
31876
+ setActiveValues(menu);
31441
31877
  }
31442
31878
 
31443
31879
  self.text(selectedText || this.settings.text);
@@ -31579,12 +32015,16 @@ define("tinymce/ui/MenuItem", [
31579
32015
 
31580
32016
  menu = self.menu = Factory.create(menu).parent(self).renderTo();
31581
32017
  menu.reflow();
31582
- menu.fire('show');
31583
32018
  menu.on('cancel', function(e) {
31584
32019
  e.stopPropagation();
31585
32020
  self.focus();
31586
32021
  menu.hide();
31587
32022
  });
32023
+ menu.on('show hide', function(e) {
32024
+ e.control.items().each(function(ctrl) {
32025
+ ctrl.active(ctrl.settings.selected);
32026
+ });
32027
+ }).fire('show');
31588
32028
 
31589
32029
  menu.on('hide', function(e) {
31590
32030
  if (e.control === menu) {
@@ -32681,5 +33121,5 @@ define("tinymce/ui/Throbber", [
32681
33121
  };
32682
33122
  });
32683
33123
 
32684
- expose(["tinymce/dom/Sizzle","tinymce/html/Styles","tinymce/dom/EventUtils","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"]);
33124
+ expose(["tinymce/dom/Sizzle","tinymce/html/Styles","tinymce/dom/EventUtils","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"]);
32685
33125
  })(this);