rails_medium_editor_insert_plugin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +86 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +139 -0
  10. data/Rakefile +6 -0
  11. data/app/assets/javascripts/medium-editor-js.js +1 -0
  12. data/app/assets/stylesheets/medium-editor-style.scss +3 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/rails_medium_editor_insert_plugin.rb +7 -0
  16. data/lib/rails_medium_editor_insert_plugin/version.rb +3 -0
  17. data/rails_medium_editor_insert_plugin.gemspec +31 -0
  18. data/vendor/assets/javascripts/medium_editor_js/handlebars.js +29 -0
  19. data/vendor/assets/javascripts/medium_editor_js/jquery-fileupload.js +1477 -0
  20. data/vendor/assets/javascripts/medium_editor_js/jquery-iframe-transport.js +217 -0
  21. data/vendor/assets/javascripts/medium_editor_js/jquery-sortable.js +693 -0
  22. data/vendor/assets/javascripts/medium_editor_js/jquery-ui-widget.js +572 -0
  23. data/vendor/assets/javascripts/medium_editor_js/medium-editor-insert-plugin.js +2091 -0
  24. data/vendor/assets/javascripts/medium_editor_js/medium-editor.js +7054 -0
  25. data/vendor/assets/javascripts/rails-medium-editor-insert-plugin.js +7 -0
  26. data/vendor/assets/stylesheets/medium-editor.scss +3 -0
  27. data/vendor/assets/stylesheets/medium_editor_style/_flat.scss +62 -0
  28. data/vendor/assets/stylesheets/medium_editor_style/_medium-editor.scss +182 -0
  29. data/vendor/assets/stylesheets/medium_editor_style/medium-editor-insert-plugin-frontend.scss +72 -0
  30. data/vendor/assets/stylesheets/medium_editor_style/medium-editor-insert-plugin.scss +209 -0
  31. data/vendor/assets/stylesheets/medium_editor_style/medium-editor-style.scss +4 -0
  32. metadata +130 -0
@@ -0,0 +1,2091 @@
1
+ /*!
2
+ * medium-editor-insert-plugin v2.2.2 - jQuery insert plugin for MediumEditor
3
+ *
4
+ * https://github.com/orthes/medium-editor-insert-plugin
5
+ *
6
+ * Copyright (c) 2014 Pavel Linkesch (http://linkesch.sk)
7
+ * Released under the MIT license
8
+ */
9
+
10
+ this["MediumInsert"] = this["MediumInsert"] || {};
11
+ this["MediumInsert"]["Templates"] = this["MediumInsert"]["Templates"] || {};
12
+
13
+ this["MediumInsert"]["Templates"]["src/js/templates/core-buttons.hbs"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) {
14
+ var stack1, helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function";
15
+
16
+ return " <li><a data-addon=\""
17
+ + container.escapeExpression(((helper = (helper = helpers.key || (data && data.key)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"key","hash":{},"data":data}) : helper)))
18
+ + "\" data-action=\"add\" class=\"medium-insert-action\">"
19
+ + ((stack1 = ((helper = (helper = helpers.label || (depth0 != null ? depth0.label : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data}) : helper))) != null ? stack1 : "")
20
+ + "</a></li>\n";
21
+ },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
22
+ var stack1;
23
+
24
+ return "<div class=\"medium-insert-buttons\" contenteditable=\"false\" style=\"display: none\">\n <a class=\"medium-insert-buttons-show\">+</a>\n <ul class=\"medium-insert-buttons-addons\" style=\"display: none\">\n"
25
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.addons : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
26
+ + " </ul>\n</div>\n";
27
+ },"useData":true});
28
+
29
+ this["MediumInsert"]["Templates"]["src/js/templates/core-caption.hbs"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
30
+ var helper;
31
+
32
+ return "<figcaption contenteditable=\"true\" class=\"medium-insert-caption-placeholder\" data-placeholder=\""
33
+ + container.escapeExpression(((helper = (helper = helpers.placeholder || (depth0 != null ? depth0.placeholder : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"placeholder","hash":{},"data":data}) : helper)))
34
+ + "\"></figcaption>";
35
+ },"useData":true});
36
+
37
+ this["MediumInsert"]["Templates"]["src/js/templates/core-empty-line.hbs"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
38
+ return "<p><br></p>\n";
39
+ },"useData":true});
40
+
41
+ this["MediumInsert"]["Templates"]["src/js/templates/embeds-toolbar.hbs"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) {
42
+ var stack1;
43
+
44
+ return " <div class=\"medium-insert-embeds-toolbar medium-editor-toolbar medium-toolbar-arrow-under medium-editor-toolbar-active\">\n <ul class=\"medium-editor-toolbar-actions clearfix\">\n"
45
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.styles : depth0),{"name":"each","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
46
+ + " </ul>\n </div>\n";
47
+ },"2":function(container,depth0,helpers,partials,data) {
48
+ var stack1;
49
+
50
+ return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.label : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
51
+ },"3":function(container,depth0,helpers,partials,data) {
52
+ var stack1, helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function";
53
+
54
+ return " <li>\n <button class=\"medium-editor-action\" data-action=\""
55
+ + container.escapeExpression(((helper = (helper = helpers.key || (data && data.key)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"key","hash":{},"data":data}) : helper)))
56
+ + "\">"
57
+ + ((stack1 = ((helper = (helper = helpers.label || (depth0 != null ? depth0.label : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data}) : helper))) != null ? stack1 : "")
58
+ + "</button>\n </li>\n";
59
+ },"5":function(container,depth0,helpers,partials,data) {
60
+ var stack1;
61
+
62
+ return " <div class=\"medium-insert-embeds-toolbar2 medium-editor-toolbar medium-editor-toolbar-active\">\n <ul class=\"medium-editor-toolbar-actions clearfix\">\n"
63
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.actions : depth0),{"name":"each","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
64
+ + " </ul>\n </div>\n";
65
+ },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
66
+ var stack1, alias1=depth0 != null ? depth0 : {};
67
+
68
+ return ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.styles : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
69
+ + "\n"
70
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.actions : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
71
+ },"useData":true});
72
+
73
+ this["MediumInsert"]["Templates"]["src/js/templates/embeds-wrapper.hbs"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
74
+ var stack1, helper;
75
+
76
+ return "<div class=\"medium-insert-embeds\" contenteditable=\"false\">\n <figure>\n <div class=\"medium-insert-embed\">\n "
77
+ + ((stack1 = ((helper = (helper = helpers.html || (depth0 != null ? depth0.html : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : {},{"name":"html","hash":{},"data":data}) : helper))) != null ? stack1 : "")
78
+ + "\n </div>\n </figure>\n <div class=\"medium-insert-embeds-overlay\"></div>\n</div>";
79
+ },"useData":true});
80
+
81
+ this["MediumInsert"]["Templates"]["src/js/templates/images-fileupload.hbs"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
82
+ return "<input type=\"file\" multiple>";
83
+ },"useData":true});
84
+
85
+ this["MediumInsert"]["Templates"]["src/js/templates/images-image.hbs"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) {
86
+ return " <div class=\"medium-insert-images-progress\"></div>\n";
87
+ },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
88
+ var stack1, helper, alias1=depth0 != null ? depth0 : {};
89
+
90
+ return "<figure contenteditable=\"false\">\n <img src=\""
91
+ + container.escapeExpression(((helper = (helper = helpers.img || (depth0 != null ? depth0.img : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"img","hash":{},"data":data}) : helper)))
92
+ + "\" alt=\"\">\n"
93
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.progress : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
94
+ + "</figure>";
95
+ },"useData":true});
96
+
97
+ this["MediumInsert"]["Templates"]["src/js/templates/images-progressbar.hbs"] = Handlebars.template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
98
+ return "<progress min=\"0\" max=\"100\" value=\"0\">0</progress>";
99
+ },"useData":true});
100
+
101
+ this["MediumInsert"]["Templates"]["src/js/templates/images-toolbar.hbs"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) {
102
+ var stack1;
103
+
104
+ return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.label : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
105
+ },"2":function(container,depth0,helpers,partials,data) {
106
+ var stack1, helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function";
107
+
108
+ return " <li>\n <button class=\"medium-editor-action\" data-action=\""
109
+ + container.escapeExpression(((helper = (helper = helpers.key || (data && data.key)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"key","hash":{},"data":data}) : helper)))
110
+ + "\">"
111
+ + ((stack1 = ((helper = (helper = helpers.label || (depth0 != null ? depth0.label : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data}) : helper))) != null ? stack1 : "")
112
+ + "</button>\n </li>\n";
113
+ },"4":function(container,depth0,helpers,partials,data) {
114
+ var stack1;
115
+
116
+ return " <div class=\"medium-insert-images-toolbar2 medium-editor-toolbar medium-editor-toolbar-active\">\n <ul class=\"medium-editor-toolbar-actions clearfix\">\n"
117
+ + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.actions : depth0),{"name":"each","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
118
+ + " </ul>\n </div>\n";
119
+ },"5":function(container,depth0,helpers,partials,data) {
120
+ var stack1;
121
+
122
+ return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.label : depth0),{"name":"if","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
123
+ },"6":function(container,depth0,helpers,partials,data) {
124
+ var stack1, helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function";
125
+
126
+ return " <li>\n <button class=\"medium-editor-action\" data-action=\""
127
+ + container.escapeExpression(((helper = (helper = helpers.key || (data && data.key)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"key","hash":{},"data":data}) : helper)))
128
+ + "\">"
129
+ + ((stack1 = ((helper = (helper = helpers.label || (depth0 != null ? depth0.label : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data}) : helper))) != null ? stack1 : "")
130
+ + "</button>\n </li>\n";
131
+ },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
132
+ var stack1, alias1=depth0 != null ? depth0 : {};
133
+
134
+ return "<div class=\"medium-insert-images-toolbar medium-editor-toolbar medium-toolbar-arrow-under medium-editor-toolbar-active\">\n <ul class=\"medium-editor-toolbar-actions clearfix\">\n"
135
+ + ((stack1 = helpers.each.call(alias1,(depth0 != null ? depth0.styles : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
136
+ + " </ul>\n</div>\n\n"
137
+ + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.actions : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
138
+ },"useData":true});
139
+ ;(function ($, window, document, undefined) {
140
+
141
+ 'use strict';
142
+
143
+ /** Default values */
144
+ var pluginName = 'mediumInsert',
145
+ defaults = {
146
+ editor: null,
147
+ enabled: true,
148
+ addons: {
149
+ images: true, // boolean or object containing configuration
150
+ embeds: true
151
+ }
152
+ };
153
+
154
+ /**
155
+ * Capitalize first character
156
+ *
157
+ * @param {string} str
158
+ * @return {string}
159
+ */
160
+
161
+ function ucfirst (str) {
162
+ return str.charAt(0).toUpperCase() + str.slice(1);
163
+ }
164
+
165
+ /**
166
+ * Core plugin's object
167
+ *
168
+ * Sets options, variables and calls init() function
169
+ *
170
+ * @constructor
171
+ * @param {DOM} el - DOM element to init the plugin on
172
+ * @param {object} options - Options to override defaults
173
+ * @return {void}
174
+ */
175
+
176
+ function Core (el, options) {
177
+ var editor;
178
+
179
+ this.el = el;
180
+ this.$el = $(el);
181
+ this.templates = window.MediumInsert.Templates;
182
+
183
+ if (options) {
184
+ // Fix #142
185
+ // Avoid deep copying editor object, because since v2.3.0 it contains circular references which causes jQuery.extend to break
186
+ // Instead copy editor object to this.options manually
187
+ editor = options.editor;
188
+ options.editor = null;
189
+ }
190
+ this.options = $.extend(true, {}, defaults, options);
191
+ this.options.editor = editor;
192
+
193
+ this._defaults = defaults;
194
+ this._name = pluginName;
195
+
196
+ // Extend editor's functions
197
+ if (this.options && this.options.editor) {
198
+ this.options.editor._serialize = this.options.editor.serialize;
199
+ this.options.editor._destroy = this.options.editor.destroy;
200
+ this.options.editor._setup = this.options.editor.setup;
201
+ this.options.editor._hideInsertButtons = this.hideButtons;
202
+
203
+ this.options.editor.serialize = this.editorSerialize;
204
+ this.options.editor.destroy = this.editorDestroy;
205
+ this.options.editor.setup = this.editorSetup;
206
+
207
+ this.options.editor.getExtensionByName('placeholder').updatePlaceholder = this.editorUpdatePlaceholder;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Initialization
213
+ *
214
+ * @return {void}
215
+ */
216
+
217
+ Core.prototype.init = function () {
218
+ this.$el.addClass('medium-editor-insert-plugin');
219
+
220
+ if (typeof this.options.addons !== 'object' || Object.keys(this.options.addons).length === 0) {
221
+ this.disable();
222
+ }
223
+
224
+ this.initAddons();
225
+ this.clean();
226
+ this.events();
227
+ };
228
+
229
+ /**
230
+ * Event listeners
231
+ *
232
+ * @return {void}
233
+ */
234
+
235
+ Core.prototype.events = function () {
236
+ var that = this;
237
+
238
+ this.$el
239
+ .on('dragover drop', function (e) {
240
+ e.preventDefault();
241
+ })
242
+ .on('keyup click', $.proxy(this, 'toggleButtons'))
243
+ .on('selectstart mousedown', '.medium-insert, .medium-insert-buttons', $.proxy(this, 'disableSelection'))
244
+ .on('click', '.medium-insert-buttons-show', $.proxy(this, 'toggleAddons'))
245
+ .on('click', '.medium-insert-action', $.proxy(this, 'addonAction'))
246
+ .on('paste', '.medium-insert-caption-placeholder', function (e) {
247
+ $.proxy(that, 'removeCaptionPlaceholder')($(e.target));
248
+ });
249
+
250
+ $(window).on('resize', $.proxy(this, 'positionButtons', null));
251
+ };
252
+
253
+ /**
254
+ * Return editor instance
255
+ *
256
+ * @return {object} MediumEditor
257
+ */
258
+
259
+ Core.prototype.getEditor = function () {
260
+ return this.options.editor;
261
+ };
262
+
263
+ /**
264
+ * Extend editor's serialize function
265
+ *
266
+ * @return {object} Serialized data
267
+ */
268
+
269
+ Core.prototype.editorSerialize = function () {
270
+ var data = this._serialize();
271
+
272
+ $.each(data, function (key) {
273
+ var $data = $('<div />').html(data[key].value);
274
+
275
+ $data.find('.medium-insert-buttons').remove();
276
+
277
+ // Restore original embed code from embed wrapper attribute value.
278
+ $data.find('[data-embed-code]').each(function() {
279
+ var $this = $(this);
280
+ $this.html($this.attr('data-embed-code'));
281
+ });
282
+
283
+ data[key].value = $data.html();
284
+ });
285
+
286
+ return data;
287
+ };
288
+
289
+ /**
290
+ * Extend editor's destroy function to deactivate this plugin too
291
+ *
292
+ * @return {void}
293
+ */
294
+
295
+ Core.prototype.editorDestroy = function () {
296
+ $.each(this.elements, function (key, el) {
297
+ $(el).data('plugin_' + pluginName).disable();
298
+ });
299
+
300
+ this._destroy();
301
+ };
302
+
303
+ /**
304
+ * Extend editor's setup function to activate this plugin too
305
+ *
306
+ * @return {void}
307
+ */
308
+
309
+ Core.prototype.editorSetup = function () {
310
+ this._setup();
311
+
312
+ $.each(this.elements, function (key, el) {
313
+ $(el).data('plugin_' + pluginName).enable();
314
+ });
315
+ };
316
+
317
+ /**
318
+ * Extend editor's placeholder.updatePlaceholder function to show placeholder dispite of the plugin buttons
319
+ *
320
+ * @return {void}
321
+ */
322
+
323
+ Core.prototype.editorUpdatePlaceholder = function (el) {
324
+ var contents = $(el).children()
325
+ .not('.medium-insert-buttons').contents();
326
+
327
+ if (contents.length === 1 && contents[0].nodeName.toLowerCase() === 'br') {
328
+ this.showPlaceholder(el);
329
+ this.base._hideInsertButtons($(el));
330
+ } else {
331
+ this.hidePlaceholder(el);
332
+ }
333
+ };
334
+
335
+ /**
336
+ * Trigger editableInput on editor
337
+ *
338
+ * @return {void}
339
+ */
340
+
341
+ Core.prototype.triggerInput = function () {
342
+ if (this.getEditor()) {
343
+ this.getEditor().trigger('editableInput', null, this.el);
344
+ }
345
+ };
346
+
347
+ /**
348
+ * Deselects selected text
349
+ *
350
+ * @return {void}
351
+ */
352
+
353
+ Core.prototype.deselect = function () {
354
+ document.getSelection().removeAllRanges();
355
+ };
356
+
357
+ /**
358
+ * Disables the plugin
359
+ *
360
+ * @return {void}
361
+ */
362
+
363
+ Core.prototype.disable = function () {
364
+ this.options.enabled = false;
365
+
366
+ this.$el.find('.medium-insert-buttons').addClass('hide');
367
+ };
368
+
369
+ /**
370
+ * Enables the plugin
371
+ *
372
+ * @return {void}
373
+ */
374
+
375
+ Core.prototype.enable = function () {
376
+ this.options.enabled = true;
377
+
378
+ this.$el.find('.medium-insert-buttons').removeClass('hide');
379
+ };
380
+
381
+ /**
382
+ * Disables selectstart mousedown events on plugin elements except images
383
+ *
384
+ * @return {void}
385
+ */
386
+
387
+ Core.prototype.disableSelection = function (e) {
388
+ var $el = $(e.target);
389
+
390
+ if ($el.is('img') === false || $el.hasClass('medium-insert-buttons-show')) {
391
+ e.preventDefault();
392
+ }
393
+ };
394
+
395
+ /**
396
+ * Initialize addons
397
+ *
398
+ * @return {void}
399
+ */
400
+
401
+ Core.prototype.initAddons = function () {
402
+ var that = this;
403
+
404
+ if (!this.options.addons || this.options.addons.length === 0) {
405
+ return;
406
+ }
407
+
408
+ $.each(this.options.addons, function (addon, options) {
409
+ var addonName = pluginName + ucfirst(addon);
410
+
411
+ if (options === false) {
412
+ delete that.options.addons[addon];
413
+ return;
414
+ }
415
+
416
+ that.$el[addonName](options);
417
+ that.options.addons[addon] = that.$el.data('plugin_'+ addonName).options;
418
+ });
419
+ };
420
+
421
+ /**
422
+ * Cleans a content of the editor
423
+ *
424
+ * @return {void}
425
+ */
426
+
427
+ Core.prototype.clean = function () {
428
+ var that = this,
429
+ $buttons, $lastEl, $text;
430
+
431
+ if (this.options.enabled === false) {
432
+ return;
433
+ }
434
+
435
+ // Fix #39
436
+ // After deleting all content (ctrl+A and delete) in Firefox, all content is deleted and only <br> appears
437
+ // To force placeholder to appear, set <p><br></p> as content of the $el
438
+
439
+ if (this.$el.html().trim() === '' || this.$el.html().trim() === '<br>') {
440
+ this.$el.html(this.templates['src/js/templates/core-empty-line.hbs']().trim());
441
+ }
442
+
443
+ // Fix #29
444
+ // Wrap content text in <p></p> to avoid Firefox problems
445
+ $text = this.$el
446
+ .contents()
447
+ .filter(function () {
448
+ return this.nodeName === '#text' && $.trim($(this).text()) !== '';
449
+ });
450
+
451
+ $text.each(function () {
452
+ $(this).wrap('<p />');
453
+
454
+ // Fix #145
455
+ // Move caret at the end of the element that's being wrapped
456
+ that.moveCaret($(this).parent(), $(this).text().length);
457
+ });
458
+
459
+ this.addButtons();
460
+
461
+ $buttons = this.$el.find('.medium-insert-buttons');
462
+ $lastEl = $buttons.prev();
463
+ if ($lastEl.attr('class') && $lastEl.attr('class').match(/medium\-insert(?!\-active)/)) {
464
+ $buttons.before(this.templates['src/js/templates/core-empty-line.hbs']().trim());
465
+ }
466
+ };
467
+
468
+ /**
469
+ * Returns HTML template of buttons
470
+ *
471
+ * @return {string} HTML template of buttons
472
+ */
473
+
474
+ Core.prototype.getButtons = function () {
475
+ if (this.options.enabled === false) {
476
+ return;
477
+ }
478
+
479
+ return this.templates['src/js/templates/core-buttons.hbs']({
480
+ addons: this.options.addons
481
+ }).trim();
482
+ };
483
+
484
+ /**
485
+ * Appends buttons at the end of the $el
486
+ *
487
+ * @return {void}
488
+ */
489
+
490
+ Core.prototype.addButtons = function () {
491
+ if (this.$el.find('.medium-insert-buttons').length === 0) {
492
+ this.$el.append(this.getButtons());
493
+ }
494
+ };
495
+
496
+ /**
497
+ * Move buttons to current active, empty paragraph and show them
498
+ *
499
+ * @return {void}
500
+ */
501
+
502
+ Core.prototype.toggleButtons = function (e) {
503
+ var $el = $(e.target),
504
+ selection = window.getSelection(),
505
+ that = this,
506
+ range, $current, $p, activeAddon;
507
+
508
+ if (this.options.enabled === false) {
509
+ return;
510
+ }
511
+
512
+ if (!selection || selection.rangeCount === 0) {
513
+ $current = $el;
514
+ } else {
515
+ range = selection.getRangeAt(0);
516
+ $current = $(range.commonAncestorContainer);
517
+ }
518
+
519
+ // When user clicks on editor's placeholder in FF, $current el is editor itself, not the first paragraph as it should
520
+ if ($current.hasClass('medium-editor-insert-plugin')) {
521
+ $current = $current.find('p:first');
522
+ }
523
+
524
+ $p = $current.is('p') ? $current : $current.closest('p');
525
+
526
+ this.clean();
527
+
528
+ if ($el.hasClass('medium-editor-placeholder') === false && $el.closest('.medium-insert-buttons').length === 0 && $current.closest('.medium-insert-buttons').length === 0) {
529
+
530
+ this.$el.find('.medium-insert-active').removeClass('medium-insert-active');
531
+
532
+ $.each(this.options.addons, function (addon) {
533
+ if ($el.closest('.medium-insert-'+ addon).length) {
534
+ $current = $el;
535
+ }
536
+
537
+ if ($current.closest('.medium-insert-'+ addon).length) {
538
+ $p = $current.closest('.medium-insert-'+ addon);
539
+ activeAddon = addon;
540
+ return;
541
+ }
542
+ });
543
+
544
+ if ($p.length && (($p.text().trim() === '' && !activeAddon) || activeAddon === 'images')) {
545
+ $p.addClass('medium-insert-active');
546
+
547
+ // If buttons are displayed on addon paragraph, wait 100ms for possible captions to display
548
+ setTimeout(function () {
549
+ that.positionButtons(activeAddon);
550
+ that.showButtons(activeAddon);
551
+ }, activeAddon ? 100 : 0);
552
+ } else {
553
+ this.hideButtons();
554
+ }
555
+ }
556
+ };
557
+
558
+ /**
559
+ * Show buttons
560
+ *
561
+ * @param {string} activeAddon - Name of active addon
562
+ * @returns {void}
563
+ */
564
+
565
+ Core.prototype.showButtons = function (activeAddon) {
566
+ var $buttons = this.$el.find('.medium-insert-buttons');
567
+
568
+ $buttons.show();
569
+ $buttons.find('li').show();
570
+
571
+ if (activeAddon) {
572
+ $buttons.find('li').hide();
573
+ $buttons.find('a[data-addon="'+ activeAddon +'"]').parent().show();
574
+ }
575
+ };
576
+
577
+ /**
578
+ * Hides buttons
579
+ *
580
+ * @param {jQuery} $el - Editor element
581
+ * @returns {void}
582
+ */
583
+
584
+ Core.prototype.hideButtons = function ($el) {
585
+ $el = $el || this.$el;
586
+
587
+ $el.find('.medium-insert-buttons').hide();
588
+ $el.find('.medium-insert-buttons-addons').hide();
589
+ $el.find('.medium-insert-buttons-show').removeClass('medium-insert-buttons-rotate');
590
+ };
591
+
592
+ /**
593
+ * Position buttons
594
+ *
595
+ * @param {string} activeAddon - Name of active addon
596
+ * @return {void}
597
+ */
598
+
599
+ Core.prototype.positionButtons = function (activeAddon) {
600
+ var $buttons = this.$el.find('.medium-insert-buttons'),
601
+ $p = this.$el.find('.medium-insert-active'),
602
+ $first = $p.find('figure:first').length ? $p.find('figure:first') : $p,
603
+ left, top;
604
+
605
+ if ($p.length) {
606
+
607
+ left = $p.position().left - parseInt($buttons.find('.medium-insert-buttons-addons').css('left'), 10) - parseInt($buttons.find('.medium-insert-buttons-addons a:first').css('margin-left'), 10);
608
+ left = left < 0 ? $p.position().left : left;
609
+ top = $p.position().top + parseInt($p.css('margin-top'), 10);
610
+
611
+ if (activeAddon) {
612
+ if ($p.position().left !== $first.position().left) {
613
+ left = $first.position().left;
614
+ }
615
+
616
+ top += $p.height() + 15; // 15px offset
617
+ }
618
+
619
+ $buttons.css({
620
+ left: left,
621
+ top: top
622
+ });
623
+ }
624
+ };
625
+
626
+ /**
627
+ * Toggles addons buttons
628
+ *
629
+ * @return {void}
630
+ */
631
+
632
+ Core.prototype.toggleAddons = function () {
633
+ this.$el.find('.medium-insert-buttons-addons').fadeToggle();
634
+ this.$el.find('.medium-insert-buttons-show').toggleClass('medium-insert-buttons-rotate');
635
+ };
636
+
637
+ /**
638
+ * Hide addons buttons
639
+ *
640
+ * @return {void}
641
+ */
642
+
643
+ Core.prototype.hideAddons = function () {
644
+ this.$el.find('.medium-insert-buttons-addons').hide();
645
+ this.$el.find('.medium-insert-buttons-show').removeClass('medium-insert-buttons-rotate');
646
+ };
647
+
648
+ /**
649
+ * Call addon's action
650
+ *
651
+ * @param {Event} e
652
+ * @return {void}
653
+ */
654
+
655
+ Core.prototype.addonAction = function (e) {
656
+ var $a = $(e.target).is('a') ? $(e.target) : $(e.target).closest('a'),
657
+ addon = $a.data('addon'),
658
+ action = $a.data('action');
659
+
660
+ this.$el.data('plugin_'+ pluginName + ucfirst(addon))[action]();
661
+ };
662
+
663
+ /**
664
+ * Move caret at the beginning of the empty paragraph
665
+ *
666
+ * @param {jQuery} $el Element where to place the caret
667
+ * @param {integer} position Position where to move caret. Default: 0
668
+ *
669
+ * @return {void}
670
+ */
671
+
672
+ Core.prototype.moveCaret = function ($el, position) {
673
+ var range, sel, el;
674
+
675
+ position = position || 0;
676
+ range = document.createRange();
677
+ sel = window.getSelection();
678
+ el = $el.get(0);
679
+
680
+ if (!el.childNodes.length) {
681
+ var textEl = document.createTextNode(' ');
682
+ el.appendChild(textEl);
683
+ }
684
+
685
+ range.setStart(el.childNodes[0], position);
686
+ range.collapse(true);
687
+ sel.removeAllRanges();
688
+ sel.addRange(range);
689
+ };
690
+
691
+ /**
692
+ * Add caption
693
+ *
694
+ * @param {jQuery Element} $el
695
+ * @param {string} placeholder
696
+ * @return {void}
697
+ */
698
+
699
+ Core.prototype.addCaption = function ($el, placeholder) {
700
+ var $caption = $el.find('figcaption');
701
+
702
+ if ($caption.length === 0) {
703
+ $el.append(this.templates['src/js/templates/core-caption.hbs']({
704
+ placeholder: placeholder
705
+ }));
706
+ }
707
+ };
708
+
709
+ /**
710
+ * Remove captions
711
+ *
712
+ * @param {jQuery Element} $ignore
713
+ * @return {void}
714
+ */
715
+
716
+ Core.prototype.removeCaptions = function ($ignore) {
717
+ var $captions = this.$el.find('figcaption');
718
+
719
+ if ($ignore) {
720
+ $captions = $captions.not($ignore);
721
+ }
722
+
723
+ $captions.each(function () {
724
+ if ($(this).hasClass('medium-insert-caption-placeholder') || $(this).text().trim() === '') {
725
+ $(this).remove();
726
+ }
727
+ });
728
+ };
729
+
730
+ /**
731
+ * Remove caption placeholder
732
+ *
733
+ * @param {jQuery Element} $el
734
+ * @return {void}
735
+ */
736
+
737
+ Core.prototype.removeCaptionPlaceholder = function ($el) {
738
+ var $caption = $el.is('figcaption') ? $el : $el.find('figcaption');
739
+
740
+ if ($caption.length) {
741
+ $caption
742
+ .removeClass('medium-insert-caption-placeholder')
743
+ .removeAttr('data-placeholder');
744
+ }
745
+ };
746
+
747
+ /** Plugin initialization */
748
+
749
+ $.fn[pluginName] = function (options) {
750
+ return this.each(function () {
751
+ var that = this,
752
+ textareaId;
753
+
754
+ if ($(that).is('textarea')) {
755
+ textareaId = $(that).attr('medium-editor-textarea-id');
756
+ that = $(that).siblings('[medium-editor-textarea-id="'+ textareaId +'"]').get(0);
757
+ }
758
+
759
+ if (!$.data(that, 'plugin_' + pluginName)) {
760
+ // Plugin initialization
761
+ $.data(that, 'plugin_' + pluginName, new Core(that, options));
762
+ $.data(that, 'plugin_' + pluginName).init();
763
+ } else if (typeof options === 'string' && $.data(that, 'plugin_' + pluginName)[options]) {
764
+ // Method call
765
+ $.data(that, 'plugin_' + pluginName)[options]();
766
+ }
767
+ });
768
+ };
769
+
770
+ })(jQuery, window, document);
771
+
772
+ ;(function ($, window, document, undefined) {
773
+
774
+ 'use strict';
775
+
776
+ /** Default values */
777
+ var pluginName = 'mediumInsert',
778
+ addonName = 'Embeds', // first char is uppercase
779
+ defaults = {
780
+ label: '<span class="fa fa-youtube-play"></span>',
781
+ placeholder: 'Paste a YouTube, Vimeo, Facebook, Twitter or Instagram link and press Enter',
782
+ oembedProxy: 'http://medium.iframe.ly/api/oembed?iframe=1',
783
+ captions: true,
784
+ captionPlaceholder: 'Type caption (optional)',
785
+ styles: {
786
+ wide: {
787
+ label: '<span class="fa fa-align-justify"></span>',
788
+ // added: function ($el) {},
789
+ // removed: function ($el) {}
790
+ },
791
+ left: {
792
+ label: '<span class="fa fa-align-left"></span>',
793
+ // added: function ($el) {},
794
+ // removed: function ($el) {}
795
+ },
796
+ right: {
797
+ label: '<span class="fa fa-align-right"></span>',
798
+ // added: function ($el) {},
799
+ // removed: function ($el) {}
800
+ }
801
+ },
802
+ actions: {
803
+ remove: {
804
+ label: '<span class="fa fa-times"></span>',
805
+ clicked: function () {
806
+ var $event = $.Event('keydown');
807
+
808
+ $event.which = 8;
809
+ $(document).trigger($event);
810
+ }
811
+ }
812
+ }
813
+ };
814
+
815
+ /**
816
+ * Embeds object
817
+ *
818
+ * Sets options, variables and calls init() function
819
+ *
820
+ * @constructor
821
+ * @param {DOM} el - DOM element to init the plugin on
822
+ * @param {object} options - Options to override defaults
823
+ * @return {void}
824
+ */
825
+
826
+ function Embeds (el, options) {
827
+ this.el = el;
828
+ this.$el = $(el);
829
+ this.templates = window.MediumInsert.Templates;
830
+ this.core = this.$el.data('plugin_'+ pluginName);
831
+
832
+ this.options = $.extend(true, {}, defaults, options);
833
+
834
+ this._defaults = defaults;
835
+ this._name = pluginName;
836
+
837
+ // Extend editor's functions
838
+ if (this.core.getEditor()) {
839
+ this.core.getEditor()._serializePreEmbeds = this.core.getEditor().serialize;
840
+ this.core.getEditor().serialize = this.editorSerialize;
841
+ }
842
+
843
+ this.init();
844
+ }
845
+
846
+ /**
847
+ * Initialization
848
+ *
849
+ * @return {void}
850
+ */
851
+
852
+ Embeds.prototype.init = function () {
853
+ var $embeds = this.$el.find('.medium-insert-embeds');
854
+
855
+ $embeds.attr('contenteditable', false);
856
+ $embeds.each(function () {
857
+ if ($(this).find('.medium-insert-embeds-overlay').length === 0) {
858
+ $(this).append($('<div />').addClass('medium-insert-embeds-overlay'));
859
+ }
860
+ });
861
+
862
+ this.events();
863
+ this.backwardsCompatibility();
864
+ };
865
+
866
+ /**
867
+ * Event listeners
868
+ *
869
+ * @return {void}
870
+ */
871
+
872
+ Embeds.prototype.events = function () {
873
+ $(document)
874
+ .on('click', $.proxy(this, 'unselectEmbed'))
875
+ .on('keydown', $.proxy(this, 'removeEmbed'))
876
+ .on('click', '.medium-insert-embeds-toolbar .medium-editor-action', $.proxy(this, 'toolbarAction'))
877
+ .on('click', '.medium-insert-embeds-toolbar2 .medium-editor-action', $.proxy(this, 'toolbar2Action'));
878
+
879
+ this.$el
880
+ .on('keyup click paste', $.proxy(this, 'togglePlaceholder'))
881
+ .on('keydown', $.proxy(this, 'processLink'))
882
+ .on('click', '.medium-insert-embeds-overlay', $.proxy(this, 'selectEmbed'))
883
+ .on('contextmenu', '.medium-insert-embeds-placeholder', $.proxy(this, 'fixRightClickOnPlaceholder'));
884
+ };
885
+
886
+ /**
887
+ * Replace v0.* class names with new ones, wrap embedded content to new structure
888
+ *
889
+ * @return {void}
890
+ */
891
+
892
+ Embeds.prototype.backwardsCompatibility = function () {
893
+ var that = this;
894
+
895
+ this.$el.find('.mediumInsert-embeds')
896
+ .removeClass('mediumInsert-embeds')
897
+ .addClass('medium-insert-embeds');
898
+
899
+ this.$el.find('.medium-insert-embeds').each(function () {
900
+ if ($(this).find('.medium-insert-embed').length === 0) {
901
+ $(this).after(that.templates['src/js/templates/embeds-wrapper.hbs']({
902
+ html: $(this).html()
903
+ }));
904
+ $(this).remove();
905
+ }
906
+ });
907
+ };
908
+
909
+ /**
910
+ * Extend editor's serialize function
911
+ *
912
+ * @return {object} Serialized data
913
+ */
914
+
915
+ Embeds.prototype.editorSerialize = function () {
916
+ var data = this._serializePreEmbeds();
917
+
918
+ $.each(data, function (key) {
919
+ var $data = $('<div />').html(data[key].value);
920
+
921
+ $data.find('.medium-insert-embeds').removeAttr('contenteditable');
922
+ $data.find('.medium-insert-embeds-overlay').remove();
923
+
924
+ data[key].value = $data.html();
925
+ });
926
+
927
+ return data;
928
+ };
929
+
930
+ /**
931
+ * Add embedded element
932
+ *
933
+ * @return {void}
934
+ */
935
+
936
+ Embeds.prototype.add = function () {
937
+ var $place = this.$el.find('.medium-insert-active');
938
+
939
+ // Fix #132
940
+ // Make sure that the content of the paragraph is empty and <br> is wrapped in <p></p> to avoid Firefox problems
941
+ $place.html(this.templates['src/js/templates/core-empty-line.hbs']().trim());
942
+
943
+ // Replace paragraph with div to prevent #124 issue with pasting in Chrome,
944
+ // because medium editor wraps inserted content into paragraph and paragraphs can't be nested
945
+ if ($place.is('p')) {
946
+ $place.replaceWith('<div class="medium-insert-active">'+ $place.html() +'</div>');
947
+ $place = this.$el.find('.medium-insert-active');
948
+ this.core.moveCaret($place);
949
+ }
950
+
951
+ $place.addClass('medium-insert-embeds medium-insert-embeds-input medium-insert-embeds-active');
952
+
953
+ this.togglePlaceholder({ target: $place.get(0) });
954
+
955
+ $place.click();
956
+ this.core.hideButtons();
957
+ };
958
+
959
+ /**
960
+ * Toggles placeholder
961
+ *
962
+ * @param {Event} e
963
+ * @return {void}
964
+ */
965
+
966
+ Embeds.prototype.togglePlaceholder = function (e) {
967
+ var $place = $(e.target),
968
+ selection = window.getSelection(),
969
+ range, $current, text;
970
+
971
+ if (!selection || selection.rangeCount === 0) {
972
+ return;
973
+ }
974
+
975
+ range = selection.getRangeAt(0);
976
+ $current = $(range.commonAncestorContainer);
977
+
978
+ if ($current.hasClass('medium-insert-embeds-active')) {
979
+ $place = $current;
980
+ } else if ($current.closest('.medium-insert-embeds-active').length) {
981
+ $place = $current.closest('.medium-insert-embeds-active');
982
+ }
983
+
984
+ if ($place.hasClass('medium-insert-embeds-active')) {
985
+
986
+ text = $place.text().trim();
987
+
988
+ if (text === '' && $place.hasClass('medium-insert-embeds-placeholder') === false) {
989
+ $place
990
+ .addClass('medium-insert-embeds-placeholder')
991
+ .attr('data-placeholder', this.options.placeholder);
992
+ } else if (text !== '' && $place.hasClass('medium-insert-embeds-placeholder')) {
993
+ $place
994
+ .removeClass('medium-insert-embeds-placeholder')
995
+ .removeAttr('data-placeholder');
996
+ }
997
+
998
+ } else {
999
+ this.$el.find('.medium-insert-embeds-active').remove();
1000
+ }
1001
+ };
1002
+
1003
+ /**
1004
+ * Right click on placeholder in Chrome selects whole line. Fix this by placing caret at the end of line
1005
+ *
1006
+ * @param {Event} e
1007
+ * @return {void}
1008
+ */
1009
+
1010
+ Embeds.prototype.fixRightClickOnPlaceholder = function (e) {
1011
+ this.core.moveCaret($(e.target));
1012
+ };
1013
+
1014
+ /**
1015
+ * Process link
1016
+ *
1017
+ * @param {Event} e
1018
+ * @return {void}
1019
+ */
1020
+
1021
+ Embeds.prototype.processLink = function (e) {
1022
+ var $place = this.$el.find('.medium-insert-embeds-active'),
1023
+ url;
1024
+
1025
+ if (!$place.length) {
1026
+ return;
1027
+ }
1028
+
1029
+ url = $place.text().trim();
1030
+
1031
+ // Return empty placeholder on backspace, delete or enter
1032
+ if (url === '' && [8, 46, 13].indexOf(e.which) !== -1) {
1033
+ $place.remove();
1034
+ return;
1035
+ }
1036
+
1037
+ if (e.which === 13) {
1038
+ e.preventDefault();
1039
+ e.stopPropagation();
1040
+
1041
+ if (this.options.oembedProxy) {
1042
+ this.oembed(url);
1043
+ } else {
1044
+ this.parseUrl(url);
1045
+ }
1046
+ }
1047
+ };
1048
+
1049
+ /**
1050
+ * Get HTML via oEmbed proxy
1051
+ *
1052
+ * @param {string} url
1053
+ * @return {void}
1054
+ */
1055
+
1056
+ Embeds.prototype.oembed = function (url) {
1057
+ var that = this;
1058
+
1059
+ $.support.cors = true;
1060
+
1061
+ $.ajax({
1062
+ crossDomain: true,
1063
+ cache: false,
1064
+ url: this.options.oembedProxy,
1065
+ dataType: 'json',
1066
+ data: {
1067
+ url: url
1068
+ },
1069
+ success: function(data) {
1070
+ var html = data && data.html;
1071
+
1072
+ if (data && !html && data.type === 'photo' && data.url) {
1073
+ html = '<img src="' + data.url + '" alt="">';
1074
+ }
1075
+
1076
+ if (!html) {
1077
+ // Prevent render empty embed.
1078
+ $.proxy(that, 'convertBadEmbed', url)();
1079
+ return;
1080
+ }
1081
+
1082
+ if (html && html.indexOf('</script>') > -1) {
1083
+ // Store embed code with <script> tag inside wrapper attribute value.
1084
+ // Make nice attribute value escaping using jQuery.
1085
+ var $div = $('<div>')
1086
+ .attr('data-embed-code', html)
1087
+ .html(html);
1088
+ html = $('<div>').append($div).html();
1089
+ }
1090
+
1091
+ $.proxy(that, 'embed', html)();
1092
+ },
1093
+ error: function(jqXHR, textStatus, errorThrown) {
1094
+ var responseJSON = (function() {
1095
+ try {
1096
+ return JSON.parse(jqXHR.responseText);
1097
+ } catch(e) {}
1098
+ })();
1099
+
1100
+ if (typeof window.console !== 'undefined') {
1101
+ window.console.log((responseJSON && responseJSON.error) || jqXHR.status || errorThrown.message);
1102
+ } else {
1103
+ window.alert('Error requesting media from ' + that.options.oembedProxy + ' to insert: ' + errorThrown + ' (response status: ' + jqXHR.status + ')');
1104
+ }
1105
+
1106
+ $.proxy(that, 'convertBadEmbed', url)();
1107
+ }
1108
+ });
1109
+ };
1110
+
1111
+ /**
1112
+ * Get HTML using regexp
1113
+ *
1114
+ * @param {string} url
1115
+ * @return {void}
1116
+ */
1117
+
1118
+ Embeds.prototype.parseUrl = function (url) {
1119
+ var html;
1120
+
1121
+ if (!(new RegExp(['youtube', 'youtu.be', 'vimeo', 'instagram'].join('|')).test(url))) {
1122
+ $.proxy(this, 'convertBadEmbed', url)();
1123
+ return false;
1124
+ }
1125
+
1126
+ html = url.replace(/\n?/g, '')
1127
+ .replace(/^((http(s)?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/(watch\?v=|v\/)?)([a-zA-Z0-9\-_]+)(.*)?$/, '<div class="video video-youtube"><iframe width="420" height="315" src="//www.youtube.com/embed/$7" frameborder="0" allowfullscreen></iframe></div>')
1128
+ .replace(/^https?:\/\/vimeo\.com(\/.+)?\/([0-9]+)$/, '<div class="video video-vimeo"><iframe src="//player.vimeo.com/video/$2" width="500" height="281" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>')
1129
+ //.replace(/^https:\/\/twitter\.com\/(\w+)\/status\/(\d+)\/?$/, '<blockquote class="twitter-tweet" align="center" lang="en"><a href="https://twitter.com/$1/statuses/$2"></a></blockquote><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>')
1130
+ //.replace(/^https:\/\/www\.facebook\.com\/(video.php|photo.php)\?v=(\d+).+$/, '<div class="fb-post" data-href="https://www.facebook.com/photo.php?v=$2"><div class="fb-xfbml-parse-ignore"><a href="https://www.facebook.com/photo.php?v=$2">Post</a></div></div>')
1131
+ .replace(/^https?:\/\/instagram\.com\/p\/(.+)\/?$/, '<span class="instagram"><iframe src="//instagram.com/p/$1/embed/" width="612" height="710" frameborder="0" scrolling="no" allowtransparency="true"></iframe></span>');
1132
+
1133
+
1134
+ this.embed((/<("[^"]*"|'[^']*'|[^'">])*>/).test(html) ? html : false);
1135
+ };
1136
+
1137
+ /**
1138
+ * Add html to page
1139
+ *
1140
+ * @param {string} html
1141
+ * @return {void}
1142
+ */
1143
+
1144
+ Embeds.prototype.embed = function (html) {
1145
+ var $place = this.$el.find('.medium-insert-embeds-active');
1146
+
1147
+ if (!html) {
1148
+ alert('Incorrect URL format specified');
1149
+ return false;
1150
+ } else {
1151
+ $place.after(this.templates['src/js/templates/embeds-wrapper.hbs']({
1152
+ html: html
1153
+ }));
1154
+ $place.remove();
1155
+
1156
+ this.core.triggerInput();
1157
+
1158
+ if (html.indexOf('facebook') !== -1) {
1159
+ if (typeof(FB) !== 'undefined') {
1160
+ setTimeout(function () {
1161
+ FB.XFBML.parse();
1162
+ }, 2000);
1163
+ }
1164
+ }
1165
+ }
1166
+ };
1167
+
1168
+ /**
1169
+ * Convert bad oEmbed content to an actual line.
1170
+ * Instead of displaying the error message we convert the bad embed
1171
+ *
1172
+ * @param {string} content Bad content
1173
+ *
1174
+ * @return {void}
1175
+ */
1176
+ Embeds.prototype.convertBadEmbed = function (content) {
1177
+ var $place, $empty, $content,
1178
+ emptyTemplate = this.templates['src/js/templates/core-empty-line.hbs']().trim();
1179
+
1180
+ $place = this.$el.find('.medium-insert-embeds-active');
1181
+
1182
+ // convert embed node to an empty node and insert the bad embed inside
1183
+ $content = $(emptyTemplate);
1184
+ $place.before($content);
1185
+ $place.remove();
1186
+ $content.html(content);
1187
+
1188
+ // add an new empty node right after to simulate Enter press
1189
+ $empty = $(emptyTemplate);
1190
+ $content.after($empty);
1191
+
1192
+ this.core.triggerInput();
1193
+
1194
+ this.core.moveCaret($place);
1195
+ };
1196
+
1197
+ /**
1198
+ * Select clicked embed
1199
+ *
1200
+ * @param {Event} e
1201
+ * @returns {void}
1202
+ */
1203
+
1204
+ Embeds.prototype.selectEmbed = function (e) {
1205
+ if(this.core.options.enabled) {
1206
+ var $embed = $(e.target).hasClass('medium-insert-embeds') ? $(e.target) : $(e.target).closest('.medium-insert-embeds'),
1207
+ that = this;
1208
+
1209
+ $embed.addClass('medium-insert-embeds-selected');
1210
+
1211
+ setTimeout(function () {
1212
+ that.addToolbar();
1213
+
1214
+ if (that.options.captions) {
1215
+ that.core.addCaption($embed.find('figure'), that.options.captionPlaceholder);
1216
+ }
1217
+ }, 50);
1218
+ }
1219
+ };
1220
+
1221
+ /**
1222
+ * Unselect selected embed
1223
+ *
1224
+ * @param {Event} e
1225
+ * @returns {void}
1226
+ */
1227
+
1228
+ Embeds.prototype.unselectEmbed = function (e) {
1229
+ var $el = $(e.target).hasClass('medium-insert-embeds') ? $(e.target) : $(e.target).closest('.medium-insert-embeds'),
1230
+ $embed = this.$el.find('.medium-insert-embeds-selected');
1231
+
1232
+ if ($el.hasClass('medium-insert-embeds-selected')) {
1233
+ $embed.not($el).removeClass('medium-insert-embeds-selected');
1234
+ $('.medium-insert-embeds-toolbar, .medium-insert-embeds-toolbar2').remove();
1235
+ this.core.removeCaptions($el.find('figcaption'));
1236
+
1237
+ if ($(e.target).is('.medium-insert-caption-placeholder') || $(e.target).is('figcaption')) {
1238
+ $el.removeClass('medium-insert-embeds-selected');
1239
+ this.core.removeCaptionPlaceholder($el.find('figure'));
1240
+ }
1241
+ return;
1242
+ }
1243
+
1244
+ $embed.removeClass('medium-insert-embeds-selected');
1245
+ $('.medium-insert-embeds-toolbar, .medium-insert-embeds-toolbar2').remove();
1246
+
1247
+ if ($(e.target).is('.medium-insert-caption-placeholder')) {
1248
+ this.core.removeCaptionPlaceholder($el.find('figure'));
1249
+ } else if ($(e.target).is('figcaption') === false) {
1250
+ this.core.removeCaptions();
1251
+ }
1252
+ };
1253
+
1254
+ /**
1255
+ * Remove embed
1256
+ *
1257
+ * @param {Event} e
1258
+ * @returns {void}
1259
+ */
1260
+
1261
+ Embeds.prototype.removeEmbed = function (e) {
1262
+ var $embed, $empty;
1263
+
1264
+ if (e.which === 8 || e.which === 46) {
1265
+ $embed = this.$el.find('.medium-insert-embeds-selected');
1266
+
1267
+ if ($embed.length) {
1268
+ e.preventDefault();
1269
+
1270
+ $('.medium-insert-embeds-toolbar, .medium-insert-embeds-toolbar2').remove();
1271
+
1272
+ $empty = $(this.templates['src/js/templates/core-empty-line.hbs']().trim());
1273
+ $embed.before($empty);
1274
+ $embed.remove();
1275
+
1276
+ // Hide addons
1277
+ this.core.hideAddons();
1278
+
1279
+ this.core.moveCaret($empty);
1280
+ this.core.triggerInput();
1281
+ }
1282
+ }
1283
+ };
1284
+
1285
+ /**
1286
+ * Adds embed toolbar to editor
1287
+ *
1288
+ * @returns {void}
1289
+ */
1290
+
1291
+ Embeds.prototype.addToolbar = function () {
1292
+ var $embed = this.$el.find('.medium-insert-embeds-selected'),
1293
+ active = false,
1294
+ $toolbar, $toolbar2, top;
1295
+
1296
+ if ($embed.length === 0) {
1297
+ return;
1298
+ }
1299
+
1300
+ var mediumEditor = this.core.getEditor();
1301
+ var toolbarContainer = mediumEditor.options.elementsContainer || 'body';
1302
+
1303
+ $(toolbarContainer).append(this.templates['src/js/templates/embeds-toolbar.hbs']({
1304
+ styles: this.options.styles,
1305
+ actions: this.options.actions
1306
+ }).trim());
1307
+
1308
+ $toolbar = $('.medium-insert-embeds-toolbar');
1309
+ $toolbar2 = $('.medium-insert-embeds-toolbar2');
1310
+
1311
+ top = $embed.offset().top - $toolbar.height() - 8 - 2 - 5; // 8px - hight of an arrow under toolbar, 2px - height of an embed outset, 5px - distance from an embed
1312
+ if (top < 0) {
1313
+ top = 0;
1314
+ }
1315
+
1316
+ $toolbar
1317
+ .css({
1318
+ top: top,
1319
+ left: $embed.offset().left + $embed.width() / 2 - $toolbar.width() / 2
1320
+ })
1321
+ .show();
1322
+
1323
+ $toolbar2
1324
+ .css({
1325
+ top: $embed.offset().top + 2, // 2px - distance from a border
1326
+ left: $embed.offset().left + $embed.width() - $toolbar2.width() - 4 // 4px - distance from a border
1327
+ })
1328
+ .show();
1329
+
1330
+ $toolbar.find('button').each(function () {
1331
+ if ($embed.hasClass('medium-insert-embeds-'+ $(this).data('action'))) {
1332
+ $(this).addClass('medium-editor-button-active');
1333
+ active = true;
1334
+ }
1335
+ });
1336
+
1337
+ if (active === false) {
1338
+ $toolbar.find('button').first().addClass('medium-editor-button-active');
1339
+ }
1340
+ };
1341
+
1342
+ /**
1343
+ * Fires toolbar action
1344
+ *
1345
+ * @param {Event} e
1346
+ * @returns {void}
1347
+ */
1348
+
1349
+ Embeds.prototype.toolbarAction = function (e) {
1350
+ var $button = $(e.target).is('button') ? $(e.target) : $(e.target).closest('button'),
1351
+ $li = $button.closest('li'),
1352
+ $ul = $li.closest('ul'),
1353
+ $lis = $ul.find('li'),
1354
+ $embed = this.$el.find('.medium-insert-embeds-selected'),
1355
+ that = this;
1356
+
1357
+ $button.addClass('medium-editor-button-active');
1358
+ $li.siblings().find('.medium-editor-button-active').removeClass('medium-editor-button-active');
1359
+
1360
+ $lis.find('button').each(function () {
1361
+ var className = 'medium-insert-embeds-'+ $(this).data('action');
1362
+
1363
+ if ($(this).hasClass('medium-editor-button-active')) {
1364
+ $embed.addClass(className);
1365
+
1366
+ if (that.options.styles[$(this).data('action')].added) {
1367
+ that.options.styles[$(this).data('action')].added($embed);
1368
+ }
1369
+ } else {
1370
+ $embed.removeClass(className);
1371
+
1372
+ if (that.options.styles[$(this).data('action')].removed) {
1373
+ that.options.styles[$(this).data('action')].removed($embed);
1374
+ }
1375
+ }
1376
+ });
1377
+
1378
+ this.core.triggerInput();
1379
+ };
1380
+
1381
+ /**
1382
+ * Fires toolbar2 action
1383
+ *
1384
+ * @param {Event} e
1385
+ * @returns {void}
1386
+ */
1387
+
1388
+ Embeds.prototype.toolbar2Action = function (e) {
1389
+ var $button = $(e.target).is('button') ? $(e.target) : $(e.target).closest('button'),
1390
+ callback = this.options.actions[$button.data('action')].clicked;
1391
+
1392
+ if (callback) {
1393
+ callback(this.$el.find('.medium-insert-embeds-selected'));
1394
+ }
1395
+
1396
+ this.core.triggerInput();
1397
+ };
1398
+
1399
+ /** Plugin initialization */
1400
+
1401
+ $.fn[pluginName + addonName] = function (options) {
1402
+ return this.each(function () {
1403
+ if (!$.data(this, 'plugin_' + pluginName + addonName)) {
1404
+ $.data(this, 'plugin_' + pluginName + addonName, new Embeds(this, options));
1405
+ }
1406
+ });
1407
+ };
1408
+
1409
+ })(jQuery, window, document);
1410
+
1411
+ /*global MediumEditor*/
1412
+
1413
+ ;(function ($, window, document, Util, undefined) {
1414
+
1415
+ 'use strict';
1416
+
1417
+ /** Default values */
1418
+ var pluginName = 'mediumInsert',
1419
+ addonName = 'Images', // first char is uppercase
1420
+ defaults = {
1421
+ label: '<span class="fa fa-camera"></span>',
1422
+ deleteMethod: 'POST',
1423
+ deleteScript: 'delete.php',
1424
+ preview: true,
1425
+ captions: true,
1426
+ captionPlaceholder: 'Type caption for image (optional)',
1427
+ autoGrid: 3,
1428
+ fileUploadOptions: { // See https://github.com/blueimp/jQuery-File-Upload/wiki/Options
1429
+ url: 'upload.php',
1430
+ acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i
1431
+ },
1432
+ fileDeleteOptions: {},
1433
+ styles: {
1434
+ wide: {
1435
+ label: '<span class="fa fa-align-justify"></span>',
1436
+ // added: function ($el) {},
1437
+ // removed: function ($el) {}
1438
+ },
1439
+ left: {
1440
+ label: '<span class="fa fa-align-left"></span>',
1441
+ // added: function ($el) {},
1442
+ // removed: function ($el) {}
1443
+ },
1444
+ right: {
1445
+ label: '<span class="fa fa-align-right"></span>',
1446
+ // added: function ($el) {},
1447
+ // removed: function ($el) {}
1448
+ },
1449
+ grid: {
1450
+ label: '<span class="fa fa-th"></span>',
1451
+ // added: function ($el) {},
1452
+ // removed: function ($el) {}
1453
+ }
1454
+ },
1455
+ actions: {
1456
+ remove: {
1457
+ label: '<span class="fa fa-times"></span>',
1458
+ clicked: function () {
1459
+ var $event = $.Event('keydown');
1460
+
1461
+ $event.which = 8;
1462
+ $(document).trigger($event);
1463
+ }
1464
+ }
1465
+ },
1466
+ sorting: function () {
1467
+ var that = this;
1468
+
1469
+ $('.medium-insert-images').sortable({
1470
+ group: 'medium-insert-images',
1471
+ containerSelector: '.medium-insert-images',
1472
+ itemSelector: 'figure',
1473
+ placeholder: '<figure class="placeholder">',
1474
+ handle: 'img',
1475
+ nested: false,
1476
+ vertical: false,
1477
+ afterMove: function () {
1478
+ that.core.triggerInput();
1479
+ }
1480
+ });
1481
+ },
1482
+ messages: {
1483
+ acceptFileTypesError: 'This file is not in a supported format: ',
1484
+ maxFileSizeError: 'This file is too big: '
1485
+ }
1486
+ // uploadCompleted: function ($el, data) {}
1487
+ };
1488
+
1489
+ /**
1490
+ * Images object
1491
+ *
1492
+ * Sets options, variables and calls init() function
1493
+ *
1494
+ * @constructor
1495
+ * @param {DOM} el - DOM element to init the plugin on
1496
+ * @param {object} options - Options to override defaults
1497
+ * @return {void}
1498
+ */
1499
+
1500
+ function Images (el, options) {
1501
+ this.el = el;
1502
+ this.$el = $(el);
1503
+ this.$currentImage = null;
1504
+ this.templates = window.MediumInsert.Templates;
1505
+ this.core = this.$el.data('plugin_'+ pluginName);
1506
+
1507
+ this.options = $.extend(true, {}, defaults, options);
1508
+
1509
+ this._defaults = defaults;
1510
+ this._name = pluginName;
1511
+
1512
+ // Allow image preview only in browsers, that support's that
1513
+ if (this.options.preview && !window.FileReader) {
1514
+ this.options.preview = false;
1515
+ }
1516
+
1517
+ // Extend editor's functions
1518
+ if (this.core.getEditor()) {
1519
+ this.core.getEditor()._serializePreImages = this.core.getEditor().serialize;
1520
+ this.core.getEditor().serialize = this.editorSerialize;
1521
+ }
1522
+
1523
+ this.init();
1524
+ }
1525
+
1526
+ /**
1527
+ * Initialization
1528
+ *
1529
+ * @return {void}
1530
+ */
1531
+
1532
+ Images.prototype.init = function () {
1533
+ var $images = this.$el.find('.medium-insert-images');
1534
+
1535
+ $images.find('figcaption').attr('contenteditable', true);
1536
+ $images.find('figure').attr('contenteditable', false);
1537
+
1538
+ this.events();
1539
+ this.backwardsCompatibility();
1540
+ this.sorting();
1541
+ };
1542
+
1543
+ /**
1544
+ * Event listeners
1545
+ *
1546
+ * @return {void}
1547
+ */
1548
+
1549
+ Images.prototype.events = function () {
1550
+ $(document)
1551
+ .on('click', $.proxy(this, 'unselectImage'))
1552
+ .on('keydown', $.proxy(this, 'removeImage'))
1553
+ .on('click', '.medium-insert-images-toolbar .medium-editor-action', $.proxy(this, 'toolbarAction'))
1554
+ .on('click', '.medium-insert-images-toolbar2 .medium-editor-action', $.proxy(this, 'toolbar2Action'));
1555
+
1556
+ this.$el
1557
+ .on('click', '.medium-insert-images img', $.proxy(this, 'selectImage'));
1558
+ };
1559
+
1560
+ /**
1561
+ * Replace v0.* class names with new ones
1562
+ *
1563
+ * @return {void}
1564
+ */
1565
+
1566
+ Images.prototype.backwardsCompatibility = function () {
1567
+ this.$el.find('.mediumInsert')
1568
+ .removeClass('mediumInsert')
1569
+ .addClass('medium-insert-images');
1570
+
1571
+ this.$el.find('.medium-insert-images.small')
1572
+ .removeClass('small')
1573
+ .addClass('medium-insert-images-left');
1574
+ };
1575
+
1576
+ /**
1577
+ * Extend editor's serialize function
1578
+ *
1579
+ * @return {object} Serialized data
1580
+ */
1581
+
1582
+ Images.prototype.editorSerialize = function () {
1583
+ var data = this._serializePreImages();
1584
+
1585
+ $.each(data, function (key) {
1586
+ var $data = $('<div />').html(data[key].value);
1587
+
1588
+ $data.find('.medium-insert-images').find('figcaption, figure').removeAttr('contenteditable');
1589
+
1590
+ data[key].value = $data.html();
1591
+ });
1592
+
1593
+ return data;
1594
+ };
1595
+
1596
+ /**
1597
+ * Add image
1598
+ *
1599
+ * @return {void}
1600
+ */
1601
+
1602
+ Images.prototype.add = function () {
1603
+ var that = this,
1604
+ $file = $(this.templates['src/js/templates/images-fileupload.hbs']()),
1605
+ fileUploadOptions = {
1606
+ dataType: 'json',
1607
+ add: function (e, data) {
1608
+ $.proxy(that, 'uploadAdd', e, data)();
1609
+ },
1610
+ done: function (e, data) {
1611
+ $.proxy(that, 'uploadDone', e, data)();
1612
+ }
1613
+ };
1614
+
1615
+ // Only add progress callbacks for browsers that support XHR2,
1616
+ // and test for XHR2 per:
1617
+ // http://stackoverflow.com/questions/6767887/
1618
+ // what-is-the-best-way-to-check-for-xhr2-file-upload-support
1619
+ if (new XMLHttpRequest().upload) {
1620
+ fileUploadOptions.progress = function (e, data) {
1621
+ $.proxy(that, 'uploadProgress', e, data)();
1622
+ };
1623
+
1624
+ fileUploadOptions.progressall = function (e, data) {
1625
+ $.proxy(that, 'uploadProgressall', e, data)();
1626
+ };
1627
+ }
1628
+
1629
+ $file.fileupload($.extend(true, {}, this.options.fileUploadOptions, fileUploadOptions));
1630
+
1631
+ $file.click();
1632
+ };
1633
+
1634
+ /**
1635
+ * Callback invoked as soon as files are added to the fileupload widget - via file input selection, drag & drop or add API call.
1636
+ * https://github.com/blueimp/jQuery-File-Upload/wiki/Options#add
1637
+ *
1638
+ * @param {Event} e
1639
+ * @param {object} data
1640
+ * @return {void}
1641
+ */
1642
+
1643
+ Images.prototype.uploadAdd = function (e, data) {
1644
+ var $place = this.$el.find('.medium-insert-active'),
1645
+ that = this,
1646
+ uploadErrors = [],
1647
+ file = data.files[0],
1648
+ acceptFileTypes = this.options.fileUploadOptions.acceptFileTypes,
1649
+ maxFileSize = this.options.fileUploadOptions.maxFileSize,
1650
+ reader;
1651
+
1652
+ if (acceptFileTypes && !acceptFileTypes.test(file['type'])) {
1653
+ uploadErrors.push(this.options.messages.acceptFileTypesError + file['name']);
1654
+ } else if (maxFileSize && file['size'] > maxFileSize) {
1655
+ uploadErrors.push(this.options.messages.maxFileSizeError + file['name']);
1656
+ }
1657
+ if (uploadErrors.length > 0) {
1658
+ alert(uploadErrors.join("\n"));
1659
+ return;
1660
+ }
1661
+
1662
+ this.core.hideButtons();
1663
+
1664
+ // Replace paragraph with div, because figure elements can't be inside paragraph
1665
+ if ($place.is('p')) {
1666
+ $place.replaceWith('<div class="medium-insert-active">'+ $place.html() +'</div>');
1667
+ $place = this.$el.find('.medium-insert-active');
1668
+ this.core.moveCaret($place);
1669
+ }
1670
+
1671
+ $place.addClass('medium-insert-images');
1672
+
1673
+ if (this.options.preview === false && $place.find('progress').length === 0 && (new XMLHttpRequest().upload)) {
1674
+ $place.append(this.templates['src/js/templates/images-progressbar.hbs']());
1675
+ }
1676
+
1677
+ if (data.autoUpload || (data.autoUpload !== false && $(e.target).fileupload('option', 'autoUpload'))) {
1678
+ data.process().done(function () {
1679
+ // If preview is set to true, let the showImage handle the upload start
1680
+ if (that.options.preview) {
1681
+ reader = new FileReader();
1682
+
1683
+ reader.onload = function (e) {
1684
+ $.proxy(that, 'showImage', e.target.result, data)();
1685
+ };
1686
+
1687
+ reader.readAsDataURL(data.files[0]);
1688
+ } else {
1689
+ data.submit();
1690
+ }
1691
+ });
1692
+ }
1693
+ };
1694
+
1695
+ /**
1696
+ * Callback for global upload progress events
1697
+ * https://github.com/blueimp/jQuery-File-Upload/wiki/Options#progressall
1698
+ *
1699
+ * @param {Event} e
1700
+ * @param {object} data
1701
+ * @return {void}
1702
+ */
1703
+
1704
+ Images.prototype.uploadProgressall = function (e, data) {
1705
+ var progress, $progressbar;
1706
+
1707
+ if (this.options.preview === false) {
1708
+ progress = parseInt(data.loaded / data.total * 100, 10);
1709
+ $progressbar = this.$el.find('.medium-insert-active').find('progress');
1710
+
1711
+ $progressbar
1712
+ .attr('value', progress)
1713
+ .text(progress);
1714
+
1715
+ if (progress === 100) {
1716
+ $progressbar.remove();
1717
+ }
1718
+ }
1719
+ };
1720
+
1721
+ /**
1722
+ * Callback for upload progress events.
1723
+ * https://github.com/blueimp/jQuery-File-Upload/wiki/Options#progress
1724
+ *
1725
+ * @param {Event} e
1726
+ * @param {object} data
1727
+ * @return {void}
1728
+ */
1729
+
1730
+ Images.prototype.uploadProgress = function (e, data) {
1731
+ var progress, $progressbar;
1732
+
1733
+ if (this.options.preview) {
1734
+ progress = 100 - parseInt(data.loaded / data.total * 100, 10);
1735
+ $progressbar = data.context.find('.medium-insert-images-progress');
1736
+
1737
+ $progressbar.css('width', progress +'%');
1738
+
1739
+ if (progress === 0) {
1740
+ $progressbar.remove();
1741
+ }
1742
+ }
1743
+ };
1744
+
1745
+ /**
1746
+ * Callback for successful upload requests.
1747
+ * https://github.com/blueimp/jQuery-File-Upload/wiki/Options#done
1748
+ *
1749
+ * @param {Event} e
1750
+ * @param {object} data
1751
+ * @return {void}
1752
+ */
1753
+
1754
+ Images.prototype.uploadDone = function (e, data) {
1755
+ $.proxy(this, 'showImage', data.result.files[0].url, data)();
1756
+
1757
+ this.core.clean();
1758
+ this.sorting();
1759
+ };
1760
+
1761
+ /**
1762
+ * Add uploaded / preview image to DOM
1763
+ *
1764
+ * @param {string} img
1765
+ * @returns {void}
1766
+ */
1767
+
1768
+ Images.prototype.showImage = function (img, data) {
1769
+ var $place = this.$el.find('.medium-insert-active'),
1770
+ domImage,
1771
+ that;
1772
+
1773
+ // Hide editor's placeholder
1774
+ $place.click();
1775
+
1776
+ // If preview is allowed and preview image already exists,
1777
+ // replace it with uploaded image
1778
+ that = this;
1779
+ if (this.options.preview && data.context) {
1780
+ domImage = this.getDOMImage();
1781
+ domImage.onload = function () {
1782
+ data.context.find('img').attr('src', domImage.src);
1783
+
1784
+ if (this.options.uploadCompleted) {
1785
+ this.options.uploadCompleted(data.context, data);
1786
+ }
1787
+
1788
+ that.core.triggerInput();
1789
+ }.bind(this);
1790
+ domImage.src = img;
1791
+ } else {
1792
+ data.context = $(this.templates['src/js/templates/images-image.hbs']({
1793
+ img: img,
1794
+ progress: this.options.preview
1795
+ })).appendTo($place);
1796
+
1797
+ $place.find('br').remove();
1798
+
1799
+ if (this.options.autoGrid && $place.find('figure').length >= this.options.autoGrid) {
1800
+ $.each(this.options.styles, function (style, options) {
1801
+ var className = 'medium-insert-images-'+ style;
1802
+
1803
+ $place.removeClass(className);
1804
+
1805
+ if (options.removed) {
1806
+ options.removed($place);
1807
+ }
1808
+ });
1809
+
1810
+ $place.addClass('medium-insert-images-grid');
1811
+
1812
+ if (this.options.styles.grid.added) {
1813
+ this.options.styles.grid.added($place);
1814
+ }
1815
+ }
1816
+
1817
+ if (this.options.preview) {
1818
+ data.submit();
1819
+ } else if (this.options.uploadCompleted) {
1820
+ this.options.uploadCompleted(data.context, data);
1821
+ }
1822
+ }
1823
+
1824
+ this.core.triggerInput();
1825
+
1826
+ return data.context;
1827
+ };
1828
+
1829
+ Images.prototype.getDOMImage = function () {
1830
+ return new window.Image();
1831
+ };
1832
+
1833
+ /**
1834
+ * Select clicked image
1835
+ *
1836
+ * @param {Event} e
1837
+ * @returns {void}
1838
+ */
1839
+
1840
+ Images.prototype.selectImage = function (e) {
1841
+ if(this.core.options.enabled) {
1842
+ var $image = $(e.target),
1843
+ that = this;
1844
+
1845
+ this.$currentImage = $image;
1846
+
1847
+ // Hide keyboard on mobile devices
1848
+ this.$el.blur();
1849
+
1850
+ $image.addClass('medium-insert-image-active');
1851
+ $image.closest('.medium-insert-images').addClass('medium-insert-active');
1852
+
1853
+ setTimeout(function () {
1854
+ that.addToolbar();
1855
+
1856
+ if (that.options.captions) {
1857
+ that.core.addCaption($image.closest('figure'), that.options.captionPlaceholder);
1858
+ }
1859
+ }, 50);
1860
+ }
1861
+ };
1862
+
1863
+ /**
1864
+ * Unselect selected image
1865
+ *
1866
+ * @param {Event} e
1867
+ * @returns {void}
1868
+ */
1869
+
1870
+ Images.prototype.unselectImage = function (e) {
1871
+ var $el = $(e.target),
1872
+ $image = this.$el.find('.medium-insert-image-active');
1873
+
1874
+ if ($el.is('img') && $el.hasClass('medium-insert-image-active')) {
1875
+ $image.not($el).removeClass('medium-insert-image-active');
1876
+ $('.medium-insert-images-toolbar, .medium-insert-images-toolbar2').remove();
1877
+ this.core.removeCaptions($el);
1878
+ return;
1879
+ }
1880
+
1881
+ $image.removeClass('medium-insert-image-active');
1882
+ $('.medium-insert-images-toolbar, .medium-insert-images-toolbar2').remove();
1883
+
1884
+ if ($el.is('.medium-insert-caption-placeholder')) {
1885
+ this.core.removeCaptionPlaceholder($image.closest('figure'));
1886
+ } else if ($el.is('figcaption') === false) {
1887
+ this.core.removeCaptions();
1888
+ }
1889
+ this.$currentImage = null;
1890
+ };
1891
+
1892
+ /**
1893
+ * Remove image
1894
+ *
1895
+ * @param {Event} e
1896
+ * @returns {void}
1897
+ */
1898
+
1899
+ Images.prototype.removeImage = function (e) {
1900
+ var $image, $parent, $empty;
1901
+
1902
+ if (e.which === 8 || e.which === 46) {
1903
+ $image = this.$el.find('.medium-insert-image-active');
1904
+
1905
+ if ($image.length) {
1906
+ e.preventDefault();
1907
+
1908
+ this.deleteFile($image.attr('src'));
1909
+
1910
+ $parent = $image.closest('.medium-insert-images');
1911
+ $image.closest('figure').remove();
1912
+
1913
+ $('.medium-insert-images-toolbar, .medium-insert-images-toolbar2').remove();
1914
+
1915
+ if ($parent.find('figure').length === 0) {
1916
+ $empty = $parent.next();
1917
+ if ($empty.is('p') === false || $empty.text() !== '') {
1918
+ $empty = $(this.templates['src/js/templates/core-empty-line.hbs']().trim());
1919
+ $parent.before($empty);
1920
+ }
1921
+ $parent.remove();
1922
+
1923
+ // Hide addons
1924
+ this.core.hideAddons();
1925
+
1926
+ this.core.moveCaret($empty);
1927
+ }
1928
+
1929
+ this.core.triggerInput();
1930
+ }
1931
+ }
1932
+ };
1933
+
1934
+ /**
1935
+ * Makes ajax call to deleteScript
1936
+ *
1937
+ * @param {String} file File name
1938
+ * @returns {void}
1939
+ */
1940
+
1941
+ Images.prototype.deleteFile = function (file) {
1942
+ if (this.options.deleteScript) {
1943
+ // If deleteMethod is somehow undefined, defaults to POST
1944
+ var method = this.options.deleteMethod || 'POST';
1945
+
1946
+ $.ajax($.extend(true, {}, {
1947
+ url: this.options.deleteScript,
1948
+ type: method,
1949
+ data: { file: file }
1950
+ }, this.options.fileDeleteOptions));
1951
+ }
1952
+ };
1953
+
1954
+ /**
1955
+ * Adds image toolbar to editor
1956
+ *
1957
+ * @returns {void}
1958
+ */
1959
+
1960
+ Images.prototype.addToolbar = function () {
1961
+ var $image = this.$el.find('.medium-insert-image-active'),
1962
+ $p = $image.closest('.medium-insert-images'),
1963
+ active = false,
1964
+ $toolbar, $toolbar2, top;
1965
+
1966
+ var mediumEditor = this.core.getEditor();
1967
+ var toolbarContainer = mediumEditor.options.elementsContainer || 'body';
1968
+
1969
+ $(toolbarContainer).append(this.templates['src/js/templates/images-toolbar.hbs']({
1970
+ styles: this.options.styles,
1971
+ actions: this.options.actions
1972
+ }).trim());
1973
+
1974
+ $toolbar = $('.medium-insert-images-toolbar');
1975
+ $toolbar2 = $('.medium-insert-images-toolbar2');
1976
+
1977
+ top = $image.offset().top - $toolbar.height() - 8 - 2 - 5; // 8px - hight of an arrow under toolbar, 2px - height of an image outset, 5px - distance from an image
1978
+ if (top < 0) {
1979
+ top = 0;
1980
+ }
1981
+
1982
+ $toolbar
1983
+ .css({
1984
+ top: top,
1985
+ left: $image.offset().left + $image.width() / 2 - $toolbar.width() / 2
1986
+ })
1987
+ .show();
1988
+
1989
+ $toolbar2
1990
+ .css({
1991
+ top: $image.offset().top + 2, // 2px - distance from a border
1992
+ left: $image.offset().left + $image.width() - $toolbar2.width() - 4 // 4px - distance from a border
1993
+ })
1994
+ .show();
1995
+
1996
+ $toolbar.find('button').each(function () {
1997
+ if ($p.hasClass('medium-insert-images-'+ $(this).data('action'))) {
1998
+ $(this).addClass('medium-editor-button-active');
1999
+ active = true;
2000
+ }
2001
+ });
2002
+
2003
+ if (active === false) {
2004
+ $toolbar.find('button').first().addClass('medium-editor-button-active');
2005
+ }
2006
+ };
2007
+
2008
+ /**
2009
+ * Fires toolbar action
2010
+ *
2011
+ * @param {Event} e
2012
+ * @returns {void}
2013
+ */
2014
+
2015
+ Images.prototype.toolbarAction = function (e) {
2016
+ if (this.$currentImage === null) return;
2017
+ var $button = $(e.target).is('button') ? $(e.target) : $(e.target).closest('button'),
2018
+ $li = $button.closest('li'),
2019
+ $ul = $li.closest('ul'),
2020
+ $lis = $ul.find('li'),
2021
+ $p = this.$el.find('.medium-insert-active'),
2022
+ that = this;
2023
+
2024
+ $button.addClass('medium-editor-button-active');
2025
+ $li.siblings().find('.medium-editor-button-active').removeClass('medium-editor-button-active');
2026
+
2027
+ $lis.find('button').each(function () {
2028
+ var className = 'medium-insert-images-'+ $(this).data('action');
2029
+
2030
+ if ($(this).hasClass('medium-editor-button-active')) {
2031
+ $p.addClass(className);
2032
+
2033
+ if (that.options.styles[$(this).data('action')].added) {
2034
+ that.options.styles[$(this).data('action')].added($p);
2035
+ }
2036
+ } else {
2037
+ $p.removeClass(className);
2038
+
2039
+ if (that.options.styles[$(this).data('action')].removed) {
2040
+ that.options.styles[$(this).data('action')].removed($p);
2041
+ }
2042
+ }
2043
+ });
2044
+
2045
+ this.core.hideButtons();
2046
+
2047
+ this.core.triggerInput();
2048
+ };
2049
+
2050
+ /**
2051
+ * Fires toolbar2 action
2052
+ *
2053
+ * @param {Event} e
2054
+ * @returns {void}
2055
+ */
2056
+
2057
+ Images.prototype.toolbar2Action = function (e) {
2058
+ if (this.$currentImage === null) return;
2059
+ var $button = $(e.target).is('button') ? $(e.target) : $(e.target).closest('button'),
2060
+ callback = this.options.actions[$button.data('action')].clicked;
2061
+
2062
+ if (callback) {
2063
+ callback(this.$el.find('.medium-insert-image-active'));
2064
+ }
2065
+
2066
+ this.core.hideButtons();
2067
+
2068
+ this.core.triggerInput();
2069
+ };
2070
+
2071
+ /**
2072
+ * Initialize sorting
2073
+ *
2074
+ * @returns {void}
2075
+ */
2076
+
2077
+ Images.prototype.sorting = function () {
2078
+ $.proxy(this.options.sorting, this)();
2079
+ };
2080
+
2081
+ /** Plugin initialization */
2082
+
2083
+ $.fn[pluginName + addonName] = function (options) {
2084
+ return this.each(function () {
2085
+ if (!$.data(this, 'plugin_' + pluginName + addonName)) {
2086
+ $.data(this, 'plugin_' + pluginName + addonName, new Images(this, options));
2087
+ }
2088
+ });
2089
+ };
2090
+
2091
+ })(jQuery, window, document, MediumEditor.util);