vue_crud 0.1.9.6 → 0.1.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/vue_crud/froala_generator.rb +12 -0
  3. data/lib/generators/vue_crud/sortable.rb +10 -0
  4. data/lib/generators/vue_crud/templates/assets/images/froala_editor/color_picker.png +0 -0
  5. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/froala_editor.js +11089 -0
  6. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/en_gb.js +178 -0
  7. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/zh_cn.js +234 -0
  8. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/zh_tw.js +234 -0
  9. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/align.js +119 -0
  10. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/char_counter.js +150 -0
  11. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/code_beautifier.js +3271 -0
  12. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/code_view.js +311 -0
  13. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/colors.js +350 -0
  14. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/draggable.js +379 -0
  15. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/emoticons.js +347 -0
  16. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/entities.js +113 -0
  17. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/file.js +609 -0
  18. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/font_family.js +178 -0
  19. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/font_size.js +116 -0
  20. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/forms.js +415 -0
  21. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/fullscreen.js +209 -0
  22. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/image.js +2401 -0
  23. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/image_manager.js +921 -0
  24. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/inline_style.js +86 -0
  25. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/line_breaker.js +435 -0
  26. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/link.js +997 -0
  27. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/lists.js +382 -0
  28. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/paragraph_format.js +293 -0
  29. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/paragraph_style.js +139 -0
  30. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/quick_insert.js +342 -0
  31. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/quote.js +138 -0
  32. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/save.js +176 -0
  33. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/table.js +3123 -0
  34. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/url.js +113 -0
  35. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/video.js +1135 -0
  36. data/lib/generators/vue_crud/templates/assets/javascripts/sortable.js +1249 -0
  37. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/froala_editor.scss +1008 -0
  38. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/froala_style.scss +273 -0
  39. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/char_counter.scss +46 -0
  40. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/code_view.scss +102 -0
  41. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/colors.scss +129 -0
  42. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/draggable.scss +32 -0
  43. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/emoticons.scss +27 -0
  44. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/file.scss +135 -0
  45. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/fullscreen.scss +28 -0
  46. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/image.scss +233 -0
  47. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/image_manager.scss +370 -0
  48. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/line_breaker.scss +26 -0
  49. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/quick_insert.scss +56 -0
  50. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/table.scss +156 -0
  51. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/video.scss +136 -0
  52. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/dark.scss +1087 -0
  53. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/gray.scss +1087 -0
  54. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/red.scss +1087 -0
  55. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/royal.scss +1087 -0
  56. data/lib/generators/vue_crud/templates/vue_crud.html +54 -71
  57. data/lib/vue_crud/version.rb +1 -1
  58. data/vendor/assets/javascripts/vue_crud.js +212 -59
  59. metadata +56 -2
@@ -0,0 +1,2401 @@
1
+ /*!
2
+ * froala_editor v2.3.3 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2016 Froala Labs
5
+ */
6
+
7
+ (function (factory) {
8
+ if (typeof define === 'function' && define.amd) {
9
+ // AMD. Register as an anonymous module.
10
+ define(['jquery'], factory);
11
+ } else if (typeof module === 'object' && module.exports) {
12
+ // Node/CommonJS
13
+ module.exports = function( root, jQuery ) {
14
+ if ( jQuery === undefined ) {
15
+ // require('jQuery') returns a factory that requires window to
16
+ // build a jQuery instance, we normalize how we use modules
17
+ // that require this pattern but the window provided is a noop
18
+ // if it's defined (how jquery works)
19
+ if ( typeof window !== 'undefined' ) {
20
+ jQuery = require('jquery');
21
+ }
22
+ else {
23
+ jQuery = require('jquery')(root);
24
+ }
25
+ }
26
+ factory(jQuery);
27
+ return jQuery;
28
+ };
29
+ } else {
30
+ // Browser globals
31
+ factory(jQuery);
32
+ }
33
+ }(function ($) {
34
+
35
+ 'use strict';
36
+
37
+ $.extend($.FE.POPUP_TEMPLATES, {
38
+ 'image.insert': '[_BUTTONS_][_UPLOAD_LAYER_][_BY_URL_LAYER_][_PROGRESS_BAR_]',
39
+ 'image.edit': '[_BUTTONS_]',
40
+ 'image.alt': '[_BUTTONS_][_ALT_LAYER_]',
41
+ 'image.size': '[_BUTTONS_][_SIZE_LAYER_]'
42
+ })
43
+
44
+ $.extend($.FE.DEFAULTS, {
45
+ imageInsertButtons: ['imageBack', '|', 'imageUpload', 'imageByURL'],
46
+ imageEditButtons: ['imageReplace', 'imageAlign', 'imageRemove', '|', 'imageLink', 'linkOpen', 'linkEdit', 'linkRemove', '-', 'imageDisplay', 'imageStyle', 'imageAlt', 'imageSize'],
47
+ imageAltButtons: ['imageBack', '|'],
48
+ imageSizeButtons: ['imageBack', '|'],
49
+ imageUploadURL: 'https://i.froala.com/upload',
50
+ imageUploadParam: 'file',
51
+ imageUploadParams: {},
52
+ imageUploadToS3: false,
53
+ imageUploadMethod: 'POST',
54
+ imageMaxSize: 10 * 1024 * 1024,
55
+ imageAllowedTypes: ['jpeg', 'jpg', 'png', 'gif', 'svg+xml'],
56
+ imageResize: true,
57
+ imageResizeWithPercent: false,
58
+ imageRoundPercent: false,
59
+ imageDefaultWidth: 300,
60
+ imageDefaultAlign: 'center',
61
+ imageDefaultDisplay: 'block',
62
+ imageSplitHTML: false,
63
+ imageStyles: {
64
+ 'fr-rounded': 'Rounded',
65
+ 'fr-bordered': 'Bordered'
66
+ },
67
+ imageMove: true,
68
+ imageMultipleStyles: true,
69
+ imageTextNear: true,
70
+ imagePaste: true,
71
+ imagePasteProcess: false,
72
+ imageMinWidth: 16,
73
+ imageOutputSize: false
74
+ });
75
+
76
+ $.FE.PLUGINS.image = function (editor) {
77
+ var $current_image;
78
+ var $image_resizer;
79
+ var $handler;
80
+ var $overlay;
81
+ var mousedown = false;
82
+
83
+ var BAD_LINK = 1;
84
+ var MISSING_LINK = 2;
85
+ var ERROR_DURING_UPLOAD = 3;
86
+ var BAD_RESPONSE = 4;
87
+ var MAX_SIZE_EXCEEDED = 5;
88
+ var BAD_FILE_TYPE = 6;
89
+ var NO_CORS_IE = 7;
90
+
91
+ var error_messages = {};
92
+ error_messages[BAD_LINK] = 'Image cannot be loaded from the passed link.',
93
+ error_messages[MISSING_LINK] = 'No link in upload response.',
94
+ error_messages[ERROR_DURING_UPLOAD] = 'Error during file upload.',
95
+ error_messages[BAD_RESPONSE] = 'Parsing response failed.',
96
+ error_messages[MAX_SIZE_EXCEEDED] = 'File is too large.',
97
+ error_messages[BAD_FILE_TYPE] = 'Image file type is invalid.',
98
+ error_messages[NO_CORS_IE] = 'Files can be uploaded only to same domain in IE 8 and IE 9.'
99
+
100
+ /**
101
+ * Refresh the image insert popup.
102
+ */
103
+
104
+ function _refreshInsertPopup () {
105
+ var $popup = editor.popups.get('image.insert');
106
+
107
+ var $url_input = $popup.find('.fr-image-by-url-layer input');
108
+ $url_input.val('');
109
+
110
+ if ($current_image) {
111
+ $url_input.val($current_image.attr('src'));
112
+ }
113
+
114
+ $url_input.trigger('change');
115
+ }
116
+
117
+ /**
118
+ * Show the image upload popup.
119
+ */
120
+
121
+ function showInsertPopup () {
122
+ var $btn = editor.$tb.find('.fr-command[data-cmd="insertImage"]');
123
+
124
+ var $popup = editor.popups.get('image.insert');
125
+ if (!$popup) $popup = _initInsertPopup();
126
+
127
+ hideProgressBar();
128
+ if (!$popup.hasClass('fr-active')) {
129
+ editor.popups.refresh('image.insert');
130
+ editor.popups.setContainer('image.insert', editor.$tb);
131
+
132
+ if ($btn.is(':visible')) {
133
+ var left = $btn.offset().left + $btn.outerWidth() / 2;
134
+ var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
135
+ editor.popups.show('image.insert', left, top, $btn.outerHeight());
136
+ } else {
137
+ editor.position.forSelection($popup);
138
+ editor.popups.show('image.insert');
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Show the image edit popup.
145
+ */
146
+
147
+ function _showEditPopup () {
148
+ var $popup = editor.popups.get('image.edit');
149
+ if (!$popup) $popup = _initEditPopup();
150
+
151
+ editor.popups.setContainer('image.edit', $(editor.opts.scrollableContainer));
152
+ editor.popups.refresh('image.edit');
153
+ var left = $current_image.offset().left + $current_image.outerWidth() / 2;
154
+ var top = $current_image.offset().top + $current_image.outerHeight();
155
+
156
+ editor.popups.show('image.edit', left, top, $current_image.outerHeight());
157
+ }
158
+
159
+ /**
160
+ * Hide image upload popup.
161
+ */
162
+
163
+ function _hideInsertPopup () {
164
+ hideProgressBar();
165
+ }
166
+
167
+ /**
168
+ * Convert style to classes.
169
+ */
170
+
171
+ function _convertStyleToClasses ($img) {
172
+ if (!$img.hasClass('fr-dii') && !$img.hasClass('fr-dib')) {
173
+ // Set float to none.
174
+ var flt = $img.css('float');
175
+ $img.css('float', 'none');
176
+
177
+ // Image has display block.
178
+ if ($img.css('display') == 'block') {
179
+ // Set float to the initial value.
180
+ $img.css('float', flt);
181
+
182
+ if (editor.opts.imageEditButtons.indexOf('imageAlign') >= 0) {
183
+ // Margin left is 0.
184
+ // Margin right is auto.
185
+ if (parseInt($img.css('margin-left'), 10) === 0 && ($img.attr('style') || '').indexOf('margin-right: auto') >= 0) {
186
+ $img.addClass('fr-fil');
187
+ }
188
+
189
+ // Margin left is auto.
190
+ // Margin right is 0.
191
+ else if (parseInt($img.css('margin-right'), 10) === 0 && ($img.attr('style') || '').indexOf('margin-left: auto') >= 0) {
192
+ $img.addClass('fr-fir');
193
+ }
194
+ }
195
+
196
+ $img.addClass('fr-dib');
197
+ }
198
+
199
+ // Display inline.
200
+ else {
201
+ // Set float.
202
+ $img.css('float', flt);
203
+
204
+ if (editor.opts.imageEditButtons.indexOf('imageAlign') >= 0) {
205
+ // Float left.
206
+ if ($img.css('float') == 'left') {
207
+ $img.addClass('fr-fil');
208
+ }
209
+
210
+ // Float right.
211
+ else if ($img.css('float') == 'right') {
212
+ $img.addClass('fr-fir');
213
+ }
214
+ }
215
+
216
+ $img.addClass('fr-dii');
217
+ }
218
+
219
+ // Reset inline style.
220
+ $img.css('margin', '');
221
+ $img.css('float', '');
222
+ $img.css('display', '');
223
+ $img.css('z-index', '');
224
+ $img.css('position', '');
225
+ $img.css('overflow', '');
226
+ $img.css('vertical-align', '');
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Refresh the image list.
232
+ */
233
+
234
+ function _refreshImageList () {
235
+ var images = editor.$el.get(0).tagName == 'IMG' ? [editor.$el.get(0)] : editor.$el.get(0).querySelectorAll('img');
236
+
237
+ for (var i = 0; i < images.length; i++) {
238
+ var $img = $(images[i]);
239
+
240
+ if (editor.opts.imageEditButtons.indexOf('imageAlign') >= 0 || editor.opts.imageEditButtons.indexOf('imageDisplay') >= 0) {
241
+ _convertStyleToClasses($img);
242
+ }
243
+
244
+ // Set width if it has width.
245
+ if ($img.attr('width')) {
246
+ $img.css('width', $img.width());
247
+ $img.removeAttr('width');
248
+ }
249
+
250
+ // Do not allow text near image.
251
+ if (!editor.opts.imageTextNear) {
252
+ $img.removeClass('fr-dii').addClass('fr-dib');
253
+ }
254
+
255
+ if (editor.opts.iframe) {
256
+ $img.on('load', editor.size.syncIframe);
257
+ }
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Keep images in sync when content changed.
263
+ */
264
+ var images;
265
+
266
+ function _syncImages () {
267
+ // Get current images.
268
+ var c_images = Array.prototype.slice.call(editor.$el.get(0).querySelectorAll('img'));
269
+
270
+ // Current images src.
271
+ var image_srcs = [];
272
+ var i;
273
+ for (i = 0; i < c_images.length; i++) {
274
+ image_srcs.push(c_images[i].getAttribute('src'));
275
+
276
+ $(c_images[i]).toggleClass('fr-draggable', editor.opts.imageMove);
277
+ if (c_images[i].className === '') c_images[i].removeAttribute('class');
278
+ if (c_images[i].getAttribute('style') === '') c_images[i].removeAttribute('style');
279
+ }
280
+
281
+ // Loop previous images and check their src.
282
+ if (images) {
283
+ for (i = 0; i < images.length; i++) {
284
+ if (image_srcs.indexOf(images[i].getAttribute('src')) < 0) {
285
+ editor.events.trigger('image.removed', [$(images[i])]);
286
+ }
287
+ }
288
+ }
289
+
290
+ // Current images are the old ones.
291
+ images = c_images;
292
+ }
293
+
294
+ /**
295
+ * Reposition resizer.
296
+ */
297
+
298
+ function _repositionResizer () {
299
+ if (!$image_resizer) _initImageResizer();
300
+
301
+ var $container = editor.$wp || $(editor.opts.scrollableContainer);
302
+
303
+ $container.append($image_resizer);
304
+ $image_resizer.data('instance', editor);
305
+
306
+ var wrap_correction_top = $container.scrollTop() - (($container.css('position') != 'static' ? $container.offset().top : 0));
307
+ var wrap_correction_left = $container.scrollLeft() - (($container.css('position') != 'static' ? $container.offset().left : 0));
308
+
309
+ wrap_correction_left -= editor.helpers.getPX($container.css('border-left-width'));
310
+ wrap_correction_top -= editor.helpers.getPX($container.css('border-top-width'));
311
+
312
+ $image_resizer
313
+ .css('top', (editor.opts.iframe ? $current_image.offset().top : $current_image.offset().top + wrap_correction_top) - 1)
314
+ .css('left', (editor.opts.iframe ? $current_image.offset().left : $current_image.offset().left + wrap_correction_left) - 1)
315
+ .css('width', $current_image.get(0).getBoundingClientRect().width)
316
+ .css('height', $current_image.get(0).getBoundingClientRect().height)
317
+ .addClass('fr-active')
318
+ }
319
+
320
+ /**
321
+ * Create resize handler.
322
+ */
323
+
324
+ function _getHandler (pos) {
325
+ return '<div class="fr-handler fr-h' + pos + '"></div>';
326
+ }
327
+
328
+ /**
329
+ * Mouse down to start resize.
330
+ */
331
+ function _handlerMousedown (e) {
332
+ // Check if resizer belongs to current instance.
333
+ if (!editor.core.sameInstance($image_resizer)) return true;
334
+
335
+ e.preventDefault();
336
+ e.stopPropagation();
337
+
338
+ if (editor.$el.find('img.fr-error').left) return false;
339
+
340
+ if (!editor.undo.canDo()) editor.undo.saveStep();
341
+
342
+ $handler = $(this);
343
+ $handler.data('start-x', e.pageX || e.originalEvent.touches[0].pageX);
344
+ $handler.data('start-width', $current_image.width());
345
+ $handler.data('start-height', $current_image.height());
346
+
347
+ // Set current width.
348
+ var width = $current_image.width();
349
+ if (editor.opts.imageResizeWithPercent) {
350
+ var p_node = $current_image.parentsUntil(editor.$el, editor.html.blockTagsQuery()).get(0) || editor.$el.get(0);
351
+
352
+ $current_image.css('width', (width / $(p_node).outerWidth() * 100).toFixed(2) + '%');
353
+ } else {
354
+ $current_image.css('width', width);
355
+ }
356
+
357
+ $overlay.show();
358
+
359
+ editor.popups.hideAll();
360
+
361
+ _unmarkExit();
362
+ }
363
+
364
+ /**
365
+ * Do resize.
366
+ */
367
+
368
+ function _handlerMousemove (e) {
369
+ // Check if resizer belongs to current instance.
370
+ if (!editor.core.sameInstance($image_resizer)) return true;
371
+
372
+ if ($handler && $current_image) {
373
+ e.preventDefault()
374
+
375
+ if (editor.$el.find('img.fr-error').left) return false;
376
+
377
+ var c_x = e.pageX || (e.originalEvent.touches ? e.originalEvent.touches[0].pageX : null);
378
+
379
+ if (!c_x) {
380
+ return false;
381
+ }
382
+
383
+ var s_x = $handler.data('start-x');
384
+
385
+ var diff_x = c_x - s_x;
386
+
387
+ var width = $handler.data('start-width');
388
+ if ($handler.hasClass('fr-hnw') || $handler.hasClass('fr-hsw')) {
389
+ diff_x = 0 - diff_x;
390
+ }
391
+
392
+ if (editor.opts.imageResizeWithPercent) {
393
+ var p_node = $current_image.parentsUntil(editor.$el, editor.html.blockTagsQuery()).get(0) || editor.$el.get(0);
394
+
395
+ width = ((width + diff_x) / $(p_node).outerWidth() * 100).toFixed(2);
396
+ if (editor.opts.imageRoundPercent) width = Math.round(width);
397
+
398
+ $current_image.css('width', width + '%');
399
+ $current_image.css('height', '').removeAttr('height');
400
+ } else {
401
+ if (width + diff_x >= editor.opts.imageMinWidth) {
402
+ $current_image.css('width', width + diff_x);
403
+ }
404
+
405
+ $current_image.css('height', $handler.data('start-height') * $current_image.width() / $handler.data('start-width'));
406
+ }
407
+
408
+ _repositionResizer();
409
+
410
+ editor.events.trigger('image.resize', [get()]);
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Stop resize.
416
+ */
417
+
418
+ function _handlerMouseup (e) {
419
+ // Check if resizer belongs to current instance.
420
+ if (!editor.core.sameInstance($image_resizer)) return true;
421
+
422
+ if ($handler && $current_image) {
423
+ if (e) e.stopPropagation();
424
+
425
+ if (editor.$el.find('img.fr-error').left) return false;
426
+
427
+ $handler = null;
428
+ $overlay.hide();
429
+ _repositionResizer();
430
+ _showEditPopup();
431
+
432
+ editor.undo.saveStep();
433
+
434
+ editor.events.trigger('image.resizeEnd', [get()]);
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Throw an image error.
440
+ */
441
+
442
+ function _throwError (code, response) {
443
+ editor.edit.on();
444
+ if ($current_image) $current_image.addClass('fr-error');
445
+ _showErrorMessage(editor.language.translate('Something went wrong. Please try again.'));
446
+
447
+ editor.events.trigger('image.error', [{
448
+ code: code,
449
+ message: error_messages[code]
450
+ },
451
+ response
452
+ ]);
453
+ }
454
+
455
+ /**
456
+ * Init the image edit popup.
457
+ */
458
+
459
+ function _initEditPopup (delayed) {
460
+ if (delayed) {
461
+ if (editor.$wp) {
462
+ editor.events.$on(editor.$wp, 'scroll', function () {
463
+ if ($current_image && editor.popups.isVisible('image.edit')) {
464
+ _showEditPopup();
465
+ }
466
+ });
467
+ }
468
+
469
+ return true;
470
+ }
471
+
472
+ // Image buttons.
473
+ var image_buttons = '';
474
+ if (editor.opts.imageEditButtons.length > 0) {
475
+ image_buttons += '<div class="fr-buttons">';
476
+ image_buttons += editor.button.buildList(editor.opts.imageEditButtons);
477
+ image_buttons += '</div>';
478
+ }
479
+
480
+ var template = {
481
+ buttons: image_buttons
482
+ };
483
+
484
+ var $popup = editor.popups.create('image.edit', template);
485
+
486
+ return $popup;
487
+ }
488
+
489
+ /**
490
+ * Show progress bar.
491
+ */
492
+
493
+ function showProgressBar (no_message) {
494
+ var $popup = editor.popups.get('image.insert');
495
+ if (!$popup) $popup = _initInsertPopup();
496
+
497
+ $popup.find('.fr-layer.fr-active').removeClass('fr-active').addClass('fr-pactive');
498
+ $popup.find('.fr-image-progress-bar-layer').addClass('fr-active');
499
+ $popup.find('.fr-buttons').hide();
500
+
501
+ if ($current_image) {
502
+ editor.popups.setContainer('image.insert', $(editor.opts.scrollableContainer));
503
+ var left = $current_image.offset().left + $current_image.width() / 2;
504
+ var top = $current_image.offset().top + $current_image.height();
505
+
506
+ editor.popups.show('image.insert', left, top, $current_image.outerHeight());
507
+ }
508
+
509
+ if (typeof no_message == 'undefined') {
510
+ _setProgressMessage('Uploading', 0);
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Hide progress bar.
516
+ */
517
+ function hideProgressBar (dismiss) {
518
+ var $popup = editor.popups.get('image.insert');
519
+
520
+ if ($popup) {
521
+ $popup.find('.fr-layer.fr-pactive').addClass('fr-active').removeClass('fr-pactive');
522
+ $popup.find('.fr-image-progress-bar-layer').removeClass('fr-active');
523
+ $popup.find('.fr-buttons').show();
524
+
525
+ // Dismiss error message.
526
+ if (dismiss || editor.$el.find('img.fr-error').length) {
527
+ editor.events.focus();
528
+ editor.$el.find('img.fr-error').remove();
529
+ editor.undo.saveStep();
530
+ editor.undo.run();
531
+ editor.undo.dropRedo();
532
+ }
533
+ }
534
+ }
535
+
536
+ /**
537
+ * Set a progress message.
538
+ */
539
+
540
+ function _setProgressMessage (message, progress) {
541
+ var $popup = editor.popups.get('image.insert');
542
+
543
+ if ($popup) {
544
+ var $layer = $popup.find('.fr-image-progress-bar-layer');
545
+ $layer.find('h3').text(message + (progress ? ' ' + progress + '%' : ''));
546
+
547
+ $layer.removeClass('fr-error');
548
+
549
+ if (progress) {
550
+ $layer.find('div').removeClass('fr-indeterminate');
551
+ $layer.find('div > span').css('width', progress + '%');
552
+ } else {
553
+ $layer.find('div').addClass('fr-indeterminate');
554
+ }
555
+ }
556
+ }
557
+
558
+ /**
559
+ * Show error message to the user.
560
+ */
561
+
562
+ function _showErrorMessage (message) {
563
+ showProgressBar();
564
+ var $popup = editor.popups.get('image.insert');
565
+ var $layer = $popup.find('.fr-image-progress-bar-layer');
566
+ $layer.addClass('fr-error')
567
+ $layer.find('h3').text(message);
568
+ }
569
+
570
+ /**
571
+ * Insert image using URL callback.
572
+ */
573
+
574
+ function insertByURL () {
575
+ var $popup = editor.popups.get('image.insert');
576
+ var $input = $popup.find('.fr-image-by-url-layer input');
577
+
578
+ if ($input.val().length > 0) {
579
+ showProgressBar();
580
+ _setProgressMessage('Loading image');
581
+ insert(editor.helpers.sanitizeURL($input.val()), true, [], $current_image);
582
+ $input.val('');
583
+ $input.blur();
584
+ }
585
+ }
586
+
587
+ function _editImg ($img) {
588
+ _edit.call($img.get(0));
589
+ }
590
+
591
+ function _loadedCallback () {
592
+ var $img = $(this);
593
+
594
+ editor.popups.hide('image.insert');
595
+
596
+ $img.removeClass('fr-uploading');
597
+
598
+ // Select the image.
599
+ if ($img.next().is('br')) {
600
+ $img.next().remove();
601
+ }
602
+
603
+ _editImg($img);
604
+
605
+ editor.events.trigger('image.loaded', [$img]);
606
+ }
607
+
608
+ /**
609
+ * Insert image into the editor.
610
+ */
611
+
612
+ function insert (link, sanitize, data, $existing_img, response) {
613
+ editor.edit.off();
614
+ _setProgressMessage('Loading image');
615
+
616
+ var image = new Image();
617
+ image.onload = function () {
618
+ var $img;
619
+ var attr;
620
+
621
+ if ($existing_img) {
622
+ var old_src = $existing_img.data('fr-old-src');
623
+
624
+ if (editor.$wp) {
625
+ // Clone existing image.
626
+ $img = $existing_img.clone().removeData('fr-old-src').removeClass('fr-uploading');
627
+
628
+ // Remove load event.
629
+ $img.off('load');
630
+
631
+ // Set new SRC.
632
+ if (old_src) $existing_img.attr('src', old_src);
633
+
634
+ // Replace existing image with its clone.
635
+ $existing_img.replaceWith($img);
636
+ } else {
637
+ $img = $existing_img;
638
+ }
639
+
640
+ // Remove old data.
641
+ var atts = $img.get(0).attributes;
642
+ for (var i = 0; i < atts.length; i++) {
643
+ var att = atts[i];
644
+ if (att.nodeName.indexOf('data-') === 0) {
645
+ $img.removeAttr(att.nodeName);
646
+ }
647
+ }
648
+
649
+ // Set new data.
650
+ if (typeof data != 'undefined') {
651
+ for (attr in data) {
652
+ if (data.hasOwnProperty(attr)) {
653
+ if (attr != 'link') {
654
+ $img.attr('data-' + attr, data[attr]);
655
+ }
656
+ }
657
+ }
658
+ }
659
+
660
+ $img.on('load', _loadedCallback);
661
+ $img.attr('src', link);
662
+ editor.edit.on();
663
+ _syncImages();
664
+ editor.undo.saveStep();
665
+ editor.events.trigger(old_src ? 'image.replaced' : 'image.inserted', [$img, response]);
666
+ } else {
667
+ $img = _addImage(link, data, _loadedCallback);
668
+ _syncImages();
669
+ editor.undo.saveStep();
670
+ editor.events.trigger('image.inserted', [$img, response]);
671
+ }
672
+ }
673
+
674
+ image.onerror = function () {
675
+ _throwError(BAD_LINK);
676
+ }
677
+
678
+ image.src = link;
679
+ }
680
+
681
+ /**
682
+ * Parse image response.
683
+ */
684
+
685
+ function _parseResponse (response) {
686
+ try {
687
+ if (editor.events.trigger('image.uploaded', [response], true) === false) {
688
+ editor.edit.on();
689
+ return false;
690
+ }
691
+
692
+ var resp = $.parseJSON(response);
693
+ if (resp.link) {
694
+ return resp;
695
+ } else {
696
+ // No link in upload request.
697
+ _throwError(MISSING_LINK, response);
698
+ return false;
699
+ }
700
+ } catch (ex) {
701
+ // Bad response.
702
+ _throwError(BAD_RESPONSE, response);
703
+ return false;
704
+ }
705
+ }
706
+
707
+ /**
708
+ * Parse image response.
709
+ */
710
+
711
+ function _parseXMLResponse (response) {
712
+ try {
713
+ var link = $(response).find('Location').text();
714
+ var key = $(response).find('Key').text();
715
+
716
+ if (editor.events.trigger('image.uploadedToS3', [link, key, response], true) === false) {
717
+ editor.edit.on();
718
+ return false;
719
+ }
720
+
721
+ return link;
722
+ } catch (ex) {
723
+ // Bad response.
724
+ _throwError(BAD_RESPONSE, response);
725
+ return false;
726
+ }
727
+ }
728
+
729
+ /**
730
+ * Image was uploaded to the server and we have a response.
731
+ */
732
+
733
+ function _imageUploaded ($img) {
734
+ _setProgressMessage('Loading image');
735
+
736
+ var status = this.status;
737
+ var response = this.response;
738
+ var responseXML = this.responseXML;
739
+ var responseText = this.responseText;
740
+
741
+ try {
742
+ if (editor.opts.imageUploadToS3) {
743
+ if (status == 201) {
744
+ var link = _parseXMLResponse(responseXML);
745
+ if (link) {
746
+ insert(link, false, [], $img, response || responseXML);
747
+ }
748
+ } else {
749
+ _throwError(BAD_RESPONSE, response || responseXML);
750
+ }
751
+ } else {
752
+ if (status >= 200 && status < 300) {
753
+ var resp = _parseResponse(responseText);
754
+ if (resp) {
755
+ insert(resp.link, false, resp, $img, response || responseText);
756
+ }
757
+ } else {
758
+ _throwError(ERROR_DURING_UPLOAD, response || responseText);
759
+ }
760
+ }
761
+ } catch (ex) {
762
+ // Bad response.
763
+ _throwError(BAD_RESPONSE, response || responseText);
764
+ }
765
+ }
766
+
767
+ /**
768
+ * Image upload error.
769
+ */
770
+
771
+ function _imageUploadError () {
772
+ _throwError(BAD_RESPONSE, this.response || this.responseText || this.responseXML);
773
+ }
774
+
775
+ /**
776
+ * Image upload progress.
777
+ */
778
+
779
+ function _imageUploadProgress (e) {
780
+ if (e.lengthComputable) {
781
+ var complete = (e.loaded / e.total * 100 | 0);
782
+ _setProgressMessage('Uploading', complete);
783
+ }
784
+ }
785
+
786
+ function _addImage (link, data, loadCallback) {
787
+ // Build image data string.
788
+ var data_str = '';
789
+ var attr;
790
+ if (data && typeof data != 'undefined') {
791
+ for (attr in data) {
792
+ if (data.hasOwnProperty(attr)) {
793
+ if (attr != 'link') {
794
+ data_str += ' data-' + attr + '="' + data[attr] + '"';
795
+ }
796
+ }
797
+ }
798
+ }
799
+
800
+ var width = editor.opts.imageDefaultWidth;
801
+ if (width && width != 'auto') {
802
+ width = width + (editor.opts.imageResizeWithPercent ? '%' : 'px');
803
+ }
804
+
805
+ // Create image object and set the load event.
806
+ var $img = $('<img class="' + (editor.opts.imageDefaultDisplay ? 'fr-di' + editor.opts.imageDefaultDisplay[0] : '') + (editor.opts.imageDefaultAlign ? (editor.opts.imageDefaultAlign != 'center' ? ' fr-fi' + editor.opts.imageDefaultAlign[0] : '') : '') + '" src="' + link + '"' + data_str + (width ? ' style="width: ' + width + ';"' : '') + '>');
807
+
808
+ $img.on('load', loadCallback);
809
+
810
+ // Make sure we have focus.
811
+ // Call the event.
812
+ editor.edit.on();
813
+ editor.events.focus(true);
814
+ editor.selection.restore();
815
+
816
+ editor.undo.saveStep();
817
+
818
+ // Insert marker and then replace it with the image.
819
+ if (editor.opts.imageSplitHTML) {
820
+ editor.markers.split();
821
+ } else {
822
+ editor.markers.insert();
823
+ }
824
+
825
+ var $marker = editor.$el.find('.fr-marker');
826
+ $marker.replaceWith($img);
827
+
828
+ editor.html.wrap();
829
+ editor.selection.clear();
830
+
831
+ return $img;
832
+ }
833
+
834
+ /**
835
+ * Image upload aborted.
836
+ */
837
+ function _imageUploadAborted () {
838
+ editor.edit.on();
839
+ hideProgressBar(true);
840
+ }
841
+
842
+ /**
843
+ * Start the uploading process.
844
+ */
845
+ function _startUpload (xhr, form_data, image) {
846
+ function _sendRequest () {
847
+ var $img = $(this);
848
+
849
+ $img.off('load');
850
+
851
+ $img.addClass('fr-uploading');
852
+
853
+ if ($img.next().is('br')) {
854
+ $img.next().remove();
855
+ }
856
+
857
+ editor.placeholder.refresh();
858
+
859
+ // Select the image.
860
+ if (!$img.is($current_image)) _editImg($img);
861
+
862
+ _repositionResizer();
863
+ showProgressBar();
864
+
865
+ editor.edit.off();
866
+
867
+ // Set upload events.
868
+ xhr.onload = function () {
869
+ _imageUploaded.call(xhr, $img);
870
+ };
871
+ xhr.onerror = _imageUploadError;
872
+ xhr.upload.onprogress = _imageUploadProgress;
873
+ xhr.onabort = _imageUploadAborted;
874
+
875
+ // Set abort event.
876
+ $img.off('abortUpload').on('abortUpload', function () {
877
+ if (xhr.readyState != 4) {
878
+ xhr.abort();
879
+ }
880
+ });
881
+
882
+ // Send data.
883
+ xhr.send(form_data);
884
+ }
885
+
886
+ var reader = new FileReader();
887
+ var $img;
888
+ reader.addEventListener('load', function () {
889
+ var link = reader.result;
890
+
891
+ if (reader.result.indexOf('svg+xml') < 0) {
892
+ // Convert image to local blob.
893
+ var binary = atob(reader.result.split(',')[1]);
894
+ var array = [];
895
+ for (var i = 0; i < binary.length; i++) {
896
+ array.push(binary.charCodeAt(i));
897
+ }
898
+
899
+ // Get local image link.
900
+ link = window.URL.createObjectURL(new Blob([new Uint8Array(array)], {
901
+ type: 'image/jpeg'
902
+ }));
903
+ }
904
+
905
+ // No image.
906
+ if (!$current_image) {
907
+ $img = _addImage(link, null, _sendRequest);
908
+ } else {
909
+ $current_image.on('load', _sendRequest);
910
+ editor.edit.on();
911
+ editor.undo.saveStep();
912
+ $current_image.data('fr-old-src', $current_image.attr('src'));
913
+ $current_image.attr('src', link);
914
+ }
915
+ }, false);
916
+
917
+ reader.readAsDataURL(image);
918
+ }
919
+
920
+ /**
921
+ * Do image upload.
922
+ */
923
+
924
+ function upload (images) {
925
+ // Check if we should cancel the image upload.
926
+ if (editor.events.trigger('image.beforeUpload', [images]) === false) {
927
+ return false;
928
+ }
929
+
930
+ // Make sure we have what to upload.
931
+ if (typeof images != 'undefined' && images.length > 0) {
932
+ var image = images[0];
933
+
934
+ // Check image max size.
935
+ if (image.size > editor.opts.imageMaxSize) {
936
+ _throwError(MAX_SIZE_EXCEEDED);
937
+ return false;
938
+ }
939
+
940
+ // Check image types.
941
+ if (editor.opts.imageAllowedTypes.indexOf(image.type.replace(/image\//g, '')) < 0) {
942
+ _throwError(BAD_FILE_TYPE);
943
+ return false;
944
+ }
945
+
946
+ // Create form Data.
947
+ var form_data;
948
+ if (editor.drag_support.formdata) {
949
+ form_data = editor.drag_support.formdata ? new FormData() : null;
950
+ }
951
+
952
+ // Prepare form data for request.
953
+ if (form_data) {
954
+ var key;
955
+
956
+ // Upload to S3.
957
+ if (editor.opts.imageUploadToS3 !== false) {
958
+ form_data.append('key', editor.opts.imageUploadToS3.keyStart + (new Date()).getTime() + '-' + (image.name || 'untitled'));
959
+ form_data.append('success_action_status', '201');
960
+ form_data.append('X-Requested-With', 'xhr');
961
+ form_data.append('Content-Type', image.type);
962
+
963
+ for (key in editor.opts.imageUploadToS3.params) {
964
+ if (editor.opts.imageUploadToS3.params.hasOwnProperty(key)) {
965
+ form_data.append(key, editor.opts.imageUploadToS3.params[key]);
966
+ }
967
+ }
968
+ }
969
+
970
+ // Add upload params.
971
+ for (key in editor.opts.imageUploadParams) {
972
+ if (editor.opts.imageUploadParams.hasOwnProperty(key)) {
973
+ form_data.append(key, editor.opts.imageUploadParams[key]);
974
+ }
975
+ }
976
+
977
+ // Set the image in the request.
978
+ form_data.append(editor.opts.imageUploadParam, image);
979
+
980
+ // Create XHR request.
981
+ var url = editor.opts.imageUploadURL;
982
+ if (editor.opts.imageUploadToS3) {
983
+ url = 'https://' + editor.opts.imageUploadToS3.region + '.amazonaws.com/' + editor.opts.imageUploadToS3.bucket;
984
+ }
985
+ var xhr = editor.core.getXHR(url, editor.opts.imageUploadMethod);
986
+
987
+ _startUpload(xhr, form_data, image);
988
+ }
989
+ }
990
+ }
991
+
992
+ /**
993
+ * Image drop inside the upload zone.
994
+ */
995
+
996
+ function _bindInsertEvents ($popup) {
997
+ // Drag over the dropable area.
998
+ editor.events.$on($popup, 'dragover dragenter', '.fr-image-upload-layer', function () {
999
+ $(this).addClass('fr-drop');
1000
+ return false;
1001
+ });
1002
+
1003
+ // Drag end.
1004
+ editor.events.$on($popup, 'dragleave dragend', '.fr-image-upload-layer', function () {
1005
+ $(this).removeClass('fr-drop');
1006
+ return false;
1007
+ });
1008
+
1009
+ // Drop.
1010
+ editor.events.$on($popup, 'drop', '.fr-image-upload-layer', function (e) {
1011
+ e.preventDefault();
1012
+ e.stopPropagation();
1013
+
1014
+ $(this).removeClass('fr-drop');
1015
+
1016
+ var dt = e.originalEvent.dataTransfer;
1017
+ if (dt && dt.files) {
1018
+ var inst = $popup.data('instance') || editor;
1019
+ inst.events.disableBlur();
1020
+ inst.image.upload(dt.files);
1021
+ inst.events.enableBlur();
1022
+ }
1023
+ });
1024
+
1025
+ editor.events.$on($popup, 'change', '.fr-image-upload-layer input[type="file"]', function () {
1026
+ if (this.files) {
1027
+ var inst = $popup.data('instance') || editor;
1028
+ inst.events.disableBlur();
1029
+ $popup.find('input:focus').blur();
1030
+ inst.events.enableBlur();
1031
+ inst.image.upload(this.files);
1032
+ }
1033
+ // Else IE 9 case.
1034
+
1035
+ // Chrome fix.
1036
+ $(this).val('');
1037
+ });
1038
+ }
1039
+
1040
+ function _drop (e) {
1041
+ // Check if we are dropping files.
1042
+ var dt = e.originalEvent.dataTransfer;
1043
+ if (dt && dt.files && dt.files.length) {
1044
+ var img = dt.files[0];
1045
+ if (img && img.type) {
1046
+ // Dropped file is an image that we allow.
1047
+ if (editor.opts.imageAllowedTypes.indexOf(img.type.replace(/image\//g, '')) >= 0) {
1048
+ editor.markers.remove();
1049
+ editor.markers.insertAtPoint(e.originalEvent);
1050
+ editor.$el.find('.fr-marker').replaceWith($.FE.MARKERS);
1051
+
1052
+ // Hide popups.
1053
+ editor.popups.hideAll();
1054
+
1055
+ // Show the image insert popup.
1056
+ var $popup = editor.popups.get('image.insert');
1057
+ if (!$popup) $popup = _initInsertPopup();
1058
+
1059
+ editor.popups.setContainer('image.insert', $(editor.opts.scrollableContainer));
1060
+ editor.popups.show('image.insert', e.originalEvent.pageX, e.originalEvent.pageY);
1061
+ showProgressBar();
1062
+
1063
+ // Upload images.
1064
+ upload(dt.files);
1065
+
1066
+ // Cancel anything else.
1067
+ e.preventDefault();
1068
+ e.stopPropagation();
1069
+
1070
+ return false;
1071
+ }
1072
+ }
1073
+ }
1074
+ }
1075
+
1076
+ function _placeCursor () {
1077
+ var t;
1078
+ var p_node;
1079
+ var r = editor.selection.ranges(0);
1080
+ if (r.collapsed && r.startContainer.nodeType == Node.ELEMENT_NODE) {
1081
+ // Click after image.
1082
+ if (r.startContainer.childNodes.length == r.startOffset) {
1083
+ t = r.startContainer.childNodes[r.startOffset - 1];
1084
+ if (t && t.tagName == 'IMG' && $(t).css('display') == 'block') {
1085
+ // Check if image is last node.
1086
+ p_node = editor.node.blockParent(t);
1087
+ if (p_node && editor.html.defaultTag()) {
1088
+ if (!p_node.nextSibling) {
1089
+ if (['TD', 'TH'].indexOf(p_node.tagName) < 0) {
1090
+ $(p_node).after('<' + editor.html.defaultTag() + '><br>' + $.FE.MARKERS + '</' + editor.html.defaultTag() + '>');
1091
+ }
1092
+ else {
1093
+ $(img).after('<br>' + $.FE.MARKERS);
1094
+ }
1095
+ editor.selection.restore();
1096
+ }
1097
+ }
1098
+ else if (!p_node) {
1099
+ $(t).after('<br>' + $.FE.MARKERS);
1100
+ editor.selection.restore();
1101
+ }
1102
+ }
1103
+ }
1104
+ else if (r.startOffset === 0 && r.startContainer.childNodes.length > r.startOffset) {
1105
+ t = r.startContainer.childNodes[r.startOffset];
1106
+ if (t && t.tagName == 'IMG' && $(t).css('display') == 'block') {
1107
+ // Check if image is last node.
1108
+ p_node = editor.node.blockParent(t);
1109
+ if (p_node && editor.html.defaultTag()) {
1110
+ if (!p_node.previousSibling) {
1111
+ if (['TD', 'TH'].indexOf(p_node.tagName) < 0) {
1112
+ $(p_node).before('<' + editor.html.defaultTag() + '><br>' + $.FE.MARKERS + '</' + editor.html.defaultTag() + '>');
1113
+ }
1114
+ else {
1115
+ $(img).before('<br>' + $.FE.MARKERS);
1116
+ }
1117
+ editor.selection.restore();
1118
+ }
1119
+ }
1120
+ else if (!p_node) {
1121
+ $(t).before($.FE.MARKERS + '<br>');
1122
+ editor.selection.restore();
1123
+ }
1124
+ }
1125
+ }
1126
+ }
1127
+ }
1128
+
1129
+ function _initEvents () {
1130
+ // Mouse down on image. It might start move.
1131
+ editor.events.$on(editor.$el, editor._mousedown, editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', function (e) {
1132
+ if ($(this).parents('[contenteditable="false"]:not(.fr-element):not(body)').length) return true;
1133
+
1134
+ editor.selection.clear();
1135
+
1136
+ mousedown = true;
1137
+
1138
+ // Prevent the image resizing.
1139
+ if (editor.browser.msie) {
1140
+ editor.events.disableBlur();
1141
+ editor.$el.attr('contenteditable', false);
1142
+ }
1143
+
1144
+ if (!editor.draggable) e.preventDefault();
1145
+
1146
+ e.stopPropagation();
1147
+ });
1148
+
1149
+ // Mouse up on an image prevent move.
1150
+ editor.events.$on(editor.$el, editor._mouseup, editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', function (e) {
1151
+ if ($(this).parents('[contenteditable="false"]:not(.fr-element):not(body)').length) return true;
1152
+
1153
+ if (mousedown) {
1154
+ mousedown = false;
1155
+
1156
+ // Remove moving class.
1157
+ e.stopPropagation();
1158
+
1159
+ if (editor.browser.msie) {
1160
+ editor.$el.attr('contenteditable', true);
1161
+ editor.events.enableBlur();
1162
+ }
1163
+ }
1164
+ });
1165
+
1166
+ // Show image popup when it was selected.
1167
+ editor.events.on('keyup', function (e) {
1168
+ if (e.shiftKey && editor.selection.text().replace(/\n/g, '') === '') {
1169
+ var s_el = editor.selection.element();
1170
+ var e_el = editor.selection.endElement();
1171
+ if (s_el && s_el.tagName == 'IMG') {
1172
+ _editImg($(s_el));
1173
+ }
1174
+ else if (e_el && e_el.tagName == 'IMG') {
1175
+ _editImg($(e_el));
1176
+ }
1177
+ }
1178
+ }, true);
1179
+
1180
+ // Drop inside the editor.
1181
+ editor.events.on('drop', _drop);
1182
+
1183
+ editor.events.on('mousedown window.mousedown', _markExit);
1184
+ editor.events.on('window.touchmove', _unmarkExit);
1185
+
1186
+ editor.events.on('mouseup window.mouseup', function () {
1187
+ if ($current_image) {
1188
+ _exitEdit();
1189
+ return false;
1190
+ }
1191
+ });
1192
+ editor.events.on('commands.mousedown', function ($btn) {
1193
+ if ($btn.parents('.fr-toolbar').length > 0) {
1194
+ _exitEdit();
1195
+ }
1196
+ });
1197
+
1198
+ editor.events.on('mouseup', _placeCursor);
1199
+
1200
+ editor.events.on('blur image.hideResizer commands.undo commands.redo element.dropped', function () {
1201
+ mousedown = false;
1202
+ _exitEdit(true);
1203
+ });
1204
+ }
1205
+
1206
+ /**
1207
+ * Init the image upload popup.
1208
+ */
1209
+
1210
+ function _initInsertPopup (delayed) {
1211
+ if (delayed) {
1212
+ editor.popups.onRefresh('image.insert', _refreshInsertPopup);
1213
+ editor.popups.onHide('image.insert', _hideInsertPopup);
1214
+
1215
+ return true;
1216
+ }
1217
+
1218
+ var active;
1219
+
1220
+ // Image buttons.
1221
+ var image_buttons = '';
1222
+ if (editor.opts.imageInsertButtons.length > 1) {
1223
+ image_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.imageInsertButtons) + '</div>';
1224
+ }
1225
+
1226
+ var uploadIndex = editor.opts.imageInsertButtons.indexOf('imageUpload');
1227
+ var urlIndex = editor.opts.imageInsertButtons.indexOf('imageByURL');
1228
+
1229
+ // Image upload layer.
1230
+ var upload_layer = '';
1231
+ if (uploadIndex >= 0) {
1232
+ active = ' fr-active';
1233
+ if (urlIndex >= 0 && uploadIndex > urlIndex) {
1234
+ active = '';
1235
+ }
1236
+
1237
+ upload_layer = '<div class="fr-image-upload-layer' + active + ' fr-layer" id="fr-image-upload-layer-' + editor.id + '"><strong>' + editor.language.translate('Drop image') + '</strong><br>(' + editor.language.translate('or click') + ')<div class="fr-form"><input type="file" accept="image/' + editor.opts.imageAllowedTypes.join(', image/').toLowerCase() + '" tabIndex="-1"></div></div>'
1238
+ }
1239
+
1240
+ // Image by url layer.
1241
+ var by_url_layer = '';
1242
+ if (urlIndex >= 0) {
1243
+ active = ' fr-active';
1244
+ if (uploadIndex >= 0 && urlIndex > uploadIndex) {
1245
+ active = '';
1246
+ }
1247
+
1248
+ by_url_layer = '<div class="fr-image-by-url-layer' + active + ' fr-layer" id="fr-image-by-url-layer-' + editor.id + '"><div class="fr-input-line"><input type="text" placeholder="http://" tabIndex="1"></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="imageInsertByURL" tabIndex="2">' + editor.language.translate('Insert') + '</button></div></div>'
1249
+ }
1250
+
1251
+ // Progress bar.
1252
+ var progress_bar_layer = '<div class="fr-image-progress-bar-layer fr-layer"><h3 class="fr-message">Uploading</h3><div class="fr-loader"><span class="fr-progress"></span></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-back" data-cmd="imageDismissError" tabIndex="2">OK</button></div></div>';
1253
+
1254
+ var template = {
1255
+ buttons: image_buttons,
1256
+ upload_layer: upload_layer,
1257
+ by_url_layer: by_url_layer,
1258
+ progress_bar: progress_bar_layer
1259
+ }
1260
+
1261
+ // Set the template in the popup.
1262
+ var $popup = editor.popups.create('image.insert', template);
1263
+
1264
+ if (editor.$wp) {
1265
+ editor.events.$on(editor.$wp, 'scroll', function () {
1266
+ if ($current_image && editor.popups.isVisible('image.insert')) {
1267
+ replace();
1268
+ }
1269
+ });
1270
+ }
1271
+
1272
+ _bindInsertEvents($popup);
1273
+
1274
+ return $popup;
1275
+ }
1276
+
1277
+ /**
1278
+ * Refresh the ALT popup.
1279
+ */
1280
+
1281
+ function _refreshAltPopup () {
1282
+ if ($current_image) {
1283
+ var $popup = editor.popups.get('image.alt');
1284
+ $popup.find('input').val($current_image.attr('alt') || '').trigger('change');
1285
+ }
1286
+ }
1287
+
1288
+ /**
1289
+ * Show the ALT popup.
1290
+ */
1291
+
1292
+ function showAltPopup () {
1293
+ var $popup = editor.popups.get('image.alt');
1294
+ if (!$popup) $popup = _initAltPopup();
1295
+
1296
+ hideProgressBar();
1297
+ editor.popups.refresh('image.alt');
1298
+ editor.popups.setContainer('image.alt', $(editor.opts.scrollableContainer));
1299
+ var left = $current_image.offset().left + $current_image.width() / 2;
1300
+ var top = $current_image.offset().top + $current_image.height();
1301
+
1302
+ editor.popups.show('image.alt', left, top, $current_image.outerHeight());
1303
+ }
1304
+
1305
+ /**
1306
+ * Init the image upload popup.
1307
+ */
1308
+
1309
+ function _initAltPopup (delayed) {
1310
+ if (delayed) {
1311
+ editor.popups.onRefresh('image.alt', _refreshAltPopup);
1312
+ return true;
1313
+ }
1314
+
1315
+ // Image buttons.
1316
+ var image_buttons = '';
1317
+ image_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.imageAltButtons) + '</div>';
1318
+
1319
+ // Image by url layer.
1320
+ var alt_layer = '';
1321
+ alt_layer = '<div class="fr-image-alt-layer fr-layer fr-active" id="fr-image-alt-layer-' + editor.id + '"><div class="fr-input-line"><input type="text" placeholder="' + editor.language.translate('Alternate Text') + '" tabIndex="1"></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="imageSetAlt" tabIndex="2">' + editor.language.translate('Update') + '</button></div></div>'
1322
+
1323
+ var template = {
1324
+ buttons: image_buttons,
1325
+ alt_layer: alt_layer
1326
+ }
1327
+
1328
+ // Set the template in the popup.
1329
+ var $popup = editor.popups.create('image.alt', template);
1330
+
1331
+ if (editor.$wp) {
1332
+ editor.events.$on(editor.$wp, 'scroll.image-alt', function () {
1333
+ if ($current_image && editor.popups.isVisible('image.alt')) {
1334
+ showAltPopup();
1335
+ }
1336
+ });
1337
+ }
1338
+
1339
+ return $popup;
1340
+ }
1341
+
1342
+ /**
1343
+ * Set ALT based on the values from the popup.
1344
+ */
1345
+
1346
+ function setAlt (alt) {
1347
+ if ($current_image) {
1348
+ var $popup = editor.popups.get('image.alt');
1349
+ $current_image.attr('alt', alt || $popup.find('input').val() || '');
1350
+ $popup.find('input:focus').blur();
1351
+ _editImg($current_image);
1352
+ }
1353
+ }
1354
+
1355
+ /**
1356
+ * Refresh the size popup.
1357
+ */
1358
+
1359
+ function _refreshSizePopup () {
1360
+ if ($current_image) {
1361
+ var $popup = editor.popups.get('image.size');
1362
+ $popup.find('input[name="width"]').val($current_image.get(0).style.width).trigger('change');
1363
+ $popup.find('input[name="height"]').val($current_image.get(0).style.height).trigger('change');
1364
+ }
1365
+ }
1366
+
1367
+ /**
1368
+ * Show the size popup.
1369
+ */
1370
+
1371
+ function showSizePopup () {
1372
+ var $popup = editor.popups.get('image.size');
1373
+ if (!$popup) $popup = _initSizePopup();
1374
+
1375
+ hideProgressBar();
1376
+ editor.popups.refresh('image.size');
1377
+ editor.popups.setContainer('image.size', $(editor.opts.scrollableContainer));
1378
+ var left = $current_image.offset().left + $current_image.width() / 2;
1379
+ var top = $current_image.offset().top + $current_image.height();
1380
+
1381
+ editor.popups.show('image.size', left, top, $current_image.outerHeight());
1382
+ }
1383
+
1384
+ /**
1385
+ * Init the image upload popup.
1386
+ */
1387
+
1388
+ function _initSizePopup (delayed) {
1389
+ if (delayed) {
1390
+ editor.popups.onRefresh('image.size', _refreshSizePopup);
1391
+
1392
+ return true;
1393
+ }
1394
+
1395
+ // Image buttons.
1396
+ var image_buttons = '';
1397
+ image_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.imageSizeButtons) + '</div>';
1398
+
1399
+ // Size layer.
1400
+ var size_layer = '';
1401
+ size_layer = '<div class="fr-image-size-layer fr-layer fr-active" id="fr-image-size-layer-' + editor.id + '"><div class="fr-image-group"><div class="fr-input-line"><input type="text" name="width" placeholder="' + editor.language.translate('Width') + '" tabIndex="1"></div><div class="fr-input-line"><input type="text" name="height" placeholder="' + editor.language.translate('Height') + '" tabIndex="1"></div></div><div class="fr-action-buttons"><button type="button" class="fr-command fr-submit" data-cmd="imageSetSize" tabIndex="2">' + editor.language.translate('Update') + '</button></div></div>'
1402
+
1403
+ var template = {
1404
+ buttons: image_buttons,
1405
+ size_layer: size_layer
1406
+ };
1407
+
1408
+ // Set the template in the popup.
1409
+ var $popup = editor.popups.create('image.size', template);
1410
+
1411
+ if (editor.$wp) {
1412
+ editor.events.$on(editor.$wp, 'scroll.image-size', function () {
1413
+ if ($current_image && editor.popups.isVisible('image.size')) {
1414
+ showSizePopup();
1415
+ }
1416
+ });
1417
+ }
1418
+
1419
+ return $popup;
1420
+ }
1421
+
1422
+ /**
1423
+ * Set size based on the current image size.
1424
+ */
1425
+
1426
+ function setSize (width, height) {
1427
+ if ($current_image) {
1428
+ var $popup = editor.popups.get('image.size');
1429
+ $current_image.css('width', width || $popup.find('input[name="width"]').val());
1430
+ $current_image.css('height', height || $popup.find('input[name="height"]').val());
1431
+
1432
+ $popup.find('input:focus').blur();
1433
+ _editImg($current_image);
1434
+ }
1435
+ }
1436
+
1437
+ /**
1438
+ * Show the image upload layer.
1439
+ */
1440
+
1441
+ function showLayer (name) {
1442
+ var $popup = editor.popups.get('image.insert');
1443
+
1444
+ var left;
1445
+ var top;
1446
+
1447
+ // Click on the button from the toolbar without image selected.
1448
+ if (!$current_image && !editor.opts.toolbarInline) {
1449
+ var $btn = editor.$tb.find('.fr-command[data-cmd="insertImage"]');
1450
+ left = $btn.offset().left + $btn.outerWidth() / 2;
1451
+ top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
1452
+ }
1453
+
1454
+ // Image is selected.
1455
+ else if ($current_image) {
1456
+ // Set the top to the bottom of the image.
1457
+ top = $current_image.offset().top + $current_image.outerHeight();
1458
+ }
1459
+
1460
+ // Image is selected and we are in inline mode.
1461
+ if (!$current_image && editor.opts.toolbarInline) {
1462
+ // Set top to the popup top.
1463
+ top = $popup.offset().top - editor.helpers.getPX($popup.css('margin-top'));
1464
+
1465
+ // If the popup is above apply height correction.
1466
+ if ($popup.hasClass('fr-above')) {
1467
+ top += $popup.outerHeight();
1468
+ }
1469
+ }
1470
+
1471
+ // Show the new layer.
1472
+ $popup.find('.fr-layer').removeClass('fr-active');
1473
+ $popup.find('.fr-' + name + '-layer').addClass('fr-active');
1474
+
1475
+ editor.popups.show('image.insert', left, top, ($current_image ? $current_image.outerHeight() : 0));
1476
+ }
1477
+
1478
+ /**
1479
+ * Refresh the upload image button.
1480
+ */
1481
+
1482
+ function refreshUploadButton ($btn) {
1483
+ var $popup = editor.popups.get('image.insert');
1484
+ if ($popup.find('.fr-image-upload-layer').hasClass('fr-active')) {
1485
+ $btn.addClass('fr-active');
1486
+ }
1487
+ }
1488
+
1489
+ /**
1490
+ * Refresh the insert by url button.
1491
+ */
1492
+
1493
+ function refreshByURLButton ($btn) {
1494
+ var $popup = editor.popups.get('image.insert');
1495
+ if ($popup.find('.fr-image-by-url-layer').hasClass('fr-active')) {
1496
+ $btn.addClass('fr-active');
1497
+ }
1498
+ }
1499
+
1500
+ /**
1501
+ * Init image resizer.
1502
+ */
1503
+
1504
+ function _initImageResizer () {
1505
+ var doc;
1506
+
1507
+ // No shared image resizer.
1508
+ if (!editor.shared.$image_resizer) {
1509
+ // Create shared image resizer.
1510
+ editor.shared.$image_resizer = $('<div class="fr-image-resizer"></div>');
1511
+ $image_resizer = editor.shared.$image_resizer;
1512
+
1513
+ // Bind mousedown event shared.
1514
+ editor.events.$on($image_resizer, 'mousedown', function (e) {
1515
+ e.stopPropagation();
1516
+ }, true);
1517
+
1518
+ // Image resize is enabled.
1519
+ if (editor.opts.imageResize) {
1520
+ $image_resizer.append(_getHandler('nw') + _getHandler('ne') + _getHandler('sw') + _getHandler('se'));
1521
+
1522
+ // Add image resizer overlay and set it.
1523
+ editor.shared.$img_overlay = $('<div class="fr-image-overlay"></div>');
1524
+ $overlay = editor.shared.$img_overlay;
1525
+ doc = $image_resizer.get(0).ownerDocument;
1526
+ $(doc).find('body').append($overlay);
1527
+ }
1528
+ } else {
1529
+ $image_resizer = editor.shared.$image_resizer;
1530
+ $overlay = editor.shared.$img_overlay;
1531
+
1532
+ editor.events.on('destroy', function () {
1533
+ $image_resizer.removeClass('fr-active').appendTo($('body'));
1534
+ }, true);
1535
+ }
1536
+
1537
+ // Shared destroy.
1538
+ editor.events.on('shared.destroy', function () {
1539
+ $image_resizer.html('').removeData().remove();
1540
+ $image_resizer = null;
1541
+
1542
+ if (editor.opts.imageResize) {
1543
+ $overlay.remove();
1544
+ $overlay = null;
1545
+ }
1546
+ }, true);
1547
+
1548
+ // Window resize. Exit from edit.
1549
+ if (!editor.helpers.isMobile()) {
1550
+ editor.events.$on($(editor.o_win), 'resize', function () {
1551
+ if ($current_image && !$current_image.hasClass('fr-uploading')) {
1552
+ _exitEdit(true);
1553
+ }
1554
+ else if ($current_image) {
1555
+ _repositionResizer();
1556
+ replace();
1557
+ showProgressBar(false);
1558
+ }
1559
+ });
1560
+ }
1561
+
1562
+ // Image resize is enabled.
1563
+ if (editor.opts.imageResize) {
1564
+ doc = $image_resizer.get(0).ownerDocument;
1565
+
1566
+ editor.events.$on($image_resizer, editor._mousedown, '.fr-handler', _handlerMousedown);
1567
+ editor.events.$on($(doc), editor._mousemove, _handlerMousemove);
1568
+ editor.events.$on($(doc.defaultView || doc.parentWindow), editor._mouseup, _handlerMouseup);
1569
+
1570
+ editor.events.$on($overlay, 'mouseleave', _handlerMouseup);
1571
+ }
1572
+ }
1573
+
1574
+ /**
1575
+ * Remove the current image.
1576
+ */
1577
+
1578
+ function remove ($img) {
1579
+ $img = $img || $current_image;
1580
+ if ($img) {
1581
+ if (editor.events.trigger('image.beforeRemove', [$img]) !== false) {
1582
+ editor.popups.hideAll();
1583
+ _exitEdit(true);
1584
+
1585
+ if ($img.get(0) == editor.$el.get(0)) {
1586
+ $img.removeAttr('src');
1587
+ } else {
1588
+ if ($img.get(0).parentNode.tagName == 'A') {
1589
+ editor.selection.setBefore($img.get(0).parentNode) || editor.selection.setAfter($img.get(0).parentNode) || $img.parent().after($.FE.MARKERS);
1590
+ $($img.get(0).parentNode).remove();
1591
+ } else {
1592
+ editor.selection.setBefore($img.get(0)) || editor.selection.setAfter($img.get(0)) || $img.after($.FE.MARKERS);
1593
+ $img.remove();
1594
+ }
1595
+
1596
+ editor.html.fillEmptyBlocks();
1597
+ editor.selection.restore();
1598
+ }
1599
+
1600
+ editor.undo.saveStep();
1601
+ }
1602
+ }
1603
+ }
1604
+
1605
+ /**
1606
+ * Initialization.
1607
+ */
1608
+
1609
+ function _init () {
1610
+ _initEvents();
1611
+
1612
+ // Init on image.
1613
+ if (editor.$el.get(0).tagName == 'IMG') {
1614
+ editor.$el.addClass('fr-view');
1615
+ }
1616
+
1617
+ editor.events.$on(editor.$el, editor.helpers.isMobile() && !editor.helpers.isWindowsPhone() ? 'touchend' : 'click', editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', _edit);
1618
+
1619
+ if (editor.helpers.isMobile()) {
1620
+ editor.events.$on(editor.$el, 'touchstart', editor.$el.get(0).tagName == 'IMG' ? null : 'img:not([contenteditable="false"])', function () {
1621
+ touchScroll = false;
1622
+ })
1623
+
1624
+ editor.events.$on(editor.$el, 'touchmove', function () {
1625
+ touchScroll = true;
1626
+ });
1627
+ }
1628
+
1629
+ editor.events.on('window.keydown keydown', function (e) {
1630
+ var key_code = e.which;
1631
+ if ($current_image && (key_code == $.FE.KEYCODE.BACKSPACE || key_code == $.FE.KEYCODE.DELETE)) {
1632
+ e.preventDefault();
1633
+ e.stopPropagation();
1634
+ remove();
1635
+ return false;
1636
+ }
1637
+
1638
+ if ($current_image && key_code == $.FE.KEYCODE.ESC) {
1639
+ var $img = $current_image;
1640
+ _exitEdit(true);
1641
+ editor.selection.setAfter($img.get(0));
1642
+ editor.selection.restore();
1643
+ e.preventDefault();
1644
+ return false;
1645
+ }
1646
+
1647
+ if ($current_image && !editor.keys.ctrlKey(e)) {
1648
+ e.preventDefault();
1649
+ return false;
1650
+ }
1651
+ }, true);
1652
+
1653
+ // Copy/cut image.
1654
+ editor.events.on('window.cut window.copy', function (e) {
1655
+ _selectImage();
1656
+ $.FE.copied_text = '\n';
1657
+ $.FE.copied_html = $current_image.get(0).outerHTML;
1658
+
1659
+ if (e.type == 'copy') {
1660
+ setTimeout(function () {
1661
+ _editImg($current_image);
1662
+ })
1663
+ }
1664
+ else {
1665
+ _exitEdit(true);
1666
+ editor.undo.saveStep();
1667
+ setTimeout(function () {
1668
+ editor.undo.saveStep();
1669
+ }, 0);
1670
+ }
1671
+ }, true);
1672
+
1673
+ // Do not leave page while uploading.
1674
+ editor.events.$on($(editor.o_win), 'keydown', function (e) {
1675
+ var key_code = e.which;
1676
+ if ($current_image && key_code == $.FE.KEYCODE.BACKSPACE) {
1677
+ e.preventDefault();
1678
+ return false;
1679
+ }
1680
+ });
1681
+
1682
+ // Check if image is uploading to abort it.
1683
+ editor.events.$on(editor.$win, 'keydown', function (e) {
1684
+ var key_code = e.which;
1685
+ if ($current_image && $current_image.hasClass('fr-uploading') && key_code == $.FE.KEYCODE.ESC) {
1686
+ $current_image.trigger('abortUpload');
1687
+ }
1688
+ });
1689
+
1690
+ editor.events.on('destroy', function () {
1691
+ if ($current_image && $current_image.hasClass('fr-uploading')) {
1692
+ $current_image.trigger('abortUpload');
1693
+ }
1694
+ });
1695
+
1696
+ editor.events.on('paste.before', _clipboardPaste);
1697
+ editor.events.on('paste.beforeCleanup', _clipboardPasteCleanup);
1698
+ editor.events.on('paste.after', _uploadPastedImages);
1699
+
1700
+ editor.events.on('html.set', _refreshImageList);
1701
+ editor.events.on('html.inserted', _refreshImageList);
1702
+ _refreshImageList();
1703
+ editor.events.on('destroy', function () {
1704
+ images = [];
1705
+ })
1706
+
1707
+ editor.events.on('html.get', function (html) {
1708
+ html = html.replace(/<(img)((?:[\w\W]*?))class="([\w\W]*?)(fr-uploading|fr-error)([\w\W]*?)"((?:[\w\W]*?))>/g, '');
1709
+
1710
+ return html;
1711
+ });
1712
+
1713
+ if (editor.opts.imageOutputSize) {
1714
+ var imgs;
1715
+
1716
+ editor.events.on('html.beforeGet', function () {
1717
+ imgs = editor.$el.get(0).querySelectorAll('img')
1718
+ for (var i = 0; i < imgs.length; i++) {
1719
+ imgs[i].setAttribute('width', $(imgs[i]).width());
1720
+ imgs[i].setAttribute('height', $(imgs[i]).height());
1721
+ }
1722
+ });
1723
+
1724
+ editor.events.on('html.afterGet', function () {
1725
+ for (var i = 0; i < imgs.length; i++) {
1726
+ imgs[i].removeAttribute('width');
1727
+ imgs[i].removeAttribute('height');
1728
+ }
1729
+ });
1730
+ }
1731
+
1732
+ if (editor.opts.iframe) {
1733
+ editor.events.on('image.loaded', editor.size.syncIframe);
1734
+ }
1735
+
1736
+ if (editor.$wp) {
1737
+ _syncImages();
1738
+ editor.events.on('contentChanged', _syncImages);
1739
+ }
1740
+
1741
+ editor.events.$on($(editor.o_win), 'orientationchange.image', function () {
1742
+ setTimeout(function () {
1743
+ var $current_image = get();
1744
+ if ($current_image) {
1745
+ _editImg($current_image);
1746
+ }
1747
+ }, 0);
1748
+ });
1749
+
1750
+ _initEditPopup(true);
1751
+ _initInsertPopup(true);
1752
+ _initSizePopup(true);
1753
+ _initAltPopup(true);
1754
+
1755
+ editor.events.on('node.remove', function ($node) {
1756
+ if ($node.get(0).tagName == 'IMG') {
1757
+ remove($node);
1758
+ return false;
1759
+ }
1760
+ });
1761
+ }
1762
+
1763
+ function _uploadPastedImages () {
1764
+ if (!editor.opts.imagePaste) {
1765
+ editor.$el.find('img[data-fr-image-pasted]').remove();
1766
+ } else {
1767
+ // Safari won't work https://bugs.webkit.org/show_bug.cgi?id=49141
1768
+ editor.$el.find('img[data-fr-image-pasted]').each(function (index, img) {
1769
+ if (editor.opts.imagePasteProcess) {
1770
+ var width = editor.opts.imageDefaultWidth;
1771
+ if (width && width != 'auto') {
1772
+ width = width + (editor.opts.imageResizeWithPercent ? '%' : 'px');
1773
+ }
1774
+ $(img).css('width', width);
1775
+
1776
+ $(img)
1777
+ .removeClass('fr-dii fr-dib fr-fir fr-fil')
1778
+ .addClass((editor.opts.imageDefaultDisplay ? 'fr-di' + editor.opts.imageDefaultDisplay[0] : '') + (editor.opts.imageDefaultAlign ? (editor.opts.imageDefaultAlign != 'center' ? ' fr-fi' + editor.opts.imageDefaultAlign[0] : '') : ''));
1779
+ }
1780
+
1781
+ // Data images.
1782
+ if (img.src.indexOf('data:') === 0) {
1783
+ if (editor.events.trigger('image.beforePasteUpload', [img]) === false) {
1784
+ return false;
1785
+ }
1786
+
1787
+ // Show the progress bar.
1788
+ $current_image = $(img);
1789
+ _repositionResizer();
1790
+ _showEditPopup();
1791
+ replace();
1792
+ showProgressBar();
1793
+ editor.edit.off();
1794
+
1795
+ // Convert image to blob.
1796
+ var binary = atob($(img).attr('src').split(',')[1]);
1797
+ var array = [];
1798
+ for (var i = 0; i < binary.length; i++) {
1799
+ array.push(binary.charCodeAt(i));
1800
+ }
1801
+ var upload_img = new Blob([new Uint8Array(array)], {
1802
+ type: 'image/jpeg'
1803
+ });
1804
+
1805
+ upload([upload_img]);
1806
+
1807
+ $(img).removeAttr('data-fr-image-pasted');
1808
+ }
1809
+
1810
+ // Images without http (Safari ones.).
1811
+ else if (img.src.indexOf('http') !== 0) {
1812
+ editor.selection.save();
1813
+ $(img).remove();
1814
+ editor.selection.restore();
1815
+ } else {
1816
+ $(img).removeAttr('data-fr-image-pasted');
1817
+ }
1818
+ });
1819
+ }
1820
+ }
1821
+
1822
+ function _clipboardPaste (e) {
1823
+ if (e && e.clipboardData) {
1824
+ if (e.clipboardData.items && e.clipboardData.items[0]) {
1825
+
1826
+ var file = e.clipboardData.items[0].getAsFile();
1827
+
1828
+ if (file) {
1829
+ var reader = new FileReader();
1830
+ reader.onload = function (e) {
1831
+ var result = e.target.result;
1832
+
1833
+ // Default width.
1834
+ var width = editor.opts.imageDefaultWidth;
1835
+ if (width && width != 'auto') {
1836
+ width = width + (editor.opts.imageResizeWithPercent ? '%' : 'px');
1837
+ }
1838
+
1839
+ editor.html.insert('<img data-fr-image-pasted="true" class="' + (editor.opts.imageDefaultDisplay ? 'fr-di' + editor.opts.imageDefaultDisplay[0] : '') + (editor.opts.imageDefaultAlign ? (editor.opts.imageDefaultAlign != 'center' ? ' fr-fi' + editor.opts.imageDefaultAlign[0] : '') : '') + '" src="' + result + '"' + (width ? ' style="width: ' + width + ';"' : '') + '>');
1840
+
1841
+ editor.events.trigger('paste.after');
1842
+ };
1843
+
1844
+ reader.readAsDataURL(file);
1845
+
1846
+ return false;
1847
+ }
1848
+ }
1849
+ }
1850
+ }
1851
+
1852
+ function _clipboardPasteCleanup (clipboard_html) {
1853
+ clipboard_html = clipboard_html.replace(/<img /gi, '<img data-fr-image-pasted="true" ');
1854
+ return clipboard_html;
1855
+ }
1856
+
1857
+ /**
1858
+ * Start edit.
1859
+ */
1860
+ var touchScroll;
1861
+
1862
+ function _edit (e) {
1863
+ if ($(this).parents('[contenteditable="false"]:not(.fr-element):not(body)').length) return true;
1864
+
1865
+ if (e && e.type == 'touchend' && touchScroll) {
1866
+ return true;
1867
+ }
1868
+
1869
+ if (e && editor.edit.isDisabled()) {
1870
+ e.stopPropagation();
1871
+ e.preventDefault();
1872
+ return false;
1873
+ }
1874
+
1875
+ // Hide resizer for other instances.
1876
+ for (var i = 0; i < $.FE.INSTANCES.length; i++) {
1877
+ if ($.FE.INSTANCES[i] != editor) {
1878
+ $.FE.INSTANCES[i].events.trigger('image.hideResizer');
1879
+ }
1880
+ }
1881
+
1882
+ editor.toolbar.disable();
1883
+
1884
+ if (e) {
1885
+ e.stopPropagation();
1886
+ e.preventDefault();
1887
+ }
1888
+
1889
+ // Hide keyboard.
1890
+ if (editor.helpers.isMobile()) {
1891
+ editor.events.disableBlur();
1892
+ editor.$el.blur();
1893
+ editor.events.enableBlur();
1894
+ }
1895
+
1896
+ if (editor.opts.iframe) {
1897
+ editor.size.syncIframe();
1898
+ }
1899
+
1900
+ $current_image = $(this);
1901
+ _selectImage();
1902
+ _repositionResizer();
1903
+ _showEditPopup();
1904
+
1905
+ editor.selection.clear();
1906
+ editor.button.bulkRefresh();
1907
+
1908
+ editor.events.trigger('video.hideResizer');
1909
+ }
1910
+
1911
+ /**
1912
+ * Exit edit.
1913
+ */
1914
+
1915
+ function _exitEdit (force_exit) {
1916
+ if ($current_image && (_canExit() || force_exit === true)) {
1917
+ editor.toolbar.enable();
1918
+
1919
+ $image_resizer.removeClass('fr-active');
1920
+
1921
+ editor.popups.hide('image.edit');
1922
+
1923
+ $current_image = null;
1924
+
1925
+ _unmarkExit();
1926
+ }
1927
+ }
1928
+
1929
+ var img_exit_flag = false;
1930
+
1931
+ function _markExit () {
1932
+ img_exit_flag = true;
1933
+ }
1934
+
1935
+ function _unmarkExit () {
1936
+ img_exit_flag = false;
1937
+ }
1938
+
1939
+ function _canExit () {
1940
+ return img_exit_flag;
1941
+ }
1942
+
1943
+ /**
1944
+ * Align image.
1945
+ */
1946
+
1947
+ function align (val) {
1948
+ $current_image.removeClass('fr-fir fr-fil');
1949
+ if (val == 'left') {
1950
+ $current_image.addClass('fr-fil');
1951
+ } else if (val == 'right') {
1952
+ $current_image.addClass('fr-fir');
1953
+ }
1954
+
1955
+ _repositionResizer();
1956
+ _showEditPopup();
1957
+ }
1958
+
1959
+ /**
1960
+ * Refresh the align icon.
1961
+ */
1962
+
1963
+ function refreshAlign ($btn) {
1964
+ if ($current_image) {
1965
+ if ($current_image.hasClass('fr-fil')) {
1966
+ $btn.find('> *:first').replaceWith(editor.icon.create('align-left'));
1967
+ } else if ($current_image.hasClass('fr-fir')) {
1968
+ $btn.find('> *:first').replaceWith(editor.icon.create('align-right'));
1969
+ } else {
1970
+ $btn.find('> *:first').replaceWith(editor.icon.create('align-justify'));
1971
+ }
1972
+ }
1973
+ }
1974
+
1975
+ /**
1976
+ * Refresh the align option from the dropdown.
1977
+ */
1978
+
1979
+ function refreshAlignOnShow ($btn, $dropdown) {
1980
+ if ($current_image) {
1981
+ var alignment = 'justify';
1982
+
1983
+ if ($current_image.hasClass('fr-fil')) {
1984
+ alignment = 'left';
1985
+ } else if ($current_image.hasClass('fr-fir')) {
1986
+ alignment = 'right';
1987
+ }
1988
+
1989
+ $dropdown.find('.fr-command[data-param1="' + alignment + '"]').addClass('fr-active');
1990
+ }
1991
+ }
1992
+
1993
+ /**
1994
+ * Align image.
1995
+ */
1996
+
1997
+ function display (val) {
1998
+ $current_image.removeClass('fr-dii fr-dib');
1999
+ if (val == 'inline') {
2000
+ $current_image.addClass('fr-dii');
2001
+ } else if (val == 'block') {
2002
+ $current_image.addClass('fr-dib');
2003
+ }
2004
+
2005
+ _repositionResizer();
2006
+ _showEditPopup();
2007
+ }
2008
+
2009
+ /**
2010
+ * Refresh the image display selected option.
2011
+ */
2012
+
2013
+ function refreshDisplayOnShow ($btn, $dropdown) {
2014
+ var d = 'block';
2015
+ if ($current_image.hasClass('fr-dii')) {
2016
+ d = 'inline';
2017
+ }
2018
+
2019
+ $dropdown.find('.fr-command[data-param1="' + d + '"]').addClass('fr-active');
2020
+ }
2021
+
2022
+ /**
2023
+ * Show the replace popup.
2024
+ */
2025
+
2026
+ function replace () {
2027
+ var $popup = editor.popups.get('image.insert');
2028
+ if (!$popup) $popup = _initInsertPopup();
2029
+
2030
+ if (!editor.popups.isVisible('image.insert')) {
2031
+ hideProgressBar();
2032
+ editor.popups.refresh('image.insert');
2033
+ editor.popups.setContainer('image.insert', $(editor.opts.scrollableContainer));
2034
+ }
2035
+
2036
+ var left = $current_image.offset().left + $current_image.width() / 2;
2037
+ var top = $current_image.offset().top + $current_image.height();
2038
+
2039
+ editor.popups.show('image.insert', left, top, $current_image.outerHeight());
2040
+ }
2041
+
2042
+ /**
2043
+ * Place selection around current image.
2044
+ */
2045
+ function _selectImage () {
2046
+ if ($current_image) {
2047
+ editor.selection.clear();
2048
+ var range = editor.doc.createRange();
2049
+ range.selectNode($current_image.get(0));
2050
+ var selection = editor.selection.get();
2051
+ selection.addRange(range);
2052
+ }
2053
+ }
2054
+
2055
+ /**
2056
+ * Get back to the image main popup.
2057
+ */
2058
+ function back () {
2059
+ if ($current_image) {
2060
+ $('.fr-popup input:focus').blur();
2061
+ _editImg($current_image);
2062
+ } else {
2063
+ editor.events.disableBlur();
2064
+ editor.selection.restore();
2065
+ editor.events.enableBlur();
2066
+
2067
+ editor.popups.hide('image.insert');
2068
+ editor.toolbar.showInline();
2069
+ }
2070
+ }
2071
+
2072
+ /**
2073
+ * Get the current image.
2074
+ */
2075
+
2076
+ function get () {
2077
+ return $current_image;
2078
+ }
2079
+
2080
+ /**
2081
+ * Apply specific style.
2082
+ */
2083
+
2084
+ function applyStyle (val, imageStyles, multipleStyles) {
2085
+ if (typeof imageStyles == 'undefined') imageStyles = editor.opts.imageStyles;
2086
+ if (typeof multipleStyles == 'undefined') multipleStyles = editor.opts.imageMultipleStyles;
2087
+
2088
+ if (!$current_image) return false;
2089
+
2090
+ // Remove multiple styles.
2091
+ if (!multipleStyles) {
2092
+ var styles = Object.keys(imageStyles);
2093
+ styles.splice(styles.indexOf(val), 1);
2094
+ $current_image.removeClass(styles.join(' '));
2095
+ }
2096
+
2097
+ $current_image.toggleClass(val);
2098
+
2099
+ _editImg($current_image);
2100
+ }
2101
+
2102
+ return {
2103
+ _init: _init,
2104
+ showInsertPopup: showInsertPopup,
2105
+ showLayer: showLayer,
2106
+ refreshUploadButton: refreshUploadButton,
2107
+ refreshByURLButton: refreshByURLButton,
2108
+ upload: upload,
2109
+ insertByURL: insertByURL,
2110
+ align: align,
2111
+ refreshAlign: refreshAlign,
2112
+ refreshAlignOnShow: refreshAlignOnShow,
2113
+ display: display,
2114
+ refreshDisplayOnShow: refreshDisplayOnShow,
2115
+ replace: replace,
2116
+ back: back,
2117
+ get: get,
2118
+ insert: insert,
2119
+ showProgressBar: showProgressBar,
2120
+ remove: remove,
2121
+ hideProgressBar: hideProgressBar,
2122
+ applyStyle: applyStyle,
2123
+ showAltPopup: showAltPopup,
2124
+ showSizePopup: showSizePopup,
2125
+ setAlt: setAlt,
2126
+ setSize: setSize,
2127
+ exitEdit: _exitEdit,
2128
+ edit: _editImg
2129
+ }
2130
+ }
2131
+
2132
+ // Insert image button.
2133
+ $.FE.DefineIcon('insertImage', {
2134
+ NAME: 'image'
2135
+ });
2136
+ $.FE.RegisterShortcut($.FE.KEYCODE.P, 'insertImage', null, 'P');
2137
+ $.FE.RegisterCommand('insertImage', {
2138
+ title: 'Insert Image',
2139
+ undo: false,
2140
+ focus: true,
2141
+ refreshAfterCallback: false,
2142
+ popup: true,
2143
+ callback: function () {
2144
+ if (!this.popups.isVisible('image.insert')) {
2145
+ this.image.showInsertPopup();
2146
+ } else {
2147
+ if (this.$el.find('.fr-marker')) {
2148
+ this.events.disableBlur();
2149
+ this.selection.restore();
2150
+ }
2151
+ this.popups.hide('image.insert');
2152
+ }
2153
+ },
2154
+ plugin: 'image'
2155
+ });
2156
+
2157
+ // Image upload button inside the insert image popup.
2158
+ $.FE.DefineIcon('imageUpload', {
2159
+ NAME: 'upload'
2160
+ });
2161
+ $.FE.RegisterCommand('imageUpload', {
2162
+ title: 'Upload Image',
2163
+ undo: false,
2164
+ focus: false,
2165
+ callback: function () {
2166
+ this.image.showLayer('image-upload');
2167
+ },
2168
+ refresh: function ($btn) {
2169
+ this.image.refreshUploadButton($btn);
2170
+ }
2171
+ });
2172
+
2173
+ // Image by URL button inside the insert image popup.
2174
+ $.FE.DefineIcon('imageByURL', {
2175
+ NAME: 'link'
2176
+ });
2177
+ $.FE.RegisterCommand('imageByURL', {
2178
+ title: 'By URL',
2179
+ undo: false,
2180
+ focus: false,
2181
+ callback: function () {
2182
+ this.image.showLayer('image-by-url');
2183
+ },
2184
+ refresh: function ($btn) {
2185
+ this.image.refreshByURLButton($btn);
2186
+ }
2187
+ })
2188
+
2189
+ // Insert image button inside the insert by URL layer.
2190
+ $.FE.RegisterCommand('imageInsertByURL', {
2191
+ title: 'Insert Image',
2192
+ undo: true,
2193
+ refreshAfterCallback: false,
2194
+ callback: function () {
2195
+ this.image.insertByURL();
2196
+ },
2197
+ refresh: function ($btn) {
2198
+ var $current_image = this.image.get();
2199
+ if (!$current_image) {
2200
+ $btn.text(this.language.translate('Insert'));
2201
+ } else {
2202
+ $btn.text(this.language.translate('Replace'));
2203
+ }
2204
+ }
2205
+ })
2206
+
2207
+ // Image display.
2208
+ $.FE.DefineIcon('imageDisplay', {
2209
+ NAME: 'star'
2210
+ })
2211
+ $.FE.RegisterCommand('imageDisplay', {
2212
+ title: 'Display',
2213
+ type: 'dropdown',
2214
+ options: {
2215
+ inline: 'Inline',
2216
+ block: 'Break Text'
2217
+ },
2218
+ callback: function (cmd, val) {
2219
+ this.image.display(val);
2220
+ },
2221
+ refresh: function ($btn) {
2222
+ if (!this.opts.imageTextNear) $btn.addClass('fr-hidden');
2223
+ },
2224
+ refreshOnShow: function ($btn, $dropdown) {
2225
+ this.image.refreshDisplayOnShow($btn, $dropdown);
2226
+ }
2227
+ })
2228
+
2229
+ // Image align.
2230
+ $.FE.DefineIcon('imageAlign', {
2231
+ NAME: 'align-center'
2232
+ })
2233
+ $.FE.RegisterCommand('imageAlign', {
2234
+ type: 'dropdown',
2235
+ title: 'Align',
2236
+ options: {
2237
+ left: 'Align Left',
2238
+ justify: 'None',
2239
+ right: 'Align Right'
2240
+ },
2241
+ html: function () {
2242
+ var c = '<ul class="fr-dropdown-list">';
2243
+ var options = $.FE.COMMANDS.imageAlign.options;
2244
+ for (var val in options) {
2245
+ if (options.hasOwnProperty(val)) {
2246
+ c += '<li><a class="fr-command fr-title" data-cmd="imageAlign" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.icon.create('align-' + val) + '</a></li>';
2247
+ }
2248
+ }
2249
+ c += '</ul>';
2250
+
2251
+ return c;
2252
+ },
2253
+ callback: function (cmd, val) {
2254
+ this.image.align(val);
2255
+ },
2256
+ refresh: function ($btn) {
2257
+ this.image.refreshAlign($btn);
2258
+ },
2259
+ refreshOnShow: function ($btn, $dropdown) {
2260
+ this.image.refreshAlignOnShow($btn, $dropdown);
2261
+ }
2262
+ })
2263
+
2264
+ // Image replace.
2265
+ $.FE.DefineIcon('imageReplace', {
2266
+ NAME: 'exchange'
2267
+ })
2268
+ $.FE.RegisterCommand('imageReplace', {
2269
+ title: 'Replace',
2270
+ undo: false,
2271
+ focus: false,
2272
+ refreshAfterCallback: false,
2273
+ callback: function () {
2274
+ this.image.replace();
2275
+ }
2276
+ })
2277
+
2278
+ // Image remove.
2279
+ $.FE.DefineIcon('imageRemove', {
2280
+ NAME: 'trash'
2281
+ })
2282
+ $.FE.RegisterCommand('imageRemove', {
2283
+ title: 'Remove',
2284
+ callback: function () {
2285
+ this.image.remove();
2286
+ }
2287
+ })
2288
+
2289
+ // Image back.
2290
+ $.FE.DefineIcon('imageBack', {
2291
+ NAME: 'arrow-left'
2292
+ });
2293
+ $.FE.RegisterCommand('imageBack', {
2294
+ title: 'Back',
2295
+ undo: false,
2296
+ focus: false,
2297
+ back: true,
2298
+ callback: function () {
2299
+ this.image.back();
2300
+ },
2301
+ refresh: function ($btn) {
2302
+ var $current_image = this.image.get();
2303
+ if (!$current_image && !this.opts.toolbarInline) {
2304
+ $btn.addClass('fr-hidden');
2305
+ $btn.next('.fr-separator').addClass('fr-hidden');
2306
+ } else {
2307
+ $btn.removeClass('fr-hidden');
2308
+ $btn.next('.fr-separator').removeClass('fr-hidden');
2309
+ }
2310
+ }
2311
+ });
2312
+
2313
+ $.FE.RegisterCommand('imageDismissError', {
2314
+ title: 'OK',
2315
+ undo: false,
2316
+ callback: function () {
2317
+ this.image.hideProgressBar(true);
2318
+ }
2319
+ })
2320
+
2321
+ // Image styles.
2322
+ $.FE.DefineIcon('imageStyle', {
2323
+ NAME: 'magic'
2324
+ })
2325
+ $.FE.RegisterCommand('imageStyle', {
2326
+ title: 'Style',
2327
+ type: 'dropdown',
2328
+ html: function () {
2329
+ var c = '<ul class="fr-dropdown-list">';
2330
+ var options = this.opts.imageStyles;
2331
+ for (var cls in options) {
2332
+ if (options.hasOwnProperty(cls)) {
2333
+ c += '<li><a class="fr-command" data-cmd="imageStyle" data-param1="' + cls + '">' + this.language.translate(options[cls]) + '</a></li>';
2334
+ }
2335
+ }
2336
+ c += '</ul>';
2337
+
2338
+ return c;
2339
+ },
2340
+ callback: function (cmd, val) {
2341
+ this.image.applyStyle(val);
2342
+ },
2343
+ refreshOnShow: function ($btn, $dropdown) {
2344
+ var $current_image = this.image.get();
2345
+
2346
+ if ($current_image) {
2347
+ $dropdown.find('.fr-command').each(function () {
2348
+ var cls = $(this).data('param1');
2349
+ $(this).toggleClass('fr-active', $current_image.hasClass(cls));
2350
+ })
2351
+ }
2352
+ }
2353
+ })
2354
+
2355
+ // Image alt.
2356
+ $.FE.DefineIcon('imageAlt', {
2357
+ NAME: 'info'
2358
+ })
2359
+ $.FE.RegisterCommand('imageAlt', {
2360
+ undo: false,
2361
+ focus: false,
2362
+ title: 'Alternate Text',
2363
+ callback: function () {
2364
+ this.image.showAltPopup();
2365
+ }
2366
+ });
2367
+
2368
+ $.FE.RegisterCommand('imageSetAlt', {
2369
+ undo: true,
2370
+ focus: false,
2371
+ title: 'Update',
2372
+ refreshAfterCallback: false,
2373
+ callback: function () {
2374
+ this.image.setAlt();
2375
+ }
2376
+ });
2377
+
2378
+ // Image size.
2379
+ $.FE.DefineIcon('imageSize', {
2380
+ NAME: 'arrows-alt'
2381
+ })
2382
+ $.FE.RegisterCommand('imageSize', {
2383
+ undo: false,
2384
+ focus: false,
2385
+ title: 'Change Size',
2386
+ callback: function () {
2387
+ this.image.showSizePopup();
2388
+ }
2389
+ });
2390
+
2391
+ $.FE.RegisterCommand('imageSetSize', {
2392
+ undo: true,
2393
+ focus: false,
2394
+ title: 'Update',
2395
+ refreshAfterCallback: false,
2396
+ callback: function () {
2397
+ this.image.setSize();
2398
+ }
2399
+ });
2400
+
2401
+ }));