wysihtml-rails 0.5.0.beta5 → 0.5.0.beta6

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