wysihtml-rails 0.5.0.beta5 → 0.5.0.beta6

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: af3af7ccddc81b3f31f45667e826c7ab99d459dc
4
- data.tar.gz: 34c09276bf9f9d8c9cdeac7331e2c949483e2942
3
+ metadata.gz: 169166e826e5cf81830b93add509b5443904cdbb
4
+ data.tar.gz: b3576b0d1ad6ac31988caa0e25e41e6920fb9e02
5
5
  SHA512:
6
- metadata.gz: f16dc55b17a900e8eb124e012ee17122a3b08b3c8982d90663ba1b7cfd0b7abeb49c81baf50916b25c1613a6958ec7c7fe295ab0887dd4d9c475fd61dab32692
7
- data.tar.gz: 488316cf546f166bf1cc877fdc1d14d7e18f17f57848aa1ba131a66e940c2de44bcd570689e9f44386c522710657f4338408d8d1ce6a644444d854458b5f645a
6
+ metadata.gz: 56d0909357b5235b426c651ce8119d86f4a50d431e5e3b9725f511d1afe6663d310f01407f61da69beb4196d0bf196cd82a235ce510ebf7d12826d349fa7f025
7
+ data.tar.gz: 21b029d33b26b2ea0b43078dae432823a3eff5937526f648e430d611dbc389ce3af640573d4dd4d798e7d06e834877d8405997853cea7192c0a9be9725b5345f
@@ -1,5 +1,5 @@
1
1
  module Wysihtml
2
2
  module Rails
3
- VERSION = "0.5.0.beta5"
3
+ VERSION = "0.5.0.beta6"
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license wysihtml5x v0.5.0-beta5
2
+ * @license wysihtml5x v0.5.0-beta6
3
3
  * https://github.com/Edicy/wysihtml5
4
4
  *
