medium-editor-rails 1.2.0 → 1.3.0

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