medium-editor-rails 1.1.3 → 1.2.0

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: 59ed3d1649f4b21ed2ac23f47a5741106dee459d
4
- data.tar.gz: e445c754424ca6d986ec525810233b1f010406d3
3
+ metadata.gz: 2797d94a0b663d77df028d929090e6693d9a0931
4
+ data.tar.gz: bce123c63f3984235f94f8edec1fabf1ebc53b00
5
5
  SHA512:
6
- metadata.gz: ffb12c796fbfb1720486e39b1f3eeea4b784d159ea2c1fe2421db9a474006a88b82899dd1f4d430ca47da200dbf230a21e0e91a2d08e829e3de492a87ff1837d
7
- data.tar.gz: 4632fae0f41e107a21d15ce407e7a3397be860998120abacf4a6762cbb0238574c47e81ffab4773ca155bd46a678ab818b11f006cba0c5f1a9e70d97cb8c152b
6
+ metadata.gz: bff23ff9bc5e9ffd3ad863e6332c078facd03a6e34db99c2566e4177a187467d2655e440f6a15a9a4fbbb7a8e3a0e4deaa8a120aad155a2499af4e2268948600
7
+ data.tar.gz: a27b421862232708df549a2585eec2f20a22a3ec94e75c4b535ca94366b5e3352c7fa1ce39a60d04e83e96045e994241d8ea7ab5697d624a5f3f8e37f280f0da
@@ -1,5 +1,12 @@
1
1
 
2
2
  #### [Current]
3
+ * [e6d136d](../../commit/e6d136d) - __(Ahmet Sezgin Duran)__ Update Medium Editor files
4
+ * [7ee9c60](../../commit/7ee9c60) - __(Ahmet Sezgin Duran)__ Merge tag '1.1.3' into develop
5
+
6
+ 1.1.3
7
+
8
+ #### 1.1.3
9
+ * [8cc5af9](../../commit/8cc5af9) - __(Ahmet Sezgin Duran)__ Bump versions 1.1.3 and 2.1.3
3
10
  * [9a54e41](../../commit/9a54e41) - __(Ahmet Sezgin Duran)__ Update Medium Editor files
4
11
  * [67de6b2](../../commit/67de6b2) - __(Ahmet Sezgin Duran)__ Merge tag '1.1.2' into develop
5
12
 
data/README.md CHANGED
@@ -8,7 +8,7 @@ This gem integrates [Medium Editor](https://github.com/daviferreira/medium-edito
8
8
 
9
9
  ## Version
10
10
 
11
- The latest version of Medium Editor bundled by this gem is [2.1.3](https://github.com/daviferreira/medium-editor/releases)
11
+ The latest version of Medium Editor bundled by this gem is [2.2.0](https://github.com/daviferreira/medium-editor/releases)
12
12
 
13
13
  ## Installation
14
14
 
@@ -1,6 +1,6 @@
1
1
  module MediumEditorRails
2
2
  module Rails
3
- VERSION = '1.1.3'
4
- MEDIUM_EDITOR_VERSION = '2.1.3'
3
+ VERSION = '1.2.0'
4
+ MEDIUM_EDITOR_VERSION = '2.2.0'
5
5
  end
6
6
  end
@@ -32,6 +32,13 @@ if (typeof module === 'object') {
32
32
  // https://github.com/jashkenas/underscore
33
33
  var now = Date.now || function () {
34
34
  return new Date().getTime();
35
+ }, keyCode = {
36
+ BACKSPACE: 8,
37
+ TAB: 9,
38
+ ENTER: 13,
39
+ ESCAPE: 27,
40
+ SPACE: 32,
41
+ DELETE: 46
35
42
  };
36
43
 
37
44
  // https://github.com/jashkenas/underscore
@@ -98,7 +105,7 @@ if (typeof module === 'object') {
98
105
  function findAdjacentTextNodeWithContent(rootNode, targetNode, ownerDocument) {
99
106
  var pastTarget = false,
100
107
  nextNode,
101
- nodeIterator = ownerDocument.createNodeIterator(rootNode, NodeFilter.SHOW_TEXT);
108
+ nodeIterator = ownerDocument.createNodeIterator(rootNode, NodeFilter.SHOW_TEXT, null, false);
102
109
 
103
110
  // Use a native NodeIterator to iterate over all the text nodes that are descendants
104
111
  // of the rootNode. Once past the targetNode, choose the first non-empty text node
@@ -107,7 +114,7 @@ if (typeof module === 'object') {
107
114
  if (nextNode === targetNode) {
108
115
  pastTarget = true;
109
116
  } else if (pastTarget) {
110
- if (nextNode.nodeType === 3 && nextNode.nodeValue && nextNode.nodeValue.length > 0) {
117
+ if (nextNode.nodeType === 3 && nextNode.nodeValue && nextNode.nodeValue.trim().length > 0) {
111
118
  break;
112
119
  }
113
120
  }
@@ -219,7 +226,9 @@ if (typeof module === 'object') {
219
226
  var selection, range, el, fragment, node, lastNode;
220
227
 
221
228
  if (doc.queryCommandSupported('insertHTML')) {
222
- return doc.execCommand('insertHTML', false, html);
229
+ try {
230
+ return doc.execCommand('insertHTML', false, html);
231
+ } catch (ignore) {}
223
232
  }
224
233
 
225
234
  selection = window.getSelection();
@@ -267,6 +276,7 @@ if (typeof module === 'object') {
267
276
  disableAnchorForm: false,
268
277
  disablePlaceholders: false,
269
278
  elementsContainer: false,
279
+ imageDragging: true,
270
280
  standardizeSelectionStart: false,
271
281
  contentWindow: window,
272
282
  ownerDocument: document,
@@ -316,6 +326,7 @@ if (typeof module === 'object') {
316
326
  this.initThrottledMethods()
317
327
  .initElements()
318
328
  .bindSelect()
329
+ .bindDragDrop()
319
330
  .bindPaste()
320
331
  .setPlaceholders()
321
332
  .bindElementActions()
@@ -597,7 +608,7 @@ if (typeof module === 'object') {
597
608
  this.on(this.elements[index], 'keypress', function (e) {
598
609
  var node,
599
610
  tagName;
600
- if (e.which === 32) {
611
+ if (e.which === keyCode.SPACE) {
601
612
  node = getSelectionStart.call(self);
602
613
  tagName = node.tagName.toLowerCase();
603
614
  if (tagName === 'a') {
@@ -614,7 +625,7 @@ if (typeof module === 'object') {
614
625
  if (node && node.getAttribute('data-medium-element') && node.children.length === 0 && !(self.options.disableReturn || node.getAttribute('data-disable-return'))) {
615
626
  self.options.ownerDocument.execCommand('formatBlock', false, 'p');
616
627
  }
617
- if (e.which === 13) {
628
+ if (e.which === keyCode.ENTER) {
618
629
  node = getSelectionStart.call(self);
619
630
  tagName = node.tagName.toLowerCase();
620
631
  editorElement = self.getSelectionElement();
@@ -657,7 +668,7 @@ if (typeof module === 'object') {
657
668
  bindReturn: function (index) {
658
669
  var self = this;
659
670
  this.on(this.elements[index], 'keypress', function (e) {
660
- if (e.which === 13) {
671
+ if (e.which === keyCode.ENTER) {
661
672
  if (self.options.disableReturn || this.getAttribute('data-disable-return')) {
662
673
  e.preventDefault();
663
674
  } else if (self.options.disableDoubleReturn || this.getAttribute('data-disable-double-return')) {
@@ -675,7 +686,7 @@ if (typeof module === 'object') {
675
686
  var self = this;
676
687
  this.on(this.elements[index], 'keydown', function (e) {
677
688
 
678
- if (e.which === 9) {
689
+ if (e.which === keyCode.TAB) {
679
690
  // Override tab only for pre nodes
680
691
  var tag = getSelectionStart.call(self).tagName.toLowerCase();
681
692
  if (tag === 'pre') {
@@ -694,7 +705,7 @@ if (typeof module === 'object') {
694
705
  self.options.ownerDocument.execCommand('indent', e);
695
706
  }
696
707
  }
697
- } else if (e.which === 8 || e.which === 46 || e.which === 13) {
708
+ } else if (e.which === keyCode.BACKSPACE || e.which === keyCode.DELETE || e.which === keyCode.ENTER) {
698
709
 
699
710
  // Bind keys which can create or destroy a block element: backspace, delete, return
700
711
  self.onBlockModifier(e);
@@ -705,26 +716,24 @@ if (typeof module === 'object') {
705
716
  },
706
717
 
707
718
  onBlockModifier: function (e) {
708
-
709
719
  var range, sel, p, node = getSelectionStart.call(this),
710
720
  tagName = node.tagName.toLowerCase(),
711
721
  isEmpty = /^(\s+|<br\/?>)?$/i,
712
722
  isHeader = /h\d/i;
713
723
 
714
- // backspace or return
715
- if ((e.which === 8 || e.which === 13)
724
+ if ((e.which === keyCode.BACKSPACE || e.which === keyCode.ENTER)
716
725
  && node.previousElementSibling
717
726
  // in a header
718
727
  && isHeader.test(tagName)
719
728
  // at the very end of the block
720
729
  && getCaretOffsets(node).left === 0) {
721
- if (e.which === 8 && isEmpty.test(node.previousElementSibling.innerHTML)) {
730
+ if (e.which === keyCode.BACKSPACE && isEmpty.test(node.previousElementSibling.innerHTML)) {
722
731
  // backspacing the begining of a header into an empty previous element will
723
732
  // change the tagName of the current node to prevent one
724
733
  // instead delete previous node and cancel the event.
725
734
  node.previousElementSibling.parentNode.removeChild(node.previousElementSibling);
726
735
  e.preventDefault();
727
- } else if (e.which === 13) {
736
+ } else if (e.which === keyCode.ENTER) {
728
737
  // hitting return in the begining of a header will create empty header elements before the current one
729
738
  // instead, make "<p><br></p>" element, which are what happens if you hit return in an empty paragraph
730
739
  p = this.options.ownerDocument.createElement('p');
@@ -732,9 +741,7 @@ if (typeof module === 'object') {
732
741
  node.previousElementSibling.parentNode.insertBefore(p, node);
733
742
  e.preventDefault();
734
743
  }
735
-
736
- // delete
737
- } else if (e.which === 46
744
+ } else if (e.which === keyCode.DELETE
738
745
  && node.nextElementSibling
739
746
  && node.previousElementSibling
740
747
  // not in a header
@@ -762,7 +769,6 @@ if (typeof module === 'object') {
762
769
 
763
770
  e.preventDefault();
764
771
  }
765
-
766
772
  },
767
773
 
768
774
  buttonTemplate: function (btnType) {
@@ -1026,6 +1032,65 @@ if (typeof module === 'object') {
1026
1032
  return this;
1027
1033
  },
1028
1034
 
1035
+
1036
+ bindDragDrop: function () {
1037
+ var self = this, i, className, onDrag, onDrop, element;
1038
+
1039
+ if (!self.options.imageDragging) {
1040
+ return;
1041
+ }
1042
+
1043
+ className = 'medium-editor-dragover';
1044
+
1045
+ onDrag = function (e) {
1046
+ e.preventDefault();
1047
+ e.dataTransfer.dropEffect = "copy";
1048
+
1049
+ if (e.type === "dragover") {
1050
+ this.classList.add(className);
1051
+ } else {
1052
+ this.classList.remove(className);
1053
+ }
1054
+ };
1055
+
1056
+ onDrop = function (e) {
1057
+ var files;
1058
+ e.preventDefault();
1059
+ e.stopPropagation();
1060
+ files = Array.prototype.slice.call(e.dataTransfer.files, 0);
1061
+ files.some(function (file) {
1062
+ if (file.type.match("image")) {
1063
+ var fileReader, id;
1064
+ fileReader = new FileReader();
1065
+ fileReader.readAsDataURL(file);
1066
+
1067
+ id = 'medium-img-' + (+new Date());
1068
+ insertHTMLCommand(self.options.ownerDocument, '<img class="medium-image-loading" id="' + id + '" />');
1069
+
1070
+ fileReader.onload = function () {
1071
+ var img = document.getElementById(id);
1072
+ if (img) {
1073
+ img.removeAttribute('id');
1074
+ img.removeAttribute('class');
1075
+ img.src = fileReader.result;
1076
+ }
1077
+ };
1078
+ }
1079
+ });
1080
+ this.classList.remove(className);
1081
+ };
1082
+
1083
+ for (i = 0; i < this.elements.length; i += 1) {
1084
+ element = this.elements[i];
1085
+
1086
+
1087
+ this.on(element, 'dragover', onDrag);
1088
+ this.on(element, 'dragleave', onDrag);
1089
+ this.on(element, 'drop', onDrop);
1090
+ }
1091
+ return this;
1092
+ },
1093
+
1029
1094
  stopSelectionUpdates: function () {
1030
1095
  this.preventSelectionUpdates = true;
1031
1096
  },
@@ -1141,15 +1206,7 @@ if (typeof module === 'object') {
1141
1206
  }
1142
1207
  },
1143
1208
 
1144
- findMatchingSelectionParent: function (testElementFunction) {
1145
- var selection = this.options.contentWindow.getSelection(), range, current;
1146
-
1147
- if (selection.rangeCount === 0) {
1148
- return false;
1149
- }
1150
-
1151
- range = selection.getRangeAt(0);
1152
- current = range.commonAncestorContainer;
1209
+ traverseUp: function (current, testElementFunction) {
1153
1210
 
1154
1211
  do {
1155
1212
  if (current.nodeType === 1) {
@@ -1166,6 +1223,21 @@ if (typeof module === 'object') {
1166
1223
  } while (current);
1167
1224
 
1168
1225
  return false;
1226
+
1227
+ },
1228
+
1229
+ findMatchingSelectionParent: function (testElementFunction) {
1230
+ var selection = this.options.contentWindow.getSelection(), range, current;
1231
+
1232
+ if (selection.rangeCount === 0) {
1233
+ return false;
1234
+ }
1235
+
1236
+ range = selection.getRangeAt(0);
1237
+ current = range.commonAncestorContainer;
1238
+
1239
+ return this.traverseUp(current, testElementFunction);
1240
+
1169
1241
  },
1170
1242
 
1171
1243
  getSelectionElement: function () {
@@ -1383,7 +1455,7 @@ if (typeof module === 'object') {
1383
1455
  getSelectedParentElement: function () {
1384
1456
  var selectedParentElement = null,
1385
1457
  range = this.selectionRange;
1386
- if (this.rangeSelectsSingleNode(range)) {
1458
+ if (this.rangeSelectsSingleNode(range) && range.startContainer.childNodes[range.startOffset].nodeType !== 3) {
1387
1459
  selectedParentElement = range.startContainer.childNodes[range.startOffset];
1388
1460
  } else if (range.startContainer.nodeType === 3) {
1389
1461
  selectedParentElement = range.startContainer.parentNode;
@@ -1542,7 +1614,7 @@ if (typeof module === 'object') {
1542
1614
  var button = null,
1543
1615
  target;
1544
1616
 
1545
- if (e.keyCode === 13) {
1617
+ if (e.keyCode === keyCode.ENTER) {
1546
1618
  e.preventDefault();
1547
1619
  if (self.options.anchorTarget && self.anchorTarget.checked) {
1548
1620
  target = "_blank";
@@ -1555,7 +1627,7 @@ if (typeof module === 'object') {
1555
1627
  }
1556
1628
 
1557
1629
  self.createLink(this, target, button);
1558
- } else if (e.keyCode === 27) {
1630
+ } else if (e.keyCode === keyCode.ESCAPE) {
1559
1631
  e.preventDefault();
1560
1632
  self.showToolbarActions();
1561
1633
  restoreSelection.call(self, self.savedSelection);
@@ -1625,7 +1697,7 @@ if (typeof module === 'object') {
1625
1697
  halfOffsetWidth,
1626
1698
  defaultLeft;
1627
1699
 
1628
- self.anchorPreview.querySelector('i').textContent = anchorEl.href;
1700
+ self.anchorPreview.querySelector('i').textContent = anchorEl.attributes.href.value;
1629
1701
  halfOffsetWidth = self.anchorPreview.offsetWidth / 2;
1630
1702
  defaultLeft = self.options.diffLeft - halfOffsetWidth;
1631
1703
 
@@ -1724,7 +1796,7 @@ if (typeof module === 'object') {
1724
1796
  // We may actually be displaying the anchor preview, which should be controlled by options.delay
1725
1797
  this.delay(function () {
1726
1798
  if (self.activeAnchor) {
1727
- self.showAnchorForm(self.activeAnchor.href);
1799
+ self.showAnchorForm(self.activeAnchor.attributes.href.value);
1728
1800
  }
1729
1801
  self.keepToolbarAlive = false;
1730
1802
  });
@@ -1822,7 +1894,23 @@ if (typeof module === 'object') {
1822
1894
  createLink: function (input, target, buttonClass) {
1823
1895
  var i, event;
1824
1896
 
1825
- if (input.value.trim().length === 0) {
1897
+ this.createLinkInternal(input.value, target, buttonClass);
1898
+
1899
+ if (this.options.targetBlank || target === "_blank" || buttonClass) {
1900
+ event = this.options.ownerDocument.createEvent("HTMLEvents");
1901
+ event.initEvent("input", true, true, this.options.contentWindow);
1902
+ for (i = 0; i < this.elements.length; i += 1) {
1903
+ this.elements[i].dispatchEvent(event);
1904
+ }
1905
+ }
1906
+
1907
+ this.checkSelection();
1908
+ this.showToolbarActions();
1909
+ input.value = '';
1910
+ },
1911
+
1912
+ createLinkInternal: function (url, target, buttonClass) {
1913
+ if (!url || url.trim().length === 0) {
1826
1914
  this.hideToolbarActions();
1827
1915
  return;
1828
1916
  }
@@ -1830,10 +1918,10 @@ if (typeof module === 'object') {
1830
1918
  restoreSelection.call(this, this.savedSelection);
1831
1919
 
1832
1920
  if (this.options.checkLinkFormat) {
1833
- input.value = this.checkLinkFormat(input.value);
1921
+ url = this.checkLinkFormat(url);
1834
1922
  }
1835
1923
 
1836
- this.options.ownerDocument.execCommand('createLink', false, input.value);
1924
+ this.options.ownerDocument.execCommand('createLink', false, url);
1837
1925
 
1838
1926
  if (this.options.targetBlank || target === "_blank") {
1839
1927
  this.setTargetBlank();
@@ -1842,18 +1930,6 @@ if (typeof module === 'object') {
1842
1930
  if (buttonClass) {
1843
1931
  this.setButtonClass(buttonClass);
1844
1932
  }
1845
-
1846
- if (this.options.targetBlank || target === "_blank" || buttonClass) {
1847
- event = this.options.ownerDocument.createEvent("HTMLEvents");
1848
- event.initEvent("input", true, true, this.options.contentWindow);
1849
- for (i = 0; i < this.elements.length; i += 1) {
1850
- this.elements[i].dispatchEvent(event);
1851
- }
1852
- }
1853
-
1854
- this.checkSelection();
1855
- this.showToolbarActions();
1856
- input.value = '';
1857
1933
  },
1858
1934
 
1859
1935
  positionToolbarIfShown: function () {
@@ -1951,11 +2027,7 @@ if (typeof module === 'object') {
1951
2027
  paragraphs = e.clipboardData.getData(dataFormatPlain).split(/[\r\n]/g);
1952
2028
  for (p = 0; p < paragraphs.length; p += 1) {
1953
2029
  if (paragraphs[p] !== '') {
1954
- if (navigator.userAgent.match(/firefox/i) && p === 0) {
1955
- html += self.htmlEntities(paragraphs[p]);
1956
- } else {
1957
- html += '<p>' + self.htmlEntities(paragraphs[p]) + '</p>';
1958
- }
2030
+ html += '<p>' + self.htmlEntities(paragraphs[p]) + '</p>';
1959
2031
  }
1960
2032
  }
1961
2033
  insertHTMLCommand(self.options.ownerDocument, html);
@@ -2092,7 +2164,7 @@ if (typeof module === 'object') {
2092
2164
  }
2093
2165
 
2094
2166
  }
2095
- this.options.ownerDocument.execCommand('insertHTML', false, fragmentBody.innerHTML.replace(/&nbsp;/g, ' '));
2167
+ insertHTMLCommand(this.options.ownerDocument, fragmentBody.innerHTML.replace(/&nbsp;/g, ' '));
2096
2168
  },
2097
2169
  isCommonBlock: function (el) {
2098
2170
  return (el && (el.tagName.toLowerCase() === 'p' || el.tagName.toLowerCase() === 'div'));
@@ -2138,7 +2210,10 @@ if (typeof module === 'object') {
2138
2210
  var i,
2139
2211
  el,
2140
2212
  new_el,
2141
- spans = container_el.querySelectorAll('.replace-with');
2213
+ spans = container_el.querySelectorAll('.replace-with'),
2214
+ isCEF = function (el) {
2215
+ return (el && el.nodeName !== '#text' && el.getAttribute('contenteditable') === 'false');
2216
+ };
2142
2217
 
2143
2218
  for (i = 0; i < spans.length; i += 1) {
2144
2219
 
@@ -2164,6 +2239,11 @@ if (typeof module === 'object') {
2164
2239
 
2165
2240
  el = spans[i];
2166
2241
 
2242
+ // bail if span is in contenteditable = false
2243
+ if (this.traverseUp(el, isCEF)) {
2244
+ return false;
2245
+ }
2246
+
2167
2247
  // remove empty spans, replace others with their contents
2168
2248
  if (/^\s*$/.test()) {
2169
2249
  el.parentNode.removeChild(el);
@@ -124,8 +124,7 @@
124
124
 
125
125
  .stalker-toolbar {
126
126
  -webkit-animation: pop-upwards 160ms forwards linear;
127
- -ms-animation: pop-upwards 160ms forwards linear;
128
- animation: pop-upwards 160ms forwards linear;
127
+ animation: pop-upwards 160ms forwards linear;
129
128
  -webkit-transition: top 0.075s ease-out, left 0.075s ease-out;
130
129
  transition: top 0.075s ease-out, left 0.075s ease-out; }
131
130
 
@@ -150,8 +149,7 @@
150
149
  outline: 0;
151
150
  border: none;
152
151
  box-shadow: none;
153
- -webkit-appearance: none;
154
- -moz-appearance: none; }
152
+ appearance: none; }
155
153
  .medium-editor-toolbar-form label {
156
154
  display: block; }
157
155
  .medium-editor-toolbar-form a {
@@ -170,3 +168,35 @@
170
168
  content: attr(data-placeholder) !important;
171
169
  font-style: italic;
172
170
  white-space: pre; }
171
+
172
+ .medium-editor-dragover {
173
+ background: #ddd; }
174
+
175
+ .medium-image-loading {
176
+ width: 40px;
177
+ height: 40px;
178
+ background-color: #333;
179
+ display: inline-block;
180
+ border-radius: 100%;
181
+ -webkit-animation: medium-image-loading-animation 1s infinite ease-in-out;
182
+ animation: medium-image-loading-animation 1s infinite ease-in-out; }
183
+
184
+ @-webkit-keyframes medium-image-loading-animation {
185
+ 0% {
186
+ -webkit-transform: scale(0);
187
+ transform: scale(0); }
188
+
189
+ 100% {
190
+ -webkit-transform: scale(1);
191
+ transform: scale(1);
192
+ opacity: 0; } }
193
+
194
+ @keyframes medium-image-loading-animation {
195
+ 0% {
196
+ -webkit-transform: scale(0);
197
+ transform: scale(0); }
198
+
199
+ 100% {
200
+ -webkit-transform: scale(1);
201
+ transform: scale(1);
202
+ opacity: 0; } }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: medium-editor-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ahmet Sezgin Duran
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-01 00:00:00.000000000 Z
11
+ date: 2015-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties