app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.0.10 vs app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.0.11
- old
+ new
@@ -1,6 +1,6 @@
-// 4.0.10 (2013-10-28)
+// 4.0.11 (2013-11-20)
/**
* Compiled inline version. (Library mode)
*/
@@ -188,42 +188,46 @@
*/
parse: function(css) {
var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
var urlConverterScope = settings.url_converter_scope || this;
- function compress(prefix, suffix) {
+ function compress(prefix, suffix, noJoin) {
var top, right, bottom, left;
- // IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
- // So lets asume it shouldn't be there
- if (styles['border-image'] === 'none') {
- delete styles['border-image'];
- }
-
- // Get values and check it it needs compressing
top = styles[prefix + '-top' + suffix];
if (!top) {
return;
}
right = styles[prefix + '-right' + suffix];
- if (top != right) {
+ if (!right) {
return;
}
bottom = styles[prefix + '-bottom' + suffix];
- if (right != bottom) {
+ if (!bottom) {
return;
}
left = styles[prefix + '-left' + suffix];
- if (bottom != left) {
+ if (!left) {
return;
}
- // Compress
- styles[prefix + suffix] = left;
+ var box = [top, right, bottom, left];
+ i = box.length - 1;
+ while (i--) {
+ if (box[i] !== box[i + 1]) {
+ break;
+ }
+ }
+
+ if (i > -1 && noJoin) {
+ return;
+ }
+
+ styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
delete styles[prefix + '-top' + suffix];
delete styles[prefix + '-right' + suffix];
delete styles[prefix + '-bottom' + suffix];
delete styles[prefix + '-left' + suffix];
}
@@ -232,11 +236,11 @@
* Checks if the specific style can be compressed in other words if all border-width are equal.
*/
function canCompress(key) {
var value = styles[key], i;
- if (!value || value.indexOf(' ') < 0) {
+ if (!value) {
return;
}
value = value.split(' ');
i = value.length;
@@ -355,13 +359,12 @@
styles[name] = isEncoded ? decode(value, true) : value;
}
styleRegExp.lastIndex = matches.index + matches[0].length;
}
-
// Compress the styles to reduce it's size for example IE will expand styles
- compress("border", "");
+ compress("border", "", true);
compress("border", "-width");
compress("border", "-color");
compress("border", "-style");
compress("padding", "");
compress("margin", "");
@@ -369,10 +372,16 @@
// Remove pointless border, IE produces these
if (styles.border === 'medium none') {
delete styles.border;
}
+
+ // IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
+ // So lets asume it shouldn't be there
+ if (styles['border-image'] === 'none') {
+ delete styles['border-image'];
+ }
}
return styles;
},
@@ -2623,11 +2632,11 @@
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/**
- * This class contains various environment constrants like browser versions etc.
+ * This class contains various environment constants like browser versions etc.
* Normally you don't want to sniff specific browser versions but sometimes you have
* to when it's impossible to feature detect. So use this with care.
*
* @class tinymce.Env
* @static
@@ -5144,13 +5153,18 @@
/**
* Loads a language pack for the specified add-on.
*
* @method requireLangPack
* @param {String} name Short name of the add-on.
+ * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
*/
- requireLangPack: function(name) {
+ requireLangPack: function(name, languages) {
if (AddOnManager.language && AddOnManager.languageLoad !== false) {
+ if (languages && new RegExp('([, ]|\\b)' + AddOnManager.language + '([, ]|\\b)').test(languages) === false) {
+ return;
+ }
+
ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + AddOnManager.language + '.js');
}
},
/**
@@ -6866,11 +6880,12 @@
var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
var attributesRequired, attributesDefault, attributesForced;
var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
- var decode = Entities.decode, fixSelfClosing, filteredAttrs = Tools.makeMap('src,href');
+ var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href');
+ var scriptUriRegExp = /(java|vb)script:/i;
function processEndTag(name) {
var pos, i;
// Find position of parent of the same type
@@ -6932,13 +6947,26 @@
if (attrRule.validValues && !(value in attrRule.validValues)) {
return;
}
}
- if (filteredAttrs[name] && !settings.allow_script_urls) {
- if (/(java|vb)script:/i.test(decodeURIComponent(value.replace(trimRegExp, '')))) {
- return;
+ // Block any javascript: urls
+ if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
+ var uri = value.replace(trimRegExp, '');
+
+ try {
+ // Might throw malformed URI sequence
+ uri = decodeURIComponent(uri);
+ if (scriptUriRegExp.test(uri)) {
+ return;
+ }
+ } catch (ex) {
+ // Fallback to non UTF-8 decoder
+ uri = unescape(uri);
+ if (scriptUriRegExp.test(uri)) {
+ return;
+ }
}
}
// Add attribute to list and map
attrList.map[name] = value;
@@ -9225,11 +9253,11 @@
return e.shiftKey || e.ctrlKey || e.altKey;
},
metaKeyPressed: function(e) {
// Check if ctrl or meta key is pressed also check if alt is false for Polish users
- return (Env.mac ? e.ctrlKey || e.metaKey : e.ctrlKey) && !e.altKey;
+ return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
}
};
});
// Included from: js/tinymce/classes/dom/ControlSelection.js
@@ -9770,21 +9798,28 @@
}
Selection.prototype = {
/**
* Move the selection cursor range to the specified node and offset.
+ * If there is no node specified it will move it to the first suitable location within the body.
*
* @method setCursorLocation
- * @param {Node} node Node to put the cursor in.
- * @param {Number} offset Offset from the start of the node to put the cursor at.
+ * @param {Node} node Optional node to put the cursor in.
+ * @param {Number} offset Optional offset from the start of the node to put the cursor at.
*/
setCursorLocation: function(node, offset) {
var self = this, rng = self.dom.createRng();
- rng.setStart(node, offset);
- rng.setEnd(node, offset);
- self.setRng(rng);
- self.collapse(false);
+
+ if (!node) {
+ self._moveEndPoint(rng, self.editor.getBody(), true);
+ self.setRng(rng);
+ } else {
+ rng.setStart(node, offset);
+ rng.setEnd(node, offset);
+ self.setRng(rng);
+ self.collapse(false);
+ }
},
/**
* Returns the selected contents using the DOM serializer passed in to this class.
*
@@ -10390,58 +10425,15 @@
* @example
* // Select the first paragraph in the active editor
* tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
*/
select: function(node, content) {
- var self = this, dom = self.dom, rng = dom.createRng(), idx, nonEmptyElementsMap;
+ var self = this, dom = self.dom, rng = dom.createRng(), idx;
// Clear stored range set by FocusManager
self.lastFocusBookmark = null;
- nonEmptyElementsMap = dom.schema.getNonEmptyElements();
-
- function setPoint(node, start) {
- var root = node, walker = new TreeWalker(node, root);
-
- do {
- // Text node
- if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
- if (start) {
- rng.setStart(node, 0);
- } else {
- rng.setEnd(node, node.nodeValue.length);
- }
-
- return;
- }
-
- // BR/IMG/INPUT elements
- if (nonEmptyElementsMap[node.nodeName]) {
- if (start) {
- rng.setStartBefore(node);
- } else {
- if (node.nodeName == 'BR') {
- rng.setEndBefore(node);
- } else {
- rng.setEndAfter(node);
- }
- }
-
- return;
- }
- } while ((node = (start ? walker.next() : walker.prev())));
-
- // Failed to find any text node or other suitable location then move to the root of body
- if (root.nodeName == 'BODY') {
- if (start) {
- rng.setStart(root, 0);
- } else {
- rng.setEnd(root, root.childNodes.length);
- }
- }
- }
-
if (node) {
if (!content && self.controlSelection.controlSelect(node)) {
return;
}
@@ -10449,12 +10441,12 @@
rng.setStart(node.parentNode, idx);
rng.setEnd(node.parentNode, idx + 1);
// Find first/last text node or BR element
if (content) {
- setPoint(node, 1);
- setPoint(node);
+ self._moveEndPoint(rng, node, true);
+ self._moveEndPoint(rng, node);
}
self.setRng(rng);
}
@@ -11101,10 +11093,63 @@
if (y < viewPort.y || y + 25 > viewPortY + viewPortH) {
self.editor.getWin().scrollTo(0, y < viewPortY ? y : y - viewPortH + 25);
}
},
+ _moveEndPoint: function(rng, node, start) {
+ var root = node, walker = new TreeWalker(node, root);
+ var nonEmptyElementsMap = this.dom.schema.getNonEmptyElements();
+
+ do {
+ // Text node
+ if (node.nodeType == 3 && trim(node.nodeValue).length !== 0) {
+ if (start) {
+ rng.setStart(node, 0);
+ } else {
+ rng.setEnd(node, node.nodeValue.length);
+ }
+
+ return;
+ }
+
+ // BR/IMG/INPUT elements
+ if (nonEmptyElementsMap[node.nodeName]) {
+ if (start) {
+ rng.setStartBefore(node);
+ } else {
+ if (node.nodeName == 'BR') {
+ rng.setEndBefore(node);
+ } else {
+ rng.setEndAfter(node);
+ }
+ }
+
+ return;
+ }
+
+ // Found empty text block old IE can place the selection inside those
+ if (Env.ie && Env.ie < 11 && this.dom.isBlock(node) && this.dom.isEmpty(node)) {
+ if (start) {
+ rng.setStart(node, 0);
+ } else {
+ rng.setEnd(node, 0);
+ }
+
+ return;
+ }
+ } while ((node = (start ? walker.next() : walker.prev())));
+
+ // Failed to find any text node or other suitable location then move to the root of body
+ if (root.nodeName == 'BODY') {
+ if (start) {
+ rng.setStart(root, 0);
+ } else {
+ rng.setEnd(root, root.childNodes.length);
+ }
+ }
+ },
+
destroy: function() {
this.win = null;
this.controlSelection.destroy();
}
};
@@ -14244,10 +14289,19 @@
node = node.nextSibling;
}
}
+ // Old IE versions doesn't properly render blocks with br elements in them
+ // For example <p><br></p> wont be rendered correctly in a contentEditable area
+ // until you remove the br producing <p></p>
+ if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
+ if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
+ dom.remove(parentBlock.firstChild);
+ }
+ }
+
if (root.nodeName == 'LI') {
var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
if (firstChild && /^(UL|OL)$/.test(firstChild.nodeName)) {
root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
@@ -15116,21 +15170,32 @@
failed = TRUE;
}
// Present alert message about clipboard access not being available
if (failed || !doc.queryCommandSupported(command)) {
- editor.windowManager.alert(
+ var msg = editor.translate(
"Your browser doesn't support direct access to the clipboard. " +
"Please use the Ctrl+X/C/V keyboard shortcuts instead."
);
+
+ if (Env.mac) {
+ msg = msg.replace(/Ctrl\+/g, '\u2318+');
+ }
+
+ editor.windowManager.alert(msg);
}
},
// Override unlink command
unlink: function(command) {
if (selection.isCollapsed()) {
- selection.select(selection.getNode());
+ var elm = selection.getNode();
+ if (elm.tagName == 'A') {
+ editor.dom.remove(elm, true);
+ }
+
+ return;
}
execNativeCommand(command);
selection.collapse(FALSE);
},
@@ -15288,11 +15353,11 @@
}
// Setup parser and serializer
parser = editor.parser;
serializer = new Serializer({}, editor.schema);
- bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark"></span>';
+ bookmarkHtml = '<span id="mce_marker" data-mce-type="bookmark">ÈB;</span>';
// Run beforeSetContent handlers on the HTML to be inserted
args = {content: value, format: 'html', selection: true};
editor.fire('BeforeSetContent', args);
value = args.content;
@@ -15304,15 +15369,20 @@
// Replace the caret marker with a span bookmark element
value = value.replace(/\{\$caret\}/, bookmarkHtml);
// If selection is at <body>|<p></p> then move it into <body><p>|</p>
+ rng = selection.getRng();
+ var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null);
var body = editor.getBody();
- if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
- body.firstChild.appendChild(dom.doc.createTextNode('\u00a0'));
- selection.select(body.firstChild, true);
- dom.remove(body.firstChild.lastChild);
+ if (caretElement === body && selection.isCollapsed()) {
+ if (dom.isBlock(body.firstChild) && dom.isEmpty(body.firstChild)) {
+ rng = dom.createRng();
+ rng.setStart(body.firstChild, 0);
+ rng.setEnd(body.firstChild, 0);
+ selection.setRng(rng);
+ }
}
// Insert node maker where we will insert the new HTML and get it's parent
if (!selection.isCollapsed()) {
editor.getDoc().execCommand('Delete', false, null);
@@ -15527,23 +15597,49 @@
formatter.apply('link', value, anchor);
}
},
selectAll: function() {
- var root = dom.getRoot(), rng = dom.createRng();
+ var root = dom.getRoot(), rng;
- // Old IE does a better job with selectall than new versions
if (selection.getRng().setStart) {
+ rng = dom.createRng();
rng.setStart(root, 0);
rng.setEnd(root, root.childNodes.length);
-
selection.setRng(rng);
} else {
- execNativeCommand('SelectAll');
+ // IE will render it's own root level block elements and sometimes
+ // even put font elements in them when the user starts typing. So we need to
+ // move the selection to a more suitable element from this:
+ // <body>|<p></p></body> to this: <body><p>|</p></body>
+ rng = selection.getRng();
+ if (!rng.item) {
+ rng.moveToElementText(root);
+ rng.select();
+ }
}
},
+ "delete": function() {
+ execNativeCommand("Delete");
+
+ // Check if body is empty after the delete call if so then set the contents
+ // to an empty string and move the caret to any block produced by that operation
+ // this fixes the issue with root blocks not being properly produced after a delete call on IE
+ var body = editor.getBody();
+
+ if (dom.isEmpty(body)) {
+ editor.setContent('');
+
+ if (body.firstChild && dom.isBlock(body.firstChild)) {
+ editor.selection.setCursorLocation(body.firstChild, 0);
+ } else {
+ editor.selection.setCursorLocation(body, 0);
+ }
+ }
+ },
+
mceNewDocument: function() {
editor.setContent('');
}
});
@@ -15671,19 +15767,25 @@
if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) {
self.source = url;
return;
}
+ var isProtocolRelative = url.indexOf('//') === 0;
+
// Absolute path with no host, fake host and protocol
- if (url.indexOf('/') === 0 && url.indexOf('//') !== 0) {
+ if (url.indexOf('/') === 0 && !isProtocolRelative) {
url = (settings.base_uri ? settings.base_uri.protocol || 'http' : 'http') + '://mce_host' + url;
}
// Relative path http:// or protocol relative //path
if (!/^[\w\-]*:?\/\//.test(url)) {
base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory;
- url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
+ if (settings.base_uri.protocol === "") {
+ url = '//mce_host' + self.toAbsPath(base_url, url);
+ } else {
+ url = ((settings.base_uri && settings.base_uri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url);
+ }
}
// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
@@ -15720,10 +15822,14 @@
}
self.source = '';
}
+ if (isProtocolRelative) {
+ self.protocol = '';
+ }
+
//t.path = t.path || '/';
}
URI.prototype = {
/**
@@ -15765,18 +15871,19 @@
}
uri = new URI(uri, {base_uri: self});
// Not on same domain/port or protocol
- if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port || self.protocol != uri.protocol) {
+ if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port ||
+ (self.protocol != uri.protocol && uri.protocol !== "")) {
return uri.getURI();
}
var tu = self.getURI(), uu = uri.getURI();
// Allow usage of the base_uri when relative_urls = true
- if(tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
+ if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) {
return tu;
}
output = self.toRelPath(self.path, uri.path);
@@ -15944,10 +16051,12 @@
s = '';
if (!noProtoHost) {
if (self.protocol) {
s += self.protocol + '://';
+ } else {
+ s += '//';
}
if (self.userInfo) {
s += self.userInfo + '@';
}
@@ -17491,50 +17600,55 @@
* Repaints the control after a layout operation.
*
* @method repaint
*/
repaint: function() {
- var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect;
+ var self = this, style, bodyStyle, rect, borderBox, borderW = 0, borderH = 0, lastRepaintRect, round;
+ // Use Math.round on all values on IE < 9
+ round = !document.createRange ? Math.round : function(value) {
+ return value;
+ };
+
style = self.getEl().style;
rect = self._layoutRect;
lastRepaintRect = self._lastRepaintRect || {};
borderBox = self._borderBox;
borderW = borderBox.left + borderBox.right;
borderH = borderBox.top + borderBox.bottom;
if (rect.x !== lastRepaintRect.x) {
- style.left = rect.x + 'px';
+ style.left = round(rect.x) + 'px';
lastRepaintRect.x = rect.x;
}
if (rect.y !== lastRepaintRect.y) {
- style.top = rect.y + 'px';
+ style.top = round(rect.y) + 'px';
lastRepaintRect.y = rect.y;
}
if (rect.w !== lastRepaintRect.w) {
- style.width = (rect.w - borderW) + 'px';
+ style.width = round(rect.w - borderW) + 'px';
lastRepaintRect.w = rect.w;
}
if (rect.h !== lastRepaintRect.h) {
- style.height = (rect.h - borderH) + 'px';
+ style.height = round(rect.h - borderH) + 'px';
lastRepaintRect.h = rect.h;
}
// Update body if needed
if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) {
bodyStyle = self.getEl('body').style;
- bodyStyle.width = (rect.innerW) + 'px';
+ bodyStyle.width = round(rect.innerW) + 'px';
lastRepaintRect.innerW = rect.innerW;
}
if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) {
bodyStyle = bodyStyle || self.getEl('body').style;
- bodyStyle.height = (rect.innerH) + 'px';
+ bodyStyle.height = round(rect.innerH) + 'px';
lastRepaintRect.innerH = rect.innerH;
}
self._lastRepaintRect = lastRepaintRect;
self.fire('repaint', {}, false);
@@ -20874,18 +20988,23 @@
*
* @method remove
* @return {tinymce.ui.Control} Current control instance.
*/
remove: function() {
- var self = this;
+ var self = this, prefix = self.classPrefix;
self.dragHelper.destroy();
self._super();
if (self.statusbar) {
this.statusbar.remove();
}
+
+ if (self._fullscreen) {
+ DomUtils.removeClass(document.documentElement, prefix + 'fullscreen');
+ DomUtils.removeClass(document.body, prefix + 'fullscreen');
+ }
}
});
return Window;
});
@@ -21493,52 +21612,65 @@
body.appendChild(contents);
return selection.serializer.serialize(body, {format: 'html'});
}
function allContentsSelected(rng) {
+ if (!rng.setStart) {
+ if (rng.item) {
+ return false;
+ }
+
+ var bodyRng = rng.duplicate();
+ bodyRng.moveToElementText(editor.getBody());
+ return RangeUtils.compareRanges(rng, bodyRng);
+ }
+
var selection = serializeRng(rng);
var allRng = dom.createRng();
allRng.selectNode(editor.getBody());
var allSelection = serializeRng(allRng);
return selection === allSelection;
}
editor.on('keydown', function(e) {
- var keyCode = e.keyCode, isCollapsed;
+ var keyCode = e.keyCode, isCollapsed, body;
// Empty the editor if it's needed for example backspace at <p><b>|</b></p>
if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
isCollapsed = editor.selection.isCollapsed();
+ body = editor.getBody();
// Selection is collapsed but the editor isn't empty
- if (isCollapsed && !dom.isEmpty(editor.getBody())) {
+ if (isCollapsed && !dom.isEmpty(body)) {
return;
}
- // IE deletes all contents correctly when everything is selected
- if (isIE && !isCollapsed) {
- return;
- }
-
// Selection isn't collapsed but not all the contents is selected
if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
return;
}
// Manually empty the editor
e.preventDefault();
editor.setContent('');
- editor.selection.setCursorLocation(editor.getBody(), 0);
+
+ if (body.firstChild && dom.isBlock(body.firstChild)) {
+ editor.selection.setCursorLocation(body.firstChild, 0);
+ } else {
+ editor.selection.setCursorLocation(body, 0);
+ }
+
editor.nodeChanged();
}
});
}
/**
* WebKit doesn't select all the nodes in the body when you press Ctrl+A.
+ * IE selects more than the contents <body>[<p>a</p>]</body> instead of <body><p>[a]</p]</body> see bug #6438
* This selects the whole body so that backspace/delete logic will delete everything
*/
function selectAll() {
editor.on('keydown', function(e) {
if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
@@ -22263,10 +22395,25 @@
}
});
}
}
+ /**
+ * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow.
+ * You might then loose all your work so we need to block that behavior and replace it with our own.
+ */
+ function blockCmdArrowNavigation() {
+ if (Env.mac) {
+ editor.on('keydown', function(e) {
+ if (VK.metaKeyPressed(e) && (e.keyCode == 37 || e.keyCode == 39)) {
+ e.preventDefault();
+ editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'word');
+ }
+ });
+ }
+ }
+
// All browsers
disableBackspaceIntoATable();
removeBlockQuoteOnBackSpace();
emptyEditorWhenDeleting();
normalizeSelection();
@@ -22281,10 +22428,11 @@
// iOS
if (Env.iOS) {
selectionChangeNodeChanged();
restoreFocusOnKeyDown();
+ bodyHeight();
} else {
selectAll();
}
}
@@ -22302,19 +22450,24 @@
if (Env.ie >= 11) {
bodyHeight();
}
+ if (Env.ie) {
+ selectAll();
+ }
+
// Gecko
if (isGecko) {
removeHrOnBackspace();
focusBody();
removeStylesWhenDeletingAcrossBlockElements();
setGeckoEditingOptions();
addBrAfterLastLinks();
removeGhostSelection();
showBrokenImageIcon();
+ blockCmdArrowNavigation();
}
};
});
// Included from: js/tinymce/classes/util/Observable.js
@@ -22592,11 +22745,11 @@
var self = this, shortcuts = {};
editor.on('keyup keypress keydown', function(e) {
if (e.altKey || e.ctrlKey || e.metaKey) {
each(shortcuts, function(shortcut) {
- var ctrlKey = Env.mac ? (e.ctrlKey || e.metaKey) : e.ctrlKey;
+ var ctrlKey = Env.mac ? e.metaKey : e.ctrlKey;
if (shortcut.ctrl != ctrlKey || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
return;
}
@@ -23198,13 +23351,10 @@
}
// Create all plugins
each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
- // Enables users to override the control factory
- self.fire('BeforeRenderUI');
-
// Measure box
if (settings.render_ui && self.theme) {
self.orgDisplay = elm.style.display;
if (typeof settings.theme != "function") {
@@ -24214,11 +24364,11 @@
*/
hide: function() {
var self = this, doc = self.getDoc();
// Fixed bug where IE has a blinking cursor left from the editor
- if (ie && doc) {
+ if (ie && doc && !self.inline) {
doc.execCommand('SelectAll');
}
// We must save before we hide so Safari doesn't crash
self.save();
@@ -24385,10 +24535,11 @@
if (content.length === 0 || /^\s+$/.test(content)) {
forcedRootBlockName = self.settings.forced_root_block;
// Check if forcedRootBlock is configured and that the block is a valid child of the body
if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) {
+ // Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly
content = ie && ie < 11 ? '' : '<br data-mce-bogus="1">';
content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content);
} else if (!ie || ie < 11) {
// We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret
content = '<br data-mce-bogus="1">';
@@ -24695,11 +24846,11 @@
DOM.remove(self.getElement().nextSibling);
}
// Fixed bug where IE has a blinking cursor left from the editor
var doc = self.getDoc();
- if (ie && doc) {
+ if (ie && doc && !self.inline) {
doc.execCommand('SelectAll');
}
// We must save before we hide so Safari doesn't crash
self.save();
@@ -25011,10 +25162,22 @@
function isUIElement(elm) {
return !!DOMUtils.DOM.getParent(elm, FocusManager.isEditorUIElement);
}
+ function isNodeInBody(node) {
+ var body = editor.getBody();
+
+ while (node) {
+ if (node == body) {
+ return true;
+ }
+
+ node = node.parentNode;
+ }
+ }
+
editor.on('init', function() {
// On IE take selection snapshot onbeforedeactivate
if ("onbeforedeactivate" in document && Env.ie < 11) {
editor.dom.bind(editor.getBody(), 'beforedeactivate', function() {
var ieSelection = editor.getDoc().selection;
@@ -25026,28 +25189,18 @@
}
});
} else if (editor.inline || Env.ie > 10) {
// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
editor.on('nodechange keyup', function() {
- var isInBody, node = document.activeElement;
+ var node = document.activeElement;
// IE 11 reports active element as iframe not body of iframe
if (node && node.id == editor.id + '_ifr') {
node = editor.getBody();
}
- // Check if selection is within editor body
- while (node) {
- if (node == editor.getBody()) {
- isInBody = true;
- break;
- }
-
- node = node.parentNode;
- }
-
- if (isInBody) {
+ if (isNodeInBody(node)) {
lastRng = editor.selection.getRng();
}
});
// Handles the issue with WebKit not retaining selection within inline document
@@ -25103,11 +25256,18 @@
}
lastRng = null;
});
- editor.on('focusout', function() {
+ editor.on('focusout', function(e) {
+ // Moving focus to elements within the body that have a control seleciton on IE
+ // will fire an focusout event so we need to check if the event is fired on the body
+ // or on a sub element see #6456
+ if (e.target !== editor.getBody() && isNodeInBody(e.target)) {
+ return;
+ }
+
editor.selection.lastFocusBookmark = createBookmark(lastRng);
window.setTimeout(function() {
var focusedEditor = editorManager.focusedEditor;
@@ -25192,19 +25352,19 @@
* Minor version of TinyMCE build.
*
* @property minorVersion
* @type String
*/
- minorVersion : '0.10',
+ minorVersion : '0.11',
/**
* Release date of TinyMCE build.
*
* @property releaseDate
* @type String
*/
- releaseDate: '2013-10-28',
+ releaseDate: '2013-11-20',
/**
* Collection of editor instances.
*
* @property editors
@@ -25252,11 +25412,15 @@
// Get base where the tinymce script is located
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
var src = scripts[i].src;
- if (/tinymce(\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
+ // Script types supported:
+ // tinymce.js tinymce.min.js tinymce.dev.js
+ // tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js
+ // tinymce.full.js tinymce.full.min.js tinymce.full.dev.js
+ if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) {
if (src.indexOf('.min') != -1) {
suffix = '.min';
}
baseURL = src.substring(0, src.lastIndexOf('/'));
@@ -26943,10 +27107,47 @@
self.addClass('btn-' + size);
}
},
/**
+ * Sets/gets the current button icon.
+ *
+ * @method icon
+ * @param {String} [icon] New icon identifier.
+ * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance.
+ */
+ icon: function(icon) {
+ var self = this, prefix = self.classPrefix;
+
+ if (typeof(icon) == 'undefined') {
+ return self.settings.icon;
+ }
+
+ self.settings.icon = icon;
+ icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : '';
+
+ if (self._rendered) {
+ var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0];
+
+ if (icon) {
+ if (!iconElm || iconElm != btnElm.firstChild) {
+ iconElm = document.createElement('i');
+ btnElm.insertBefore(iconElm, btnElm.firstChild);
+ }
+
+ iconElm.className = icon;
+ } else if (iconElm) {
+ btnElm.removeChild(iconElm);
+ }
+
+ self.text(self._text); // Set text again to fix whitespace between icon + text
+ }
+
+ return self;
+ },
+
+ /**
* Repaints the button for example after it's been resizes by a layout engine.
*
* @method repaint
*/
repaint: function() {
@@ -28413,11 +28614,11 @@
ratio = availableSpace / totalFlex;
for (i = 0, l = maxSizeItems.length; i < l; i++) {
ctrl = maxSizeItems[i];
ctrlLayoutRect = ctrl.layoutRect();
maxSize = ctrlLayoutRect[maxSizeName];
- size = ctrlLayoutRect[minSizeName] + Math.ceil(ctrlLayoutRect.flex * ratio);
+ size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio;
if (size > maxSize) {
availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]);
totalFlex -= ctrlLayoutRect.flex;
ctrlLayoutRect.flex = 0;
@@ -28472,11 +28673,11 @@
rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top;
}
// Calculate new size based on flex
if (ctrlLayoutRect.flex > 0) {
- size += Math.ceil(ctrlLayoutRect.flex * ratio);
+ size += ctrlLayoutRect.flex * ratio;
}
rect[sizeName] = size;
rect[posName] = pos;
ctrl.layoutRect(rect);
@@ -28595,11 +28796,11 @@
return '';
}
// Default preview
if (!previewStyles) {
- previewStyles = 'font-family font-size font-weight text-decoration ' +
+ previewStyles = 'font-family font-size font-weight font-style text-decoration ' +
'text-transform color background-color border border-radius';
}
// Removes any variables since these can't be previewed
function removeVars(val) {
@@ -29349,11 +29550,11 @@
}
// Calculate new column widths based on flex values
var ratio = availableWidth / totalFlex;
for (x = 0; x < cols; x++) {
- colWidths[x] += flexWidths ? Math.ceil(flexWidths[x] * ratio) : ratio;
+ colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio;
}
// Move/resize controls
posY = contPaddingBox.top;
for (y = 0; y < rows; y++) {
@@ -29938,11 +30139,11 @@
var self = this, i, children;
if (self._rendered) {
children = self.getEl('open').getElementsByTagName('span');
for (i = 0; i < children.length; i++) {
- children[i].innerHTML = self.encode(text);
+ children[i].innerHTML = (self.settings.icon && text ? '\u00a0' : '') + self.encode(text);
}
}
return this._super(text);
},
@@ -30104,12 +30305,13 @@
* @class tinymce.ui.MenuItem
* @extends tinymce.ui.Control
*/
define("tinymce/ui/MenuItem", [
"tinymce/ui/Widget",
- "tinymce/ui/Factory"
-], function(Widget, Factory) {
+ "tinymce/ui/Factory",
+ "tinymce/Env"
+], function(Widget, Factory, Env) {
"use strict";
return Widget.extend({
Defaults: {
border: 0,
@@ -30299,28 +30501,36 @@
* @method renderHtml
* @return {String} HTML representing the control.
*/
renderHtml: function() {
var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.encode(self._text);
- var icon = self.settings.icon, image = '';
+ var icon = self.settings.icon, image = '', shortcut = settings.shortcut;
if (icon) {
self.parent().addClass('menu-has-icons');
}
if (settings.image) {
icon = 'none';
image = ' style="background-image: url(\'' + settings.image + '\')"';
}
+ if (shortcut && Env.mac) {
+ // format shortcut for Mac
+ shortcut = shortcut.replace(/ctrl\+alt\+/i, '⌥⌘'); // ctrl+cmd
+ shortcut = shortcut.replace(/ctrl\+/i, '⌘'); // ctrl symbol
+ shortcut = shortcut.replace(/alt\+/i, '⌥'); // cmd symbol
+ shortcut = shortcut.replace(/shift\+/i, '⇧'); // shift symbol
+ }
+
icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none');
return (
'<div id="' + id + '" class="' + self.classes() + '" tabindex="-1">' +
(text !== '-' ? '<i class="' + icon + '"' + image + '></i> ' : '') +
(text !== '-' ? '<span id="' + id + '-text" class="' + prefix + 'text">' + text + '</span>' : '') +
- (settings.shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' +
- settings.shortcut + '</div>' : '') +
+ (shortcut ? '<div id="' + id + '-shortcut" class="' + prefix + 'menu-shortcut">' +
+ shortcut + '</div>' : '') +
(settings.menu ? '<div class="' + prefix + 'caret"></div>' : '') +
'</div>'
);
},
\ No newline at end of file