wysihtml5x-rails 0.4.15 → 0.4.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba024a9b86e38263a64b95fde68d97da99bb70af
4
- data.tar.gz: 51a1b1328c88a9b8d764166cdcad14f676f498b5
3
+ metadata.gz: 09f662c19d4439847f1ce68e8e8b2426d910e89e
4
+ data.tar.gz: d20ca36a6b4d55f761c8d349d8ed0d850bdfd627
5
5
  SHA512:
6
- metadata.gz: 90a924710a8ebbbd0181886aaf43c42dd4799197d6ebd73c3e870be8c54a3fb515adfa1f9e5c0b1f08a71f85bcf14e43d9a0c91307ac6cb905b9ca8473d449bd
7
- data.tar.gz: 0a8bd1c1d27715c7241ca089ab9b30d3624ec343ebbbdf9f231f4df5ff474f6aac4966df5c06145d67552cbdc4e5b71cf1e91953a972c8c1cfc368edb9636f73
6
+ metadata.gz: 545f630a0749c55b84e7235d8183b63a8852099f869f6db594889fe48e2fb4f0fbea9b8b3df7811fcc0f9e6bd6c4ff1f8f6a114059acde363ae86bb74b77300f
7
+ data.tar.gz: 78ff06fa47c2b931ae39e4facff9bf949d8a5a1d2faa31462955b77b3def8a0422e10e85316db9a47271deefd0e4a2dc4e30919abff7bb427d87da616710c672
@@ -1,5 +1,5 @@
1
1
  module Wysihtml5x
2
2
  module Rails
3
- VERSION = "0.4.15"
3
+ VERSION = "0.4.16"
4
4
  end
5
5
  end
@@ -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.15
28
+ * @license wysihtml5x v0.4.16
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.15",
39
+ version: "0.4.16",
40
40
 
41
41
  // namespaces
42
42
  commands: {},
@@ -5369,9 +5369,36 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
5369
5369
  }
5370
5370
 
5371
5371
  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
+ }
5373
5384
 
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
+ }
5374
5399
 
5400
+ return wysihtml5.dom.domNode(lastChild).lastLeafNode(options);
5401
+ }
5375
5402
 
5376
5403
  };
5377
5404
  };
@@ -5494,8 +5521,13 @@ wysihtml5.dom.getParentElement = (function() {
5494
5521
 
5495
5522
  levels = levels || 50; // Go max 50 nodes upwards from current node
5496
5523
 
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
+
5497
5529
  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)) &&
5499
5531
  (!findByStyle || _hasStyle(node, matchingSet.cssStyle, matchingSet.styleRegExp)) &&
5500
5532
  (!findByClass || _hasClassName(node, matchingSet.className, matchingSet.classRegExp))
5501
5533
  ) {
@@ -8922,15 +8954,25 @@ wysihtml5.quirks.ensureProperClearing = (function() {
8922
8954
  return false;
8923
8955
  },
8924
8956
 
8957
+ // deletes selection contents making sure uneditables/unselectables are not partially deleted
8925
8958
  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
+ }
8929
8969
  }
8930
- this.setSelection(ranges[0]);
8970
+ range.deleteContents();
8971
+ this.setSelection(range);
8931
8972
  },
8932
8973
 
8933
8974
  getPreviousNode: function(node, ignoreEmpty) {
8975
+ var displayStyle;
8934
8976
  if (!node) {
8935
8977
  var selection = this.getSelection();
8936
8978
  node = selection.anchorNode;
@@ -8951,12 +8993,19 @@ wysihtml5.quirks.ensureProperClearing = (function() {
8951
8993
  // do not count comments and other node types
8952
8994
  ret = this.getPreviousNode(ret, ignoreEmpty);
8953
8995
  } 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
8955
8997
  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) {
8957
8999
  // 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
+ }
8960
9009
  } else if (!ret && node !== this.contain) {
8961
9010
  parent = node.parentNode;
8962
9011
  if (parent !== this.contain) {
@@ -9008,12 +9057,14 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9008
9057
  range = this.getRange(),
9009
9058
  startNode = range.startContainer;
9010
9059
 
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
+ }
9017
9068
  }
9018
9069
  },
9019
9070
 
@@ -9021,9 +9072,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9021
9072
  var selection = this.getSelection(),
9022
9073
  node = selection.anchorNode,
9023
9074
  offset = selection.anchorOffset;
9024
- if (ofNode) {
9075
+ if (ofNode && node) {
9025
9076
  return (offset === 0 && (node.nodeName && node.nodeName === ofNode.toUpperCase() || wysihtml5.dom.getParentElement(node.parentNode, { nodeName: ofNode }, 1)));
9026
- } else {
9077
+ } else if (node) {
9027
9078
  return (offset === 0 && !this.getPreviousNode(node, true));
9028
9079
  }
9029
9080
  },
