Skip to content

Commit fb7d0b3

Browse files
author
Oliver Pulges
committed
Update to v0.4.16
1 parent 45bec62 commit fb7d0b3

File tree

3 files changed

+271
-83
lines changed

3 files changed

+271
-83
lines changed

lib/wysihtml5x/rails/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Wysihtml5x
22
module Rails
3-
VERSION = "0.4.15"
3+
VERSION = "0.4.16"
44
end
55
end

vendor/assets/javascripts/wysihtml5x-toolbar.js

Lines changed: 135 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ if(!Array.isArray) {
2525
return Object.prototype.toString.call(arg) === '[object Array]';
2626
};
2727
};/**
28-
* @license wysihtml5x v0.4.15
28+
* @license wysihtml5x v0.4.16
2929
* https://github.com/Edicy/wysihtml5
3030
*
3131
* Author: Christopher Blum (https://github.com/tiff)
@@ -36,7 +36,7 @@ if(!Array.isArray) {
3636
*
3737
*/
3838
var wysihtml5 = {
39-
version: "0.4.15",
39+
version: "0.4.16",
4040

4141
// namespaces
4242
commands: {},
@@ -5369,9 +5369,36 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
53695369
}
53705370

53715371
return nextNode;
5372-
}
5372+
},
5373+
5374+
// Traverses a node for last children and their chidren (including itself), and finds the last node that has no children.
5375+
// Array of classes for forced last-leaves (ex: uneditable-container) can be defined (options = {leafClasses: [...]})
5376+
// Useful for finding the actually visible element before cursor
5377+
lastLeafNode: function(options) {
5378+
var lastChild;
5379+
5380+
// Returns non-element nodes
5381+
if (node.nodeType !== 1) {
5382+
return node;
5383+
}
53735384

5385+
// Returns if element is leaf
5386+
lastChild = node.lastChild;
5387+
if (!lastChild) {
5388+
return node;
5389+
}
5390+
5391+
// Returns if element is of of options.leafClasses leaf
5392+
if (options && options.leafClasses) {
5393+
for (var i = options.leafClasses.length; i--;) {
5394+
if (wysihtml5.dom.hasClass(node, options.leafClasses[i])) {
5395+
return node;
5396+
}
5397+
}
5398+
}
53745399

5400+
return wysihtml5.dom.domNode(lastChild).lastLeafNode(options);
5401+
}
53755402

53765403
};
53775404
};
@@ -5494,8 +5521,13 @@ wysihtml5.dom.getParentElement = (function() {
54945521

54955522
levels = levels || 50; // Go max 50 nodes upwards from current node
54965523

5524+
// make the matching class regex from class name if omitted
5525+
if (findByClass && !matchingSet.classRegExp) {
5526+
matchingSet.classRegExp = new RegExp(matchingSet.className);
5527+
}
5528+
54975529
while (levels-- && node && node.nodeName !== "BODY" && (!container || node !== container)) {
5498-
if (_isElement(node) && _isSameNodeName(node.nodeName, matchingSet.nodeName) &&
5530+
if (_isElement(node) && (!matchingSet.nodeName || _isSameNodeName(node.nodeName, matchingSet.nodeName)) &&
54995531
(!findByStyle || _hasStyle(node, matchingSet.cssStyle, matchingSet.styleRegExp)) &&
55005532
(!findByClass || _hasClassName(node, matchingSet.className, matchingSet.classRegExp))
55015533
) {
@@ -8922,15 +8954,25 @@ wysihtml5.quirks.ensureProperClearing = (function() {
89228954
return false;
89238955
},
89248956

8957+
// deletes selection contents making sure uneditables/unselectables are not partially deleted
89258958
deleteContents: function() {
8926-
var ranges = this.getOwnRanges();
8927-
for (var i = ranges.length; i--;) {
8928-
ranges[i].deleteContents();
8959+
var range = this.getRange(),
8960+
startParent, endParent;
8961+
8962+
if (this.unselectableClass) {
8963+
if ((startParent = wysihtml5.dom.getParentElement(range.startContainer, { className: this.unselectableClass }, false, this.contain))) {
8964+
range.setStartBefore(startParent);
8965+
}
8966+
if ((endParent = wysihtml5.dom.getParentElement(range.endContainer, { className: this.unselectableClass }, false, this.contain))) {
8967+
range.setEndAfter(endParent);
8968+
}
89298969
}
8930-
this.setSelection(ranges[0]);
8970+
range.deleteContents();
8971+
this.setSelection(range);
89318972
},
89328973

89338974
getPreviousNode: function(node, ignoreEmpty) {
8975+
var displayStyle;
89348976
if (!node) {
89358977
var selection = this.getSelection();
89368978
node = selection.anchorNode;
@@ -8951,12 +8993,19 @@ wysihtml5.quirks.ensureProperClearing = (function() {
89518993
// do not count comments and other node types
89528994
ret = this.getPreviousNode(ret, ignoreEmpty);
89538995
} else if (ret && ret.nodeType === 3 && (/^\s*$/).test(ret.textContent)) {
8954-
// do not count empty textnodes as previus nodes
8996+
// do not count empty textnodes as previous nodes
89558997
ret = this.getPreviousNode(ret, ignoreEmpty);
8956-
} else if (ignoreEmpty && ret && ret.nodeType === 1 && !wysihtml5.lang.array(["BR", "HR", "IMG"]).contains(ret.nodeName) && (/^[\s]*$/).test(ret.innerHTML)) {
8998+
} else if (ignoreEmpty && ret && ret.nodeType === 1) {
89578999
// Do not count empty nodes if param set.
8958-
// Contenteditable tends to bypass and delete these silently when deleting with caret
8959-
ret = this.getPreviousNode(ret, ignoreEmpty);
9000+
// Contenteditable tends to bypass and delete these silently when deleting with caret when element is inline-like
9001+
displayStyle = wysihtml5.dom.getStyle("display").from(ret);
9002+
if (
9003+
!wysihtml5.lang.array(["BR", "HR", "IMG"]).contains(ret.nodeName) &&
9004+
!wysihtml5.lang.array(["block", "inline-block", "flex", "list-item", "table"]).contains(displayStyle) &&
9005+
(/^[\s]*$/).test(ret.innerHTML)
9006+
) {
9007+
ret = this.getPreviousNode(ret, ignoreEmpty);
9008+
}
89609009
} else if (!ret && node !== this.contain) {
89619010
parent = node.parentNode;
89629011
if (parent !== this.contain) {
@@ -9008,40 +9057,64 @@ wysihtml5.quirks.ensureProperClearing = (function() {
90089057
range = this.getRange(),
90099058
startNode = range.startContainer;
90109059

9011-
if (startNode.nodeType === wysihtml5.TEXT_NODE) {
9012-
return this.isCollapsed() && (startNode.nodeType === wysihtml5.TEXT_NODE && (/^\s*$/).test(startNode.data.substr(0,range.startOffset)));
9013-
} else {
9014-
r.selectNodeContents(this.getRange().commonAncestorContainer);
9015-
r.collapse(true);
9016-
return (this.isCollapsed() && (r.startContainer === s.anchorNode || r.endContainer === s.anchorNode) && r.startOffset === s.anchorOffset);
9060+
if (startNode) {
9061+
if (startNode.nodeType === wysihtml5.TEXT_NODE) {
9062+
return this.isCollapsed() && (startNode.nodeType === wysihtml5.TEXT_NODE && (/^\s*$/).test(startNode.data.substr(0,range.startOffset)));
9063+
} else {
9064+
r.selectNodeContents(this.getRange().commonAncestorContainer);
9065+
r.collapse(true);
9066+
return (this.isCollapsed() && (r.startContainer === s.anchorNode || r.endContainer === s.anchorNode) && r.startOffset === s.anchorOffset);
9067+
}
90179068
}
90189069
},
90199070

90209071
caretIsInTheBeginnig: function(ofNode) {
90219072
var selection = this.getSelection(),
90229073
node = selection.anchorNode,
90239074
offset = selection.anchorOffset;
9024-
if (ofNode) {
9075+
if (ofNode && node) {
90259076
return (offset === 0 && (node.nodeName && node.nodeName === ofNode.toUpperCase() || wysihtml5.dom.getParentElement(node.parentNode, { nodeName: ofNode }, 1)));
9026-
} else {
9077+
} else if (node) {
90279078
return (offset === 0 && !this.getPreviousNode(node, true));
90289079
}
90299080
},
90309081

90319082
caretIsBeforeUneditable: function() {
90329083
var selection = this.getSelection(),
90339084
node = selection.anchorNode,
9034-
offset = selection.anchorOffset;
9035-
9036-
if (offset === 0) {
9037-
var prevNode = this.getPreviousNode(node, true);
9038-
if (prevNode) {
9039-
var uneditables = this.getOwnUneditables();
9040-
for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
9041-
if (prevNode === uneditables[i]) {
9042-
return uneditables[i];
9085+
offset = selection.anchorOffset,
9086+
childNodes = [],
9087+
range, contentNodes, lastNode;
9088+
9089+
if (node) {
9090+
if (offset === 0) {
9091+
var prevNode = this.getPreviousNode(node, true),
9092+
prevLeaf = prevNode ? wysihtml5.dom.domNode(prevNode).lastLeafNode((this.unselectableClass) ? {leafClasses: [this.unselectableClass]} : false) : null;
9093+
if (prevLeaf) {
9094+
var uneditables = this.getOwnUneditables();
9095+
for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
9096+
if (prevLeaf === uneditables[i]) {
9097+
return uneditables[i];
9098+
}
9099+
}
9100+
}
9101+
} else {
9102+
range = selection.getRangeAt(0);
9103+
range.setStart(range.startContainer, range.startOffset - 1);
9104+
// TODO: make getting children on range a separate funtion
9105+
if (range) {
9106+
contentNodes = range.getNodes([1,3]);
9107+
for (var n = 0, max = contentNodes.length; n < max; n++) {
9108+
if (contentNodes[n].parentNode && contentNodes[n].parentNode === node) {
9109+
childNodes.push(contentNodes[n]);
9110+
}
90439111
}
90449112
}
9113+
lastNode = childNodes.length > 0 ? childNodes[childNodes.length -1] : null;
9114+
if (lastNode && lastNode.nodeType === 1 && wysihtml5.dom.hasClass(lastNode, this.unselectableClass)) {
9115+
return lastNode;
9116+
}
9117+
90459118
}
90469119
}
90479120
return false;
@@ -9495,6 +9568,10 @@ wysihtml5.quirks.ensureProperClearing = (function() {
94959568
return this.getSelection().toHtml();
94969569
},
94979570

9571+
getPlainText: function () {
9572+
return this.getSelection().toString();
9573+
},
9574+
94989575
isEndToEndInNode: function(nodeNames) {
94999576
var range = this.getRange(),
95009577
parentElement = range.commonAncestorContainer,
@@ -11998,11 +12075,11 @@ wysihtml5.views.View = Base.extend(
1199812075
},
1199912076

1200012077
focus: function() {
12001-
if (this.element.ownerDocument.querySelector(":focus") === this.element) {
12078+
if (this.element && this.element.ownerDocument && this.element.ownerDocument.querySelector(":focus") === this.element) {
1200212079
return;
1200312080
}
1200412081

12005-
try { this.element.focus(); } catch(e) {}
12082+
try { if(this.element) { this.element.focus(); } } catch(e) {}
1200612083
},
1200712084

1200812085
hide: function() {
@@ -12285,18 +12362,17 @@ wysihtml5.views.View = Base.extend(
1228512362
if (!supportsAutoLinking || (supportsAutoLinking && supportsDisablingOfAutoLinking)) {
1228612363
this.parent.on("newword:composer", function() {
1228712364
if (dom.getTextContent(that.element).match(dom.autoLink.URL_REG_EXP)) {
12288-
that.selection.executeAndRestore(function(startContainer, endContainer) {
12289-
var uneditables = that.element.querySelectorAll("." + that.config.uneditableContainerClassname),
12290-
isInUneditable = false;
12365+
var nodeWithSelection = that.selection.getSelectedNode(),
12366+
uneditables = that.element.querySelectorAll("." + that.config.uneditableContainerClassname),
12367+
isInUneditable = false;
1229112368

12292-
for (var i = uneditables.length; i--;) {
12293-
if (wysihtml5.dom.contains(uneditables[i], endContainer)) {
12294-
isInUneditable = true;
12295-
}
12369+
for (var i = uneditables.length; i--;) {
12370+
if (wysihtml5.dom.contains(uneditables[i], nodeWithSelection)) {
12371+
isInUneditable = true;
1229612372
}
12373+
}
1229712374

12298-
if (!isInUneditable) dom.autoLink(endContainer.parentNode, [that.config.uneditableContainerClassname]);
12299-
});
12375+
if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.uneditableContainerClassname]);
1230012376
}
1230112377
});
1230212378

@@ -12766,7 +12842,13 @@ wysihtml5.views.View = Base.extend(
1276612842
// Do a special delete if caret would delete uneditable
1276712843
if (beforeUneditable) {
1276812844
event.preventDefault();
12769-
deleteAroundEditable(selection, beforeUneditable, element);
12845+
// If customevents present notify element of being deleted
12846+
// TODO: Investigate if browser support can be extended
12847+
try {
12848+
var ev = new CustomEvent("wysihtml5:uneditable:delete");
12849+
beforeUneditable.dispatchEvent(ev);
12850+
} catch (err) {}
12851+
beforeUneditable.parentNode.removeChild(beforeUneditable);
1277012852
}
1277112853
}
1277212854
} else {
@@ -12877,6 +12959,7 @@ wysihtml5.views.View = Base.extend(
1287712959
dom.observe(element, "copy", function(event) {
1287812960
if (event.clipboardData) {
1287912961
event.clipboardData.setData("text/html", that.config.copyedFromMarking + that.selection.getHtml());
12962+
event.clipboardData.setData("text/plain", that.selection.getPlainText());
1288012963
event.preventDefault();
1288112964
}
1288212965
that.parent.fire(event.type, event).fire(event.type + ":composer", event);
@@ -12909,6 +12992,17 @@ wysihtml5.views.View = Base.extend(
1290912992
});
1291012993
}
1291112994

12995+
// If uneditables configured makes click on uneditable moves caret after clicked element (so it can be deleted like text)
12996+
// If uneditable needs text selection itself event.stopPropagation can be used to prevent this behaviour
12997+
if (this.config.uneditableContainerClassname) {
12998+
dom.observe(element, "click", function(event) {
12999+
var uneditable = wysihtml5.dom.getParentElement(event.target, { className: that.config.uneditableContainerClassname }, false, that.element);
13000+
if (uneditable) {
13001+
that.selection.setAfter(uneditable);
13002+
}
13003+
});
13004+
}
13005+
1291213006
if (!browser.canSelectImagesInContentEditable()) {
1291313007
dom.observe(element, "drop", function(event) {
1291413008
// TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case

0 commit comments

Comments
 (0)