app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.1.2 vs app/assets/source/tinymce/tinymce.jquery.js in tinymce-rails-4.1.3
- old
+ new
@@ -1,6 +1,6 @@
-// 4.1.2 (2014-07-15)
+// 4.1.3 (2014-07-29)
/**
* Compiled inline version. (Library mode)
*/
@@ -452,10 +452,11 @@
callbackList = events[id][name];
if (!callbackList) {
events[id][name] = callbackList = [{func: callback, scope: scope}];
callbackList.fakeName = fakeName;
callbackList.capture = capture;
+ //callbackList.callback = callback;
// Add the nativeHandler to the callback list so that we can later unbind it
callbackList.nativeHandler = nativeHandler;
// Check if the target has native events support
@@ -1448,11 +1449,11 @@
'for': 'htmlFor',
'class': 'className',
'readonly': 'readOnly'
};
var cssFix = {
- float: 'cssFloat'
+ 'float': 'cssFloat'
};
var attrHooks = {}, cssHooks = {};
function DomQuery(selector, context) {
@@ -2776,27 +2777,27 @@
DomQuery.fn.init.prototype = DomQuery.fn;
DomQuery.overrideDefaults = function(callback) {
var defaults;
- function jQuerySub(selector, context) {
+ function sub(selector, context) {
defaults = defaults || callback();
if (arguments.length === 0) {
selector = defaults.element;
}
if (!context) {
context = defaults.context;
}
- return new jQuerySub.fn.init(selector, context);
+ return new sub.fn.init(selector, context);
}
- DomQuery.extend(jQuerySub, this);
+ DomQuery.extend(sub, this);
- return jQuerySub;
+ return sub;
};
function appendHooks(targetHooks, prop, hooks) {
each(hooks, function(name, func) {
targetHooks[name] = targetHooks[name] || {};
@@ -2851,11 +2852,13 @@
}
});
}
if (Env.ie && Env.ie < 9) {
- cssFix.float = 'styleFloat';
+ /*jshint sub:true */
+ /*eslint dot-notation: 0*/
+ cssFix['float'] = 'styleFloat';
appendHooks(cssHooks, 'set', {
opacity: function(elm, value) {
var style = elm.style;
@@ -5302,10 +5305,14 @@
}
elm = self.$$(elm);
originalValue = elm.attr(name);
+ if (!elm.length) {
+ return;
+ }
+
hook = self.attrHooks[name];
if (hook && hook.set) {
hook.set(elm, value, name);
} else {
elm.attr(name, value);
@@ -5355,15 +5362,18 @@
getAttrib: function(elm, name, defaultVal) {
var self = this, hook, value;
elm = self.$$(elm);
- hook = self.attrHooks[name];
- if (hook && hook.get) {
- value = hook.get(elm, name);
- } else {
- value = elm.attr(name);
+ if (elm.length) {
+ hook = self.attrHooks[name];
+
+ if (hook && hook.get) {
+ value = hook.get(elm, name);
+ } else {
+ value = elm.attr(name);
+ }
}
if (typeof value == 'undefined') {
value = defaultVal || '';
}
@@ -5378,25 +5388,26 @@
* @param {Element/String} elm HTML element or element id to get x, y position from.
* @param {Element} rootElm Optional root element to stop calculations at.
* @return {object} Absolute position of the specified element object with x, y fields.
*/
getPos: function(elm, rootElm) {
- var self = this, x = 0, y = 0, offsetParent, doc = self.doc, pos;
+ var self = this, x = 0, y = 0, offsetParent, doc = self.doc, body = doc.body, pos;
elm = self.get(elm);
- rootElm = rootElm || doc.body;
+ rootElm = rootElm || body;
if (elm) {
// Use getBoundingClientRect if it exists since it's faster than looping offset nodes
- if (rootElm === doc.body && elm.getBoundingClientRect) {
+ // Fallback to offsetParent calculations if the body isn't static better since it stops at the body root
+ if (rootElm === body && elm.getBoundingClientRect && $(body).css('position') === 'static') {
pos = elm.getBoundingClientRect();
- rootElm = self.boxModel ? doc.documentElement : doc.body;
+ rootElm = self.boxModel ? doc.documentElement : body;
// Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
// Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
- x = pos.left + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - rootElm.clientLeft;
- y = pos.top + (doc.documentElement.scrollTop || doc.body.scrollTop) - rootElm.clientTop;
+ x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft;
+ y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop;
return {x: x, y: y};
}
offsetParent = elm;
@@ -7463,11 +7474,11 @@
return false;
}
// Gecko doesn't support the "selectionchange" event
if (!('onselectionchange' in editor.getDoc())) {
- editor.on('NodeChange Click MouseUp KeyUp', function(e) {
+ editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) {
var nativeRng, fakeRng;
// Since DOM Ranges mutate on modification
// of the DOM we need to clone it's contents
nativeRng = editor.selection.getRng();
@@ -7493,18 +7504,25 @@
editor.on('contextmenu', function() {
editor.fire('SelectionChange');
});
editor.on('SelectionChange', function() {
- var startElm = editor.selection.getStart();
+ var startElm = editor.selection.getStart(true);
// Selection change might fire when focus is lost so check if the start is still within the body
if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
editor.nodeChanged({selectionChange: true});
}
});
+ // Fire an extra nodeChange on mouseup for compatibility reasons
+ editor.on('MouseUp', function(e) {
+ if (!e.isDefaultPrevented()) {
+ editor.nodeChanged();
+ }
+ });
+
/**
* Distpaches out a onNodeChange event to all observers. This method should be called when you
* need to update the UI states or element path etc.
*
* @method nodeChanged
@@ -8071,11 +8089,11 @@
* @version 3.4
*/
define("tinymce/html/Schema", [
"tinymce/util/Tools"
], function(Tools) {
- var mapCache = {};
+ var mapCache = {}, dummyObj = {};
var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
function split(items, delim) {
return items ? items.split(delim || ' ') : [];
}
@@ -8092,15 +8110,15 @@
var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
function add(name, attributes, children) {
var ni, i, attributesOrder, args = arguments;
- function arrayToMap(array) {
+ function arrayToMap(array, obj) {
var map = {}, i, l;
for (i = 0, l = array.length; i < l; i++) {
- map[array[i]] = {};
+ map[array[i]] = obj || {};
}
return map;
}
@@ -8125,11 +8143,11 @@
while (ni--) {
attributesOrder = [].concat(globalAttributes, split(attributes));
schema[name[ni]] = {
attributes: arrayToMap(attributesOrder),
attributesOrder: attributesOrder,
- children: arrayToMap(children)
+ children: arrayToMap(children, dummyObj)
};
}
}
function addAttrs(name, attributes) {
@@ -11848,10 +11866,11 @@
if (!isIE || selectedElm.nodeName == "TABLE") {
showResizeRect(selectedElm);
}
editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
+ dom.setAttrib(selectedElm, 'style', dom.getAttrib(selectedElm, 'style'));
editor.nodeChanged();
}
function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
var position, targetWidth, targetHeight, e, rect;
@@ -11995,11 +12014,11 @@
}
}
}
function updateResizeRect(e) {
- var controlElm;
+ var startElm, controlElm;
function isChildOrEqual(node, parent) {
if (node) {
do {
if (node === parent) {
@@ -12017,13 +12036,14 @@
controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
controlElm = dom.$(controlElm).closest(isIE ? 'table' : 'table,img,hr')[0];
if (isChildOrEqual(controlElm, rootElement)) {
disableGeckoResize();
+ startElm = selection.getStart(true);
- if (isChildOrEqual(selection.getStart(), controlElm) && isChildOrEqual(selection.getEnd(), controlElm)) {
- if (!isIE || (controlElm != selection.getStart() && selection.getStart().nodeName !== 'IMG')) {
+ if (isChildOrEqual(startElm, controlElm) && isChildOrEqual(selection.getEnd(true), controlElm)) {
+ if (!isIE || (controlElm != startElm && startElm.nodeName !== 'IMG')) {
showResizeRect(controlElm);
return;
}
}
}
@@ -12179,11 +12199,11 @@
}
});
}
}
- editor.on('nodechange mousedown mouseup ResizeEditor', updateResizeRect);
+ editor.on('nodechange ResizeEditor', updateResizeRect);
// Update resize rect while typing in a table
editor.on('keydown keyup', function(e) {
if (selectedElm && selectedElm.nodeName == "TABLE") {
updateResizeRect(e);
@@ -12851,13 +12871,14 @@
/**
* Returns the start element of a selection range. If the start is in a text
* node the parent element will be returned.
*
* @method getStart
+ * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
* @return {Element} Start element of selection range.
*/
- getStart: function() {
+ getStart: function(real) {
var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
if (rng.duplicate || rng.item) {
// Control selection, return first item
if (rng.item) {
@@ -12885,11 +12906,13 @@
return startElement;
} else {
startElement = rng.startContainer;
if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
- startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
+ if (!real || !rng.collapsed) {
+ startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
+ }
}
if (startElement && startElement.nodeType == 3) {
return startElement.parentNode;
}
@@ -12901,13 +12924,14 @@
/**
* Returns the end element of a selection range. If the end is in a text
* node the parent element will be returned.
*
* @method getEnd
+ * @param {Boolean} real Optional state to get the real parent when the selection is collapsed not the closest element.
* @return {Element} End element of selection range.
*/
- getEnd: function() {
+ getEnd: function(real) {
var self = this, rng = self.getRng(), endElement, endOffset;
if (rng.duplicate || rng.item) {
if (rng.item) {
return rng.item(0);
@@ -12928,11 +12952,13 @@
} else {
endElement = rng.endContainer;
endOffset = rng.endOffset;
if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
- endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
+ if (!real || !rng.collapsed) {
+ endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
+ }
}
if (endElement && endElement.nodeType == 3) {
return endElement.parentNode;
}
@@ -13520,12 +13546,12 @@
}
return;
}
- // BR/IMG/INPUT elements
- if (nonEmptyElementsMap[node.nodeName]) {
+ // BR/IMG/INPUT elements but not table cells
+ if (nonEmptyElementsMap[node.nodeName] && !/^(TD|TH)$/.test(node.nodeName)) {
if (start) {
rng.setStartBefore(node);
} else {
if (node.nodeName == 'BR') {
rng.setEndBefore(node);
@@ -13977,12 +14003,12 @@
strikethrough: [
{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true},
{inline: 'strike', remove: 'all'}
],
- forecolor: {inline: 'span', styles: {color: '%value'}, wrap_links: false, remove_similar: true},
- hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, wrap_links: false, remove_similar: true},
+ forecolor: {inline: 'span', styles: {color: '%value'}, links: true, remove_similar: true},
+ hilitecolor: {inline: 'span', styles: {backgroundColor: '%value'}, links: true, remove_similar: true},
fontname: {inline: 'span', styles: {fontFamily: '%value'}},
fontsize: {inline: 'span', styles: {fontSize: '%value'}},
fontsize_class: {inline: 'span', attributes: {'class': '%value'}},
blockquote: {block: 'blockquote', wrapper: 1, remove: 'all'},
subscript: {inline: 'sub'},
@@ -14314,26 +14340,16 @@
// Process siblings from range
each(nodes, process);
});
- // Wrap links inside as well, for example color inside a link when the wrapper is around the link
- if (format.wrap_links === false) {
+ // Apply formats to links as well to get the color of the underline to change as well
+ if (format.links === true) {
each(newWrappers, function(node) {
function process(node) {
- var i, currentWrapElm, children;
-
if (node.nodeName === 'A') {
- currentWrapElm = dom.clone(wrapElm, FALSE);
- newWrappers.push(currentWrapElm);
-
- children = grep(node.childNodes);
- for (i = 0; i < children.length; i++) {
- currentWrapElm.appendChild(children[i]);
- }
-
- node.appendChild(currentWrapElm);
+ setElementFormat(node, format);
}
each(grep(node.childNodes), process);
}
@@ -14399,28 +14415,14 @@
each(formatList, function(format) {
// Merge all children of similar type will move styles from child to parent
// this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span>
// will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span>
each(dom.select(format.inline, node), function(child) {
- var parent;
-
if (isBookmarkNode(child)) {
return;
}
- // When wrap_links is set to false we don't want
- // to remove the format on children within links
- if (format.wrap_links === false) {
- parent = child.parentNode;
-
- do {
- if (parent.nodeName === 'A') {
- return;
- }
- } while ((parent = parent.parentNode));
- }
-
removeFormat(format, vars, child, format.exact ? child : null);
});
});
// Remove child if direct parent is of same type
@@ -14958,11 +14960,11 @@
ed.on('NodeChange', function(e) {
var parents = getParents(e.element), matchedFormats = {};
// Ignore bogus nodes like the <a> tag created by moveStart()
parents = Tools.grep(parents, function(node) {
- return !node.getAttribute('data-mce-bogus');
+ return node.nodeType == 1 && !node.getAttribute('data-mce-bogus');
});
// Check for new formats
each(formatChangeData, function(callbacks, format) {
each(parents, function(node) {
@@ -15038,12 +15040,12 @@
});
// Initialize
defaultFormats();
addKeyboardShortcuts();
- ed.on('BeforeGetContent', function() {
- if (markCaretContainersBogus) {
+ ed.on('BeforeGetContent', function(e) {
+ if (markCaretContainersBogus && e.format != 'raw') {
markCaretContainersBogus();
}
});
ed.on('mouseup keydown', function(e) {
if (disableCaretContainer) {
@@ -15529,10 +15531,14 @@
endContainer: endContainer,
endOffset: endOffset
};
}
+ function isColorFormatAndAnchor(node, format) {
+ return format.links && node.tagName == 'A';
+ }
+
/**
* Removes the specified format for the specified node. It will also remove the node if it doesn't have
* any attributes if the format specifies it to do so.
*
* @private
@@ -15544,11 +15550,11 @@
*/
function removeFormat(format, vars, node, compare_node) {
var i, attrs, stylesModified;
// Check if node matches format
- if (!matchName(node, format)) {
+ if (!matchName(node, format) && !isColorFormatAndAnchor(node, format)) {
return FALSE;
}
// Should we compare with format attribs and styles
if (format.remove != 'all') {
@@ -18757,11 +18763,11 @@
], function(Tools) {
var nativeEvents = Tools.makeMap(
"focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " +
"mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " +
"draggesture dragdrop drop drag submit " +
- "compositionstart compositionend compositionupdate",
+ "compositionstart compositionend compositionupdate touchstart touchend",
' '
);
function Dispatcher(settings) {
var self = this, scope, bindings = {}, toggleEvent;
@@ -19951,11 +19957,10 @@
"tinymce/ui/Collection",
"tinymce/ui/DomUtils"
], function(Class, Tools, EventDispatcher, Collection, DomUtils) {
"use strict";
- var elementIdCache = {};
var hasMouseWheelEventSupport = "onmousewheel" in document;
var hasWheelEventSupport = false;
var classPrefix = "mce-";
function getEventDispatcher(obj) {
@@ -19981,11 +19986,10 @@
return obj._eventDispatcher;
}
var Control = Class.extend({
Statics: {
- elementIdCache: elementIdCache,
classPrefix: classPrefix
},
isRtl: function() {
return Control.rtl;
@@ -20024,10 +20028,11 @@
// Initial states
self._id = settings.id || DomUtils.id();
self._text = self._name = '';
self._width = self._height = 0;
self._aria = {role: settings.role};
+ this._elmCache = {};
// Setup classes
classes = settings.classes;
if (classes) {
classes = classes.split(' ');
@@ -20765,19 +20770,20 @@
/**
* Returns the control DOM element or sub element.
*
* @method getEl
* @param {String} [suffix] Suffix to get element by.
- * @param {Boolean} [dropCache] True if the cache for the element should be dropped.
* @return {Element} HTML DOM element for the current control or it's children.
*/
- getEl: function(suffix, dropCache) {
- var elm, id = suffix ? this._id + '-' + suffix : this._id;
+ getEl: function(suffix) {
+ var id = suffix ? this._id + '-' + suffix : this._id;
- elm = elementIdCache[id] = (dropCache === true ? null : elementIdCache[id]) || DomUtils.get(id);
+ if (!this._elmCache[id]) {
+ this._elmCache[id] = DomUtils.get(id);
+ }
- return elm;
+ return this._elmCache[id];
},
/**
* Sets/gets the visible for the control.
*
@@ -20984,20 +20990,11 @@
var lookup = self.getRoot().controlIdLookup;
if (lookup) {
delete lookup[self._id];
}
- delete elementIdCache[self._id];
-
if (elm && elm.parentNode) {
- var nodes = elm.getElementsByTagName('*');
-
- i = nodes.length;
- while (i--) {
- delete elementIdCache[nodes[i].id];
- }
-
elm.parentNode.removeChild(elm);
}
self._rendered = false;
@@ -23146,12 +23143,19 @@
}
}
function bindWindowResizeHandler() {
if (!windowResizeHandler) {
+ var docElm = document.documentElement, clientWidth = docElm.clientWidth, clientHeight = docElm.clientHeight;
+
windowResizeHandler = function() {
- FloatPanel.hideAll();
+ // Workaround for #7065 IE 7 fires resize events event though the window wasn't resized
+ if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) {
+ clientWidth = docElm.clientWidth;
+ clientHeight = docElm.clientHeight;
+ FloatPanel.hideAll();
+ }
};
DomUtils.on(window, 'resize', windowResizeHandler);
}
}
@@ -24093,10 +24097,18 @@
}
}
self.windows = windows;
+ editor.on('remove', function() {
+ var i = windows.length;
+
+ while (i--) {
+ windows[i].close();
+ }
+ });
+
/**
* Opens a new window.
*
* @method open
* @param {Object} args Optional name/value settings collection contains things like width/height/url etc.
@@ -24419,11 +24431,11 @@
elm.setAttribute('mce-data-marked', 1);
}
// Make sure all elements has a data-mce-style attribute
if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) {
- editor.dom.setAttrib(elm, 'style', elm.getAttribute('style'));
+ editor.dom.setAttrib(elm, 'style', editor.dom.getAttrib(elm, 'style'));
}
});
// Observe added nodes and style attribute changes
mutationObserver.observe(editor.getDoc(), {
@@ -24842,39 +24854,10 @@
}
});
}
/**
- * Fire a nodeChanged when the selection is changed on WebKit this fixes selection issues on iOS5. It only fires the nodeChange
- * event every 50ms since it would other wise update the UI when you type and it hogs the CPU.
- */
- function selectionChangeNodeChanged() {
- var lastRng, selectionTimer;
-
- editor.on('selectionchange', function() {
- if (selectionTimer) {
- clearTimeout(selectionTimer);
- selectionTimer = 0;
- }
-
- selectionTimer = window.setTimeout(function() {
- if (editor.removed) {
- return;
- }
-
- var rng = selection.getRng();
-
- // Compare the ranges to see if it was a real change or not
- if (!lastRng || !RangeUtils.compareRanges(rng, lastRng)) {
- editor.nodeChanged();
- lastRng = rng;
- }
- }, 50);
- });
- }
-
- /**
* Screen readers on IE needs to have the role application set on the body.
*/
function ensureBodyHasRoleApplication() {
document.body.setAttribute("role", "application");
}
@@ -25397,10 +25380,57 @@
editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}');
}
/**
+ * iOS Safari and possible other browsers have a bug where it won't fire
+ * a click event when a contentEditable is focused. This function fakes click events
+ * by using touchstart/touchend and measuring the time and distance travelled.
+ */
+ function touchClickEvent() {
+ editor.on('touchstart', function(e) {
+ var elm, time, startTouch, changedTouches;
+
+ elm = e.target;
+ time = new Date().getTime();
+ changedTouches = e.changedTouches;
+
+ if (!changedTouches || changedTouches.length > 1) {
+ return;
+ }
+
+ startTouch = changedTouches[0];
+
+ editor.once('touchend', function(e) {
+ var endTouch = e.changedTouches[0], args;
+
+ if (new Date().getTime() - time > 500) {
+ return;
+ }
+
+ if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) {
+ return;
+ }
+
+ if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) {
+ return;
+ }
+
+ args = {
+ target: elm
+ };
+
+ each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) {
+ args[key] = endTouch[key];
+ });
+
+ args = editor.fire('click', args);
+ });
+ });
+ }
+
+ /**
* WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element.
* For example this: <form><button></form>
*/
function blockFormSubmitInsideEditor() {
editor.on('init', function() {
@@ -25442,14 +25472,14 @@
selectControlElements();
setDefaultBlockType();
blockFormSubmitInsideEditor();
disableBackspaceIntoATable();
removeAppleInterchangeBrs();
+ touchClickEvent();
// iOS
if (Env.iOS) {
- selectionChangeNodeChanged();
restoreFocusOnKeyDown();
bodyHeight();
tapLinksAndImages();
} else {
selectAll();
@@ -25647,84 +25677,134 @@
define("tinymce/EditorObservable", [
"tinymce/util/Observable",
"tinymce/dom/DOMUtils",
"tinymce/util/Tools"
], function(Observable, DOMUtils, Tools) {
- var DOM = DOMUtils.DOM;
+ var DOM = DOMUtils.DOM, customEventRootDelegates;
+ /**
+ * Returns the event target so for the specified event. Some events fire
+ * only on document, some fire on documentElement etc. This also handles the
+ * custom event root setting where it returns that element instead of the body.
+ *
+ * @private
+ * @param {tinymce.Editor} editor Editor instance to get event target from.
+ * @param {String} eventName Name of the event for example "click".
+ * @return {Element/Document} HTML Element or document target to bind on.
+ */
function getEventTarget(editor, eventName) {
if (eventName == 'selectionchange') {
return editor.getDoc();
}
// Need to bind mousedown/mouseup etc to document not body in iframe mode
// Since the user might click on the HTML element not the BODY
if (!editor.inline && /^mouse|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
- return editor.getDoc();
+ return editor.getDoc().documentElement;
}
+ // Bind to event root instead of body if it's defined
+ if (editor.settings.event_root) {
+ if (!editor.eventRoot) {
+ editor.eventRoot = DOM.select(editor.settings.event_root)[0];
+ }
+
+ return editor.eventRoot;
+ }
+
return editor.getBody();
}
- function bindEventDelegate(editor, name) {
- var eventRootSelector = editor.settings.event_root, editorManager = editor.editorManager;
- var eventRootElm = editorManager.eventRootElm || getEventTarget(editor, name);
+ /**
+ * Binds a event delegate for the specified name this delegate will fire
+ * the event to the editor dispatcher.
+ *
+ * @private
+ * @param {tinymce.Editor} editor Editor instance to get event target from.
+ * @param {String} eventName Name of the event for example "click".
+ */
+ function bindEventDelegate(editor, eventName) {
+ var eventRootElm = getEventTarget(editor, eventName), delegate;
- if (eventRootSelector) {
- if (!editorManager.rootEvents) {
- editorManager.rootEvents = {};
+ if (!editor.delegates) {
+ editor.delegates = {};
+ }
- editorManager.on('RemoveEditor', function() {
- if (!editorManager.activeEditor) {
- DOM.unbind(eventRootElm);
- delete editorManager.rootEvents;
+ if (editor.delegates[eventName]) {
+ return;
+ }
+
+ if (editor.settings.event_root) {
+ if (!customEventRootDelegates) {
+ customEventRootDelegates = {};
+ editor.editorManager.on('removeEditor', function() {
+ var name;
+
+ if (!editor.editorManager.activeEditor) {
+ if (customEventRootDelegates) {
+ for (name in customEventRootDelegates) {
+ editor.dom.unbind(getEventTarget(editor, name));
+ }
+
+ customEventRootDelegates = null;
+ }
}
});
}
- if (editorManager.rootEvents[name]) {
+ if (customEventRootDelegates[eventName]) {
return;
}
- if (eventRootElm == editor.getBody()) {
- eventRootElm = DOM.select(eventRootSelector)[0];
- editorManager.eventRootElm = eventRootElm;
- }
+ delegate = function(e) {
+ var target = e.target, editors = editor.editorManager.editors, i = editors.length;
- editorManager.rootEvents[name] = true;
-
- DOM.bind(eventRootElm, name, function(e) {
- var target = e.target, editors = editorManager.editors, i = editors.length;
-
while (i--) {
var body = editors[i].getBody();
if (body === target || DOM.isChildOf(target, body)) {
if (!editors[i].hidden) {
- editors[i].fire(name, e);
+ editors[i].fire(eventName, e);
}
}
}
- });
+ };
+
+ customEventRootDelegates[eventName] = delegate;
+ DOM.bind(eventRootElm, eventName, delegate);
} else {
- editor.dom.bind(eventRootElm, name, function(e) {
+ delegate = function(e) {
if (!editor.hidden) {
- editor.fire(name, e);
+ editor.fire(eventName, e);
}
- });
+ };
+
+ DOM.bind(eventRootElm, eventName, delegate);
+ editor.delegates[eventName] = delegate;
}
}
var EditorObservable = {
+ /**
+ * Bind any pending event delegates. This gets executed after the target body/document is created.
+ *
+ * @private
+ */
bindPendingEventDelegates: function() {
var self = this;
Tools.each(self._pendingNativeEvents, function(name) {
bindEventDelegate(self, name);
});
},
+ /**
+ * Toggles a native event on/off this is called by the EventDispatcher when
+ * the first native event handler is added and when the last native event handler is removed.
+ *
+ * @private
+ */
toggleNativeEvent: function(name, state) {
var self = this;
if (self.settings.readonly) {
return;
@@ -25744,12 +25824,39 @@
} else {
self._pendingNativeEvents.push(name);
}
}
} else if (self.initialized) {
- self.dom.unbind(getEventTarget(self, name), name);
+ self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
+ delete self.delegates[name];
}
+ },
+
+ /**
+ * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
+ *
+ * @private
+ */
+ unbindAllNativeEvents: function() {
+ var self = this, name;
+
+ if (self.delegates) {
+ for (name in self.delegates) {
+ self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
+ }
+
+ delete self.delegates;
+ }
+
+ if (!self.inline) {
+ self.getBody().onload = null;
+ self.dom.unbind(self.getWin());
+ self.dom.unbind(self.getDoc());
+ }
+
+ self.dom.unbind(self.getBody());
+ self.dom.unbind(self.getContainer());
}
};
EditorObservable = Tools.extend({}, Observable, EditorObservable);
@@ -26552,10 +26659,13 @@
self.fire("load");
};
DOM.setAttrib("src", url || 'javascript:""');
+ self.contentAreaContainer = o.iframeContainer;
+ self.iframeElement = ifr;
+
n = DOM.add(o.iframeContainer, ifr);
// Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
// Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
if (ie) {
@@ -26564,13 +26674,10 @@
} catch (e) {
n.src = url = domainRelaxUrl;
}
}
- self.contentAreaContainer = o.iframeContainer;
- self.iframeElement = ifr;
-
if (o.editorContainer) {
DOM.get(o.editorContainer).style.display = self.orgDisplay;
self.hidden = DOM.isHidden(o.editorContainer);
}
@@ -26877,16 +26984,19 @@
});
// Handle auto focus
if (settings.auto_focus) {
setTimeout(function() {
- var ed = self.editorManager.get(settings.auto_focus);
+ var editor;
- ed.selection.select(ed.getBody(), 1);
- ed.selection.collapse(1);
- ed.getBody().focus();
- ed.getWin().focus();
+ if (settings.auto_focus === true) {
+ editor = self;
+ } else {
+ editor = self.editorManager.get(settings.auto_focus);
+ }
+
+ editor.focus();
}, 100);
}
// Clean up references for IE
targetElm = doc = body = null;
@@ -27925,10 +28035,11 @@
var self = this;
if (!self.removed) {
self.save();
self.removed = 1;
+ self.unbindAllNativeEvents();
// Remove any hidden input
if (self.hasHiddenInput) {
DOM.remove(self.getElement().nextSibling);
}
@@ -27940,25 +28051,16 @@
self.getDoc().execCommand('SelectAll', false, null);
}
DOM.setStyle(self.id, 'display', self.orgDisplay);
self.getBody().onload = null; // Prevent #6816
-
- // Don't clear the window or document if content editable
- // is enabled since other instances might still be present
- Event.unbind(self.getWin());
- Event.unbind(self.getDoc());
}
- var elm = self.getContainer();
- Event.unbind(self.getBody());
- Event.unbind(elm);
-
self.fire('remove');
self.editorManager.remove(self);
- DOM.remove(elm);
+ DOM.remove(self.getContainer());
self.destroy();
}
},
/**
@@ -27982,18 +28084,10 @@
if (!automatic && !self.removed) {
self.remove();
return;
}
- // We must unbind on Gecko since it would otherwise produce the pesky "attempt
- // to run compile-and-go script on a cleared scope" message
- if (automatic && isGecko) {
- Event.unbind(self.getDoc());
- Event.unbind(self.getWin());
- Event.unbind(self.getBody());
- }
-
if (!automatic) {
self.editorManager.off('beforeunload', self._beforeUnload);
// Manual destroy
if (self.theme && self.theme.destroy) {
@@ -28472,10 +28566,11 @@
function purgeDestroyedEditor(editor) {
// User has manually destroyed the editor lets clean up the mess
if (editor && !(editor.getContainer() || editor.getBody()).parentNode) {
removeEditorFromList(editor);
+ editor.unbindAllNativeEvents();
editor.destroy(true);
editor = null;
}
return editor;
@@ -28502,19 +28597,19 @@
* Minor version of TinyMCE build.
*
* @property minorVersion
* @type String
*/
- minorVersion: '1.2',
+ minorVersion: '1.3',
/**
* Release date of TinyMCE build.
*
* @property releaseDate
* @type String
*/
- releaseDate: '2014-07-15',
+ releaseDate: '2014-07-29',
/**
* Collection of editor instances.
*
* @property editors
@@ -29129,22 +29224,31 @@
*/
/**
* This class enables you to send XMLHTTPRequests cross browser.
* @class tinymce.util.XHR
+ * @mixes tinymce.util.Observable
* @static
* @example
* // Sends a low level Ajax request
* tinymce.util.XHR.send({
* url: 'someurl',
* success: function(text) {
* console.debug(text);
* }
* });
+ *
+ * // Add custom header to XHR request
+ * tinymce.util.XHR.on('beforeSend', function(e) {
+ * e.xhr.setRequestHeader('X-Requested-With', 'Something');
+ * });
*/
-define("tinymce/util/XHR", [], function() {
- return {
+define("tinymce/util/XHR", [
+ "tinymce/util/Observable",
+ "tinymce/util/Tools"
+], function(Observable, Tools) {
+ var XHR = {
/**
* Sends a XMLHTTPRequest.
* Consult the Wiki for details on what settings this method takes.
*
* @method send
@@ -29184,16 +29288,18 @@
xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async);
if (settings.crossDomain) {
xhr.withCredentials = true;
}
+
if (settings.content_type) {
xhr.setRequestHeader('Content-Type', settings.content_type);
}
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr;
xhr.send(settings.data);
// Syncronous request
if (!settings.async) {
return ready();
@@ -29202,10 +29308,14 @@
// Wait for response, onReadyStateChange can not be used since it leaks memory in IE
setTimeout(ready, 10);
}
}
};
+
+ Tools.extend(XHR, Observable);
+
+ return XHR;
});
// Included from: js/tinymce/classes/util/JSON.js
/**
@@ -31068,10 +31178,19 @@
}
}
});
return self._super();
+ },
+
+ remove: function() {
+ if (this.panel) {
+ this.panel.remove();
+ this.panel = null;
+ }
+
+ return this._super();
}
});
});
// Included from: js/tinymce/classes/ui/ColorButton.js
@@ -32854,10 +32973,9 @@
cut: ['Cut', 'Cut'],
copy: ['Copy', 'Copy'],
paste: ['Paste', 'Paste'],
help: ['Help', 'mceHelp'],
selectall: ['Select all', 'SelectAll'],
- hr: ['Insert horizontal rule', 'InsertHorizontalRule'],
removeformat: ['Clear formatting', 'RemoveFormat'],
visualaid: ['Visual aids', 'mceToggleVisualAid'],
newdocument: ['New document', 'mceNewDocument']
}, function(item, name) {
editor.addButton(name, {
\ No newline at end of file