medium-editor-rails 1.2.0 → 1.3.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: 2797d94a0b663d77df028d929090e6693d9a0931
4
- data.tar.gz: bce123c63f3984235f94f8edec1fabf1ebc53b00
3
+ metadata.gz: 91d565261c4f78127bd277b71c67000ef8385ac8
4
+ data.tar.gz: 1780af7bf1a9bda664d0093fb4715dd706dbb4c2
5
5
  SHA512:
6
- metadata.gz: bff23ff9bc5e9ffd3ad863e6332c078facd03a6e34db99c2566e4177a187467d2655e440f6a15a9a4fbbb7a8e3a0e4deaa8a120aad155a2499af4e2268948600
7
- data.tar.gz: a27b421862232708df549a2585eec2f20a22a3ec94e75c4b535ca94366b5e3352c7fa1ce39a60d04e83e96045e994241d8ea7ab5697d624a5f3f8e37f280f0da
6
+ metadata.gz: 956fc017044e1056bebe177ca2c6006e1d164f93d28b7daa57211cbe7de76f0695a99757794dc8cb32f36ba3fe9f21d06bd3fad4a6b52b8a4deea00ad7a459da
7
+ data.tar.gz: 5a1c9016bee4ade393655346e42d34335366ddca0fa7fe0817f36ba60f4ebac8791c174145f1c882edd3a117160b6887f56d4a5289f2444bad9fedc46f6f2ae1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
 
2
2
  #### [Current]
3
+ * [0ce611b](../../commit/0ce611b) - __(Ahmet Sezgin Duran)__ Update Medium Editor files
4
+ * [423eaf7](../../commit/423eaf7) - __(Ahmet Sezgin Duran)__ Merge tag '1.2.0' into develop
5
+
6
+ 1.2.0
7
+
8
+ #### 1.2.0
9
+ * [fc6f4d6](../../commit/fc6f4d6) - __(Ahmet Sezgin Duran)__ Bump versions 1.2.0 and 2.2.0
3
10
  * [e6d136d](../../commit/e6d136d) - __(Ahmet Sezgin Duran)__ Update Medium Editor files
4
11
  * [7ee9c60](../../commit/7ee9c60) - __(Ahmet Sezgin Duran)__ Merge tag '1.1.3' 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.2.0](https://github.com/daviferreira/medium-editor/releases)
11
+ The latest version of Medium Editor bundled by this gem is [2.3.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.2.0'
4
- MEDIUM_EDITOR_VERSION = '2.2.0'
3
+ VERSION = '1.3.0'
4
+ MEDIUM_EDITOR_VERSION = '2.3.0'
5
5
  end
6
6
  end
@@ -16,6 +16,295 @@ if (typeof module === 'object') {
16
16
  (function (window, document) {
17
17
  'use strict';
18
18
 
19
+ var now,
20
+ keyCode,
21
+ DefaultButton,
22
+ ButtonsData = {
23
+ 'bold': {
24
+ name: 'bold',
25
+ action: 'bold',
26
+ aria: 'bold',
27
+ tagNames: ['b', 'strong'],
28
+ contentDefault: '<b>B</b>',
29
+ contentFA: '<i class="fa fa-bold"></i>'
30
+ },
31
+ 'italic': {
32
+ name: 'italic',
33
+ action: 'italic',
34
+ aria: 'italic',
35
+ tagNames: ['i', 'em'],
36
+ style: {
37
+ prop: 'font-style',
38
+ value: 'italic'
39
+ },
40
+ contentDefault: '<b><i>I</i></b>',
41
+ contentFA: '<i class="fa fa-italic"></i>'
42
+ },
43
+ 'underline': {
44
+ name: 'underline',
45
+ action: 'underline',
46
+ aria: 'underline',
47
+ tagNames: ['u'],
48
+ contentDefault: '<b><u>U</u></b>',
49
+ contentFA: '<i class="fa fa-underline"></i>'
50
+ },
51
+ 'strikethrough': {
52
+ name: 'strikethrough',
53
+ action: 'strikethrough',
54
+ aria: 'strike through',
55
+ tagNames: ['strike'],
56
+ contentDefault: '<s>A</s>',
57
+ contentFA: '<i class="fa fa-strikethrough"></i>'
58
+ },
59
+ 'superscript': {
60
+ name: 'superscript',
61
+ action: 'superscript',
62
+ aria: 'superscript',
63
+ tagNames: ['sup'],
64
+ contentDefault: '<b>x<sup>1</sup></b>',
65
+ contentFA: '<i class="fa fa-superscript"></i>'
66
+ },
67
+ 'subscript': {
68
+ name: 'subscript',
69
+ action: 'subscript',
70
+ aria: 'subscript',
71
+ tagNames: ['sub'],
72
+ contentDefault: '<b>x<sub>1</sub></b>',
73
+ contentFA: '<i class="fa fa-subscript"></i>'
74
+ },
75
+ 'anchor': {
76
+ name: 'anchor',
77
+ action: 'anchor',
78
+ aria: 'link',
79
+ tagNames: ['a'],
80
+ contentDefault: '<b>#</b>',
81
+ contentFA: '<i class="fa fa-link"></i>'
82
+ },
83
+ 'image': {
84
+ name: 'image',
85
+ action: 'image',
86
+ aria: 'image',
87
+ tagNames: ['img'],
88
+ contentDefault: '<b>image</b>',
89
+ contentFA: '<i class="fa fa-picture-o"></i>'
90
+ },
91
+ 'quote': {
92
+ name: 'quote',
93
+ action: 'append-blockquote',
94
+ aria: 'blockquote',
95
+ tagNames: ['blockquote'],
96
+ contentDefault: '<b>&ldquo;</b>',
97
+ contentFA: '<i class="fa fa-quote-right"></i>'
98
+ },
99
+ 'orderedlist': {
100
+ name: 'orderedlist',
101
+ action: 'insertorderedlist',
102
+ aria: 'ordered list',
103
+ tagNames: ['ol'],
104
+ contentDefault: '<b>1.</b>',
105
+ contentFA: '<i class="fa fa-list-ol"></i>'
106
+ },
107
+ 'unorderedlist': {
108
+ name: 'unorderedlist',
109
+ action: 'insertunorderedlist',
110
+ aria: 'unordered list',
111
+ tagNames: ['ul'],
112
+ contentDefault: '<b>&bull;</b>',
113
+ contentFA: '<i class="fa fa-list-ul"></i>'
114
+ },
115
+ 'pre': {
116
+ name: 'pre',
117
+ action: 'append-pre',
118
+ aria: 'preformatted text',
119
+ tagNames: ['pre'],
120
+ contentDefault: '<b>0101</b>',
121
+ contentFA: '<i class="fa fa-code fa-lg"></i>'
122
+ },
123
+ 'indent': {
124
+ name: 'indent',
125
+ action: 'indent',
126
+ aria: 'indent',
127
+ tagNames: [],
128
+ contentDefault: '<b>&rarr;</b>',
129
+ contentFA: '<i class="fa fa-indent"></i>'
130
+ },
131
+ 'outdent': {
132
+ name: 'outdent',
133
+ action: 'outdent',
134
+ aria: 'outdent',
135
+ tagNames: [],
136
+ contentDefault: '<b>&larr;</b>',
137
+ contentFA: '<i class="fa fa-outdent"></i>'
138
+ },
139
+ 'justifyCenter': {
140
+ name: 'justifyCenter',
141
+ action: 'justifyCenter',
142
+ aria: 'center justify',
143
+ tagNames: [],
144
+ style: {
145
+ prop: 'text-align',
146
+ value: 'center'
147
+ },
148
+ contentDefault: '<b>C</b>',
149
+ contentFA: '<i class="fa fa-align-center"></i>'
150
+ },
151
+ 'justifyFull': {
152
+ name: 'justifyFull',
153
+ action: 'justifyFull',
154
+ aria: 'full justify',
155
+ tagNames: [],
156
+ style: {
157
+ prop: 'text-align',
158
+ value: 'justify'
159
+ },
160
+ contentDefault: '<b>J</b>',
161
+ contentFA: '<i class="fa fa-align-justify"></i>'
162
+ },
163
+ 'justifyLeft': {
164
+ name: 'justifyLeft',
165
+ action: 'justifyLeft',
166
+ aria: 'left justify',
167
+ tagNames: [],
168
+ style: {
169
+ prop: 'text-align',
170
+ value: 'left'
171
+ },
172
+ contentDefault: '<b>L</b>',
173
+ contentFA: '<i class="fa fa-align-left"></i>'
174
+ },
175
+ 'justifyRight': {
176
+ name: 'justifyRight',
177
+ action: 'justifyRight',
178
+ aria: 'right justify',
179
+ tagNames: [],
180
+ style: {
181
+ prop: 'text-align',
182
+ value: 'right'
183
+ },
184
+ contentDefault: '<b>R</b>',
185
+ contentFA: '<i class="fa fa-align-right"></i>'
186
+ },
187
+ 'header1': {
188
+ name: 'header1',
189
+ action: function (options) {
190
+ return 'append-' + options.firstHeader;
191
+ },
192
+ aria: function (options) {
193
+ return options.firstHeader;
194
+ },
195
+ tagNames: function (options) {
196
+ return [options.firstHeader];
197
+ },
198
+ contentDefault: '<b>H1</b>'
199
+ },
200
+ 'header2': {
201
+ name: 'header2',
202
+ action: function (options) {
203
+ return 'append-' + options.secondHeader;
204
+ },
205
+ aria: function (options) {
206
+ return options.secondHeader;
207
+ },
208
+ tagNames: function (options) {
209
+ return [options.secondHeader];
210
+ },
211
+ contentDefault: '<b>H2</b>'
212
+ }
213
+ };
214
+
215
+ DefaultButton = function (options, instance) {
216
+ this.options = options;
217
+ this.name = options.name;
218
+ this.init(instance);
219
+ };
220
+
221
+ DefaultButton.prototype = {
222
+ init: function (instance) {
223
+ this.base = instance;
224
+
225
+ this.button = this.createButton();
226
+ this.base.on(this.button, 'click', this.handleClick.bind(this));
227
+ },
228
+ getButton: function () {
229
+ return this.button;
230
+ },
231
+ getAction: function () {
232
+ return (typeof this.options.action === 'function') ? this.options.action(this.base.options) : this.options.action;
233
+ },
234
+ getAria: function () {
235
+ return (typeof this.options.aria === 'function') ? this.options.aria(this.base.options) : this.options.aria;
236
+ },
237
+ getTagNames: function () {
238
+ return (typeof this.options.tagNames === 'function') ? this.options.tagNames(this.base.options) : this.options.tagNames;
239
+ },
240
+ createButton: function () {
241
+ var button = this.base.options.ownerDocument.createElement('button'),
242
+ content = this.options.contentDefault;
243
+ button.classList.add('medium-editor-action');
244
+ button.classList.add('medium-editor-action-' + this.name);
245
+ button.setAttribute('data-action', this.getAction());
246
+ button.setAttribute('aria-label', this.getAria());
247
+ if (this.base.options.buttonLabels) {
248
+ if (this.base.options.buttonLabels === 'fontawesome' && this.options.contentFA) {
249
+ content = this.options.contentFA;
250
+ } else if (typeof this.base.options.buttonLabels === 'object' && this.base.options.buttonLabels[this.name]) {
251
+ content = this.base.options.buttonLabels[this.options.name];
252
+ }
253
+ }
254
+ button.innerHTML = content;
255
+ return button;
256
+ },
257
+ handleClick: function (evt) {
258
+ evt.preventDefault();
259
+ evt.stopPropagation();
260
+ var action = this.getAction();
261
+ if (!this.base.selection) {
262
+ this.base.checkSelection();
263
+ }
264
+
265
+ if (this.isActive()) {
266
+ this.deactivate();
267
+ } else {
268
+ this.activate();
269
+ }
270
+
271
+ if (action) {
272
+ this.base.execAction(action, evt);
273
+ }
274
+ //if (this.options.form) {
275
+ // this.base.showForm(this.form, evt);
276
+ //}
277
+ },
278
+ isActive: function () {
279
+ return this.button.classList.contains(this.base.options.activeButtonClass);
280
+ },
281
+ deactivate: function () {
282
+ this.button.classList.remove(this.base.options.activeButtonClass);
283
+ delete this.knownState;
284
+ },
285
+ activate: function () {
286
+ this.button.classList.add(this.base.options.activeButtonClass);
287
+ delete this.knownState;
288
+ },
289
+ shouldActivate: function (node) {
290
+ var isMatch = false,
291
+ tagNames = this.getTagNames();
292
+ if (this.knownState === false || this.knownState === true) {
293
+ return this.knownState;
294
+ }
295
+
296
+ if (tagNames && tagNames.length > 0 && node.tagName) {
297
+ isMatch = tagNames.indexOf(node.tagName.toLowerCase()) !== -1;
298
+ }
299
+
300
+ if (!isMatch && this.options.style) {
301
+ this.knownState = isMatch = (this.base.options.contentWindow.getComputedStyle(node, null).getPropertyValue(this.options.style.prop).indexOf(this.options.style.value) !== -1);
302
+ }
303
+
304
+ return isMatch;
305
+ }
306
+ };
307
+
19
308
  function extend(b, a) {
20
309
  var prop;
21
310
  if (b === undefined) {
@@ -30,9 +319,11 @@ if (typeof module === 'object') {
30
319
  }
31
320
 
32
321
  // https://github.com/jashkenas/underscore
33
- var now = Date.now || function () {
322
+ now = Date.now || function () {
34
323
  return new Date().getTime();
35
- }, keyCode = {
324
+ };
325
+
326
+ keyCode = {
36
327
  BACKSPACE: 8,
37
328
  TAB: 9,
38
329
  ENTER: 13,
@@ -221,40 +512,10 @@ if (typeof module === 'object') {
221
512
  return !!(obj && obj.nodeType === 1);
222
513
  }
223
514
 
224
- // http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
225
- function insertHTMLCommand(doc, html) {
226
- var selection, range, el, fragment, node, lastNode;
227
-
228
- if (doc.queryCommandSupported('insertHTML')) {
229
- try {
230
- return doc.execCommand('insertHTML', false, html);
231
- } catch (ignore) {}
232
- }
233
-
234
- selection = window.getSelection();
235
- if (selection.getRangeAt && selection.rangeCount) {
236
- range = selection.getRangeAt(0);
237
- range.deleteContents();
238
-
239
- el = doc.createElement("div");
240
- el.innerHTML = html;
241
- fragment = doc.createDocumentFragment();
242
- while (el.firstChild) {
243
- node = el.firstChild;
244
- lastNode = fragment.appendChild(node);
245
- }
246
- range.insertNode(fragment);
247
-
248
- // Preserve the selection:
249
- if (lastNode) {
250
- range = range.cloneRange();
251
- range.setStartAfter(lastNode);
252
- range.collapse(true);
253
- selection.removeAllRanges();
254
- selection.addRange(range);
255
- }
256
- }
257
- }
515
+ MediumEditor.statics = {
516
+ ButtonsData: ButtonsData,
517
+ DefaultButton: DefaultButton
518
+ };
258
519
 
259
520
  MediumEditor.prototype = {
260
521
  defaults: {
@@ -324,6 +585,7 @@ if (typeof module === 'object') {
324
585
  this.events = [];
325
586
  this.isActive = true;
326
587
  this.initThrottledMethods()
588
+ .initCommands()
327
589
  .initElements()
328
590
  .bindSelect()
329
591
  .bindDragDrop()
@@ -331,7 +593,6 @@ if (typeof module === 'object') {
331
593
  .setPlaceholders()
332
594
  .bindElementActions()
333
595
  .bindWindowActions();
334
- //.passInstance();
335
596
  },
336
597
 
337
598
  on: function (target, event, listener, useCapture) {
@@ -421,9 +682,7 @@ if (typeof module === 'object') {
421
682
  }
422
683
  // Init toolbar
423
684
  if (addToolbar) {
424
- this.passInstance()
425
- .callExtensions('init')
426
- .initToolbar()
685
+ this.initToolbar()
427
686
  .bindButtons()
428
687
  .bindAnchorForm()
429
688
  .bindAnchorPreview();
@@ -516,7 +775,6 @@ if (typeof module === 'object') {
516
775
  // Bind the return and tab keypress events
517
776
  this.bindReturn(i)
518
777
  .bindKeydown(i)
519
- .bindBlur()
520
778
  .bindClick(i);
521
779
  }
522
780
 
@@ -553,6 +811,45 @@ if (typeof module === 'object') {
553
811
  return content;
554
812
  },
555
813
 
814
+ initExtension: function (extension, name) {
815
+ if (extension.parent) {
816
+ extension.base = this;
817
+ }
818
+ if (typeof extension.init === 'function') {
819
+ extension.init(this);
820
+ }
821
+ if (!extension.name) {
822
+ extension.name = name;
823
+ }
824
+ return extension;
825
+ },
826
+
827
+ initCommands: function () {
828
+ var buttons = this.options.buttons,
829
+ extensions = this.options.extensions,
830
+ ext,
831
+ name;
832
+ this.commands = [];
833
+
834
+ buttons.forEach(function (buttonName) {
835
+ if (extensions[buttonName]) {
836
+ ext = this.initExtension(extensions[buttonName], buttonName);
837
+ this.commands.push(ext);
838
+ } else if (ButtonsData.hasOwnProperty(buttonName)) {
839
+ ext = new DefaultButton(ButtonsData[buttonName], this);
840
+ this.commands.push(ext);
841
+ }
842
+ }.bind(this));
843
+
844
+ for (name in extensions) {
845
+ if (extensions.hasOwnProperty(name) && buttons.indexOf(name) === -1) {
846
+ ext = this.initExtension(extensions[name], name);
847
+ }
848
+ }
849
+
850
+ return this;
851
+ },
852
+
556
853
  /**
557
854
  * Helper function to call a method with a number of parameters on all registered extensions.
558
855
  * The function assures that the function exists before calling.
@@ -580,29 +877,6 @@ if (typeof module === 'object') {
580
877
  return this;
581
878
  },
582
879
 
583
- /**
584
- * Pass current Medium Editor instance to all extensions
585
- * if extension constructor has 'parent' attribute set to 'true'
586
- *
587
- */
588
- passInstance: function () {
589
- var self = this,
590
- ext,
591
- name;
592
-
593
- for (name in self.options.extensions) {
594
- if (self.options.extensions.hasOwnProperty(name)) {
595
- ext = self.options.extensions[name];
596
-
597
- if (ext.parent) {
598
- ext.base = self;
599
- }
600
- }
601
- }
602
-
603
- return self;
604
- },
605
-
606
880
  bindParagraphCreation: function (index) {
607
881
  var self = this;
608
882
  this.on(this.elements[index], 'keypress', function (e) {
@@ -688,14 +962,16 @@ if (typeof module === 'object') {
688
962
 
689
963
  if (e.which === keyCode.TAB) {
690
964
  // Override tab only for pre nodes
691
- var tag = getSelectionStart.call(self).tagName.toLowerCase();
965
+ var node = getSelectionStart.call(self) || e.target,
966
+ tag = node && node.tagName.toLowerCase();
967
+
692
968
  if (tag === 'pre') {
693
969
  e.preventDefault();
694
970
  self.options.ownerDocument.execCommand('insertHtml', null, ' ');
695
971
  }
696
972
 
697
973
  // Tab to indent list structures!
698
- if (tag === 'li') {
974
+ if (tag === 'li' || self.isListItemChild(node)) {
699
975
  e.preventDefault();
700
976
 
701
977
  // If Shift is down, outdent, otherwise indent
@@ -771,99 +1047,11 @@ if (typeof module === 'object') {
771
1047
  }
772
1048
  },
773
1049
 
774
- buttonTemplate: function (btnType) {
775
- var buttonLabels = this.getButtonLabels(this.options.buttonLabels),
776
- buttonTemplates = {
777
- 'bold': '<button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b" aria-label="bold">' + buttonLabels.bold + '</button>',
778
- 'italic': '<button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i" aria-label="italic">' + buttonLabels.italic + '</button>',
779
- 'underline': '<button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u" aria-label="underline">' + buttonLabels.underline + '</button>',
780
- 'strikethrough': '<button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike" aria-label="strike through">' + buttonLabels.strikethrough + '</button>',
781
- 'superscript': '<button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup" aria-label="superscript">' + buttonLabels.superscript + '</button>',
782
- 'subscript': '<button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub" aria-label="subscript">' + buttonLabels.subscript + '</button>',
783
- 'anchor': '<button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a" aria-label="link">' + buttonLabels.anchor + '</button>',
784
- 'image': '<button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img" aria-label="image">' + buttonLabels.image + '</button>',
785
- 'header1': '<button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '" aria-label="h1">' + buttonLabels.header1 + '</button>',
786
- 'header2': '<button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + ' "aria-label="h2">' + buttonLabels.header2 + '</button>',
787
- 'quote': '<button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote" aria-label="blockquote">' + buttonLabels.quote + '</button>',
788
- 'orderedlist': '<button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol" aria-label="ordered list">' + buttonLabels.orderedlist + '</button>',
789
- 'unorderedlist': '<button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul" aria-label="unordered list">' + buttonLabels.unorderedlist + '</button>',
790
- 'pre': '<button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre" aria-label="preformatted text">' + buttonLabels.pre + '</button>',
791
- 'indent': '<button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul" aria-label="indent">' + buttonLabels.indent + '</button>',
792
- 'outdent': '<button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul" aria-label="outdent">' + buttonLabels.outdent + '</button>',
793
- 'justifyCenter': '<button class="medium-editor-action medium-editor-action-justifyCenter" data-action="justifyCenter" data-element="" aria-label="center justify">' + buttonLabels.justifyCenter + '</button>',
794
- 'justifyFull': '<button class="medium-editor-action medium-editor-action-justifyFull" data-action="justifyFull" data-element="" aria-label="full justify">' + buttonLabels.justifyFull + '</button>',
795
- 'justifyLeft': '<button class="medium-editor-action medium-editor-action-justifyLeft" data-action="justifyLeft" data-element="" aria-label="left justify">' + buttonLabels.justifyLeft + '</button>',
796
- 'justifyRight': '<button class="medium-editor-action medium-editor-action-justifyRight" data-action="justifyRight" data-element="" aria-label="right justify">' + buttonLabels.justifyRight + '</button>'
797
- };
798
- return buttonTemplates[btnType] || false;
799
- },
800
-
801
- // TODO: break method
802
- getButtonLabels: function (buttonLabelType) {
803
- var customButtonLabels,
804
- attrname,
805
- buttonLabels = {
806
- 'bold': '<b>B</b>',
807
- 'italic': '<b><i>I</i></b>',
808
- 'underline': '<b><u>U</u></b>',
809
- 'strikethrough': '<s>A</s>',
810
- 'superscript': '<b>x<sup>1</sup></b>',
811
- 'subscript': '<b>x<sub>1</sub></b>',
812
- 'anchor': '<b>#</b>',
813
- 'image': '<b>image</b>',
814
- 'header1': '<b>H1</b>',
815
- 'header2': '<b>H2</b>',
816
- 'quote': '<b>&ldquo;</b>',
817
- 'orderedlist': '<b>1.</b>',
818
- 'unorderedlist': '<b>&bull;</b>',
819
- 'pre': '<b>0101</b>',
820
- 'indent': '<b>&rarr;</b>',
821
- 'outdent': '<b>&larr;</b>',
822
- 'justifyCenter': '<b>C</b>',
823
- 'justifyFull': '<b>J</b>',
824
- 'justifyLeft': '<b>L</b>',
825
- 'justifyRight': '<b>R</b>'
826
- };
827
- if (buttonLabelType === 'fontawesome') {
828
- customButtonLabels = {
829
- 'bold': '<i class="fa fa-bold"></i>',
830
- 'italic': '<i class="fa fa-italic"></i>',
831
- 'underline': '<i class="fa fa-underline"></i>',
832
- 'strikethrough': '<i class="fa fa-strikethrough"></i>',
833
- 'superscript': '<i class="fa fa-superscript"></i>',
834
- 'subscript': '<i class="fa fa-subscript"></i>',
835
- 'anchor': '<i class="fa fa-link"></i>',
836
- 'image': '<i class="fa fa-picture-o"></i>',
837
- 'quote': '<i class="fa fa-quote-right"></i>',
838
- 'orderedlist': '<i class="fa fa-list-ol"></i>',
839
- 'unorderedlist': '<i class="fa fa-list-ul"></i>',
840
- 'pre': '<i class="fa fa-code fa-lg"></i>',
841
- 'indent': '<i class="fa fa-indent"></i>',
842
- 'outdent': '<i class="fa fa-outdent"></i>',
843
- 'justifyCenter': '<i class="fa fa-align-center"></i>',
844
- 'justifyFull': '<i class="fa fa-align-justify"></i>',
845
- 'justifyLeft': '<i class="fa fa-align-left"></i>',
846
- 'justifyRight': '<i class="fa fa-align-right"></i>'
847
- };
848
- } else if (typeof buttonLabelType === 'object') {
849
- customButtonLabels = buttonLabelType;
850
- }
851
- if (typeof customButtonLabels === 'object') {
852
- for (attrname in customButtonLabels) {
853
- if (customButtonLabels.hasOwnProperty(attrname)) {
854
- buttonLabels[attrname] = customButtonLabels[attrname];
855
- }
856
- }
857
- }
858
- return buttonLabels;
859
- },
860
-
861
1050
  initToolbar: function () {
862
1051
  if (this.toolbar) {
863
1052
  return this;
864
1053
  }
865
1054
  this.toolbar = this.createToolbar();
866
- this.addExtensionForms();
867
1055
  this.keepToolbarAlive = false;
868
1056
  this.toolbarActions = this.toolbar.querySelector('.medium-editor-toolbar-actions');
869
1057
  this.anchorPreview = this.createAnchorPreview();
@@ -874,6 +1062,9 @@ if (typeof module === 'object') {
874
1062
  this.anchorTarget = this.anchorForm.querySelector('input.medium-editor-toolbar-anchor-target');
875
1063
  this.anchorButton = this.anchorForm.querySelector('input.medium-editor-toolbar-anchor-button');
876
1064
  }
1065
+
1066
+ this.addExtensionForms();
1067
+
877
1068
  return this;
878
1069
  },
879
1070
 
@@ -898,28 +1089,16 @@ if (typeof module === 'object') {
898
1089
 
899
1090
  //TODO: actionTemplate
900
1091
  toolbarButtons: function () {
901
- var btns = this.options.buttons,
902
- ul = this.options.ownerDocument.createElement('ul'),
1092
+ var ul = this.options.ownerDocument.createElement('ul'),
903
1093
  li,
904
- i,
905
- btn,
906
- ext;
1094
+ btn;
907
1095
 
908
1096
  ul.id = 'medium-editor-toolbar-actions' + this.id;
909
1097
  ul.className = 'medium-editor-toolbar-actions clearfix';
910
1098
 
911
- for (i = 0; i < btns.length; i += 1) {
912
- if (this.options.extensions.hasOwnProperty(btns[i])) {
913
- ext = this.options.extensions[btns[i]];
914
- btn = ext.getButton !== undefined ? ext.getButton(this) : null;
915
- if (ext.hasForm) {
916
- btn.setAttribute('data-form', 'medium-editor-toolbar-form-' + btns[i] + '-' + this.id);
917
- }
918
- } else {
919
- btn = this.buttonTemplate(btns[i]);
920
- }
921
-
922
- if (btn) {
1099
+ this.commands.forEach(function (extension) {
1100
+ if (typeof extension.getButton === 'function') {
1101
+ btn = extension.getButton(this);
923
1102
  li = this.options.ownerDocument.createElement('li');
924
1103
  if (isElement(btn)) {
925
1104
  li.appendChild(btn);
@@ -928,33 +1107,26 @@ if (typeof module === 'object') {
928
1107
  }
929
1108
  ul.appendChild(li);
930
1109
  }
931
- }
1110
+ }.bind(this));
932
1111
 
933
1112
  return ul;
934
1113
  },
935
1114
 
936
1115
  addExtensionForms: function () {
937
- var extensions = this.options.extensions,
938
- ext,
939
- name,
940
- form,
1116
+ var form,
941
1117
  id;
942
1118
 
943
- for (name in extensions) {
944
- if (extensions.hasOwnProperty(name)) {
945
- ext = extensions[name];
946
- if (ext.hasForm) {
947
- form = ext.getForm !== undefined ? ext.getForm() : null;
948
- }
949
- if (form) {
950
- id = 'medium-editor-toolbar-form-' + name + '-' + this.id;
951
- form.className = 'medium-editor-toolbar-form';
952
- form.id = id;
953
- ext.getForm().id = id;
954
- this.toolbar.appendChild(form);
955
- }
1119
+ this.commands.forEach(function (extension) {
1120
+ if (extension.hasForm) {
1121
+ form = (typeof extension.getForm === 'function') ? extension.getForm() : null;
956
1122
  }
957
- }
1123
+ if (form) {
1124
+ id = 'medium-editor-toolbar-form-' + extension.name + '-' + this.id;
1125
+ form.className += ' medium-editor-toolbar-form';
1126
+ form.id = id;
1127
+ this.toolbar.appendChild(form);
1128
+ }
1129
+ }.bind(this));
958
1130
  },
959
1131
 
960
1132
  toolbarFormAnchor: function () {
@@ -1011,15 +1183,22 @@ if (typeof module === 'object') {
1011
1183
 
1012
1184
  bindSelect: function () {
1013
1185
  var self = this,
1014
- i;
1186
+ i,
1187
+ timer;
1015
1188
 
1016
1189
  this.checkSelectionWrapper = function (e) {
1190
+ e.stopPropagation();
1191
+
1192
+ clearTimeout(timer);
1193
+
1017
1194
  // Do not close the toolbar when bluring the editable area and clicking into the anchor form
1018
1195
  if (!self.options.disableAnchorForm && e && self.clickingIntoArchorForm(e)) {
1019
1196
  return false;
1020
1197
  }
1021
1198
 
1022
- self.checkSelection();
1199
+ timer = setTimeout(function () {
1200
+ self.checkSelection();
1201
+ }, 10);
1023
1202
  };
1024
1203
 
1025
1204
  this.on(this.options.ownerDocument.documentElement, 'mouseup', this.checkSelectionWrapper);
@@ -1027,17 +1206,52 @@ if (typeof module === 'object') {
1027
1206
  for (i = 0; i < this.elements.length; i += 1) {
1028
1207
  this.on(this.elements[i], 'keyup', this.checkSelectionWrapper);
1029
1208
  this.on(this.elements[i], 'blur', this.checkSelectionWrapper);
1030
- this.on(this.elements[i], 'click', this.checkSelectionWrapper);
1209
+ this.on(this.elements[i], 'mouseup', this.checkSelectionWrapper);
1031
1210
  }
1211
+
1032
1212
  return this;
1033
1213
  },
1034
1214
 
1215
+ // http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
1216
+ insertHTML: function insertHTML(html) {
1217
+ var selection, range, el, fragment, node, lastNode;
1218
+
1219
+ if (this.options.ownerDocument.queryCommandSupported('insertHTML')) {
1220
+ try {
1221
+ return this.options.ownerDocument.execCommand('insertHTML', false, html);
1222
+ } catch (ignore) {}
1223
+ }
1224
+
1225
+ selection = window.getSelection();
1226
+ if (selection.getRangeAt && selection.rangeCount) {
1227
+ range = selection.getRangeAt(0);
1228
+ range.deleteContents();
1229
+
1230
+ el = this.options.ownerDocument.createElement("div");
1231
+ el.innerHTML = html;
1232
+ fragment = this.options.ownerDocument.createDocumentFragment();
1233
+ while (el.firstChild) {
1234
+ node = el.firstChild;
1235
+ lastNode = fragment.appendChild(node);
1236
+ }
1237
+ range.insertNode(fragment);
1238
+
1239
+ // Preserve the selection:
1240
+ if (lastNode) {
1241
+ range = range.cloneRange();
1242
+ range.setStartAfter(lastNode);
1243
+ range.collapse(true);
1244
+ selection.removeAllRanges();
1245
+ selection.addRange(range);
1246
+ }
1247
+ }
1248
+ },
1035
1249
 
1036
1250
  bindDragDrop: function () {
1037
1251
  var self = this, i, className, onDrag, onDrop, element;
1038
1252
 
1039
1253
  if (!self.options.imageDragging) {
1040
- return;
1254
+ return this;
1041
1255
  }
1042
1256
 
1043
1257
  className = 'medium-editor-dragover';
@@ -1065,7 +1279,7 @@ if (typeof module === 'object') {
1065
1279
  fileReader.readAsDataURL(file);
1066
1280
 
1067
1281
  id = 'medium-img-' + (+new Date());
1068
- insertHTMLCommand(self.options.ownerDocument, '<img class="medium-image-loading" id="' + id + '" />');
1282
+ self.insertHTML('<img class="medium-image-loading" id="' + id + '" />');
1069
1283
 
1070
1284
  fileReader.onload = function () {
1071
1285
  var img = document.getElementById(id);
@@ -1100,6 +1314,7 @@ if (typeof module === 'object') {
1100
1314
  },
1101
1315
 
1102
1316
  checkSelection: function () {
1317
+
1103
1318
  var newSelection,
1104
1319
  selectionElement;
1105
1320
 
@@ -1108,6 +1323,7 @@ if (typeof module === 'object') {
1108
1323
  !this.options.disableToolbar) {
1109
1324
 
1110
1325
  newSelection = this.options.contentWindow.getSelection();
1326
+
1111
1327
  if ((!this.options.updateOnEmptySelection && newSelection.toString().trim() === '') ||
1112
1328
  (this.options.allowMultiParagraphSelection === false && this.hasMultiParagraphs()) ||
1113
1329
  this.selectionInContentEditableFalse()) {
@@ -1336,21 +1552,30 @@ if (typeof module === 'object') {
1336
1552
  },
1337
1553
 
1338
1554
  setToolbarButtonStates: function () {
1339
- var buttons = this.toolbarActions.querySelectorAll('button'),
1340
- i;
1341
- for (i = 0; i < buttons.length; i += 1) {
1342
- buttons[i].classList.remove(this.options.activeButtonClass);
1343
- }
1555
+ this.commands.forEach(function (extension) {
1556
+ if (typeof extension.deactivate === 'function') {
1557
+ extension.deactivate();
1558
+ }
1559
+ }.bind(this));
1344
1560
  this.checkActiveButtons();
1345
1561
  return this;
1346
1562
  },
1347
1563
 
1348
1564
  checkActiveButtons: function () {
1349
1565
  var elements = Array.prototype.slice.call(this.elements),
1350
- parentNode = this.getSelectedParentElement();
1566
+ parentNode = this.getSelectedParentElement(),
1567
+ checkExtension = function (extension) {
1568
+ if (typeof extension.checkState === 'function') {
1569
+ extension.checkState(parentNode);
1570
+ } else if (typeof extension.isActive === 'function') {
1571
+ if (!extension.isActive() && extension.shouldActivate(parentNode)) {
1572
+ extension.activate();
1573
+ }
1574
+ }
1575
+ };
1351
1576
  while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName.toLowerCase) === -1) {
1352
1577
  this.activateButton(parentNode.tagName.toLowerCase());
1353
- this.callExtensions('checkState', parentNode);
1578
+ this.commands.forEach(checkExtension.bind(this));
1354
1579
 
1355
1580
  // we can abort the search upwards if we leave the contentEditable element
1356
1581
  if (elements.indexOf(parentNode) !== -1) {
@@ -1362,44 +1587,19 @@ if (typeof module === 'object') {
1362
1587
 
1363
1588
  activateButton: function (tag) {
1364
1589
  var el = this.toolbar.querySelector('[data-element="' + tag + '"]');
1365
- if (el !== null && el.className.indexOf(this.options.activeButtonClass) === -1) {
1366
- el.className += ' ' + this.options.activeButtonClass;
1590
+ if (el !== null && !el.classList.contains(this.options.activeButtonClass)) {
1591
+ el.classList.add(this.options.activeButtonClass);
1367
1592
  }
1368
1593
  },
1369
1594
 
1370
1595
  bindButtons: function () {
1371
- var buttons = this.toolbar.querySelectorAll('button'),
1372
- i,
1373
- self = this,
1374
- triggerAction = function (e) {
1375
- e.preventDefault();
1376
- e.stopPropagation();
1377
- if (self.selection === undefined) {
1378
- self.checkSelection();
1379
- }
1380
- if (this.className.indexOf(self.options.activeButtonClass) > -1) {
1381
- this.classList.remove(self.options.activeButtonClass);
1382
- } else {
1383
- this.className += ' ' + self.options.activeButtonClass;
1384
- }
1385
- if (this.hasAttribute('data-action')) {
1386
- self.execAction(this.getAttribute('data-action'), e);
1387
- }
1388
- // Allows extension buttons to show a form
1389
- // TO DO: Improve this
1390
- if (this.hasAttribute('data-form')) {
1391
- self.showForm(this.getAttribute('data-form'), e);
1392
- }
1393
- };
1394
- for (i = 0; i < buttons.length; i += 1) {
1395
- this.on(buttons[i], 'click', triggerAction);
1396
- }
1397
- this.setFirstAndLastItems(buttons);
1596
+ this.setFirstAndLastItems(this.toolbar.querySelectorAll('button'));
1398
1597
  return this;
1399
1598
  },
1400
1599
 
1401
1600
  setFirstAndLastItems: function (buttons) {
1402
1601
  if (buttons.length > 0) {
1602
+
1403
1603
  buttons[0].className += ' ' + this.options.firstButtonClass;
1404
1604
  buttons[buttons.length - 1].className += ' ' + this.options.lastButtonClass;
1405
1605
  }
@@ -1420,6 +1620,9 @@ if (typeof module === 'object') {
1420
1620
  } else {
1421
1621
  this.options.ownerDocument.execCommand(action, false, null);
1422
1622
  this.setToolbarPosition();
1623
+ if (action.indexOf('justify') === 0) {
1624
+ this.setToolbarButtonStates();
1625
+ }
1423
1626
  }
1424
1627
  },
1425
1628
 
@@ -1549,6 +1752,7 @@ if (typeof module === 'object') {
1549
1752
  hideToolbar: function () {
1550
1753
  if (this.isToolbarShown()) {
1551
1754
  this.toolbar.classList.remove('medium-editor-toolbar-active');
1755
+ // TODO: this should be an option?
1552
1756
  if (this.onHideToolbar) {
1553
1757
  this.onHideToolbar();
1554
1758
  }
@@ -1556,6 +1760,11 @@ if (typeof module === 'object') {
1556
1760
  },
1557
1761
 
1558
1762
  hideToolbarActions: function () {
1763
+ this.commands.forEach(function (extension) {
1764
+ if (extension.onHide && typeof extension.onHide === 'function') {
1765
+ extension.onHide();
1766
+ }
1767
+ });
1559
1768
  this.keepToolbarAlive = false;
1560
1769
  this.hideToolbar();
1561
1770
  },
@@ -1952,6 +2161,9 @@ if (typeof module === 'object') {
1952
2161
  this.on(this.options.contentWindow, 'resize', function () {
1953
2162
  self.handleResize();
1954
2163
  });
2164
+
2165
+ this.bindBlur();
2166
+
1955
2167
  return this;
1956
2168
  },
1957
2169
 
@@ -2030,10 +2242,10 @@ if (typeof module === 'object') {
2030
2242
  html += '<p>' + self.htmlEntities(paragraphs[p]) + '</p>';
2031
2243
  }
2032
2244
  }
2033
- insertHTMLCommand(self.options.ownerDocument, html);
2245
+ self.insertHTML(html);
2034
2246
  } else {
2035
2247
  html = self.htmlEntities(e.clipboardData.getData(dataFormatPlain));
2036
- insertHTMLCommand(self.options.ownerDocument, html);
2248
+ self.insertHTML(html);
2037
2249
  }
2038
2250
  }
2039
2251
  };
@@ -2164,7 +2376,7 @@ if (typeof module === 'object') {
2164
2376
  }
2165
2377
 
2166
2378
  }
2167
- insertHTMLCommand(this.options.ownerDocument, fragmentBody.innerHTML.replace(/&nbsp;/g, ' '));
2379
+ this.insertHTML(fragmentBody.innerHTML.replace(/&nbsp;/g, ' '));
2168
2380
  },
2169
2381
  isCommonBlock: function (el) {
2170
2382
  return (el && (el.tagName.toLowerCase() === 'p' || el.tagName.toLowerCase() === 'div'));
@@ -7,6 +7,7 @@
7
7
  font-size: 0; }
8
8
 
9
9
  @-webkit-keyframes pop-upwards {
10
+
10
11
  0% {
11
12
  -webkit-transform: matrix(0.97, 0, 0, 1, 0, 12);
12
13
  transform: matrix(0.97, 0, 0, 1, 0, 12);
@@ -100,6 +101,8 @@
100
101
  line-height: 1.33;
101
102
  text-decoration: none;
102
103
  box-sizing: border-box; }
104
+ .medium-editor-toolbar li button:focus, .medium-editor-anchor-preview li button:focus {
105
+ outline: none; }
103
106
  .medium-editor-toolbar li .medium-editor-action-underline, .medium-editor-anchor-preview li .medium-editor-action-underline {
104
107
  text-decoration: underline; }
105
108
  .medium-editor-toolbar li .medium-editor-action-pre, .medium-editor-anchor-preview li .medium-editor-action-pre {
@@ -122,11 +125,9 @@
122
125
  position: fixed;
123
126
  top: 1px; }
124
127
 
125
- .stalker-toolbar {
128
+ .medium-editor-toolbar-active.stalker-toolbar {
126
129
  -webkit-animation: pop-upwards 160ms forwards linear;
127
- animation: pop-upwards 160ms forwards linear;
128
- -webkit-transition: top 0.075s ease-out, left 0.075s ease-out;
129
- transition: top 0.075s ease-out, left 0.075s ease-out; }
130
+ animation: pop-upwards 160ms forwards linear; }
130
131
 
131
132
  .medium-editor-action-bold {
132
133
  font-weight: bolder; }
@@ -182,6 +183,7 @@
182
183
  animation: medium-image-loading-animation 1s infinite ease-in-out; }
183
184
 
184
185
  @-webkit-keyframes medium-image-loading-animation {
186
+
185
187
  0% {
186
188
  -webkit-transform: scale(0);
187
189
  transform: scale(0); }
@@ -8,9 +8,7 @@
8
8
  .medium-editor-toolbar {
9
9
  border: 1px solid #357ebd;
10
10
  background-color: #428bca;
11
- border-radius: 4px;
12
- -webkit-transition: top 0.075s ease-out, left 0.075s ease-out;
13
- transition: top 0.075s ease-out, left 0.075s ease-out; }
11
+ border-radius: 4px; }
14
12
  .medium-editor-toolbar li button {
15
13
  min-width: 60px;
16
14
  height: 60px;
@@ -12,9 +12,7 @@
12
12
  background: -webkit-linear-gradient(bottom, #242424, rgba(36, 36, 36, 0.75));
13
13
  background: linear-gradient(bottom, #242424, rgba(36, 36, 36, 0.75));
14
14
  border-radius: 5px;
15
- box-shadow: 0 0 3px #000;
16
- -webkit-transition: top 0.075s ease-out, left 0.075s ease-out;
17
- transition: top 0.075s ease-out, left 0.075s ease-out; }
15
+ box-shadow: 0 0 3px #000; }
18
16
  .medium-editor-toolbar li button {
19
17
  min-width: 50px;
20
18
  height: 50px;
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.2.0
4
+ version: 1.3.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-07 00:00:00.000000000 Z
11
+ date: 2015-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties