wysihtml5x-rails 0.4.13 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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;
|