@@ -9031,17 +9082,39 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9031
9082
  caretIsBeforeUneditable: function() {
9032
9083
  var selection = this.getSelection(),
9033
9084
  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
+ }
9043
9111
  }
9044
9112
  }
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
+
9045
9118
  }
9046
9119
  }
9047
9120
  return false;
@@ -9495,6 +9568,10 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9495
9568
  return this.getSelection().toHtml();
9496
9569
  },
9497
9570
 
9571
+ getPlainText: function () {
9572
+ return this.getSelection().toString();
9573
+ },
9574
+
9498
9575
  isEndToEndInNode: function(nodeNames) {
9499
9576
  var range = this.getRange(),
9500
9577
  parentElement = range.commonAncestorContainer,
@@ -11998,11 +12075,11 @@ wysihtml5.views.View = Base.extend(
11998
12075
  },
11999
12076
 
12000
12077
  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) {
12002
12079
  return;
12003
12080
  }
12004
12081
 
12005
- try { this.element.focus(); } catch(e) {}
12082
+ try { if(this.element) { this.element.focus(); } } catch(e) {}
12006
12083
  },
12007
12084
 
12008
12085
  hide: function() {
@@ -12285,18 +12362,17 @@ wysihtml5.views.View = Base.extend(
12285
12362
  if (!supportsAutoLinking || (supportsAutoLinking && supportsDisablingOfAutoLinking)) {
12286
12363
  this.parent.on("newword:composer", function() {
12287
12364
  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;
12291
12368
 
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;
12296
12372
  }
12373
+ }
12297
12374
 
12298
- if (!isInUneditable) dom.autoLink(endContainer.parentNode, [that.config.uneditableContainerClassname]);
12299
- });
12375
+ if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.uneditableContainerClassname]);
12300
12376
  }
12301
12377
  });
12302
12378
 
@@ -12766,7 +12842,13 @@ wysihtml5.views.View = Base.extend(
12766
12842
  // Do a special delete if caret would delete uneditable
12767
12843
  if (beforeUneditable) {
12768
12844
  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);
12770
12852
  }
12771
12853
  }
12772
12854
  } else {
@@ -12877,6 +12959,7 @@ wysihtml5.views.View = Base.extend(
12877
12959
  dom.observe(element, "copy", function(event) {
12878
12960
  if (event.clipboardData) {
12879
12961
  event.clipboardData.setData("text/html", that.config.copyedFromMarking + that.selection.getHtml());
12962
+ event.clipboardData.setData("text/plain", that.selection.getPlainText());
12880
12963
  event.preventDefault();
12881
12964
  }
12882
12965
  that.parent.fire(event.type, event).fire(event.type + ":composer", event);
@@ -12909,6 +12992,17 @@ wysihtml5.views.View = Base.extend(
12909
12992
  });
12910
12993
  }
12911
12994
 
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
+
12912
13006
  if (!browser.canSelectImagesInContentEditable()) {
12913
13007
  dom.observe(element, "drop", function(event) {
12914
13008
  // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
@@ -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.15
28
+ * @license wysihtml5x v0.4.16
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.15",
39
+ version: "0.4.16",
40
40
 
41
41
  // namespaces
42
42
  commands: {},
@@ -5369,9 +5369,36 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
5369
5369
  }
5370
5370
 
5371
5371
  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
+ }
5373
5384
 
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
+ }
5374
5399
 
5400
+ return wysihtml5.dom.domNode(lastChild).lastLeafNode(options);
5401
+ }
5375
5402
 
5376
5403
  };
5377
5404
  };
@@ -5494,8 +5521,13 @@ wysihtml5.dom.getParentElement = (function() {
5494
5521
 
5495
5522
  levels = levels || 50; // Go max 50 nodes upwards from current node
5496
5523
 
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
+
5497
5529
  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)) &&
5499
5531
  (!findByStyle || _hasStyle(node, matchingSet.cssStyle, matchingSet.styleRegExp)) &&
5500
5532
  (!findByClass || _hasClassName(node, matchingSet.className, matchingSet.classRegExp))
5501
5533
  ) {
@@ -8922,15 +8954,25 @@ wysihtml5.quirks.ensureProperClearing = (function() {
8922
8954
  return false;
8923
8955
  },
8924
8956
 
8957
+ // deletes selection contents making sure uneditables/unselectables are not partially deleted
8925
8958
  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
+ }
8929
8969
  }
8930
- this.setSelection(ranges[0]);
8970
+ range.deleteContents();
8971
+ this.setSelection(range);
8931
8972
  },
8932
8973
 
