wysihtml5x-rails 0.4.13 → 0.4.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/wysihtml5x/rails/version.rb +1 -1
- data/vendor/assets/javascripts/wysihtml5x-toolbar.js +426 -155
- data/vendor/assets/javascripts/wysihtml5x.js +413 -155
- metadata +2 -2
@@ -25,7 +25,7 @@ if(!Array.isArray) {
|
|
25
25
|
return Object.prototype.toString.call(arg) === '[object Array]';
|
26
26
|
};
|
27
27
|
};/**
|
28
|
-
* @license wysihtml5x v0.4.
|
28
|
+
* @license wysihtml5x v0.4.14
|
29
29
|
* https://github.com/Edicy/wysihtml5
|
30
30
|
*
|
31
31
|
* Author: Christopher Blum (https://github.com/tiff)
|
@@ -36,7 +36,7 @@ if(!Array.isArray) {
|
|
36
36
|
*
|
37
37
|
*/
|
38
38
|
var wysihtml5 = {
|
39
|
-
version: "0.4.
|
39
|
+
version: "0.4.14",
|
40
40
|
|
41
41
|
// namespaces
|
42
42
|
commands: {},
|
@@ -4562,6 +4562,15 @@ wysihtml5.browser = (function() {
|
|
4562
4562
|
|
4563
4563
|
supportsMutationEvents: function() {
|
4564
4564
|
return ("MutationEvent" in window);
|
4565
|
+
},
|
4566
|
+
|
4567
|
+
/**
|
4568
|
+
IE (at least up to 11) does not support clipboardData on event.
|
4569
|
+
It is on window but cannot return text/html
|
4570
|
+
Should actually check for clipboardData on paste event, but cannot in firefox
|
4571
|
+
*/
|
4572
|
+
supportsModenPaste: function () {
|
4573
|
+
return !("clipboardData" in window);
|
4565
4574
|
}
|
4566
4575
|
};
|
4567
4576
|
})();
|
@@ -4763,12 +4772,25 @@ wysihtml5.browser = (function() {
|
|
4763
4772
|
* @example
|
4764
4773
|
* wysihtml5.lang.object({ foo: 1 }).clone();
|
4765
4774
|
* // => { foo: 1 }
|
4775
|
+
*
|
4776
|
+
* v0.4.14 adds options for deep clone : wysihtml5.lang.object({ foo: 1 }).clone(true);
|
4766
4777
|
*/
|
4767
|
-
clone: function() {
|
4778
|
+
clone: function(deep) {
|
4768
4779
|
var newObj = {},
|
4769
4780
|
i;
|
4781
|
+
|
4782
|
+
if (obj === null || !wysihtml5.lang.object(obj).isPlainObject()) {
|
4783
|
+
return obj;
|
4784
|
+
}
|
4785
|
+
|
4770
4786
|
for (i in obj) {
|
4771
|
-
|
4787
|
+
if(obj.hasOwnProperty(i)) {
|
4788
|
+
if (deep) {
|
4789
|
+
newObj[i] = wysihtml5.lang.object(obj[i]).clone(deep);
|
4790
|
+
} else {
|
4791
|
+
newObj[i] = obj[i];
|
4792
|
+
}
|
4793
|
+
}
|
4772
4794
|
}
|
4773
4795
|
return newObj;
|
4774
4796
|
},
|
@@ -4780,18 +4802,32 @@ wysihtml5.browser = (function() {
|
|
4780
4802
|
*/
|
4781
4803
|
isArray: function() {
|
4782
4804
|
return Object.prototype.toString.call(obj) === "[object Array]";
|
4805
|
+
},
|
4806
|
+
|
4807
|
+
/**
|
4808
|
+
* @example
|
4809
|
+
* wysihtml5.lang.object(function() {}).isFunction();
|
4810
|
+
* // => true
|
4811
|
+
*/
|
4812
|
+
isFunction: function() {
|
4813
|
+
return Object.prototype.toString.call(obj) === '[object Function]';
|
4814
|
+
},
|
4815
|
+
|
4816
|
+
isPlainObject: function () {
|
4817
|
+
return Object.prototype.toString.call(obj) === '[object Object]';
|
4783
4818
|
}
|
4784
4819
|
};
|
4785
4820
|
};
|
4786
4821
|
;(function() {
|
4787
4822
|
var WHITE_SPACE_START = /^\s+/,
|
4788
4823
|
WHITE_SPACE_END = /\s+$/,
|
4789
|
-
ENTITY_REG_EXP = /[
|
4824
|
+
ENTITY_REG_EXP = /[&<>\t"]/g,
|
4790
4825
|
ENTITY_MAP = {
|
4791
4826
|
'&': '&',
|
4792
4827
|
'<': '<',
|
4793
4828
|
'>': '>',
|
4794
|
-
'"': """
|
4829
|
+
'"': """,
|
4830
|
+
'\t':" "
|
4795
4831
|
};
|
4796
4832
|
wysihtml5.lang.string = function(str) {
|
4797
4833
|
str = String(str);
|
@@ -4835,8 +4871,15 @@ wysihtml5.browser = (function() {
|
|
4835
4871
|
* wysihtml5.lang.string("hello<br>").escapeHTML();
|
4836
4872
|
* // => "hello<br>"
|
4837
4873
|
*/
|
4838
|
-
escapeHTML: function() {
|
4839
|
-
|
4874
|
+
escapeHTML: function(linebreaks, convertSpaces) {
|
4875
|
+
var html = str.replace(ENTITY_REG_EXP, function(c) { return ENTITY_MAP[c]; });
|
4876
|
+
if (linebreaks) {
|
4877
|
+
html = html.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
4878
|
+
}
|
4879
|
+
if (convertSpaces) {
|
4880
|
+
html = html.replace(/ /gi, " ");
|
4881
|
+
}
|
4882
|
+
return html;
|
4840
4883
|
}
|
4841
4884
|
};
|
4842
4885
|
};
|
@@ -5816,7 +5859,9 @@ wysihtml5.dom.observe = function(element, eventNames, handler) {
|
|
5816
5859
|
* // => '<p class="red">foo</p><p>bar</p>'
|
5817
5860
|
*/
|
5818
5861
|
|
5819
|
-
wysihtml5.dom.parse =
|
5862
|
+
wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
|
5863
|
+
/* TODO: Currently escaped module pattern as otherwise folloowing default swill be shared among multiple editors.
|
5864
|
+
* Refactor whole code as this method while workind is kind of awkward too */
|
5820
5865
|
|
5821
5866
|
/**
|
5822
5867
|
* It's not possible to use a XMLParser/DOMParser as HTML5 is not always well-formed XML
|
@@ -5834,8 +5879,7 @@ wysihtml5.dom.parse = (function() {
|
|
5834
5879
|
DEFAULT_NODE_NAME = "span",
|
5835
5880
|
WHITE_SPACE_REG_EXP = /\s+/,
|
5836
5881
|
defaultRules = { tags: {}, classes: {} },
|
5837
|
-
currentRules = {}
|
5838
|
-
uneditableClass = false;
|
5882
|
+
currentRules = {};
|
5839
5883
|
|
5840
5884
|
/**
|
5841
5885
|
* Iterates over all childs of the element, recreates them, appends them into a document fragment
|
@@ -5856,19 +5900,19 @@ wysihtml5.dom.parse = (function() {
|
|
5856
5900
|
clearInternals = true;
|
5857
5901
|
}
|
5858
5902
|
|
5859
|
-
if (config.uneditableClass) {
|
5860
|
-
uneditableClass = config.uneditableClass;
|
5861
|
-
}
|
5862
|
-
|
5863
5903
|
if (isString) {
|
5864
5904
|
element = wysihtml5.dom.getAsDom(elementOrHtml, context);
|
5865
5905
|
} else {
|
5866
5906
|
element = elementOrHtml;
|
5867
5907
|
}
|
5868
5908
|
|
5909
|
+
if (currentRules.selectors) {
|
5910
|
+
_applySelectorRules(element, currentRules.selectors);
|
5911
|
+
}
|
5912
|
+
|
5869
5913
|
while (element.firstChild) {
|
5870
5914
|
firstChild = element.firstChild;
|
5871
|
-
newNode = _convert(firstChild, config.cleanUp, clearInternals);
|
5915
|
+
newNode = _convert(firstChild, config.cleanUp, clearInternals, config.uneditableClass);
|
5872
5916
|
if (newNode) {
|
5873
5917
|
fragment.appendChild(newNode);
|
5874
5918
|
}
|
@@ -5877,6 +5921,14 @@ wysihtml5.dom.parse = (function() {
|
|
5877
5921
|
}
|
5878
5922
|
}
|
5879
5923
|
|
5924
|
+
if (config.unjoinNbsps) {
|
5925
|
+
// replace joined non-breakable spaces with unjoined
|
5926
|
+
var txtnodes = wysihtml5.dom.getTextNodes(fragment);
|
5927
|
+
for (var n = txtnodes.length; n--;) {
|
5928
|
+
txtnodes[n].nodeValue = txtnodes[n].nodeValue.replace(/([\S\u00A0])\u00A0/gi, "$1 ");
|
5929
|
+
}
|
5930
|
+
}
|
5931
|
+
|
5880
5932
|
// Clear element contents
|
5881
5933
|
element.innerHTML = "";
|
5882
5934
|
|
@@ -5886,7 +5938,7 @@ wysihtml5.dom.parse = (function() {
|
|
5886
5938
|
return isString ? wysihtml5.quirks.getCorrectInnerHTML(element) : element;
|
5887
5939
|
}
|
5888
5940
|
|
5889
|
-
function _convert(oldNode, cleanUp, clearInternals) {
|
5941
|
+
function _convert(oldNode, cleanUp, clearInternals, uneditableClass) {
|
5890
5942
|
var oldNodeType = oldNode.nodeType,
|
5891
5943
|
oldChilds = oldNode.childNodes,
|
5892
5944
|
oldChildsLength = oldChilds.length,
|
@@ -5911,7 +5963,7 @@ wysihtml5.dom.parse = (function() {
|
|
5911
5963
|
|
5912
5964
|
for (i = oldChildsLength; i--;) {
|
5913
5965
|
if (oldChilds[i]) {
|
5914
|
-
newChild = _convert(oldChilds[i], cleanUp, clearInternals);
|
5966
|
+
newChild = _convert(oldChilds[i], cleanUp, clearInternals, uneditableClass);
|
5915
5967
|
if (newChild) {
|
5916
5968
|
if (oldChilds[i] === newChild) {
|
5917
5969
|
i--;
|
@@ -5921,6 +5973,10 @@ wysihtml5.dom.parse = (function() {
|
|
5921
5973
|
}
|
5922
5974
|
}
|
5923
5975
|
|
5976
|
+
if (wysihtml5.dom.getStyle("display").from(oldNode) === "block") {
|
5977
|
+
fragment.appendChild(oldNode.ownerDocument.createElement("br"));
|
5978
|
+
}
|
5979
|
+
|
5924
5980
|
// TODO: try to minimize surplus spaces
|
5925
5981
|
if (wysihtml5.lang.array([
|
5926
5982
|
"div", "pre", "p",
|
@@ -5949,7 +6005,7 @@ wysihtml5.dom.parse = (function() {
|
|
5949
6005
|
// Converts all childnodes
|
5950
6006
|
for (i=0; i<oldChildsLength; i++) {
|
5951
6007
|
if (oldChilds[i]) {
|
5952
|
-
newChild = _convert(oldChilds[i], cleanUp, clearInternals);
|
6008
|
+
newChild = _convert(oldChilds[i], cleanUp, clearInternals, uneditableClass);
|
5953
6009
|
if (newChild) {
|
5954
6010
|
if (oldChilds[i] === newChild) {
|
5955
6011
|
i--;
|
@@ -5982,12 +6038,31 @@ wysihtml5.dom.parse = (function() {
|
|
5982
6038
|
return newNode;
|
5983
6039
|
}
|
5984
6040
|
|
6041
|
+
function _applySelectorRules (element, selectorRules) {
|
6042
|
+
var sel, method, els;
|
6043
|
+
|
6044
|
+
for (sel in selectorRules) {
|
6045
|
+
if (selectorRules.hasOwnProperty(sel)) {
|
6046
|
+
if (wysihtml5.lang.object(selectorRules[sel]).isFunction()) {
|
6047
|
+
method = selectorRules[sel];
|
6048
|
+
} else if (typeof(selectorRules[sel]) === "string" && elementHandlingMethods[selectorRules[sel]]) {
|
6049
|
+
method = elementHandlingMethods[selectorRules[sel]];
|
6050
|
+
}
|
6051
|
+
els = element.querySelectorAll(sel);
|
6052
|
+
for (var i = els.length; i--;) {
|
6053
|
+
method(els[i]);
|
6054
|
+
}
|
6055
|
+
}
|
6056
|
+
}
|
6057
|
+
}
|
6058
|
+
|
5985
6059
|
function _handleElement(oldNode, clearInternals) {
|
5986
6060
|
var rule,
|
5987
6061
|
newNode,
|
5988
6062
|
tagRules = currentRules.tags,
|
5989
6063
|
nodeName = oldNode.nodeName.toLowerCase(),
|
5990
|
-
scopeName = oldNode.scopeName
|
6064
|
+
scopeName = oldNode.scopeName,
|
6065
|
+
renameTag;
|
5991
6066
|
|
5992
6067
|
/**
|
5993
6068
|
* We already parsed that element
|
@@ -6039,14 +6114,25 @@ wysihtml5.dom.parse = (function() {
|
|
6039
6114
|
return null;
|
6040
6115
|
}
|
6041
6116
|
|
6042
|
-
|
6043
|
-
_handleAttributes(oldNode, newNode, rule, clearInternals);
|
6044
|
-
_handleStyles(oldNode, newNode, rule);
|
6045
|
-
// tests if type condition is met or node should be removed/unwrapped
|
6117
|
+
// tests if type condition is met or node should be removed/unwrapped/renamed
|
6046
6118
|
if (rule.one_of_type && !_testTypes(oldNode, currentRules, rule.one_of_type, clearInternals)) {
|
6047
|
-
|
6119
|
+
if (rule.remove_action) {
|
6120
|
+
if (rule.remove_action === "unwrap") {
|
6121
|
+
return false;
|
6122
|
+
} else if (rule.remove_action === "rename") {
|
6123
|
+
renameTag = rule.remove_action_rename_to || DEFAULT_NODE_NAME;
|
6124
|
+
} else {
|
6125
|
+
return null;
|
6126
|
+
}
|
6127
|
+
} else {
|
6128
|
+
return null;
|
6129
|
+
}
|
6048
6130
|
}
|
6049
6131
|
|
6132
|
+
newNode = oldNode.ownerDocument.createElement(renameTag || rule.rename_tag || nodeName);
|
6133
|
+
_handleAttributes(oldNode, newNode, rule, clearInternals);
|
6134
|
+
_handleStyles(oldNode, newNode, rule);
|
6135
|
+
|
6050
6136
|
oldNode = null;
|
6051
6137
|
|
6052
6138
|
if (newNode.normalize) { newNode.normalize(); }
|
@@ -6134,7 +6220,7 @@ wysihtml5.dom.parse = (function() {
|
|
6134
6220
|
if (definition.attrs) {
|
6135
6221
|
for (a in definition.attrs) {
|
6136
6222
|
if (definition.attrs.hasOwnProperty(a)) {
|
6137
|
-
attr =
|
6223
|
+
attr = wysihtml5.dom.getAttribute(oldNode, a);
|
6138
6224
|
if (typeof(attr) === "string") {
|
6139
6225
|
if (attr.search(definition.attrs[a]) > -1) {
|
6140
6226
|
return true;
|
@@ -6147,24 +6233,79 @@ wysihtml5.dom.parse = (function() {
|
|
6147
6233
|
}
|
6148
6234
|
|
6149
6235
|
function _handleStyles(oldNode, newNode, rule) {
|
6150
|
-
var s;
|
6236
|
+
var s, v;
|
6151
6237
|
if(rule && rule.keep_styles) {
|
6152
6238
|
for (s in rule.keep_styles) {
|
6153
6239
|
if (rule.keep_styles.hasOwnProperty(s)) {
|
6154
|
-
|
6240
|
+
v = (s === "float") ? oldNode.style.styleFloat || oldNode.style.cssFloat : oldNode.style[s];
|
6241
|
+
// value can be regex and if so should match or style skipped
|
6242
|
+
if (rule.keep_styles[s] instanceof RegExp && !(rule.keep_styles[s].test(v))) {
|
6243
|
+
continue;
|
6244
|
+
}
|
6245
|
+
if (s === "float") {
|
6155
6246
|
// IE compability
|
6156
|
-
|
6157
|
-
newNode.style.styleFloat = oldNode.style.styleFloat;
|
6158
|
-
}
|
6159
|
-
if (oldNode.style.cssFloat) {
|
6160
|
-
newNode.style.cssFloat = oldNode.style.cssFloat;
|
6161
|
-
}
|
6247
|
+
newNode.style[(oldNode.style.styleFloat) ? 'styleFloat': 'cssFloat'] = v;
|
6162
6248
|
} else if (oldNode.style[s]) {
|
6163
|
-
newNode.style[s] =
|
6249
|
+
newNode.style[s] = v;
|
6164
6250
|
}
|
6165
6251
|
}
|
6166
6252
|
}
|
6167
6253
|
}
|
6254
|
+
};
|
6255
|
+
|
6256
|
+
function _getAttributesBeginningWith(beginning, attributes) {
|
6257
|
+
var returnAttributes = [];
|
6258
|
+
for (var attr in attributes) {
|
6259
|
+
if (attributes.hasOwnProperty(attr) && attr.indexOf(beginning) === 0) {
|
6260
|
+
returnAttributes.push(attr);
|
6261
|
+
}
|
6262
|
+
}
|
6263
|
+
return returnAttributes;
|
6264
|
+
}
|
6265
|
+
|
6266
|
+
function _checkAttribute(attributeName, attributeValue, methodName, nodeName) {
|
6267
|
+
var method = attributeCheckMethods[methodName],
|
6268
|
+
newAttributeValue;
|
6269
|
+
|
6270
|
+
if (method) {
|
6271
|
+
if (attributeValue || (attributeName === "alt" && nodeName == "IMG")) {
|
6272
|
+
newAttributeValue = method(attributeValue);
|
6273
|
+
if (typeof(newAttributeValue) === "string") {
|
6274
|
+
return newAttributeValue;
|
6275
|
+
}
|
6276
|
+
}
|
6277
|
+
}
|
6278
|
+
|
6279
|
+
return false;
|
6280
|
+
}
|
6281
|
+
|
6282
|
+
function _checkAttributes(oldNode, local_attributes) {
|
6283
|
+
var globalAttributes = wysihtml5.lang.object(currentRules.attributes || {}).clone(), // global values for check/convert values of attributes
|
6284
|
+
checkAttributes = wysihtml5.lang.object(globalAttributes).merge( wysihtml5.lang.object(local_attributes || {}).clone()).get(),
|
6285
|
+
attributes = {},
|
6286
|
+
oldAttributes = wysihtml5.dom.getAttributes(oldNode),
|
6287
|
+
attributeName, newValue, matchingAttributes;
|
6288
|
+
|
6289
|
+
for (attributeName in checkAttributes) {
|
6290
|
+
if ((/\*$/).test(attributeName)) {
|
6291
|
+
|
6292
|
+
matchingAttributes = _getAttributesBeginningWith(attributeName.slice(0,-1), oldAttributes);
|
6293
|
+
for (var i = 0, imax = matchingAttributes.length; i < imax; i++) {
|
6294
|
+
|
6295
|
+
newValue = _checkAttribute(matchingAttributes[i], oldAttributes[matchingAttributes[i]], checkAttributes[attributeName], oldNode.nodeName);
|
6296
|
+
if (newValue !== false) {
|
6297
|
+
attributes[matchingAttributes[i]] = newValue;
|
6298
|
+
}
|
6299
|
+
}
|
6300
|
+
} else {
|
6301
|
+
newValue = _checkAttribute(attributeName, oldAttributes[attributeName], checkAttributes[attributeName], oldNode.nodeName);
|
6302
|
+
if (newValue !== false) {
|
6303
|
+
attributes[attributeName] = newValue;
|
6304
|
+
}
|
6305
|
+
}
|
6306
|
+
}
|
6307
|
+
|
6308
|
+
return attributes;
|
6168
6309
|
}
|
6169
6310
|
|
6170
6311
|
// TODO: refactor. Too long to read
|
@@ -6174,7 +6315,6 @@ wysihtml5.dom.parse = (function() {
|
|
6174
6315
|
addClass = rule.add_class, // add classes based on existing attributes
|
6175
6316
|
addStyle = rule.add_style, // add styles based on existing attributes
|
6176
6317
|
setAttributes = rule.set_attributes, // attributes to set on the current node
|
6177
|
-
checkAttributes = rule.check_attributes, // check/convert values of attributes
|
6178
6318
|
allowedClasses = currentRules.classes,
|
6179
6319
|
i = 0,
|
6180
6320
|
classes = [],
|
@@ -6186,29 +6326,14 @@ wysihtml5.dom.parse = (function() {
|
|
6186
6326
|
currentClass,
|
6187
6327
|
newClass,
|
6188
6328
|
attributeName,
|
6189
|
-
|
6190
|
-
method,
|
6191
|
-
oldAttribute;
|
6329
|
+
method;
|
6192
6330
|
|
6193
6331
|
if (setAttributes) {
|
6194
6332
|
attributes = wysihtml5.lang.object(setAttributes).clone();
|
6195
6333
|
}
|
6196
6334
|
|
6197
|
-
|
6198
|
-
|
6199
|
-
method = attributeCheckMethods[checkAttributes[attributeName]];
|
6200
|
-
if (!method) {
|
6201
|
-
continue;
|
6202
|
-
}
|
6203
|
-
oldAttribute = _getAttribute(oldNode, attributeName);
|
6204
|
-
if (oldAttribute || (attributeName === "alt" && oldNode.nodeName == "IMG")) {
|
6205
|
-
newAttributeValue = method(oldAttribute);
|
6206
|
-
if (typeof(newAttributeValue) === "string") {
|
6207
|
-
attributes[attributeName] = newAttributeValue;
|
6208
|
-
}
|
6209
|
-
}
|
6210
|
-
}
|
6211
|
-
}
|
6335
|
+
// check/convert values of attributes
|
6336
|
+
attributes = wysihtml5.lang.object(attributes).merge(_checkAttributes(oldNode, rule.check_attributes)).get();
|
6212
6337
|
|
6213
6338
|
if (setClass) {
|
6214
6339
|
classes.push(setClass);
|
@@ -6220,7 +6345,7 @@ wysihtml5.dom.parse = (function() {
|
|
6220
6345
|
if (!method) {
|
6221
6346
|
continue;
|
6222
6347
|
}
|
6223
|
-
newClass = method(
|
6348
|
+
newClass = method(wysihtml5.dom.getAttribute(oldNode, attributeName));
|
6224
6349
|
if (typeof(newClass) === "string") {
|
6225
6350
|
classes.push(newClass);
|
6226
6351
|
}
|
@@ -6234,7 +6359,7 @@ wysihtml5.dom.parse = (function() {
|
|
6234
6359
|
continue;
|
6235
6360
|
}
|
6236
6361
|
|
6237
|
-
newStyle = method(
|
6362
|
+
newStyle = method(wysihtml5.dom.getAttribute(oldNode, attributeName));
|
6238
6363
|
if (typeof(newStyle) === "string") {
|
6239
6364
|
styles.push(newStyle);
|
6240
6365
|
}
|
@@ -6243,7 +6368,27 @@ wysihtml5.dom.parse = (function() {
|
|
6243
6368
|
|
6244
6369
|
|
6245
6370
|
if (typeof(allowedClasses) === "string" && allowedClasses === "any" && oldNode.getAttribute("class")) {
|
6246
|
-
|
6371
|
+
if (currentRules.classes_blacklist) {
|
6372
|
+
oldClasses = oldNode.getAttribute("class");
|
6373
|
+
if (oldClasses) {
|
6374
|
+
classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));
|
6375
|
+
}
|
6376
|
+
|
6377
|
+
classesLength = classes.length;
|
6378
|
+
for (; i<classesLength; i++) {
|
6379
|
+
currentClass = classes[i];
|
6380
|
+
if (!currentRules.classes_blacklist[currentClass]) {
|
6381
|
+
newClasses.push(currentClass);
|
6382
|
+
}
|
6383
|
+
}
|
6384
|
+
|
6385
|
+
if (newClasses.length) {
|
6386
|
+
attributes["class"] = wysihtml5.lang.array(newClasses).unique().join(" ");
|
6387
|
+
}
|
6388
|
+
|
6389
|
+
} else {
|
6390
|
+
attributes["class"] = oldNode.getAttribute("class");
|
6391
|
+
}
|
6247
6392
|
} else {
|
6248
6393
|
// make sure that wysihtml5 temp class doesn't get stripped out
|
6249
6394
|
if (!clearInternals) {
|
@@ -6304,49 +6449,6 @@ wysihtml5.dom.parse = (function() {
|
|
6304
6449
|
}
|
6305
6450
|
}
|
6306
6451
|
|
6307
|
-
/**
|
6308
|
-
* IE gives wrong results for hasAttribute/getAttribute, for example:
|
6309
|
-
* var td = document.createElement("td");
|
6310
|
-
* td.getAttribute("rowspan"); // => "1" in IE
|
6311
|
-
*
|
6312
|
-
* Therefore we have to check the element's outerHTML for the attribute
|
6313
|
-
*/
|
6314
|
-
var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly();
|
6315
|
-
function _getAttribute(node, attributeName) {
|
6316
|
-
attributeName = attributeName.toLowerCase();
|
6317
|
-
var nodeName = node.nodeName;
|
6318
|
-
if (nodeName == "IMG" && attributeName == "src" && _isLoadedImage(node) === true) {
|
6319
|
-
// Get 'src' attribute value via object property since this will always contain the
|
6320
|
-
// full absolute url (http://...)
|
6321
|
-
// this fixes a very annoying bug in firefox (ver 3.6 & 4) and IE 8 where images copied from the same host
|
6322
|
-
// will have relative paths, which the sanitizer strips out (see attributeCheckMethods.url)
|
6323
|
-
return node.src;
|
6324
|
-
} else if (HAS_GET_ATTRIBUTE_BUG && "outerHTML" in node) {
|
6325
|
-
// Don't trust getAttribute/hasAttribute in IE 6-8, instead check the element's outerHTML
|
6326
|
-
var outerHTML = node.outerHTML.toLowerCase(),
|
6327
|
-
// TODO: This might not work for attributes without value: <input disabled>
|
6328
|
-
hasAttribute = outerHTML.indexOf(" " + attributeName + "=") != -1;
|
6329
|
-
|
6330
|
-
return hasAttribute ? node.getAttribute(attributeName) : null;
|
6331
|
-
} else{
|
6332
|
-
return node.getAttribute(attributeName);
|
6333
|
-
}
|
6334
|
-
}
|
6335
|
-
|
6336
|
-
/**
|
6337
|
-
* Check whether the given node is a proper loaded image
|
6338
|
-
* FIXME: Returns undefined when unknown (Chrome, Safari)
|
6339
|
-
*/
|
6340
|
-
function _isLoadedImage(node) {
|
6341
|
-
try {
|
6342
|
-
return node.complete && !node.mozMatchesSelector(":-moz-broken");
|
6343
|
-
} catch(e) {
|
6344
|
-
if (node.complete && node.readyState === "complete") {
|
6345
|
-
return true;
|
6346
|
-
}
|
6347
|
-
}
|
6348
|
-
}
|
6349
|
-
|
6350
6452
|
var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
|
6351
6453
|
function _handleText(oldNode) {
|
6352
6454
|
var nextSibling = oldNode.nextSibling;
|
@@ -6531,8 +6633,18 @@ wysihtml5.dom.parse = (function() {
|
|
6531
6633
|
})()
|
6532
6634
|
};
|
6533
6635
|
|
6534
|
-
|
6535
|
-
|
6636
|
+
var elementHandlingMethods = {
|
6637
|
+
unwrap: function (element) {
|
6638
|
+
wysihtml5.dom.unwrap(element);
|
6639
|
+
},
|
6640
|
+
|
6641
|
+
remove: function (element) {
|
6642
|
+
element.parentNode.removeChild(element);
|
6643
|
+
}
|
6644
|
+
};
|
6645
|
+
|
6646
|
+
return parse(elementOrHtml_current, config_current);
|
6647
|
+
};
|
6536
6648
|
;/**
|
6537
6649
|
* Checks for empty text node childs and removes them
|
6538
6650
|
*
|
@@ -7159,7 +7271,7 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
|
|
7159
7271
|
var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly();
|
7160
7272
|
attributeName = attributeName.toLowerCase();
|
7161
7273
|
var nodeName = node.nodeName;
|
7162
|
-
if (nodeName == "IMG" && attributeName == "src" &&
|
7274
|
+
if (nodeName == "IMG" && attributeName == "src" && wysihtml5.dom.isLoadedImage(node) === true) {
|
7163
7275
|
// Get 'src' attribute value via object property since this will always contain the
|
7164
7276
|
// full absolute url (http://...)
|
7165
7277
|
// this fixes a very annoying bug in firefox (ver 3.6 & 4) and IE 8 where images copied from the same host
|
@@ -7176,6 +7288,52 @@ wysihtml5.dom.getAttribute = function(node, attributeName) {
|
|
7176
7288
|
return node.getAttribute(attributeName);
|
7177
7289
|
}
|
7178
7290
|
};
|
7291
|
+
;/**
|
7292
|
+
* Get all attributes of an element
|
7293
|
+
*
|
7294
|
+
* IE gives wrong results for hasAttribute/getAttribute, for example:
|
7295
|
+
* var td = document.createElement("td");
|
7296
|
+
* td.getAttribute("rowspan"); // => "1" in IE
|
7297
|
+
*
|
7298
|
+
* Therefore we have to check the element's outerHTML for the attribute
|
7299
|
+
*/
|
7300
|
+
|
7301
|
+
wysihtml5.dom.getAttributes = function(node) {
|
7302
|
+
var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly(),
|
7303
|
+
nodeName = node.nodeName,
|
7304
|
+
attributes = [],
|
7305
|
+
attr;
|
7306
|
+
|
7307
|
+
for (attr in node.attributes) {
|
7308
|
+
if ((node.attributes.hasOwnProperty && node.attributes.hasOwnProperty(attr)) || (!node.attributes.hasOwnProperty && Object.prototype.hasOwnProperty.call(node.attributes, attr))) {
|
7309
|
+
if (node.attributes[attr].specified) {
|
7310
|
+
if (nodeName == "IMG" && node.attributes[attr].name.toLowerCase() == "src" && wysihtml5.dom.isLoadedImage(node) === true) {
|
7311
|
+
attributes['src'] = node.src;
|
7312
|
+
} else if (wysihtml5.lang.array(['rowspan', 'colspan']).contains(node.attributes[attr].name.toLowerCase()) && HAS_GET_ATTRIBUTE_BUG) {
|
7313
|
+
if (node.attributes[attr].value !== 1) {
|
7314
|
+
attributes[node.attributes[attr].name] = node.attributes[attr].value;
|
7315
|
+
}
|
7316
|
+
} else {
|
7317
|
+
attributes[node.attributes[attr].name] = node.attributes[attr].value;
|
7318
|
+
}
|
7319
|
+
}
|
7320
|
+
}
|
7321
|
+
}
|
7322
|
+
return attributes;
|
7323
|
+
};;/**
|
7324
|
+
* Check whether the given node is a proper loaded image
|
7325
|
+
* FIXME: Returns undefined when unknown (Chrome, Safari)
|
7326
|
+
*/
|
7327
|
+
|
7328
|
+
wysihtml5.dom.isLoadedImage = function (node) {
|
7329
|
+
try {
|
7330
|
+
return node.complete && !node.mozMatchesSelector(":-moz-broken");
|
7331
|
+
} catch(e) {
|
7332
|
+
if (node.complete && node.readyState === "complete") {
|
7333
|
+
return true;
|
7334
|
+
}
|
7335
|
+
}
|
7336
|
+
};
|
7179
7337
|
;(function(wysihtml5) {
|
7180
7338
|
|
7181
7339
|
var api = wysihtml5.dom;
|
@@ -8143,6 +8301,46 @@ wysihtml5.dom.query = function(elements, query) {
|
|
8143
8301
|
}
|
8144
8302
|
node.parentNode.removeChild(node);
|
8145
8303
|
}
|
8304
|
+
};;/*
|
8305
|
+
* Methods for fetching pasted html before it gets inserted into content
|
8306
|
+
**/
|
8307
|
+
|
8308
|
+
/* Modern event.clipboardData driven approach.
|
8309
|
+
* Advantage is that it does not have to loose selection or modify dom to catch the data.
|
8310
|
+
* IE does not support though.
|
8311
|
+
**/
|
8312
|
+
wysihtml5.dom.getPastedHtml = function(event) {
|
8313
|
+
var html;
|
8314
|
+
if (event.clipboardData) {
|
8315
|
+
if (wysihtml5.lang.array(event.clipboardData.types).contains('text/html')) {
|
8316
|
+
html = event.clipboardData.getData('text/html');
|
8317
|
+
} else if (wysihtml5.lang.array(event.clipboardData.types).contains('text/plain')) {
|
8318
|
+
html = wysihtml5.lang.string(event.clipboardData.getData('text/plain')).escapeHTML(true, true);
|
8319
|
+
}
|
8320
|
+
}
|
8321
|
+
return html;
|
8322
|
+
};
|
8323
|
+
|
8324
|
+
/* Older temprorary contenteditable as paste source catcher method for fallbacks */
|
8325
|
+
wysihtml5.dom.getPastedHtmlWithDiv = function (composer, f) {
|
8326
|
+
var selBookmark = composer.selection.getBookmark(),
|
8327
|
+
doc = composer.element.ownerDocument,
|
8328
|
+
cleanerDiv = coc.createElement('DIV');
|
8329
|
+
|
8330
|
+
cleanerDiv.style.width = "1px";
|
8331
|
+
cleanerDiv.style.height = "1px";
|
8332
|
+
cleanerDiv.style.visibility = "hidden";
|
8333
|
+
cleanerDiv.style.overflow = "hidden";
|
8334
|
+
|
8335
|
+
cleanerDiv.setAttribute('contenteditable', 'true');
|
8336
|
+
doc.body.appendChild(cleanerDiv);
|
8337
|
+
cleanerDiv.focus();
|
8338
|
+
|
8339
|
+
setTimeout(function () {
|
8340
|
+
composer.selection.setBookmark(selBookmark);
|
8341
|
+
f(cleanerDiv.innerHTML);
|
8342
|
+
cleanerDiv.parentNode.removeChild(cleanerDiv);
|
8343
|
+
}, 0);
|
8146
8344
|
};;/**
|
8147
8345
|
* Fix most common html formatting misbehaviors of browsers implementation when inserting
|
8148
8346
|
* content via copy & paste contentEditable
|
@@ -8150,52 +8348,76 @@ wysihtml5.dom.query = function(elements, query) {
|
|
8150
8348
|
* @author Christopher Blum
|
8151
8349
|
*/
|
8152
8350
|
wysihtml5.quirks.cleanPastedHTML = (function() {
|
8153
|
-
|
8154
|
-
var
|
8155
|
-
|
8156
|
-
|
8351
|
+
|
8352
|
+
var styleToRegex = function (styleStr) {
|
8353
|
+
var trimmedStr = wysihtml5.lang.string(styleStr).trim(),
|
8354
|
+
escapedStr = trimmedStr.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
8355
|
+
|
8356
|
+
return new RegExp("^((?!^" + escapedStr + "$).)*$", "i");
|
8157
8357
|
};
|
8158
8358
|
|
8159
|
-
function
|
8160
|
-
|
8161
|
-
|
8162
|
-
|
8163
|
-
|
8164
|
-
isString = typeof(elementOrHtml) === "string",
|
8165
|
-
method,
|
8166
|
-
matches,
|
8167
|
-
matchesLength,
|
8168
|
-
i,
|
8169
|
-
j = 0, n;
|
8170
|
-
if (isString) {
|
8171
|
-
element = wysihtml5.dom.getAsDom(elementOrHtml, context);
|
8172
|
-
} else {
|
8173
|
-
element = elementOrHtml;
|
8174
|
-
}
|
8359
|
+
var extendRulesWithStyleExceptions = function (rules, exceptStyles) {
|
8360
|
+
var newRules = wysihtml5.lang.object(rules).clone(true),
|
8361
|
+
tag, style;
|
8362
|
+
|
8363
|
+
for (tag in newRules.tags) {
|
8175
8364
|
|
8176
|
-
|
8177
|
-
|
8178
|
-
|
8179
|
-
|
8180
|
-
|
8181
|
-
|
8365
|
+
if (newRules.tags.hasOwnProperty(tag)) {
|
8366
|
+
if (newRules.tags[tag].keep_styles) {
|
8367
|
+
for (style in newRules.tags[tag].keep_styles) {
|
8368
|
+
if (newRules.tags[tag].keep_styles.hasOwnProperty(style)) {
|
8369
|
+
if (exceptStyles[style]) {
|
8370
|
+
newRules.tags[tag].keep_styles[style] = styleToRegex(exceptStyles[style]);
|
8371
|
+
}
|
8372
|
+
}
|
8373
|
+
}
|
8374
|
+
}
|
8182
8375
|
}
|
8183
8376
|
}
|
8184
8377
|
|
8185
|
-
|
8186
|
-
|
8187
|
-
|
8188
|
-
|
8378
|
+
return newRules;
|
8379
|
+
};
|
8380
|
+
|
8381
|
+
var pickRuleset = function(ruleset, html) {
|
8382
|
+
var pickedSet, defaultSet;
|
8383
|
+
|
8384
|
+
if (!ruleset) {
|
8385
|
+
return null;
|
8189
8386
|
}
|
8190
8387
|
|
8191
|
-
|
8388
|
+
for (var i = 0, max = ruleset.length; i < max; i++) {
|
8389
|
+
if (!ruleset[i].condition) {
|
8390
|
+
defaultSet = ruleset[i].set;
|
8391
|
+
}
|
8392
|
+
if (ruleset[i].condition && ruleset[i].condition.test(html)) {
|
8393
|
+
return ruleset[i].set;
|
8394
|
+
}
|
8395
|
+
}
|
8192
8396
|
|
8193
|
-
return
|
8194
|
-
}
|
8397
|
+
return defaultSet;
|
8398
|
+
};
|
8195
8399
|
|
8196
|
-
return
|
8197
|
-
|
8198
|
-
|
8400
|
+
return function(html, options) {
|
8401
|
+
var exceptStyles = {
|
8402
|
+
'color': wysihtml5.dom.getStyle("color").from(options.referenceNode),
|
8403
|
+
'fontSize': wysihtml5.dom.getStyle("font-size").from(options.referenceNode)
|
8404
|
+
},
|
8405
|
+
rules = extendRulesWithStyleExceptions(pickRuleset(options.rules, html) || {}, exceptStyles),
|
8406
|
+
newHtml;
|
8407
|
+
|
8408
|
+
newHtml = wysihtml5.dom.parse(html, {
|
8409
|
+
"rules": rules,
|
8410
|
+
"cleanUp": true, // <span> elements, empty or without attributes, should be removed/replaced with their content
|
8411
|
+
"context": options.referenceNode.ownerDocument,
|
8412
|
+
"uneditableClass": options.uneditableClass,
|
8413
|
+
"clearInternals" : true, // don't paste temprorary selection and other markings
|
8414
|
+
"unjoinNbsps" : true
|
8415
|
+
});
|
8416
|
+
|
8417
|
+
return newHtml;
|
8418
|
+
};
|
8419
|
+
|
8420
|
+
})();;/**
|
8199
8421
|
* IE and Opera leave an empty paragraph in the contentEditable element after clearing it
|
8200
8422
|
*
|
8201
8423
|
* @param {Object} contentEditableElement The contentEditable element to observe for clearing events
|
@@ -8825,7 +9047,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
8825
9047
|
return false;
|
8826
9048
|
},
|
8827
9049
|
|
8828
|
-
// TODO: Figure out a method from following
|
9050
|
+
// TODO: Figure out a method from following 2 that would work universally
|
8829
9051
|
executeAndRestoreRangy: function(method, restoreScrollPosition) {
|
8830
9052
|
var win = this.doc.defaultView || this.doc.parentWindow,
|
8831
9053
|
sel = rangy.saveSelection(win);
|
@@ -8938,10 +9160,18 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
8938
9160
|
*/
|
8939
9161
|
insertHTML: function(html) {
|
8940
9162
|
var range = rangy.createRange(this.doc),
|
8941
|
-
node
|
8942
|
-
|
9163
|
+
node = this.doc.createElement('DIV'),
|
9164
|
+
fragment = this.doc.createDocumentFragment(),
|
9165
|
+
lastChild;
|
9166
|
+
|
9167
|
+
node.innerHTML = html;
|
9168
|
+
lastChild = node.lastChild;
|
9169
|
+
|
9170
|
+
while (node.firstChild) {
|
9171
|
+
fragment.appendChild(node.firstChild);
|
9172
|
+
}
|
9173
|
+
this.insertNode(fragment);
|
8943
9174
|
|
8944
|
-
this.insertNode(node);
|
8945
9175
|
if (lastChild) {
|
8946
9176
|
this.setAfter(lastChild);
|
8947
9177
|
}
|
@@ -12560,7 +12790,7 @@ wysihtml5.views.View = Base.extend(
|
|
12560
12790
|
container = (this.sandbox.getIframe) ? this.sandbox.getIframe() : this.sandbox.getContentEditable(),
|
12561
12791
|
element = this.element,
|
12562
12792
|
focusBlurElement = (browser.supportsEventsInIframeCorrectly() || this.sandbox.getContentEditable) ? element : this.sandbox.getWindow(),
|
12563
|
-
pasteEvents = ["drop", "paste"],
|
12793
|
+
pasteEvents = ["drop", "paste", "beforepaste"],
|
12564
12794
|
interactionEvents = ["drop", "paste", "mouseup", "focus", "keyup"];
|
12565
12795
|
|
12566
12796
|
// --------- destroy:composer event ---------
|
@@ -12633,9 +12863,9 @@ wysihtml5.views.View = Base.extend(
|
|
12633
12863
|
});
|
12634
12864
|
|
12635
12865
|
dom.observe(element, pasteEvents, function(event) {
|
12636
|
-
setTimeout(function() {
|
12866
|
+
//setTimeout(function() {
|
12637
12867
|
that.parent.fire(event.type, event).fire(event.type + ":composer", event);
|
12638
|
-
}, 0);
|
12868
|
+
//}, 0);
|
12639
12869
|
});
|
12640
12870
|
|
12641
12871
|
// --------- neword event ---------
|
@@ -12993,10 +13223,12 @@ wysihtml5.views.View = Base.extend(
|
|
12993
13223
|
handleTables: true,
|
12994
13224
|
// Tab key inserts tab into text as default behaviour. It can be disabled to regain keyboard navigation
|
12995
13225
|
handleTabKey: true,
|
12996
|
-
// Object which includes parser rules to apply when html gets
|
13226
|
+
// Object which includes parser rules to apply when html gets cleaned
|
12997
13227
|
// See parser_rules/*.js for examples
|
12998
13228
|
parserRules: { tags: { br: {}, span: {}, div: {}, p: {} }, classes: {} },
|
12999
|
-
//
|
13229
|
+
// Object which includes parser when the user inserts content via copy & paste. If null parserRules will be used instead
|
13230
|
+
pasteParserRulesets: null,
|
13231
|
+
// Parser method to use when the user inserts content
|
13000
13232
|
parser: wysihtml5.dom.parse,
|
13001
13233
|
// Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
|
13002
13234
|
composerClassName: "wysihtml5-editor",
|
@@ -13141,14 +13373,40 @@ wysihtml5.views.View = Base.extend(
|
|
13141
13373
|
* - Observes for paste and drop
|
13142
13374
|
*/
|
13143
13375
|
_initParser: function() {
|
13144
|
-
this
|
13145
|
-
|
13146
|
-
|
13147
|
-
|
13148
|
-
|
13149
|
-
|
13150
|
-
|
13376
|
+
var that = this,
|
13377
|
+
oldHtml,
|
13378
|
+
cleanHtml;
|
13379
|
+
|
13380
|
+
if (wysihtml5.browser.supportsModenPaste()) {
|
13381
|
+
this.on("paste:composer", function(event) {
|
13382
|
+
event.preventDefault();
|
13383
|
+
oldHtml = wysihtml5.dom.getPastedHtml(event);
|
13384
|
+
if (oldHtml) {
|
13385
|
+
that._cleanAndPaste(oldHtml);
|
13386
|
+
}
|
13387
|
+
});
|
13388
|
+
|
13389
|
+
} else {
|
13390
|
+
this.on("beforepaste:composer", function(event) {
|
13391
|
+
event.preventDefault();
|
13392
|
+
wysihtml5.dom.getPastedHtmlWithDiv(that.composer, function(pastedHTML) {
|
13393
|
+
if (pastedHTML) {
|
13394
|
+
that._cleanAndPaste(pastedHTML);
|
13395
|
+
}
|
13396
|
+
});
|
13397
|
+
});
|
13398
|
+
|
13399
|
+
}
|
13400
|
+
},
|
13401
|
+
|
13402
|
+
_cleanAndPaste: function (oldHtml) {
|
13403
|
+
var cleanHtml = wysihtml5.quirks.cleanPastedHTML(oldHtml, {
|
13404
|
+
"referenceNode": this.composer.element,
|
13405
|
+
"rules": this.config.pasteParserRulesets || [{"set": this.config.parserRules}],
|
13406
|
+
"uneditableClass": this.config.uneditableContainerClassname
|
13151
13407
|
});
|
13408
|
+
this.composer.selection.deleteContents();
|
13409
|
+
this.composer.selection.insertHTML(cleanHtml);
|
13152
13410
|
}
|
13153
13411
|
});
|
13154
13412
|
})(wysihtml5);
|
@@ -13478,6 +13736,19 @@ wysihtml5.views.View = Base.extend(
|
|
13478
13736
|
this._observe();
|
13479
13737
|
if (showOnInit) { this.show(); }
|
13480
13738
|
|
13739
|
+
if (editor.config.classNameCommandDisabled != null) {
|
13740
|
+
CLASS_NAME_COMMAND_DISABLED = editor.config.classNameCommandDisabled;
|
13741
|
+
}
|
13742
|
+
if (editor.config.classNameCommandsDisabled != null) {
|
13743
|
+
CLASS_NAME_COMMANDS_DISABLED = editor.config.classNameCommandsDisabled;
|
13744
|
+
}
|
13745
|
+
if (editor.config.classNameCommandActive != null) {
|
13746
|
+
CLASS_NAME_COMMAND_ACTIVE = editor.config.classNameCommandActive;
|
13747
|
+
}
|
13748
|
+
if (editor.config.classNameActionActive != null) {
|
13749
|
+
CLASS_NAME_ACTION_ACTIVE = editor.config.classNameActionActive;
|
13750
|
+
}
|
13751
|
+
|
13481
13752
|
var speechInputLinks = this.container.querySelectorAll("[data-wysihtml5-command=insertSpeech]"),
|
13482
13753
|
length = speechInputLinks.length,
|
13483
13754
|
i = 0;
|