5
5
  * Author: Christopher Blum (https://github.com/tiff)
@@ -10,7 +10,7 @@
10
10
  *
11
11
  */
12
12
  var wysihtml5 = {
13
- version: "0.5.0-beta5",
13
+ version: "0.5.0-beta6",
14
14
 
15
15
  // namespaces
16
16
  commands: {},
@@ -6692,7 +6692,7 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6692
6692
  }
6693
6693
 
6694
6694
  function _checkAttribute(attributeName, attributeValue, methodName, nodeName) {
6695
- var method = attributeCheckMethods[methodName],
6695
+ var method = wysihtml5.lang.object(methodName).isFunction() ? methodName : attributeCheckMethods[methodName],
6696
6696
  newAttributeValue;
6697
6697
 
6698
6698
  if (method) {
@@ -9304,6 +9304,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9304
9304
  }
9305
9305
  };
9306
9306
 
9307
+ caretPlaceholder.className = '_wysihtml5-temp-caret-fix';
9307
9308
  caretPlaceholder.style.position = 'absolute';
9308
9309
  caretPlaceholder.style.display = 'block';
9309
9310
  caretPlaceholder.style.minWidth = '1px';
@@ -9339,7 +9340,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9339
9340
  fixWebkitSelection = function() {
9340
9341
  // Webkit fails to add selection if there are no textnodes in that region
9341
9342
  // (like an uneditable container at the end of content).
9342
- if (!sel) {
9343
+ var parent = node.parentNode,
9344
+ lastSibling = parent ? parent.childNodes[parent.childNodes.length - 1] : null;
9345
+ if (!sel || (lastSibling === node && this.win.getComputedStyle(node).display === "block")) {
9343
9346
  if (notVisual) {
9344
9347
  // If setAfter is used as internal between actions, self-removing caretPlaceholder has simpler implementation
9345
9348
  // and remove itself in call stack end instead on user interaction
@@ -9640,45 +9643,69 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9640
9643
  }
9641
9644
  },
9642
9645
 
9643
- caretIsBeforeUneditable: function() {
9644
- var selection = this.getSelection(),
9645
- node = selection.anchorNode,
9646
- offset = selection.anchorOffset,
9647
- childNodes = [],
9648
- range, contentNodes, lastNode;
9649
-
9650
- if (node) {
9651
- if (offset === 0) {
9652
- var prevNode = this.getPreviousNode(node, true),
9653
- prevLeaf = prevNode ? wysihtml5.dom.domNode(prevNode).lastLeafNode((this.unselectableClass) ? {leafClasses: [this.unselectableClass]} : false) : null;
9654
- if (prevLeaf) {
9655
- var uneditables = this.getOwnUneditables();
9656
- for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
9657
- if (prevLeaf === uneditables[i]) {
9658
- return uneditables[i];
9659
- }
9660
- }
9646
+ // Returns object describing node/text before selection
9647
+ // If includePrevLeaves is true returns also previous last leaf child if selection is in the beginning of current node
9648
+ getBeforeSelection: function(includePrevLeaves) {
9649
+ var sel = this.getSelection(),
9650
+ startNode = (sel.isBackwards()) ? sel.focusNode : sel.anchorNode,
9651
+ startOffset = (sel.isBackwards()) ? sel.focusOffset : sel.anchorOffset,
9652
+ rng = this.createRange(), endNode, inTmpCaret;
9653
+
9654
+ // Escape temproray helper nodes if selection in them
9655
+ inTmpCaret = wysihtml5.dom.getParentElement(startNode, { query: '._wysihtml5-temp-caret-fix' }, 1);
9656
+ if (inTmpCaret) {
9657
+ startNode = inTmpCaret.parentNode;
9658
+ startOffset = Array.prototype.indexOf.call(startNode.childNodes, inTmpCaret);
9659
+ }
9660
+
9661
+ if (startNode) {
9662
+ if (startOffset > 0) {
9663
+ if (startNode.nodeType === 3) {
9664
+ rng.setStart(startNode, 0);
9665
+ rng.setEnd(startNode, startOffset);
9666
+ return {
9667
+ type: "text",
9668
+ range: rng,
9669
+ offset : startOffset,
9670
+ node: startNode
9671
+ };
9672
+ } else {
9673
+ rng.setStartBefore(startNode.childNodes[0]);
9674
+ endNode = startNode.childNodes[startOffset - 1];
9675
+ rng.setEndAfter(endNode);
9676
+ return {
9677
+ type: "element",
9678
+ range: rng,
9679
+ offset : startOffset,
9680
+ node: endNode
9681
+ };
9661
9682
  }
9662
9683
  } else {
9663
- range = selection.getRangeAt(0);
9664
- range.setStart(range.startContainer, range.startOffset - 1);
9665
- // TODO: make getting children on range a separate funtion
9666
- if (range) {
9667
- contentNodes = range.getNodes([1,3]);
9668
- for (var n = 0, max = contentNodes.length; n < max; n++) {
9669
- if (contentNodes[n].parentNode && contentNodes[n].parentNode === node) {
9670
- childNodes.push(contentNodes[n]);
9671
- }
9684
+ rng.setStartAndEnd(startNode, 0);
9685
+
9686
+ if (includePrevLeaves) {
9687
+ var prevNode = this.getPreviousNode(startNode, true),
9688
+ prevLeaf = prevNode ? wysihtml5.dom.domNode(prevNode).lastLeafNode() : null;
9689
+
9690
+ if (prevLeaf) {
9691
+ return {
9692
+ type: "leafnode",
9693
+ range: rng,
9694
+ offset : startOffset,
9695
+ node: prevLeaf
9696
+ };
9672
9697
  }
9673
9698
  }
9674
- lastNode = childNodes.length > 0 ? childNodes[childNodes.length -1] : null;
9675
- if (lastNode && lastNode.nodeType === 1 && wysihtml5.dom.hasClass(lastNode, this.unselectableClass)) {
9676
- return lastNode;
9677
- }
9678
9699
 
9700
+ return {
9701
+ type: "none",
9702
+ range: rng,
9703
+ offset : startOffset,
9704
+ node: startNode
9705
+ };
9679
9706
  }
9680
9707
  }
9681
- return false;
9708
+ return null;
9682
9709
  },
9683
9710
 
9684
9711
  // TODO: Figure out a method from following 2 that would work universally
@@ -9758,7 +9785,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9758
9785
  }
9759
9786
  this.setSelection(newRange);
9760
9787
  for (var i = caretPlaceholder.length; i--;) {
9761
- caretPlaceholder[i].parentNode.removeChild(caretPlaceholder[i]);
9788
+ caretPlaceholder[i].parentNode.removeChild(caretPlaceholder[i]);
9762
9789
  }
9763
9790
 
9764
9791
  } else {
@@ -12414,7 +12441,7 @@ wysihtml5.Commands = Base.extend(
12414
12441
  for (row = 0; row < value.rows; row ++) {
12415
12442
  html += '<tr>';
12416
12443
  for (col = 0; col < value.cols; col ++) {
12417
- html += "<td>&nbsp;</td>";
12444
+ html += "<td></td>";
12418
12445
  }
12419
12446
  html += '</tr>';
12420
12447
  }
@@ -13670,59 +13697,98 @@ wysihtml5.views.View = Base.extend(
13670
13697
  }
13671
13698
  };
13672
13699
 
13700
+ // Override for giving user ability to delete last line break in table cell
13701
+ var fixLastBrDeletionInTable = function(composer, force) {
13702
+ if (composer.selection.caretIsLastInSelection()) {
13703
+ var sel = composer.selection.getSelection(),
13704
+ aNode = sel.anchorNode;
13705
+ if (aNode && aNode.nodeType === 1 && (wysihtml5.dom.getParentElement(aNode, {query: 'td, th'}, false, composer.element) || force)) {
13706
+ var nextNode = aNode.childNodes[sel.anchorOffset];
13707
+ if (nextNode && nextNode.nodeType === 1 & nextNode.nodeName === "BR") {
13708
+ nextNode.parentNode.removeChild(nextNode);
13709
+ return true;
13710
+ }
13711
+ }
13712
+ }
13713
+ return false;
13714
+ };
13715
+
13716
+ // If found an uneditable before caret then notify it before deletion
13717
+ var handleUneditableDeletion = function(composer) {
13718
+ var before = composer.selection.getBeforeSelection(true);
13719
+ if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.uneditableContainerClassname)) {
13720
+ if (fixLastBrDeletionInTable(composer, true)) {
13721
+ return true;
13722
+ }
13723
+ try {
13724
+ var ev = new CustomEvent("wysihtml5:uneditable:delete");
13725
+ before.node.dispatchEvent(ev);
13726
+ } catch (err) {}
13727
+ before.node.parentNode.removeChild(before.node);
13728
+ return true;
13729
+ }
13730
+ return false;
13731
+ };
13732
+
13733
+ // Deletion with caret in the beginning of headings needs special attention
13734
+ // Heading does not concate text to previous block node correctly (browsers do unexpected miracles here especially webkit)
13735
+ var fixDeleteInTheBeginnigOfHeading = function(composer) {
13736
+ var selection = composer.selection;
13737
+
13738
+ if (selection.caretIsFirstInSelection() &&
13739
+ selection.getPreviousNode() &&
13740
+ selection.getPreviousNode().nodeName &&
13741
+ (/^H\d$/gi).test(selection.getPreviousNode().nodeName)
13742
+ ) {
13743
+ var prevNode = selection.getPreviousNode();
13744
+ if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
13745
+ // If heading is empty remove the heading node
13746
+ prevNode.parentNode.removeChild(prevNode);
13747
+ return true;
13748
+ } else {
13749
+ if (prevNode.lastChild) {
13750
+ var selNode = prevNode.lastChild,
13751
+ curNode = wysihtml5.dom.getParentElement(selection.getSelectedNode(), { query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote" }, false, composer.element);
13752
+ if (prevNode) {
13753
+ if (curNode) {
13754
+ while (curNode.firstChild) {
13755
+ prevNode.appendChild(curNode.firstChild);
13756
+ }
13757
+ selection.setAfter(selNode);
13758
+ return true;
13759
+ } else if (selection.getSelectedNode().nodeType === 3) {
13760
+ prevNode.appendChild(selection.getSelectedNode());
13761
+ selection.setAfter(selNode);
13762
+ return true;
13763
+ }
13764
+ }
13765
+ }
13766
+ }
13767
+ }
13768
+ return false;
13769
+ };
13770
+
13673
13771
  var handleDeleteKeyPress = function(event, composer) {
13674
13772
  var selection = composer.selection,
13675
13773
  element = composer.element;
13676
13774
 
13677
13775
  if (selection.isCollapsed()) {
13678
13776
  if (selection.caretIsInTheBeginnig('li')) {
13777
+ // delete in the beginnig of LI will outdent not delete
13679
13778
  event.preventDefault();
13680
13779
  composer.commands.exec('outdentList');
13681
- } else if (selection.caretIsInTheBeginnig()) {
13682
- event.preventDefault();
13683
13780
  } else {
13684
- if (selection.caretIsFirstInSelection() &&
13685
- selection.getPreviousNode() &&
13686
- selection.getPreviousNode().nodeName &&
13687
- (/^H\d$/gi).test(selection.getPreviousNode().nodeName)
13688
- ) {
13689
- var prevNode = selection.getPreviousNode();
13690
- if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
13691
- // heading is empty
13692
- event.preventDefault();
13693
- prevNode.parentNode.removeChild(prevNode);
13694
- } else {
13695
- if (prevNode.lastChild) {
13696
- var selNode = prevNode.lastChild,
13697
- curNode = wysihtml5.dom.getParentElement(selection.getSelectedNode(), { query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote" }, false, composer.element);
13698
- if (prevNode) {
13699
- if (curNode) {
13700
- event.preventDefault();
13701
- while (curNode.firstChild) {
13702
- prevNode.appendChild(curNode.firstChild);
13703
- }
13704
- selection.setAfter(selNode);
13705
- } else if (selection.getSelectedNode().nodeType === 3) {
13706
- event.preventDefault();
13707
- prevNode.appendChild(selection.getSelectedNode());
13708
- selection.setAfter(selNode);
13709
- }
13710
- }
13711
- }
13712
- }
13781
+ if (fixDeleteInTheBeginnigOfHeading(composer)) {
13782
+ event.preventDefault();
13783
+ return;
13713
13784
  }
13714
-
13715
- var beforeUneditable = selection.caretIsBeforeUneditable();
13716
- // Do a special delete if caret would delete uneditable
13717
- if (beforeUneditable) {
13785
+ if (fixLastBrDeletionInTable(composer)) {
13718
13786
  event.preventDefault();
13719
- // If customevents present notify element of being deleted
13720
- // TODO: Investigate if browser support can be extended
13721
- try {
13722
- var ev = new CustomEvent("wysihtml5:uneditable:delete");
13723
- beforeUneditable.dispatchEvent(ev);
13724
- } catch (err) {}
13725
- beforeUneditable.parentNode.removeChild(beforeUneditable);
13787
+ return;
13788
+ }
13789
+ if (handleUneditableDeletion(composer)) {
13790
+ event.preventDefault();
13791
+ return;
13726
13792
  }
13727
13793
  }
13728
13794
  } else {
@@ -14089,6 +14155,61 @@ wysihtml5.views.View = Base.extend(
14089
14155
  }
14090
14156
  });
14091
14157
  })(wysihtml5);
14158
+ ;(function(wysihtml5) {
14159
+
14160
+ wysihtml5.views.SourceView = Base.extend(
14161
+ /** @scope wysihtml5.views.SourceView.prototype */ {
14162
+
14163
+ constructor: function(editor, composer) {
14164
+ this.editor = editor;
14165
+ this.composer = composer;
14166
+
14167
+ this._observe();
14168
+ },
14169
+
14170
+ switchToTextarea: function(shouldParseHtml) {
14171
+ var composerStyles = this.composer.win.getComputedStyle(this.composer.element),
14172
+ width = parseFloat(composerStyles.width),
14173
+ height = Math.max(parseFloat(composerStyles.height), 100);
14174
+
14175
+ if (!this.textarea) {
14176
+ this.textarea = this.composer.doc.createElement('textarea');
14177
+ this.textarea.className = "wysihtml5-source-view";
14178
+ }
14179
+ this.textarea.style.width = width + 'px';
14180
+ this.textarea.style.height = height + 'px';
14181
+ this.textarea.value = this.editor.getValue(shouldParseHtml, true);
14182
+ this.composer.element.parentNode.insertBefore(this.textarea, this.composer.element);
14183
+ this.editor.currentView = "source";
14184
+ this.composer.element.style.display = 'none';
14185
+ },
14186
+
14187
+ switchToComposer: function(shouldParseHtml) {
14188
+ var textareaValue = this.textarea.value;
14189
+ if (textareaValue) {
14190
+ this.composer.setValue(textareaValue, shouldParseHtml);
14191
+ } else {
14192
+ this.composer.clear();
14193
+ this.editor.fire("set_placeholder");
14194
+ }
14195
+ this.textarea.parentNode.removeChild(this.textarea);
14196
+ this.editor.currentView = this.composer;
14197
+ this.composer.element.style.display = '';
14198
+ },
14199
+
14200
+ _observe: function() {
14201
+ this.editor.on("change_view", function(view) {
14202
+ if (view === "composer") {
14203
+ this.switchToComposer(true);
14204
+ } else if (view === "textarea") {
14205
+ this.switchToTextarea(true);
14206
+ }
14207
+ }.bind(this));
14208
+ }
14209
+
14210
+ });
14211
+
14212
+ })(wysihtml5);
14092
14213
  ;wysihtml5.views.Textarea = wysihtml5.views.View.extend(
14093
14214
  /** @scope wysihtml5.views.Textarea.prototype */ {
14094
14215
  name: "textarea",
@@ -14281,7 +14402,9 @@ wysihtml5.views.View = Base.extend(
14281
14402
 
14282
14403
  handleBeforeLoad: function() {
14283
14404
  if (!this.config.noTextarea) {
14284
- this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
14405
+ this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
14406
+ } else {
14407
+ this.sourceView = new wysihtml5.views.SourceView(this, this.composer);
14285
14408
  }
14286
14409
  if (this.config.toolbar) {
14287
14410
  this.toolbar = new wysihtml5.toolbar.Toolbar(this, this.config.toolbar, this.config.showToolbarAfterInit);
@@ -14847,12 +14970,10 @@ wysihtml5.views.View = Base.extend(
14847
14970
  execAction: function(action) {
14848
14971
  var editor = this.editor;
14849
14972
  if (action === "change_view") {
14850
- if (editor.textarea) {
14851
- if (editor.currentView === editor.textarea) {
14852
- editor.fire("change_view", "composer");
14853
- } else {
14854
- editor.fire("change_view", "textarea");
14855
- }
14973
+ if (editor.currentView === editor.textarea || editor.currentView === "source") {
14974
+ editor.fire("change_view", "composer");
14975
+ } else {
14976
+ editor.fire("change_view", "textarea");
14856
14977
  }
14857
14978
  }
14858
14979
  if (action == "showSource") {
@@ -14917,17 +15038,15 @@ wysihtml5.views.View = Base.extend(
14917
15038
 
14918
15039
  editor.on("change_view", function(currentView) {
14919
15040
  // Set timeout needed in order to let the blur event fire first
14920
- if (editor.textarea) {
14921
- setTimeout(function() {
14922
- that.commandsDisabled = (currentView !== "composer");
14923
- that._updateLinkStates();
14924
- if (that.commandsDisabled) {
14925
- dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
14926
- } else {
14927
- dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
14928
- }
14929
- }, 0);
14930
- }
15041
+ setTimeout(function() {
15042
+ that.commandsDisabled = (currentView !== "composer");
15043
+ that._updateLinkStates();
15044
+ if (that.commandsDisabled) {
15045
+ dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
15046
+ } else {
15047
+ dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
15048
+ }
15049
+ }, 0);
14931
15050
  });
14932
15051
  },
14933
15052
 
@@ -15008,7 +15127,7 @@ wysihtml5.views.View = Base.extend(
15008
15127
  action = actionMapping[i];
15009
15128
 
15010
15129
  if (action.name === "change_view") {
15011
- action.state = this.editor.currentView === this.editor.textarea;
15130
+ action.state = this.editor.currentView === this.editor.textarea || this.editor.currentView === "source";
15012
15131
  if (action.state) {
15013
15132
  dom.addClass(action.link, CLASS_NAME_ACTION_ACTIVE);
15014
15133
  } else {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license wysihtml5x v0.5.0-beta5
2
+ * @license wysihtml5x v0.5.0-beta6
3
3
  * https://github.com/Edicy/wysihtml5
4
4
  *
5
5
  * Author: Christopher Blum (https://github.com/tiff)
@@ -10,7 +10,7 @@
10
10
  *
11
11
  */
12
12
  var wysihtml5 = {
13
- version: "0.5.0-beta5",
13
+ version: "0.5.0-beta6",
14
14
 
15
15
  // namespaces
16
16
  commands: {},
@@ -6692,7 +6692,7 @@ wysihtml5.dom.parse = function(elementOrHtml_current, config_current) {
6692
6692
  }
6693
6693
 
6694
6694
  function _checkAttribute(attributeName, attributeValue, methodName, nodeName) {
6695
- var method = attributeCheckMethods[methodName],
6695
+ var method = wysihtml5.lang.object(methodName).isFunction() ? methodName : attributeCheckMethods[methodName],
6696
6696
  newAttributeValue;
6697
6697
 
6698
6698
  if (method) {
@@ -9304,6 +9304,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9304
9304
  }
9305
9305
  };
9306
9306
 
9307
+ caretPlaceholder.className = '_wysihtml5-temp-caret-fix';
9307
9308
  caretPlaceholder.style.position = 'absolute';
9308
9309
  caretPlaceholder.style.display = 'block';
9309
9310
  caretPlaceholder.style.minWidth = '1px';
@@ -9339,7 +9340,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9339
9340
  fixWebkitSelection = function() {
9340
9341
  // Webkit fails to add selection if there are no textnodes in that region
9341
9342
  // (like an uneditable container at the end of content).
9342
- if (!sel) {
9343
+ var parent = node.parentNode,
9344
+ lastSibling = parent ? parent.childNodes[parent.childNodes.length - 1] : null;
9345
+ if (!sel || (lastSibling === node && this.win.getComputedStyle(node).display === "block")) {
9343
9346
  if (notVisual) {
9344
9347
  // If setAfter is used as internal between actions, self-removing caretPlaceholder has simpler implementation
9345
9348
  // and remove itself in call stack end instead on user interaction
@@ -9640,45 +9643,69 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9640
9643
  }
9641
9644
  },
9642
9645
 
9643
- caretIsBeforeUneditable: function() {
9644
- var selection = this.getSelection(),
9645
- node = selection.anchorNode,
9646
- offset = selection.anchorOffset,
9647
- childNodes = [],
9648
- range, contentNodes, lastNode;
9649
-
9650
- if (node) {
9651
- if (offset === 0) {
9652
- var prevNode = this.getPreviousNode(node, true),
9653
- prevLeaf = prevNode ? wysihtml5.dom.domNode(prevNode).lastLeafNode((this.unselectableClass) ? {leafClasses: [this.unselectableClass]} : false) : null;
9654
- if (prevLeaf) {
9655
- var uneditables = this.getOwnUneditables();
9656
- for (var i = 0, maxi = uneditables.length; i < maxi; i++) {
9657
- if (prevLeaf === uneditables[i]) {
9658
- return uneditables[i];
9659
- }
9660
- }
9646
+ // Returns object describing node/text before selection
9647
+ // If includePrevLeaves is true returns also previous last leaf child if selection is in the beginning of current node
9648
+ getBeforeSelection: function(includePrevLeaves) {
9649
+ var sel = this.getSelection(),
9650
+ startNode = (sel.isBackwards()) ? sel.focusNode : sel.anchorNode,
9651
+ startOffset = (sel.isBackwards()) ? sel.focusOffset : sel.anchorOffset,
9652
+ rng = this.createRange(), endNode, inTmpCaret;
9653
+
9654
+ // Escape temproray helper nodes if selection in them
9655
+ inTmpCaret = wysihtml5.dom.getParentElement(startNode, { query: '._wysihtml5-temp-caret-fix' }, 1);
9656
+ if (inTmpCaret) {
9657
+ startNode = inTmpCaret.parentNode;
9658
+ startOffset = Array.prototype.indexOf.call(startNode.childNodes, inTmpCaret);
9659
+ }
9660
+
9661
+ if (startNode) {
9662
+ if (startOffset > 0) {
9663
+ if (startNode.nodeType === 3) {
9664
+ rng.setStart(startNode, 0);
9665
+ rng.setEnd(startNode, startOffset);
9666
+ return {
9667
+ type: "text",
9668
+ range: rng,
9669
+ offset : startOffset,
9670
+ node: startNode
9671
+ };
9672
+ } else {
9673
+ rng.setStartBefore(startNode.childNodes[0]);
9674
+ endNode = startNode.childNodes[startOffset - 1];
9675
+ rng.setEndAfter(endNode);
9676
+ return {
9677
+ type: "element",
9678
+ range: rng,
9679
+ offset : startOffset,
9680
+ node: endNode
9681
+ };
9661
9682
  }
9662
9683
  } else {
9663
- range = selection.getRangeAt(0);
9664
- range.setStart(range.startContainer, range.startOffset - 1);
9665
- // TODO: make getting children on range a separate funtion
9666
- if (range) {
9667
- contentNodes = range.getNodes([1,3]);
9668
- for (var n = 0, max = contentNodes.length; n < max; n++) {
9669
- if (contentNodes[n].parentNode && contentNodes[n].parentNode === node) {
9670
- childNodes.push(contentNodes[n]);
9671
- }
9684
+ rng.setStartAndEnd(startNode, 0);
9685
+
9686
+ if (includePrevLeaves) {
9687
+ var prevNode = this.getPreviousNode(startNode, true),
9688
+ prevLeaf = prevNode ? wysihtml5.dom.domNode(prevNode).lastLeafNode() : null;
9689
+
9690
+ if (prevLeaf) {
9691
+ return {
9692
+ type: "leafnode",
9693
+ range: rng,
9694
+ offset : startOffset,
9695
+ node: prevLeaf
9696
+ };
9672
9697
  }
9673
9698
  }
9674
- lastNode = childNodes.length > 0 ? childNodes[childNodes.length -1] : null;
9675
- if (lastNode && lastNode.nodeType === 1 && wysihtml5.dom.hasClass(lastNode, this.unselectableClass)) {
9676
- return lastNode;
9677
- }
9678
9699
 
9700
+ return {
9701
+ type: "none",
9702
+ range: rng,
9703
+ offset : startOffset,
9704
+ node: startNode
9705
+ };
9679
9706
  }
9680
9707
  }
9681
- return false;
9708
+ return null;
9682
9709
  },
9683
9710
 
9684
9711
  // TODO: Figure out a method from following 2 that would work universally
@@ -9758,7 +9785,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
9758
9785
  }
9759
9786
  this.setSelection(newRange);
9760
9787
  for (var i = caretPlaceholder.length; i--;) {
9761
- caretPlaceholder[i].parentNode.removeChild(caretPlaceholder[i]);
9788
+ caretPlaceholder[i].parentNode.removeChild(caretPlaceholder[i]);
9762
9789
  }
9763
9790
 
9764
9791
  } else {
@@ -12414,7 +12441,7 @@ wysihtml5.Commands = Base.extend(
12414
12441
  for (row = 0; row < value.rows; row ++) {
12415
12442
  html += '<tr>';
12416
12443
  for (col = 0; col < value.cols; col ++) {
12417
- html += "<td>&nbsp;</td>";
12444
+ html += "<td></td>";
12418
12445
  }
12419
12446
  html += '</tr>';
12420
12447
  }
@@ -13670,59 +13697,98 @@ wysihtml5.views.View = Base.extend(
13670
13697
  }
13671
13698
  };
13672
13699
 
13700
+ // Override for giving user ability to delete last line break in table cell
13701
+ var fixLastBrDeletionInTable = function(composer, force) {
13702
+ if (composer.selection.caretIsLastInSelection()) {
13703
+ var sel = composer.selection.getSelection(),
13704
+ aNode = sel.anchorNode;
13705
+ if (aNode && aNode.nodeType === 1 && (wysihtml5.dom.getParentElement(aNode, {query: 'td, th'}, false, composer.element) || force)) {
13706
+ var nextNode = aNode.childNodes[sel.anchorOffset];
13707
+ if (nextNode && nextNode.nodeType === 1 & nextNode.nodeName === "BR") {
13708
+ nextNode.parentNode.removeChild(nextNode);
13709
+ return true;
13710
+ }
13711
+ }
13712
+ }
13713
+ return false;
13714
+ };
13715
+
13716
+ // If found an uneditable before caret then notify it before deletion
13717
+ var handleUneditableDeletion = function(composer) {
13718
+ var before = composer.selection.getBeforeSelection(true);
13719
+ if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.uneditableContainerClassname)) {
13720
+ if (fixLastBrDeletionInTable(composer, true)) {
13721
+ return true;
13722
+ }
13723
+ try {
13724
+ var ev = new CustomEvent("wysihtml5:uneditable:delete");
13725
+ before.node.dispatchEvent(ev);
13726
+ } catch (err) {}
13727
+ before.node.parentNode.removeChild(before.node);
13728
+ return true;
13729
+ }
13730
+ return false;
13731
+ };
13732
+
13733
+ // Deletion with caret in the beginning of headings needs special attention
13734
+ // Heading does not concate text to previous block node correctly (browsers do unexpected miracles here especially webkit)
13735
+ var fixDeleteInTheBeginnigOfHeading = function(composer) {
13736
+ var selection = composer.selection;
13737
+
13738
+ if (selection.caretIsFirstInSelection() &&
13739
+ selection.getPreviousNode() &&
13740
+ selection.getPreviousNode().nodeName &&
13741
+ (/^H\d$/gi).test(selection.getPreviousNode().nodeName)
13742
+ ) {
13743
+ var prevNode = selection.getPreviousNode();
13744
+ if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
13745
+ // If heading is empty remove the heading node
13746
+ prevNode.parentNode.removeChild(prevNode);
13747
+ return true;
13748
+ } else {
13749
+ if (prevNode.lastChild) {
13750
+ var selNode = prevNode.lastChild,
13751
+ curNode = wysihtml5.dom.getParentElement(selection.getSelectedNode(), { query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote" }, false, composer.element);
13752
+ if (prevNode) {
13753
+ if (curNode) {
13754
+ while (curNode.firstChild) {
13755
+ prevNode.appendChild(curNode.firstChild);
13756
+ }
13757
+ selection.setAfter(selNode);
13758
+ return true;
13759
+ } else if (selection.getSelectedNode().nodeType === 3) {
13760
+ prevNode.appendChild(selection.getSelectedNode());
13761
+ selection.setAfter(selNode);
13762
+ return true;
13763
+ }
13764
+ }
13765
+ }
13766
+ }
13767
+ }
13768
+ return false;
13769
+ };
13770
+
13673
13771
  var handleDeleteKeyPress = function(event, composer) {
13674
13772
  var selection = composer.selection,
13675
13773
  element = composer.element;
13676
13774
 
13677
13775
  if (selection.isCollapsed()) {
13678
13776
  if (selection.caretIsInTheBeginnig('li')) {
13777
+ // delete in the beginnig of LI will outdent not delete
13679
13778
  event.preventDefault();
13680
13779
  composer.commands.exec('outdentList');
13681
- } else if (selection.caretIsInTheBeginnig()) {
13682
- event.preventDefault();
13683
13780
  } else {
13684
- if (selection.caretIsFirstInSelection() &&
13685
- selection.getPreviousNode() &&
13686
- selection.getPreviousNode().nodeName &&
13687
- (/^H\d$/gi).test(selection.getPreviousNode().nodeName)
13688
- ) {
13689
- var prevNode = selection.getPreviousNode();
13690
- if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
13691
- // heading is empty
13692
- event.preventDefault();
13693
- prevNode.parentNode.removeChild(prevNode);
13694
- } else {
13695
- if (prevNode.lastChild) {
13696
- var selNode = prevNode.lastChild,
13697
- curNode = wysihtml5.dom.getParentElement(selection.getSelectedNode(), { query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote" }, false, composer.element);
13698
- if (prevNode) {
13699
- if (curNode) {
13700
- event.preventDefault();
13701
- while (curNode.firstChild) {
13702
- prevNode.appendChild(curNode.firstChild);
13703
- }
13704
- selection.setAfter(selNode);
13705
- } else if (selection.getSelectedNode().nodeType === 3) {
13706
- event.preventDefault();
13707
- prevNode.appendChild(selection.getSelectedNode());
13708
- selection.setAfter(selNode);
13709
- }
13710
- }
13711
- }
13712
- }
13781
+ if (fixDeleteInTheBeginnigOfHeading(composer)) {
13782
+ event.preventDefault();
13783
+ return;
13713
13784
  }
13714
-
13715
- var beforeUneditable = selection.caretIsBeforeUneditable();
13716
- // Do a special delete if caret would delete uneditable
13717
- if (beforeUneditable) {
13785
+ if (fixLastBrDeletionInTable(composer)) {
13718
13786
  event.preventDefault();
13719
- // If customevents present notify element of being deleted
13720
- // TODO: Investigate if browser support can be extended
13721
- try {
13722
- var ev = new CustomEvent("wysihtml5:uneditable:delete");
13723
- beforeUneditable.dispatchEvent(ev);
13724
- } catch (err) {}
13725
- beforeUneditable.parentNode.removeChild(beforeUneditable);
13787
+ return;
13788
+ }
13789
+ if (handleUneditableDeletion(composer)) {
13790
+ event.preventDefault();
13791
+ return;
13726
13792
  }
13727
13793
  }
13728
13794
  } else {
@@ -14089,6 +14155,61 @@ wysihtml5.views.View = Base.extend(
14089
14155
  }
14090
14156
  });
14091
14157
  })(wysihtml5);
14158
+ ;(function(wysihtml5) {
14159
+
14160
+ wysihtml5.views.SourceView = Base.extend(
14161
+ /** @scope wysihtml5.views.SourceView.prototype */ {
14162
+
14163
+ constructor: function(editor, composer) {
14164
+ this.editor = editor;
14165
+ this.composer = composer;
14166
+
14167
+ this._observe();
14168
+ },
14169
+
14170
+ switchToTextarea: function(shouldParseHtml) {
14171
+ var composerStyles = this.composer.win.getComputedStyle(this.composer.element),
14172
+ width = parseFloat(composerStyles.width),
14173
+ height = Math.max(parseFloat(composerStyles.height), 100);
14174
+
14175
+ if (!this.textarea) {
14176
+ this.textarea = this.composer.doc.createElement('textarea');
14177
+ this.textarea.className = "wysihtml5-source-view";
14178
+ }
14179
+ this.textarea.style.width = width + 'px';
14180
+ this.textarea.style.height = height + 'px';
14181
+ this.textarea.value = this.editor.getValue(shouldParseHtml, true);
14182
+ this.composer.element.parentNode.insertBefore(this.textarea, this.composer.element);
14183
+ this.editor.currentView = "source";
14184
+ this.composer.element.style.display = 'none';
14185
+ },
14186
+
14187
+ switchToComposer: function(shouldParseHtml) {
14188
+ var textareaValue = this.textarea.value;
14189
+ if (textareaValue) {
14190
+ this.composer.setValue(textareaValue, shouldParseHtml);
14191
+ } else {
14192
+ this.composer.clear();
14193
+ this.editor.fire("set_placeholder");
14194
+ }
14195
+ this.textarea.parentNode.removeChild(this.textarea);
14196
+ this.editor.currentView = this.composer;
14197
+ this.composer.element.style.display = '';
14198
+ },
14199
+
14200
+ _observe: function() {
14201
+ this.editor.on("change_view", function(view) {
14202
+ if (view === "composer") {
14203
+ this.switchToComposer(true);
14204
+ } else if (view === "textarea") {
14205
+ this.switchToTextarea(true);
14206
+ }
14207
+ }.bind(this));
14208
+ }
14209
+
14210
+ });
14211
+
14212
+ })(wysihtml5);
14092
14213
  ;wysihtml5.views.Textarea = wysihtml5.views.View.extend(
14093
14214
  /** @scope wysihtml5.views.Textarea.prototype */ {
14094
14215
  name: "textarea",
@@ -14281,7 +14402,9 @@ wysihtml5.views.View = Base.extend(
14281
14402
 
14282
14403
  handleBeforeLoad: function() {
14283
14404
  if (!this.config.noTextarea) {
14284
- this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
14405
+ this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
14406
+ } else {
14407
+ this.sourceView = new wysihtml5.views.SourceView(this, this.composer);
14285
14408
  }
14286
14409
  if (this.config.toolbar) {
14287
14410
  this.toolbar = new wysihtml5.toolbar.Toolbar(this, this.config.toolbar, this.config.showToolbarAfterInit);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wysihtml-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.beta5
4
+ version: 0.5.0.beta6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tanel Jakobsoo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-17 00:00:00.000000000 Z
11
+ date: 2015-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties