vendor/assets/javascripts/wysihtml5x.js in wysihtml5x-rails-0.4.16 vs vendor/assets/javascripts/wysihtml5x.js in wysihtml5x-rails-0.4.17
- old
+ new
@@ -1,10 +1,86 @@
// TODO: in future try to replace most inline compability checks with polyfills for code readability
-// element.textContent polyfill.
-// Unsupporting browsers: IE8
+// IE8 SUPPORT BLOCK
+// You can compile wuthout all this if IE8 is not needed
+// addEventListener, removeEventListener
+// TODO: make usage of wysihtml5.dom.observe obsolete
+(function() {
+ if (!Event.prototype.preventDefault) {
+ Event.prototype.preventDefault=function() {
+ this.returnValue=false;
+ };
+ }
+ if (!Event.prototype.stopPropagation) {
+ Event.prototype.stopPropagation=function() {
+ this.cancelBubble=true;
+ };
+ }
+ if (!Element.prototype.addEventListener) {
+ var eventListeners=[];
+
+ var addEventListener=function(type,listener /*, useCapture (will be ignored) */) {
+ var self=this;
+ var wrapper=function(e) {
+ e.target=e.srcElement;
+ e.currentTarget=self;
+ if (listener.handleEvent) {
+ listener.handleEvent(e);
+ } else {
+ listener.call(self,e);
+ }
+ };
+ if (type=="DOMContentLoaded") {
+ var wrapper2=function(e) {
+ if (document.readyState=="complete") {
+ wrapper(e);
+ }
+ };
+ document.attachEvent("onreadystatechange",wrapper2);
+ eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2});
+
+ if (document.readyState=="complete") {
+ var e=new Event();
+ e.srcElement=window;
+ wrapper2(e);
+ }
+ } else {
+ this.attachEvent("on"+type,wrapper);
+ eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper});
+ }
+ };
+ var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) {
+ var counter=0;
+ while (counter<eventListeners.length) {
+ var eventListener=eventListeners[counter];
+ if (eventListener.object==this && eventListener.type==type && eventListener.listener==listener) {
+ if (type=="DOMContentLoaded") {
+ this.detachEvent("onreadystatechange",eventListener.wrapper);
+ } else {
+ this.detachEvent("on"+type,eventListener.wrapper);
+ }
+ eventListeners.splice(counter, 1);
+ break;
+ }
+ ++counter;
+ }
+ };
+ Element.prototype.addEventListener=addEventListener;
+ Element.prototype.removeEventListener=removeEventListener;
+ if (HTMLDocument) {
+ HTMLDocument.prototype.addEventListener=addEventListener;
+ HTMLDocument.prototype.removeEventListener=removeEventListener;
+ }
+ if (Window) {
+ Window.prototype.addEventListener=addEventListener;
+ Window.prototype.removeEventListener=removeEventListener;
+ }
+ }
+})();
+
+// element.textContent polyfill.
if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(Element.prototype, "textContent") && !Object.getOwnPropertyDescriptor(Element.prototype, "textContent").get) {
(function() {
var innerText = Object.getOwnPropertyDescriptor(Element.prototype, "innerText");
Object.defineProperty(Element.prototype, "textContent",
{
@@ -22,23 +98,50 @@
// isArray polyfill for ie8
if(!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
+}
+
+// Function.prototype.bind()
+// TODO: clean the code from variable 'that' as it can be confusing
+if (!Function.prototype.bind) {
+ Function.prototype.bind = function(oThis) {
+ if (typeof this !== 'function') {
+ // closest thing possible to the ECMAScript 5
+ // internal IsCallable function
+ throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
+ }
+
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function() {},
+ fBound = function() {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
+
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
+
+ return fBound;
+ };
};/**
- * @license wysihtml5x v0.4.16
+ * @license wysihtml5x v0.4.17
* https://github.com/Edicy/wysihtml5
*
* Author: Christopher Blum (https://github.com/tiff)
* Secondary author of extended features: Oliver Pulges (https://github.com/pulges)
*
* Copyright (C) 2012 XING AG
* Licensed under the MIT license (MIT)
*
*/
var wysihtml5 = {
- version: "0.4.16",
+ version: "0.4.17",
// namespaces
commands: {},
dom: {},
quirks: {},
@@ -46,46 +149,44 @@
lang: {},
selection: {},
views: {},
INVISIBLE_SPACE: "\uFEFF",
+ INVISIBLE_SPACE_REG_EXP: /\uFEFF/g,
EMPTY_FUNCTION: function() {},
ELEMENT_NODE: 1,
TEXT_NODE: 3,
BACKSPACE_KEY: 8,
ENTER_KEY: 13,
ESCAPE_KEY: 27,
SPACE_KEY: 32,
+ TAB_KEY: 9,
DELETE_KEY: 46
};
;/**
* Rangy, a cross-browser JavaScript range and selection library
- * http://code.google.com/p/rangy/
+ * https://github.com/timdown/rangy
*
* Copyright 2014, Tim Down
* Licensed under the MIT license.
- * Version: 1.3alpha.20140804
- * Build date: 4 August 2014
+ * Version: 1.3.0-alpha.20140921
+ * Build date: 21 September 2014
*/
-(function(factory, global) {
+(function(factory, root) {
if (typeof define == "function" && define.amd) {
// AMD. Register as an anonymous module.
define(factory);
-/*
- TODO: look into this properly.
-
- } else if (typeof exports == "object") {
- // Node/CommonJS style for Browserify
- module.exports = factory;
-*/
+ } else if (typeof module != "undefined" && typeof exports == "object") {
+ // Node/CommonJS style
+ module.exports = factory();
} else {
- // No AMD or CommonJS support so we place Rangy in a global variable
- global.rangy = factory();
+ // No AMD or CommonJS support so we place Rangy in (probably) the global variable
+ root.rangy = factory();
}
})(function() {
var OBJECT = "object", FUNCTION = "function", UNDEFINED = "undefined";
@@ -148,55 +249,57 @@
return isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
}
var modules = {};
+ var isBrowser = (typeof window != UNDEFINED && typeof document != UNDEFINED);
+
+ var util = {
+ isHostMethod: isHostMethod,
+ isHostObject: isHostObject,
+ isHostProperty: isHostProperty,
+ areHostMethods: areHostMethods,
+ areHostObjects: areHostObjects,
+ areHostProperties: areHostProperties,
+ isTextRange: isTextRange,
+ getBody: getBody
+ };
+
var api = {
- version: "1.3alpha.20140804",
+ version: "1.3.0-alpha.20140921",
initialized: false,
+ isBrowser: isBrowser,
supported: true,
-
- util: {
- isHostMethod: isHostMethod,
- isHostObject: isHostObject,
- isHostProperty: isHostProperty,
- areHostMethods: areHostMethods,
- areHostObjects: areHostObjects,
- areHostProperties: areHostProperties,
- isTextRange: isTextRange,
- getBody: getBody
- },
-
+ util: util,
features: {},
-
modules: modules,
config: {
alertOnFail: true,
alertOnWarn: false,
preferTextRange: false,
autoInitialize: (typeof rangyAutoInitialize == UNDEFINED) ? true : rangyAutoInitialize
}
};
function consoleLog(msg) {
- if (isHostObject(window, "console") && isHostMethod(window.console, "log")) {
- window.console.log(msg);
+ if (typeof console != UNDEFINED && isHostMethod(console, "log")) {
+ console.log(msg);
}
}
function alertOrLog(msg, shouldAlert) {
- if (shouldAlert) {
- window.alert(msg);
+ if (isBrowser && shouldAlert) {
+ alert(msg);
} else {
consoleLog(msg);
}
}
function fail(reason) {
api.initialized = true;
api.supported = false;
- alertOrLog("Rangy is not supported on this page in your browser. Reason: " + reason, api.config.alertOnFail);
+ alertOrLog("Rangy is not supported in this environment. Reason: " + reason, api.config.alertOnFail);
}
api.fail = fail;
function warn(msg) {
@@ -204,87 +307,106 @@
}
api.warn = warn;
// Add utility extend() method
+ var extend;
if ({}.hasOwnProperty) {
- api.util.extend = function(obj, props, deep) {
+ util.extend = extend = function(obj, props, deep) {
var o, p;
for (var i in props) {
if (props.hasOwnProperty(i)) {
o = obj[i];
p = props[i];
if (deep && o !== null && typeof o == "object" && p !== null && typeof p == "object") {
- api.util.extend(o, p, true);
+ extend(o, p, true);
}
obj[i] = p;
}
}
// Special case for toString, which does not show up in for...in loops in IE <= 8
if (props.hasOwnProperty("toString")) {
obj.toString = props.toString;
}
return obj;
};
+
+ util.createOptions = function(optionsParam, defaults) {
+ var options = {};
+ extend(options, defaults);
+ if (optionsParam) {
+ extend(options, optionsParam);
+ }
+ return options;
+ };
} else {
fail("hasOwnProperty not supported");
}
+
+ // Test whether we're in a browser and bail out if not
+ if (!isBrowser) {
+ fail("Rangy can only run in a browser");
+ }
// Test whether Array.prototype.slice can be relied on for NodeLists and use an alternative toArray() if not
(function() {
- var el = document.createElement("div");
- el.appendChild(document.createElement("span"));
- var slice = [].slice;
var toArray;
- try {
- if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
- toArray = function(arrayLike) {
- return slice.call(arrayLike, 0);
- };
- }
- } catch (e) {}
+ if (isBrowser) {
+ var el = document.createElement("div");
+ el.appendChild(document.createElement("span"));
+ var slice = [].slice;
+ try {
+ if (slice.call(el.childNodes, 0)[0].nodeType == 1) {
+ toArray = function(arrayLike) {
+ return slice.call(arrayLike, 0);
+ };
+ }
+ } catch (e) {}
+ }
+
if (!toArray) {
toArray = function(arrayLike) {
var arr = [];
for (var i = 0, len = arrayLike.length; i < len; ++i) {
arr[i] = arrayLike[i];
}
return arr;
};
}
- api.util.toArray = toArray;
+ util.toArray = toArray;
})();
-
// Very simple event handler wrapper function that doesn't attempt to solve issues such as "this" handling or
// normalization of event properties
var addListener;
- if (isHostMethod(document, "addEventListener")) {
- addListener = function(obj, eventType, listener) {
- obj.addEventListener(eventType, listener, false);
- };
- } else if (isHostMethod(document, "attachEvent")) {
- addListener = function(obj, eventType, listener) {
- obj.attachEvent("on" + eventType, listener);
- };
- } else {
- fail("Document does not have required addEventListener or attachEvent method");
+ if (isBrowser) {
+ if (isHostMethod(document, "addEventListener")) {
+ addListener = function(obj, eventType, listener) {
+ obj.addEventListener(eventType, listener, false);
+ };
+ } else if (isHostMethod(document, "attachEvent")) {
+ addListener = function(obj, eventType, listener) {
+ obj.attachEvent("on" + eventType, listener);
+ };
+ } else {
+ fail("Document does not have required addEventListener or attachEvent method");
+ }
+
+ util.addListener = addListener;
}
- api.util.addListener = addListener;
-
var initListeners = [];
function getErrorDesc(ex) {
return ex.message || ex.description || String(ex);
}
// Initialization
function init() {
- if (api.initialized) {
+ if (!isBrowser || api.initialized) {
return;
}
var testRange;
var implementsDomRange = false, implementsTextRange = false;
@@ -366,11 +488,13 @@
for (var i = 0, len = shimListeners.length; i < len; ++i) {
shimListeners[i](win);
}
}
- api.shim = api.createMissingNativeApi = shim;
+ if (isBrowser) {
+ api.shim = api.createMissingNativeApi = shim;
+ }
function Module(name, dependencies, initializer) {
this.name = name;
this.dependencies = dependencies;
this.initialized = false;
@@ -418,24 +542,28 @@
createError: function(msg) {
return new Error("Error in Rangy " + this.name + " module: " + msg);
}
};
- function createModule(isCore, name, dependencies, initFunc) {
+ function createModule(name, dependencies, initFunc) {
var newModule = new Module(name, dependencies, function(module) {
if (!module.initialized) {
module.initialized = true;
try {
initFunc(api, module);
module.supported = true;
} catch (ex) {
var errorMessage = "Module '" + name + "' failed to load: " + getErrorDesc(ex);
consoleLog(errorMessage);
+ if (ex.stack) {
+ consoleLog(ex.stack);
+ }
}
}
});
modules[name] = newModule;
+ return newModule;
}
api.createModule = function(name) {
// Allow 2 or 3 arguments (second argument is an optional array of dependencies)
var initFunc, dependencies;
@@ -445,20 +573,20 @@
} else {
initFunc = arguments[2];
dependencies = arguments[1];
}
- var module = createModule(false, name, dependencies, initFunc);
+ var module = createModule(name, dependencies, initFunc);
// Initialize the module immediately if the core is already initialized
- if (api.initialized) {
+ if (api.initialized && api.supported) {
module.init();
}
};
api.createCoreModule = function(name, dependencies, initFunc) {
- createModule(true, name, dependencies, initFunc);
+ createModule(name, dependencies, initFunc);
};
/*----------------------------------------------------------------------------------------------------------------*/
// Ensure rangy.rangePrototype and rangy.selectionPrototype are available immediately
@@ -470,42 +598,10 @@
function SelectionPrototype() {}
api.selectionPrototype = new SelectionPrototype();
/*----------------------------------------------------------------------------------------------------------------*/
- // Wait for document to load before running tests
-
- var docReady = false;
-
- var loadHandler = function(e) {
- if (!docReady) {
- docReady = true;
- if (!api.initialized && api.config.autoInitialize) {
- init();
- }
- }
- };
-
- // Test whether we have window and document objects that we will need
- if (typeof window == UNDEFINED) {
- fail("No window found");
- return;
- }
- if (typeof document == UNDEFINED) {
- fail("No document found");
- return;
- }
-
- if (isHostMethod(document, "addEventListener")) {
- document.addEventListener("DOMContentLoaded", loadHandler, false);
- }
-
- // Add a fallback in case the DOMContentLoaded event isn't supported
- addListener(window, "load", loadHandler);
-
- /*----------------------------------------------------------------------------------------------------------------*/
-
// DOM utility methods used by Rangy
api.createCoreModule("DomUtil", [], function(api, module) {
var UNDEF = "undefined";
var util = api.util;
@@ -2383,11 +2479,11 @@
};
}
/*--------------------------------------------------------------------------------------------------------*/
- // Test for IE 9 deleteContents() and extractContents() bug and correct it. See issue 107.
+ // Test for IE deleteContents() and extractContents() bug and correct it. See issue 107.
var el = document.createElement("div");
el.innerHTML = "123";
var textNode = el.firstChild;
var body = getBody(document);
@@ -2741,11 +2837,11 @@
// IE 9 and above have both implementations and Rangy makes both available. The next few lines sets which
// implementation to use by default.
if (!api.features.implementsDomRange || api.config.preferTextRange) {
// Add WrappedTextRange as the Range property of the global object to allow expression like Range.END_TO_END to work
- var globalObj = (function() { return this; })();
+ var globalObj = (function(f) { return f("return this;")(); })(Function);
if (typeof globalObj.Range == "undefined") {
globalObj.Range = WrappedTextRange;
}
api.createNativeRange = function(doc) {
@@ -3277,11 +3373,15 @@
previousRangeCount = 0;
}
// Clone the native range so that changing the selected range does not affect the selection.
// This is contrary to the spec but is the only way to achieve consistency between browsers. See
// issue 80.
- this.nativeSelection.addRange(getNativeRange(range).cloneRange());
+ var clonedNativeRange = getNativeRange(range).cloneRange();
+ try {
+ this.nativeSelection.addRange(clonedNativeRange);
+ } catch (ex) {
+ }
// Check whether adding the range was successful
this.rangeCount = this.nativeSelection.rangeCount;
if (this.rangeCount == previousRangeCount + 1) {
@@ -3790,37 +3890,61 @@
});
/*----------------------------------------------------------------------------------------------------------------*/
+ // Wait for document to load before initializing
+ var docReady = false;
+
+ var loadHandler = function(e) {
+ if (!docReady) {
+ docReady = true;
+ if (!api.initialized && api.config.autoInitialize) {
+ init();
+ }
+ }
+ };
+
+ if (isBrowser) {
+ // Test whether the document has already been loaded and initialize immediately if so
+ if (document.readyState == "complete") {
+ loadHandler();
+ } else {
+ if (isHostMethod(document, "addEventListener")) {
+ document.addEventListener("DOMContentLoaded", loadHandler, false);
+ }
+
+ // Add a fallback in case the DOMContentLoaded event isn't supported
+ addListener(window, "load", loadHandler);
+ }
+ }
+
return api;
}, this);;/**
* Selection save and restore module for Rangy.
* Saves and restores user selections using marker invisible elements in the DOM.
*
* Part of Rangy, a cross-browser JavaScript range and selection library
- * http://code.google.com/p/rangy/
+ * https://github.com/timdown/rangy
*
* Depends on Rangy core.
*
* Copyright 2014, Tim Down
* Licensed under the MIT license.
- * Version: 1.3alpha.20140804
- * Build date: 4 August 2014
+ * Version: 1.3.0-alpha.20140921
+ * Build date: 21 September 2014
*/
-(function(factory, global) {
+(function(factory, root) {
if (typeof define == "function" && define.amd) {
// AMD. Register as an anonymous module with a dependency on Rangy.
- define(["rangy"], factory);
- /*
- } else if (typeof exports == "object") {
- // Node/CommonJS style for Browserify
- module.exports = factory;
- */
+ define(["./rangy-core"], factory);
+ } else if (typeof module != "undefined" && typeof exports == "object") {
+ // Node/CommonJS style
+ module.exports = factory( require("rangy") );
} else {
- // No AMD or CommonJS support so we use the rangy global variable
- factory(global.rangy);
+ // No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
+ factory(root.rangy);
}
})(function(rangy) {
rangy.createModule("SaveRestore", ["WrappedRange"], function(api, module) {
var dom = api.dom;
@@ -4301,17 +4425,10 @@
hasCurrentStyleProperty: function() {
return "currentStyle" in testElement;
},
/**
- * Firefox on OSX navigates through history when hitting CMD + Arrow right/left
- */
- hasHistoryIssue: function() {
- return isGecko && navigator.platform.substr(0, 3) === "Mac";
- },
-
- /**
* Whether the browser inserts a <br> when pressing enter in a contentEditable element
*/
insertsLineBreaksOnReturn: function() {
return isGecko;
},
@@ -5909,11 +6026,14 @@
},
// Rename unknown tags to this
DEFAULT_NODE_NAME = "span",
WHITE_SPACE_REG_EXP = /\s+/,
defaultRules = { tags: {}, classes: {} },
- currentRules = {};
+ currentRules = {},
+ blockElements = ["ADDRESS" ,"BLOCKQUOTE" ,"CENTER" ,"DIR" ,"DIV" ,"DL" ,"FIELDSET" ,
+ "FORM", "H1" ,"H2" ,"H3" ,"H4" ,"H5" ,"H6" ,"ISINDEX" ,"MENU",
+ "NOFRAMES", "NOSCRIPT" ,"OL" ,"P" ,"PRE","TABLE", "UL"];
/**
* Iterates over all childs of the element, recreates them, appends them into a document fragment
* which later replaces the entire body content
*/
@@ -5976,11 +6096,12 @@
oldChildsLength = oldChilds.length,
method = NODE_TYPE_MAPPING[oldNodeType],
i = 0,
fragment,
newNode,
- newChild;
+ newChild,
+ nodeDisplay;
// Passes directly elemets with uneditable class
if (uneditableClass && oldNodeType === 1 && wysihtml5.dom.hasClass(oldNode, uneditableClass)) {
return oldNode;
}
@@ -6003,11 +6124,17 @@
fragment.insertBefore(newChild, fragment.firstChild);
}
}
}
- if (wysihtml5.dom.getStyle("display").from(oldNode) === "block") {
+ nodeDisplay = wysihtml5.dom.getStyle("display").from(oldNode);
+
+ if (nodeDisplay === '') {
+ // Handle display style when element not in dom
+ nodeDisplay = wysihtml5.lang.array(blockElements).contains(oldNode.tagName) ? "block" : "";
+ }
+ if (wysihtml5.lang.array(["block", "flex", "table"]).contains(nodeDisplay)) {
fragment.appendChild(oldNode.ownerDocument.createElement("br"));
}
// TODO: try to minimize surplus spaces
if (wysihtml5.lang.array([
@@ -6479,19 +6606,18 @@
newNode.setAttribute("height", attributes.height);
}
}
}
- var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
function _handleText(oldNode) {
var nextSibling = oldNode.nextSibling;
if (nextSibling && nextSibling.nodeType === wysihtml5.TEXT_NODE) {
// Concatenate text nodes
- nextSibling.data = oldNode.data.replace(INVISIBLE_SPACE_REG_EXP, "") + nextSibling.data.replace(INVISIBLE_SPACE_REG_EXP, "");
+ nextSibling.data = oldNode.data.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "") + nextSibling.data.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
} else {
// \uFEFF = wysihtml5.INVISIBLE_SPACE (used as a hack in certain rich text editing situations)
- var data = oldNode.data.replace(INVISIBLE_SPACE_REG_EXP, "");
+ var data = oldNode.data.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
return oldNode.ownerDocument.createTextNode(data);
}
}
function _handleComment(oldNode) {
@@ -8830,23 +8956,100 @@
range.setStartBefore(node);
range.setEndBefore(node);
return this.setSelection(range);
},
+ // Constructs a self removing whitespace (ain absolute positioned span) for placing selection caret when normal methods fail.
+ // Webkit has an issue with placing caret into places where there are no textnodes near by.
+ creteTemporaryCaretSpaceAfter: function (node) {
+ var caretPlaceholder = this.doc.createElement('span'),
+ caretPlaceholderText = this.doc.createTextNode(wysihtml5.INVISIBLE_SPACE),
+ placeholderRemover = (function(event) {
+ // Self-destructs the caret and keeps the text inserted into it by user
+ var lastChild;
+
+ this.contain.removeEventListener('mouseup', placeholderRemover);
+ this.contain.removeEventListener('keydown', keyDownHandler);
+ this.contain.removeEventListener('touchstart', placeholderRemover);
+ this.contain.removeEventListener('focus', placeholderRemover);
+ this.contain.removeEventListener('blur', placeholderRemover);
+ this.contain.removeEventListener('paste', delayedPlaceholderRemover);
+ this.contain.removeEventListener('drop', delayedPlaceholderRemover);
+ this.contain.removeEventListener('beforepaste', delayedPlaceholderRemover);
+
+ // If user inserted sth it is in the placeholder and sgould be unwrapped and stripped of invisible whitespace hack
+ // Otherwise the wrapper can just be removed
+ if (caretPlaceholder && caretPlaceholder.parentNode) {
+ caretPlaceholder.innerHTML = caretPlaceholder.innerHTML.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
+ if ((/[^\s]+/).test(caretPlaceholder.innerHTML)) {
+ lastChild = caretPlaceholder.lastChild;
+ wysihtml5.dom.unwrap(caretPlaceholder);
+ this.setAfter(lastChild);
+ } else {
+ caretPlaceholder.parentNode.removeChild(caretPlaceholder);
+ }
+
+ }
+ }).bind(this),
+ delayedPlaceholderRemover = function (event) {
+ if (caretPlaceholder && caretPlaceholder.parentNode) {
+ setTimeout(placeholderRemover, 0);
+ }
+ },
+ keyDownHandler = function(event) {
+ if (event.which !== 8 && event.which !== 91 && event.which !== 17 && (event.which !== 86 || (!event.ctrlKey && !event.metaKey))) {
+ placeholderRemover();
+ }
+ };
+
+ caretPlaceholder.style.position = 'absolute';
+ caretPlaceholder.style.display = 'block';
+ caretPlaceholder.style.minWidth = '1px';
+ caretPlaceholder.style.zIndex = '99999';
+ caretPlaceholder.appendChild(caretPlaceholderText);
+
+ node.parentNode.insertBefore(caretPlaceholder, node.nextSibling);
+ this.setBefore(caretPlaceholderText);
+
+ // Remove the caret fix on any of the following events (some are delayed as content change happens after event)
+ this.contain.addEventListener('mouseup', placeholderRemover);
+ this.contain.addEventListener('keydown', keyDownHandler);
+ this.contain.addEventListener('touchstart', placeholderRemover);
+ this.contain.addEventListener('focus', placeholderRemover);
+ this.contain.addEventListener('blur', placeholderRemover);
+ this.contain.addEventListener('paste', delayedPlaceholderRemover);
+ this.contain.addEventListener('drop', delayedPlaceholderRemover);
+ this.contain.addEventListener('beforepaste', delayedPlaceholderRemover);
+
+ return caretPlaceholder;
+ },
+
/**
* Set the caret after the given node
*
* @param {Object} node The element or text node where to position the caret in front of
* @example
* selection.setBefore(myElement);
*/
setAfter: function(node) {
- var range = rangy.createRange(this.doc);
+ var range = rangy.createRange(this.doc),
+ originalScrollTop = this.doc.documentElement.scrollTop || this.doc.body.scrollTop || this.doc.defaultView.pageYOffset,
+ originalScrollLeft = this.doc.documentElement.scrollLeft || this.doc.body.scrollLeft || this.doc.defaultView.pageXOffset,
+ sel;
range.setStartAfter(node);
range.setEndAfter(node);
- return this.setSelection(range);
+ this.composer.element.focus();
+ this.doc.defaultView.scrollTo(originalScrollLeft, originalScrollTop);
+ sel = this.setSelection(range);
+
+ // Webkit fails to add selection if there are no textnodes in that region
+ // (like an uneditable container at the end of content).
+ if (!sel) {
+ this.creteTemporaryCaretSpaceAfter(node);
+ }
+ return sel;
},
/**
* Ability to select/mark nodes
*
@@ -8952,22 +9155,35 @@
}
return false;
},
- // deletes selection contents making sure uneditables/unselectables are not partially deleted
+ // Deletes selection contents making sure uneditables/unselectables are not partially deleted
+ // Triggers wysihtml5:uneditable:delete custom event on all deleted uneditables if customevents suppoorted
deleteContents: function() {
var range = this.getRange(),
- startParent, endParent;
+ startParent, endParent, uneditables, ev;
if (this.unselectableClass) {
if ((startParent = wysihtml5.dom.getParentElement(range.startContainer, { className: this.unselectableClass }, false, this.contain))) {
range.setStartBefore(startParent);
}
if ((endParent = wysihtml5.dom.getParentElement(range.endContainer, { className: this.unselectableClass }, false, this.contain))) {
range.setEndAfter(endParent);
}
+
+ // If customevents present notify uneditable elements of being deleted
+ uneditables = range.getNodes([1], (function (node) {
+ return wysihtml5.dom.hasClass(node, this.unselectableClass);
+ }).bind(this));
+ for (var i = uneditables.length; i--;) {
+ try {
+ ev = new CustomEvent("wysihtml5:uneditable:delete");
+ uneditables[i].dispatchEvent(ev);
+ } catch (err) {}
+ }
+
}
range.deleteContents();
this.setSelection(range);
},
@@ -9383,10 +9599,28 @@
selection = win.getSelection();
selection.modify("move", "left", "lineboundary");
selection.modify("extend", "right", "lineboundary");
},
+ // collapses selection to current line beginning or end
+ toLineBoundary: function (location, collapse) {
+ collapse = (typeof collapse === 'undefined') ? false : collapse;
+ if (wysihtml5.browser.supportsSelectionModify()) {
+ var win = this.doc.defaultView,
+ selection = win.getSelection();
+
+ selection.modify("extend", location, "lineboundary");
+ if (collapse) {
+ if (location === "left") {
+ selection.collapseToStart();
+ } else if (location === "right") {
+ selection.collapseToEnd();
+ }
+ }
+ }
+ },
+
_selectLine_MSIE: function() {
var range = this.doc.selection.createRange(),
rangeTop = range.boundingTop,
scrollWidth = this.doc.body.scrollWidth,
rangeBottom,
@@ -9548,14 +9782,18 @@
getSelection: function() {
return rangy.getSelection(this.doc.defaultView || this.doc.parentWindow);
},
+ // Sets selection in document to a given range
+ // Set selection method detects if it fails to set any selection in document and returns null on fail
+ // (especially needed in webkit where some ranges just can not create selection for no reason)
setSelection: function(range) {
var win = this.doc.defaultView || this.doc.parentWindow,
selection = rangy.getSelection(win);
- return selection.setSingleRange(range);
+ selection.setSingleRange(range);
+ return (selection && selection.anchorNode && selection.focusNode) ? selection : null;
},
createRange: function() {
return rangy.createRange(this.doc);
},
@@ -10302,10 +10540,17 @@
var obj = wysihtml5.commands[command],
args = wysihtml5.lang.array(arguments).get(),
method = obj && obj.exec,
result = null;
+ // If composer ahs placeholder unset it before command
+ // Do not apply on commands that are behavioral
+ if (this.composer.hasPlaceholderSet() && !wysihtml5.lang.array(['styleWithCSS', 'enableObjectResizing', 'enableInlineTableEditing']).contains(command)) {
+ this.composer.element.innerHTML = "";
+ this.composer.selection.selectNode(this.composer.element);
+ }
+
this.editor.fire("beforecommand:composer");
if (method) {
args.unshift(this.composer);
result = method.apply(obj, args);
@@ -11410,11 +11655,11 @@
}),
isEmpty, list;
// This space causes new lists to never break on enter
var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
- tempElement.innerHTML = tempElement.innerHTML.replace(INVISIBLE_SPACE_REG_EXP, "");
+ tempElement.innerHTML = tempElement.innerHTML.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
if (tempElement) {
isEmpty = wysihtml5.lang.array(["", "<br>", wysihtml5.INVISIBLE_SPACE]).contains(tempElement.innerHTML);
list = wysihtml5.dom.convertToList(tempElement, nodeName.toLowerCase(), composer.parent.config.uneditableContainerClassname);
if (isEmpty) {
@@ -12775,10 +13020,26 @@
"66": "bold", // B
"73": "italic", // I
"85": "underline" // U
};
+ // Adds multiple eventlisteners to target, bound to one callback
+ // TODO: If needed elsewhere make it part of wysihtml5.dom or sth
+ var addListeners = function (target, events, callback) {
+ for(var i = 0, max = events.length; i < max; i++) {
+ target.addEventListener(events[i], callback, false);
+ }
+ };
+
+ // Removes multiple eventlisteners from target, bound to one callback
+ // TODO: If needed elsewhere make it part of wysihtml5.dom or sth
+ var removeListeners = function (target, events, callback) {
+ for(var i = 0, max = events.length; i < max; i++) {
+ target.removeEventListener(events[i], callback, false);
+ }
+ };
+
var deleteAroundEditable = function(selection, uneditable, element) {
// merge node with previous node from uneditable
var prevNode = selection.getPreviousNode(uneditable, true),
curNode = selection.getSelectedNode();
@@ -12809,11 +13070,14 @@
selection.setBefore(curNode);
}
}
};
- var handleDeleteKeyPress = function(event, selection, element, composer) {
+ var handleDeleteKeyPress = function(event, composer) {
+ var selection = composer.selection,
+ element = composer.element;
+
if (selection.isCollapsed()) {
if (selection.caretIsInTheBeginnig('LI')) {
event.preventDefault();
composer.commands.exec('outdentList');
} else if (selection.caretIsInTheBeginnig()) {
@@ -12868,251 +13132,261 @@
// Is   close enough to tab. Could not find enough counter arguments for now.
composer.commands.exec("insertHTML", " ");
};
- wysihtml5.views.Composer.prototype.observe = function() {
- var that = this,
- state = this.getValue(false, false),
- container = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
- element = this.element,
- focusBlurElement = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? element : this.sandbox.getWindow(),
- pasteEvents = ["drop", "paste", "beforepaste"],
- interactionEvents = ["drop", "paste", "mouseup", "focus", "keyup"];
+ var handleDomNodeRemoved = function(event) {
+ if (this.domNodeRemovedInterval) {
+ clearInterval(domNodeRemovedInterval);
+ }
+ this.parent.fire("destroy:composer");
+ };
- // --------- destroy:composer event ---------
- dom.observe(container, "DOMNodeRemoved", function() {
- clearInterval(domNodeRemovedInterval);
- that.parent.fire("destroy:composer");
- });
+ // Listens to "drop", "paste", "mouseup", "focus", "keyup" events and fires
+ var handleUserInteraction = function (event) {
+ this.parent.fire("beforeinteraction").fire("beforeinteraction:composer");
+ setTimeout((function() {
+ this.parent.fire("interaction").fire("interaction:composer");
+ }).bind(this), 0);
+ };
- // DOMNodeRemoved event is not supported in IE 8
- if (!browser.supportsMutationEvents()) {
- var domNodeRemovedInterval = setInterval(function() {
- if (!dom.contains(document.documentElement, container)) {
- clearInterval(domNodeRemovedInterval);
- that.parent.fire("destroy:composer");
- }
- }, 250);
- }
+ var handleFocus = function(event) {
+ this.parent.fire("focus", event).fire("focus:composer", event);
- // --------- User interaction tracking --
+ // Delay storing of state until all focus handler are fired
+ // especially the one which resets the placeholder
+ setTimeout((function() {
+ this.focusState = this.getValue(false, false);
+ }).bind(this), 0);
+ };
- dom.observe(focusBlurElement, interactionEvents, function() {
- setTimeout(function() {
- that.parent.fire("interaction").fire("interaction:composer");
- }, 0);
- });
-
-
- if (this.config.handleTables) {
- if(!this.tableClickHandle && this.doc.execCommand && wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") && wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing")) {
- if (this.sandbox.getIframe) {
- this.tableClickHandle = dom.observe(container , ["focus", "mouseup", "mouseover"], function() {
- that.doc.execCommand("enableObjectResizing", false, "false");
- that.doc.execCommand("enableInlineTableEditing", false, "false");
- that.tableClickHandle.stop();
- });
- } else {
- setTimeout(function() {
- that.doc.execCommand("enableObjectResizing", false, "false");
- that.doc.execCommand("enableInlineTableEditing", false, "false");
- }, 0);
- }
+ var handleBlur = function(event) {
+ if (this.focusState !== this.getValue(false, false)) {
+ //create change event if supported (all except IE8)
+ var changeevent = event;
+ if(typeof Object.create == 'function') {
+ changeevent = Object.create(event, { type: { value: 'change' } });
}
- this.tableSelection = wysihtml5.quirks.tableCellsSelection(element, that.parent);
+ this.parent.fire("change", changeevent).fire("change:composer", changeevent);
}
+ this.parent.fire("blur", event).fire("blur:composer", event);
+ };
- // --------- Focus & blur logic ---------
- dom.observe(focusBlurElement, "focus", function(event) {
- that.parent.fire("focus", event).fire("focus:composer", event);
+ var handlePaste = function(event) {
+ this.parent.fire(event.type, event).fire(event.type + ":composer", event);
+ if (event.type === "paste") {
+ setTimeout((function() {
+ this.parent.fire("newword:composer");
+ }).bind(this), 0);
+ }
+ };
- // Delay storing of state until all focus handler are fired
- // especially the one which resets the placeholder
- setTimeout(function() { state = that.getValue(false, false); }, 0);
- });
-
- dom.observe(focusBlurElement, "blur", function(event) {
- if (state !== that.getValue(false, false)) {
- //create change event if supported (all except IE8)
- var changeevent = event;
- if(typeof Object.create == 'function') {
- changeevent = Object.create(event, { type: { value: 'change' } });
- }
- that.parent.fire("change", changeevent).fire("change:composer", changeevent);
- }
- that.parent.fire("blur", event).fire("blur:composer", event);
- });
-
- // --------- Drag & Drop logic ---------
- dom.observe(element, "dragenter", function() {
- that.parent.fire("unset_placeholder");
- });
-
- dom.observe(element, pasteEvents, function(event) {
- that.parent.fire(event.type, event).fire(event.type + ":composer", event);
- });
-
-
+ var handleCopy = function(event) {
if (this.config.copyedFromMarking) {
- // If supported the copied source is based directly on selection
+ // If supported the copied source can be based directly on selection
// Very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection.
- dom.observe(element, "copy", function(event) {
- if (event.clipboardData) {
- event.clipboardData.setData("text/html", that.config.copyedFromMarking + that.selection.getHtml());
- event.clipboardData.setData("text/plain", that.selection.getPlainText());
- event.preventDefault();
- }
- that.parent.fire(event.type, event).fire(event.type + ":composer", event);
- });
+ if (event.clipboardData) {
+ event.clipboardData.setData("text/html", this.config.copyedFromMarking + this.selection.getHtml());
+ event.clipboardData.setData("text/plain", this.selection.getPlainText());
+ event.preventDefault();
+ }
+ this.parent.fire(event.type, event).fire(event.type + ":composer", event);
}
+ };
- // --------- neword event ---------
- dom.observe(element, "keyup", function(event) {
- var keyCode = event.keyCode;
- if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
- that.parent.fire("newword:composer");
- }
- });
+ var handleKeyUp = function(event) {
+ var keyCode = event.keyCode;
+ if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
+ this.parent.fire("newword:composer");
+ }
+ };
- this.parent.on("paste:composer", function() {
- setTimeout(function() { that.parent.fire("newword:composer"); }, 0);
- });
-
- // --------- Make sure that images are selected when clicking on them ---------
+ var handleMouseDown = function(event) {
if (!browser.canSelectImagesInContentEditable()) {
- dom.observe(element, "mousedown", function(event) {
- var target = event.target;
- var allImages = element.querySelectorAll('img'),
- notMyImages = element.querySelectorAll('.' + that.config.uneditableContainerClassname + ' img'),
- myImages = wysihtml5.lang.array(allImages).without(notMyImages);
+ // Make sure that images are selected when clicking on them
+ var target = event.target,
+ allImages = this.element.querySelectorAll('img'),
+ notMyImages = this.element.querySelectorAll('.' + this.config.uneditableContainerClassname + ' img'),
+ myImages = wysihtml5.lang.array(allImages).without(notMyImages);
- if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
- that.selection.selectNode(target);
- }
- });
+ if (target.nodeName === "IMG" && wysihtml5.lang.array(myImages).contains(target)) {
+ this.selection.selectNode(target);
+ }
}
+ };
- // If uneditables configured makes click on uneditable moves caret after clicked element (so it can be deleted like text)
- // If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
+ // TODO: mouseover is not actually a foolproof and obvious place for this, must be changed as it modifies dom on random basis
+ // Shows url in tooltip when hovering links or images
+ var handleMouseOver = function(event) {
+ var titlePrefixes = {
+ IMG: "Image: ",
+ A: "Link: "
+ },
+ target = event.target,
+ nodeName = target.nodeName,
+ title;
+
+ if (nodeName !== "A" && nodeName !== "IMG") {
+ return;
+ }
+ if(!target.hasAttribute("title")){
+ title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
+ target.setAttribute("title", title);
+ }
+ };
+
+ var handleClick = function(event) {
if (this.config.uneditableContainerClassname) {
- dom.observe(element, "click", function(event) {
- var uneditable = wysihtml5.dom.getParentElement(event.target, { className: that.config.uneditableContainerClassname }, false, that.element);
- if (uneditable) {
- that.selection.setAfter(uneditable);
- }
- });
+ // If uneditables is configured, makes clicking on uneditable move caret after clicked element (so it can be deleted like text)
+ // If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
+ var uneditable = wysihtml5.dom.getParentElement(event.target, { className: this.config.uneditableContainerClassname }, false, this.element);
+ if (uneditable) {
+ this.selection.setAfter(uneditable);
+ }
}
+ };
+ var handleDrop = function(event) {
if (!browser.canSelectImagesInContentEditable()) {
- dom.observe(element, "drop", function(event) {
- // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
- setTimeout(function() {
- that.selection.getSelection().removeAllRanges();
- }, 0);
- });
+ // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
+ setTimeout((function() {
+ this.selection.getSelection().removeAllRanges();
+ }).bind(this), 0);
}
+ };
- if (browser.hasHistoryIssue() && browser.supportsSelectionModify()) {
- dom.observe(element, "keydown", function(event) {
- if (!event.metaKey && !event.ctrlKey) {
- return;
- }
+ var handleKeyDown = function(event) {
+ var keyCode = event.keyCode,
+ command = shortcuts[keyCode],
+ target, parent;
- var keyCode = event.keyCode,
- win = element.ownerDocument.defaultView,
- selection = win.getSelection();
+ // Shortcut logic
+ if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
+ this.commands.exec(command);
+ event.preventDefault();
+ }
- if (keyCode === 37 || keyCode === 39) {
- if (keyCode === 37) {
- selection.modify("extend", "left", "lineboundary");
- if (!event.shiftKey) {
- selection.collapseToStart();
- }
- }
- if (keyCode === 39) {
- selection.modify("extend", "right", "lineboundary");
- if (!event.shiftKey) {
- selection.collapseToEnd();
- }
- }
- event.preventDefault();
- }
- });
+ if (keyCode === wysihtml5.BACKSPACE_KEY) {
+ // Delete key override for special cases
+ handleDeleteKeyPress(event, this);
}
- // --------- Shortcut logic ---------
- dom.observe(element, "keydown", function(event) {
- var keyCode = event.keyCode,
- command = shortcuts[keyCode];
- if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
- that.commands.exec(command);
+ // Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor
+ if (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY) {
+ target = this.selection.getSelectedNode(true);
+ if (target && target.nodeName === "IMG") {
event.preventDefault();
- }
- if (keyCode === 8) {
- // delete key
- handleDeleteKeyPress(event, that.selection, element, that);
- } else if (that.config.handleTabKey && keyCode === 9) {
- event.preventDefault();
- handleTabKeyDown(that, element);
- }
- });
-
- // --------- Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor ---------
- dom.observe(element, "keydown", function(event) {
- var target = that.selection.getSelectedNode(true),
- keyCode = event.keyCode,
- parent;
- if (target && target.nodeName === "IMG" && (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY)) { // 8 => backspace, 46 => delete
parent = target.parentNode;
- // delete the <img>
- parent.removeChild(target);
- // and it's parent <a> too if it hasn't got any other child nodes
+ parent.removeChild(target);// delete the <img>
+ // And it's parent <a> too if it hasn't got any other child nodes
if (parent.nodeName === "A" && !parent.firstChild) {
parent.parentNode.removeChild(parent);
}
-
- setTimeout(function() { wysihtml5.quirks.redraw(element); }, 0);
- event.preventDefault();
- }
- });
-
- // --------- IE 8+9 focus the editor when the iframe is clicked (without actually firing the 'focus' event on the <body>) ---------
- if (!this.config.contentEditableMode && browser.hasIframeFocusIssue()) {
- dom.observe(container, "focus", function() {
setTimeout(function() {
- if (that.doc.querySelector(":focus") !== that.element) {
- that.focus();
- }
+ wysihtml5.quirks.redraw(element);
}, 0);
- });
+ }
+ }
- dom.observe(this.element, "blur", function() {
- setTimeout(function() {
- that.selection.getSelection().removeAllRanges();
- }, 0);
- });
+ if (this.config.handleTabKey && keyCode === wysihtml5.TAB_KEY) {
+ // TAB key handling
+ event.preventDefault();
+ handleTabKeyDown(this, element);
}
- // --------- Show url in tooltip when hovering links or images ---------
- var titlePrefixes = {
- IMG: "Image: ",
- A: "Link: "
- };
+ };
- dom.observe(element, "mouseover", function(event) {
- var target = event.target,
- nodeName = target.nodeName,
- title;
- if (nodeName !== "A" && nodeName !== "IMG") {
- return;
+ var handleIframeFocus = function(event) {
+ setTimeout((function() {
+ if (this.doc.querySelector(":focus") !== this.element) {
+ this.focus();
}
- var hasTitle = target.hasAttribute("title");
- if(!hasTitle){
- title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
- target.setAttribute("title", title);
+ }).bind(this), 0);
+ };
+
+ var handleIframeBlur = function(event) {
+ setTimeout((function() {
+ this.selection.getSelection().removeAllRanges();
+ }).bind(this), 0);
+ };
+
+ // Table management
+ // If present enableObjectResizing and enableInlineTableEditing command should be called with false to prevent native table handlers
+ var initTableHandling = function () {
+ var hideHandlers = function () {
+ this.doc.execCommand("enableObjectResizing", false, "false");
+ this.doc.execCommand("enableInlineTableEditing", false, "false");
+ },
+ iframeInitiator = (function() {
+ hideHandlers.call(this);
+ removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
+ }).bind(this);
+
+ if( this.doc.execCommand &&
+ wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") &&
+ wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing"))
+ {
+ if (this.sandbox.getIframe) {
+ addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
+ } else {
+ setTimeout((function() {
+ hideHandlers.call(this);
+ }).bind(this), 0);
}
- });
+ }
+ this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
+ };
+
+ wysihtml5.views.Composer.prototype.observe = function() {
+ var that = this,
+ container = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
+ element = this.element,
+ focusBlurElement = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? this.element : this.sandbox.getWindow();
+
+ this.focusState = this.getValue(false, false);
+
+ // --------- destroy:composer event ---------
+ container.addEventListener(["DOMNodeRemoved"], handleDomNodeRemoved.bind(this), false);
+
+ // DOMNodeRemoved event is not supported in IE 8
+ // TODO: try to figure out a polyfill style fix, so it could be transferred to polyfills and removed if ie8 is not needed
+ if (!browser.supportsMutationEvents()) {
+ this.domNodeRemovedInterval = setInterval(function() {
+ if (!dom.contains(document.documentElement, container)) {
+ handleDomNodeRemoved.call(this);
+ }
+ }, 250);
+ }
+
+ // --------- User interactions --
+ if (this.config.handleTables) {
+ // If handleTables option is true, table handling functions are bound
+ initTableHandling.call(this);
+ }
+
+ addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
+ focusBlurElement.addEventListener("focus", handleFocus.bind(this), false);
+ focusBlurElement.addEventListener("blur", handleBlur.bind(this), false);
+
+ addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
+ this.element.addEventListener("copy", handleCopy.bind(this), false);
+ this.element.addEventListener("mousedown", handleMouseDown.bind(this), false);
+ this.element.addEventListener("mouseover", handleMouseOver.bind(this), false);
+ this.element.addEventListener("click", handleClick.bind(this), false);
+ this.element.addEventListener("drop", handleDrop.bind(this), false);
+ this.element.addEventListener("keyup", handleKeyUp.bind(this), false);
+ this.element.addEventListener("keydown", handleKeyDown.bind(this), false);
+
+ this.element.addEventListener("dragenter", (function() {
+ this.parent.fire("unset_placeholder");
+ }).bind(this), false);
+
+ // --------- IE 8+9 focus the editor when the iframe is clicked (without actually firing the 'focus' event on the <body>) ---------
+ if (!this.config.contentEditableMode && browser.hasIframeFocusIssue()) {
+ container.addEventListener("focus", handleIframeFocus.bind(this), false);
+ container.addEventListener("blur", handleIframeBlur.bind(this), false);
+ }
+
};
})(wysihtml5);
;/**
* Class that takes care that the value of the composer and the textarea is always in sync
*/