8933
8974
  getPreviousNode: function(node, ignoreEmpty) {
8975
+ var displayStyle;
8934
8976
  if (!node) {
8935
8977
  var selection = this.getSelection();
8936
8978
  node = selection.anchorNode;
@@ -8951,12 +8993,19 @@ wysihtml5.quirks.ensureProperClearing = (function() {
8951
8993
  // do not count comments and other node types
8952
8994
  ret = this.getPreviousNode(ret, ignoreEmpty);
8953
8995
  } 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
8955
8997
  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) {
8957
8999
  // 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
+ }
8960
9009
  } else if (!ret && node !== this.contain) {
8961
9010
  parent = node.parentNode;
8962
9011
  if (parent !== this.contain) {
@@ -9008,12 +9057,14 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9008
9057
  range = this.getRange(),
9009
9058
  startNode = range.startContainer;
9010
9059
 
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
+ }
9017
9068
  }
9018
9069
  },
9019
9070
 
@@ -9021,9 +9072,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9021
9072
  var selection = this.getSelection(),
9022
9073
  node = selection.anchorNode,
9023
9074
  offset = selection.anchorOffset;
9024
- if (ofNode) {
9075
+ if (ofNode && node) {
9025
9076
  return (offset === 0 && (node.nodeName && node.nodeName === ofNode.toUpperCase() || wysihtml5.dom.getParentElement(node.parentNode, { nodeName: ofNode }, 1)));
9026
- } else {
9077
+ } else if (node) {
9027
9078
  return (offset === 0 && !this.getPreviousNode(node, true));
9028
9079
  }
9029
9080
  },
@@ -9031,17 +9082,39 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9031
9082
  caretIsBeforeUneditable: function() {
9032
9083
  var selection = this.getSelection(),
9033
9084
  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
+ }
9043
9111
  }
9044
9112
  }
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
+
9045
9118
  }
9046
9119
  }
9047
9120
  return false;
@@ -9495,6 +9568,10 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9495
9568
  return this.getSelection().toHtml();
9496
9569
  },
9497
9570
 
9571
+ getPlainText: function () {
9572
+ return this.getSelection().toString();
9573
+ },
9574
+
9498
9575
  isEndToEndInNode: function(nodeNames) {
9499
9576
  var range = this.getRange(),
9500
9577
  parentElement = range.commonAncestorContainer,
@@ -11998,11 +12075,11 @@ wysihtml5.views.View = Base.extend(
11998
12075
  },
11999
12076
 
12000
12077
  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) {
12002
12079
  return;
12003
12080
  }
12004
12081
 
12005
- try { this.element.focus(); } catch(e) {}
12082
+ try { if(this.element) { this.element.focus(); } } catch(e) {}
12006
12083
  },
12007
12084
 
12008
12085
  hide: function() {
@@ -12285,18 +12362,17 @@ wysihtml5.views.View = Base.extend(
12285
12362
  if (!supportsAutoLinking || (supportsAutoLinking && supportsDisablingOfAutoLinking)) {
12286
12363
  this.parent.on("newword:composer", function() {
12287
12364
  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;
12291
12368
 
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;
12296
12372
  }
12373
+ }
12297
12374
 
12298
- if (!isInUneditable) dom.autoLink(endContainer.parentNode, [that.config.uneditableContainerClassname]);
12299
- });
12375
+ if (!isInUneditable) dom.autoLink(nodeWithSelection, [that.config.uneditableContainerClassname]);
12300
12376
  }
12301
12377
  });
12302
12378
 
@@ -12766,7 +12842,13 @@ wysihtml5.views.View = Base.extend(
12766
12842
  // Do a special delete if caret would delete uneditable
12767
12843
  if (beforeUneditable) {
12768
12844
  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);
12770
12852
  }
12771
12853
  }
12772
12854
  } else {
@@ -12877,6 +12959,7 @@ wysihtml5.views.View = Base.extend(
12877
12959
  dom.observe(element, "copy", function(event) {
12878
12960
  if (event.clipboardData) {
12879
12961
  event.clipboardData.setData("text/html", that.config.copyedFromMarking + that.selection.getHtml());
12962
+ event.clipboardData.setData("text/plain", that.selection.getPlainText());
12880
12963
  event.preventDefault();
12881
12964
  }
12882
12965
  that.parent.fire(event.type, event).fire(event.type + ":composer", event);
@@ -12909,6 +12992,17 @@ wysihtml5.views.View = Base.extend(
12909
12992
  });
12910
12993
  }
12911
12994
 
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
+
12912
13006
  if (!browser.canSelectImagesInContentEditable()) {
12913
13007
  dom.observe(element, "drop", function(event) {
12914
13008
  // TODO: if I knew how to get dropped elements list from event I could limit it to only IMG element case
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wysihtml5x-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.15
4
+ version: 0.4.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanel Jakobsoo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-17 00:00:00.000000000 Z
11
+ date: 2014-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties