wysihtml5x-rails 0.4.15 → 0.4.16

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 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