editorjs-image 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -1,2 +1,886 @@
1
- const ImageTool = require( "./image-tool-tune" ).default;
2
- module.exports = ImageTool;
1
+ import ImageTool from '@editorjs/image';
2
+ export default ImageTool;
3
+
4
+ import Cropper from 'cropperjs';
5
+ export default class ImageToolTune {
6
+
7
+ constructor( { api, data, config, block } ) {
8
+
9
+ this.settings = [
10
+ {
11
+ name: 'floatLeft',
12
+ icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M952 792H72c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h880c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-632H72c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h880c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM608 660c8.8 0 16-7.2 16-16V380c0-8.8-7.2-16-16-16H96c-8.8 0-16 7.2-16 16v264c0 8.8 7.2 16 16 16h512zM152 436h400v152H152V436zm552 210c0 4.4 3.6 8 8 8h224c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H712c-4.4 0-8 3.6-8 8v56zm8-204h224c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H712c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8z"></path></svg>',
13
+ label: '',
14
+ group: 'align',
15
+ }, {
16
+ name: 'center',
17
+ icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M952 792H72c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h880c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-632H72c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h880c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM848 660c8.8 0 16-7.2 16-16V380c0-8.8-7.2-16-16-16H176c-8.8 0-16 7.2-16 16v264c0 8.8 7.2 16 16 16h672zM232 436h560v152H232V436z"></path></svg>',
18
+ label: '',
19
+ group: 'align',
20
+ }, {
21
+ name: 'floatRight',
22
+ icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M952 792H72c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h880c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0-632H72c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h880c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-24 500c8.8 0 16-7.2 16-16V380c0-8.8-7.2-16-16-16H416c-8.8 0-16 7.2-16 16v264c0 8.8 7.2 16 16 16h512zM472 436h400v152H472V436zM80 646c0 4.4 3.6 8 8 8h224c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H88c-4.4 0-8 3.6-8 8v56zm8-204h224c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H88c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8z"></path></svg>',
23
+ label: '',
24
+ group: 'align',
25
+ },
26
+ {
27
+ name: 'sizeSmall',
28
+ icon: '<svg stroke="currentColor" fill="none" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 6V4H20V20H12V18H8V16H4V8H8V6H12ZM14 6H18V18H14V6ZM12 8H10V16H12V8ZM8 10V14H6V10H8Z" fill="currentColor"></path></svg>',
29
+ label: '50%',
30
+ group: 'size',
31
+ }, {
32
+ name: 'sizeMiddle',
33
+ icon: '<svg stroke="currentColor" fill="none" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 6V4H20V20H12V18H8V16H4V8H8V6H12ZM14 6H18V18H14V6ZM12 8H10V16H12V8ZM8 10V14H6V10H8Z" fill="currentColor"></path></svg>',
34
+ label: '70%',
35
+ group: 'size',
36
+ }, {
37
+ name: 'sizeLarge',
38
+ icon: '<svg stroke="currentColor" fill="none" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 6V4H20V20H12V18H8V16H4V8H8V6H12ZM14 6H18V18H14V6ZM12 8H10V16H12V8ZM8 10V14H6V10H8Z" fill="currentColor"></path></svg>',
39
+ label: '100%',
40
+ group: 'size',
41
+ }, {
42
+ name: 'resize',
43
+ icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M29 30l1 90h36V66h26V30H29zm99 0v36h72V30h-72zm108 0v36h72V30h-72zm108 0v36h72V30h-72zm102 0v78h36V30h-36zm-206 80v36h100.543l-118 118H30v218h218V289.457l118-118V272h36V110H240zm206 34v72h36v-72h-36zM30 156v72h36v-72H30zm416 96v72h36v-72h-36zm0 108v72h36v-72h-36zm-166 86v36h72v-36h-72zm108 0v36h72v-36h-72z"></path></svg>',
44
+ label: '',
45
+ group: 'size',
46
+ }, {
47
+ name: 'crop',
48
+ icon: '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M21 15h2v2h-2v-2zm0-4h2v2h-2v-2zm2 8h-2v2c1 0 2-1 2-2zM13 3h2v2h-2V3zm8 4h2v2h-2V7zm0-4v2h2c0-1-1-2-2-2zM1 7h2v2H1V7zm16-4h2v2h-2V3zm0 16h2v2h-2v-2zM3 3C2 3 1 4 1 5h2V3zm6 0h2v2H9V3zM5 3h2v2H5V3zm-4 8v8c0 1.1.9 2 2 2h12V11H1zm2 8l2.5-3.21 1.79 2.15 2.5-3.22L13 19H3z"></path></svg>',
49
+ label: '',
50
+ group: 'size',
51
+ }
52
+ ];
53
+
54
+ this.api = api;
55
+ this.block = block;
56
+ this.config = config;
57
+ this.data = {
58
+ floatLeft: data !== undefined && data.floatLeft !== undefined ? data.floatLeft : false,
59
+ floatRight: data !== undefined && data.floatRight !== undefined ? data.floatRight : false,
60
+ center: data !== undefined && data.center !== undefined ? data.center : false,
61
+ sizeSmall: data !== undefined && data.sizeSmall !== undefined ? data.sizeSmall : false,
62
+ sizeMiddle: data !== undefined && data.sizeMiddle !== undefined ? data.sizeMiddle : false,
63
+ sizeLarge: data !== undefined && data.sizeLarge !== undefined ? data.sizeLarge : false,
64
+ resize: data !== undefined && data.resize !== undefined ? data.resize : false,
65
+ resizeSize: data !== undefined && data.resizeSize !== undefined ? data.resizeSize : 0,
66
+ crop: data !== undefined && data.crop !== undefined ? data.crop : false,
67
+ cropperFrameHeight: data !== undefined && data.cropperFrameHeight !== undefined ? data.cropperFrameHeight : 0,
68
+ cropperFrameWidth: data !== undefined && data.cropperFrameWidth !== undefined ? data.cropperFrameWidth : 0,
69
+ cropperFrameLeft: data !== undefined && data.cropperFrameLeft !== undefined ? data.cropperFrameLeft : 0,
70
+ cropperFrameTop: data !== undefined && data.cropperFrameTop !== undefined ? data.cropperFrameTop : 0,
71
+ cropperImageHeight: data !== undefined && data.cropperImageHeight !== undefined ? data.cropperImageHeight : 0,
72
+ cropperImageWidth: data !== undefined && data.cropperImageWidth !== undefined ? data.cropperImageWidth : 0,
73
+ cropperInterface: undefined,
74
+ };
75
+ this.wrapper = undefined;
76
+
77
+ this.buttons = [];
78
+
79
+ }
80
+
81
+ static get isTune() {
82
+ return true;
83
+ }
84
+
85
+ static get sanitize() {
86
+ return {
87
+ floatLeft: {},
88
+ floatRight: {},
89
+ center: {},
90
+ sizeSmall: {},
91
+ sizeMiddle: {},
92
+ sizeLarge: {},
93
+ resize: {},
94
+ resizeSize: {},
95
+ crop: {},
96
+ cropperFrameHeight: {},
97
+ cropperFrameWidth: {},
98
+ cropperFrameLeft: {},
99
+ cropperFrameTop: {},
100
+ cropperImageHeight: {},
101
+ cropperImageWidth: {},
102
+ cropperInterface: {},
103
+ };
104
+ }
105
+
106
+ /**
107
+ * CSS classes
108
+ * @return {object}
109
+ * @constructor
110
+ * @property {string} CSS.wrapper - wrapper for buttons
111
+ * @property {string} CSS.button - button
112
+ * @property {string} CSS.buttonActive - active button
113
+ * @property {string} CSS.buttonModifier - button with modifier
114
+ * @property {string} CSS.buttonModifierActive - active button with modifier
115
+ */
116
+ get CSS() {
117
+ return {
118
+ wrapper: 'cdx-image-tool-tune',
119
+ button: this.api.styles.settingsButton,
120
+ buttonActive: this.api.styles.settingsButtonActive,
121
+ buttonModifier: this.api.styles.settingsButtonModifier,
122
+ buttonModifierActive: this.api.styles.settingsButtonModifierActive,
123
+ isFloatLeft: 'cdx-image-tool-tune--floatLeft',
124
+ isFloatRight: 'cdx-image-tool-tune--floatRight',
125
+ isCenter: 'cdx-image-tool-tune--center',
126
+ isSizeSmall: 'cdx-image-tool-tune--sizeSmall',
127
+ isSizeMiddle: 'cdx-image-tool-tune--sizeMiddle',
128
+ isSizeLarge: 'cdx-image-tool-tune--sizeLarge',
129
+ isResize: 'cdx-image-tool-tune--resize',
130
+ isCrop: 'cdx-image-tool-tune--crop',
131
+ };
132
+ }
133
+
134
+ /**
135
+ *
136
+ * @return {HTMLElement}
137
+ * @public
138
+ * @readonly
139
+ * @property {HTMLElement} wrapper - tune buttons wrapper
140
+ */
141
+ get view() {
142
+ if ( !this.wrapper ) {
143
+ this.wrapper = this.createView();
144
+ }
145
+
146
+ return this.wrapper;
147
+ }
148
+
149
+ /**
150
+ * Clicks to one of the tunes
151
+ * @param {MouseEvent} e - click
152
+ * @param {HTMLElement} tune - clicked tune button
153
+ * @private
154
+ * @return {void}
155
+ * */
156
+ tuneClicked( e, tune ) {
157
+ e.preventDefault();
158
+ e.stopPropagation();
159
+
160
+
161
+ let tuneName = tune.dataset.tune;
162
+
163
+ let tuneGroup = this.settings.find( tune => tune.name === tuneName ).group;
164
+
165
+
166
+ this.buttons.forEach( button => {
167
+
168
+ //if is the same group
169
+ if ( this.settings.find( tune => tune.name === button.dataset.tune ).group === tuneGroup ) {
170
+ if ( button !== tune ) {
171
+ button.classList.remove( this.CSS.buttonActive );
172
+ }
173
+ }
174
+ } );
175
+
176
+ tune.classList.toggle( this.CSS.buttonActive );
177
+
178
+
179
+ this.setTune( tune.dataset.tune );
180
+ }
181
+
182
+ /**
183
+ * Styles the image with a tune
184
+ * @param {string} tune - tune name
185
+ * @private
186
+ * @return {void}
187
+ * */
188
+ setTune( tune ) {
189
+ switch ( tune ) {
190
+ case 'floatLeft':
191
+ this.data.floatLeft = !this.data.floatLeft;
192
+ this.data.floatRight = false;
193
+ this.data.center = false;
194
+ break;
195
+ case 'floatRight':
196
+ this.data.floatLeft = false;
197
+ this.data.floatRight = !this.data.floatRight;
198
+ this.data.center = false;
199
+ break;
200
+ case 'center':
201
+ this.data.center = !this.data.center;
202
+ this.data.floatLeft = false;
203
+ this.data.floatRight = false;
204
+ break;
205
+ case 'sizeSmall':
206
+ this.data.sizeSmall = !this.data.sizeSmall;
207
+ this.data.sizeMiddle = false;
208
+ this.data.sizeLarge = false;
209
+ this.data.resize = false;
210
+ this.data.crop = false;
211
+ break;
212
+ case 'sizeMiddle':
213
+ this.data.sizeSmall = false;
214
+ this.data.sizeMiddle = !this.data.sizeMiddle;
215
+ this.data.sizeLarge = false;
216
+ this.data.resize = false;
217
+ this.data.crop = false;
218
+ break;
219
+ case 'sizeLarge':
220
+ this.data.sizeSmall = false;
221
+ this.data.sizeMiddle = false;
222
+ this.data.sizeLarge = !this.data.sizeLarge;
223
+ this.data.resize = false;
224
+ this.data.crop = false;
225
+ break;
226
+ case 'resize':
227
+ this.data.sizeSmall = false;
228
+ this.data.sizeMiddle = false;
229
+ this.data.sizeLarge = false;
230
+ this.data.resize = !this.data.resize;
231
+ this.data.crop = false;
232
+ break;
233
+ case 'crop':
234
+ this.data.crop = !this.data.crop;
235
+ this.data.sizeSmall = false;
236
+ this.data.sizeMiddle = false;
237
+ this.data.sizeLarge = false;
238
+ this.data.resize = false;
239
+ this.data.resizeSize = 0;
240
+ break;
241
+ default:
242
+ this.data.floatLeft = false;
243
+ this.data.floatRight = false;
244
+ this.data.sizeSmall = false;
245
+ this.data.sizeMiddle = false;
246
+ this.data.sizeLarge = false;
247
+ this.data.resize = false;
248
+ this.data.crop = false;
249
+ break;
250
+ }
251
+
252
+ if ( !this.data.resize ) {
253
+ this.data.resizeSize = 0;
254
+ }
255
+
256
+ if ( !this.data.crop ) {
257
+ this.data.cropperFrameHeight = 0;
258
+ this.data.cropperFrameWidth = 0;
259
+ this.data.cropperFrameLeft = 0;
260
+ this.data.cropperFrameTop = 0;
261
+ this.data.cropperImageHeight = 0;
262
+ this.data.cropperImageWidth = 0;
263
+ }
264
+
265
+ const blockContent = this.block.holder.querySelector( '.ce-block__content' );
266
+
267
+ this.apply( blockContent );
268
+
269
+ this.block.dispatchChange();
270
+
271
+
272
+
273
+ }
274
+
275
+
276
+ /**
277
+ * Append class to block by tune data
278
+ * @param {HTMLElement} blockContent - wrapper for block content
279
+ * @public
280
+ * @return {void}
281
+ * */
282
+ apply( blockContent ) {
283
+
284
+
285
+ if ( this.data.floatLeft ) {
286
+ blockContent.classList.add( this.CSS.isFloatLeft );
287
+ } else {
288
+ blockContent.classList.remove( this.CSS.isFloatLeft );
289
+ }
290
+
291
+ if ( this.data.floatRight ) {
292
+ blockContent.classList.add( this.CSS.isFloatRight );
293
+ } else {
294
+ blockContent.classList.remove( this.CSS.isFloatRight );
295
+ }
296
+
297
+ if ( this.data.center ) {
298
+ blockContent.classList.add( this.CSS.isCenter );
299
+ } else {
300
+ blockContent.classList.remove( this.CSS.isCenter );
301
+ }
302
+
303
+ if ( this.data.sizeSmall ) {
304
+ blockContent.classList.add( this.CSS.isSizeSmall );
305
+ } else {
306
+ blockContent.classList.remove( this.CSS.isSizeSmall );
307
+ }
308
+
309
+ if ( this.data.sizeMiddle ) {
310
+ blockContent.classList.add( this.CSS.isSizeMiddle );
311
+ } else {
312
+ blockContent.classList.remove( this.CSS.isSizeMiddle );
313
+ }
314
+
315
+ if ( this.data.sizeLarge ) {
316
+ blockContent.classList.add( this.CSS.isSizeLarge );
317
+ } else {
318
+ blockContent.classList.remove( this.CSS.isSizeLarge );
319
+ }
320
+
321
+ if ( this.data.resize ) {
322
+ blockContent.classList.add( this.CSS.isResize );
323
+
324
+ if ( this.data.resizeSize > 0 ) {
325
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.width = this.data.resizeSize + 'px';
326
+ }
327
+
328
+ this.resize( blockContent );
329
+ } else {
330
+ blockContent.classList.remove( this.CSS.isResize );
331
+ this.unresize( blockContent );
332
+ }
333
+
334
+ if ( this.data.crop ) {
335
+ blockContent.classList.add( this.CSS.isCrop );
336
+
337
+ this.crop( blockContent );
338
+ if ( this.data.cropperFrameHeight > 0 && this.data.cropperFrameWidth > 0 ) {
339
+ this.applyCrop( blockContent );
340
+ }
341
+
342
+
343
+ } else {
344
+ blockContent.classList.remove( this.CSS.isCrop );
345
+ this.uncrop( blockContent );
346
+ }
347
+
348
+
349
+
350
+
351
+ }
352
+
353
+
354
+
355
+
356
+ /**
357
+ * Add crop handles to image
358
+ * @param {HTMLCollection} images - images in block
359
+ * @public
360
+ * @return {void}
361
+ */
362
+ crop( blockContent ) {
363
+
364
+ //this.appendCrop( blockContent );
365
+
366
+
367
+
368
+ //add append crop button to image-tool__image
369
+ const image = blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ];
370
+ const cropBtn = document.createElement( 'div' );
371
+ cropBtn.classList.add( 'crop-btn', 'btn-crop-action' );
372
+ cropBtn.innerHTML = 'Crop';
373
+
374
+ cropBtn.addEventListener( 'click', e => {
375
+ //remove crop button
376
+ image.removeChild( cropBtn );
377
+ this.appendCrop( blockContent );
378
+ }
379
+ );
380
+
381
+ image.appendChild( cropBtn );
382
+
383
+ }
384
+
385
+
386
+ appendCrop( blockContent ) {
387
+ this.uncrop( blockContent );
388
+ const image = blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].getElementsByTagName( 'img' )[ 0 ];
389
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].classList.add( 'isCropping' );
390
+ this.cropperInterface = new Cropper( image, {
391
+ crop( event ) {
392
+ console.log( event.detail.x );
393
+ console.log( event.detail.y );
394
+ console.log( event.detail.width );
395
+ console.log( event.detail.height );
396
+ console.log( event.detail.scaleX );
397
+ console.log( event.detail.scaleY );
398
+
399
+
400
+ },
401
+ } );
402
+
403
+
404
+ //append save crop button
405
+ const cropSaveBtn = document.createElement( 'div' );
406
+ cropSaveBtn.classList.add( 'crop-save', 'btn-crop-action' );
407
+
408
+ cropSaveBtn.innerHTML = 'Apply';
409
+
410
+ cropSaveBtn.addEventListener( 'click', e => {
411
+
412
+ console.log( this.cropperInterface.getCropBoxData() );
413
+ console.log( this.cropperInterface.getImageData() );
414
+ console.log( this.cropperInterface.getCanvasData() );
415
+
416
+
417
+ this.data.cropperFrameHeight = this.cropperInterface.getCropBoxData().height;
418
+ this.data.cropperFrameWidth = this.cropperInterface.getCropBoxData().width;
419
+ this.data.cropperFrameLeft = this.cropperInterface.getCanvasData().left - this.cropperInterface.getCropBoxData().left;
420
+ this.data.cropperFrameTop = this.cropperInterface.getCanvasData().top - this.cropperInterface.getCropBoxData().top;
421
+ this.data.cropperImageHeight = this.cropperInterface.getImageData().height;
422
+ this.data.cropperImageWidth = this.cropperInterface.getImageData().width;
423
+
424
+ this.applyCrop( blockContent );
425
+
426
+ }
427
+ );
428
+
429
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].appendChild( cropSaveBtn );
430
+
431
+ //add temporary style to block content so that it comes in front of every other block
432
+ blockContent.classList.add( 'isCropping' );
433
+
434
+ }
435
+
436
+
437
+ applyCrop( blockContent ) {
438
+
439
+ //apply data to image and remove cropper interface and save button, add crop button
440
+
441
+ const image = blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].getElementsByTagName( 'img' )[ 0 ];
442
+
443
+
444
+
445
+
446
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.minWidth = this.data.cropperFrameWidth + 'px';
447
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.maxWidth = this.data.cropperFrameWidth + 'px';
448
+
449
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].style.width = this.data.cropperFrameWidth + 'px';
450
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].style.height = this.data.cropperFrameHeight + 'px';
451
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.left = this.data.cropperFrameLeft + 'px';
452
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.top = this.data.cropperFrameTop + 'px';
453
+
454
+ image.style.width = this.data.cropperImageWidth + 'px';
455
+ image.style.height = this.data.cropperImageHeight + 'px';
456
+
457
+
458
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].classList.add( 'isCropped' );
459
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].classList.remove( 'isCropping' );
460
+
461
+
462
+ const cropSaveBtn = blockContent.getElementsByClassName( 'btn-crop-action' )[ 0 ];
463
+ if ( cropSaveBtn ) {
464
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].removeChild( cropSaveBtn );
465
+ }
466
+
467
+
468
+
469
+ //remove cropper interface
470
+ if ( this.cropperInterface ) {
471
+ this.cropperInterface.destroy();
472
+ }
473
+
474
+
475
+
476
+
477
+ //add crop button
478
+
479
+ const cropBtn = document.createElement( 'div' );
480
+ cropBtn.classList.add( 'crop-btn', 'btn-crop-action' );
481
+ cropBtn.innerHTML = 'Crop';
482
+
483
+ cropBtn.addEventListener( 'click', e => {
484
+ //remove crop button
485
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].removeChild( cropBtn );
486
+ this.appendCrop( blockContent );
487
+ }
488
+ );
489
+
490
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].appendChild( cropBtn );
491
+
492
+ blockContent.classList.remove( 'isCropping' );
493
+
494
+ this.block.dispatchChange();
495
+
496
+ }
497
+
498
+
499
+ uncrop( blockContent ) {
500
+
501
+ //remove crop and save button
502
+ const cropSaveBtn = blockContent.getElementsByClassName( 'btn-crop-action' )[ 0 ];
503
+ if ( cropSaveBtn ) {
504
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].removeChild( cropSaveBtn );
505
+ }
506
+
507
+
508
+ //remove crop button
509
+ const cropBtn = blockContent.getElementsByClassName( 'btn-crop-action' )[ 0 ];
510
+ if ( cropBtn ) {
511
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].removeChild( cropBtn );
512
+ }
513
+
514
+ //remove isCropped class
515
+
516
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].classList.remove( 'isCropped' );
517
+
518
+ //remove isCropping class
519
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].classList.remove( 'isCropping' );
520
+
521
+ //remove min and max width
522
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.minWidth = '';
523
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.maxWidth = '';
524
+
525
+ //remove image width and height
526
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].style.width = '';
527
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].style.height = '';
528
+
529
+ //remove image left and top
530
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.left = '';
531
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.top = '';
532
+
533
+ //remove image width and height
534
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.width = '';
535
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.height = '';
536
+
537
+
538
+
539
+ blockContent.classList.remove( 'isCropping' );
540
+
541
+
542
+
543
+ //remove cropper interface
544
+ if ( this.cropperInterface ) {
545
+ this.cropperInterface.destroy();
546
+ }
547
+
548
+ //remove crop data
549
+ this.data.cropperFrameHeight = 0;
550
+ this.data.cropperFrameWidth = 0;
551
+ this.data.cropperFrameLeft = 0;
552
+ this.data.cropperFrameTop = 0;
553
+ this.data.cropperImageHeight = 0;
554
+ this.data.cropperImageWidth = 0;
555
+
556
+ this.block.dispatchChange();
557
+
558
+ }
559
+
560
+
561
+
562
+
563
+
564
+ /**
565
+ * Add resize handles to block
566
+ * @param {HTMLElement} blockContent - wrapper for block content
567
+ * @public
568
+ * @return {void}
569
+ * */
570
+ resize( blockContent ) {
571
+
572
+
573
+ const resizable = document.createElement( 'div' );
574
+ resizable.classList.add( 'resizable' );
575
+
576
+ console.log( resizable );
577
+
578
+
579
+ const resizers = document.createElement( 'div' );
580
+ resizers.classList.add( 'resizers' );
581
+
582
+ const resizerTopLeft = document.createElement( 'div' );
583
+ resizerTopLeft.classList.add( 'resizer', 'top-left' );
584
+ resizerTopLeft.addEventListener( 'mousedown', e => {
585
+ this.resizeClick( blockContent.getElementsByClassName( 'cdx-block' )[ 0 ], resizerTopLeft, e );
586
+ } );
587
+
588
+
589
+ const resizerTopRight = document.createElement( 'div' );
590
+ resizerTopRight.classList.add( 'resizer', 'top-right' );
591
+ resizerTopRight.addEventListener( 'mousedown', e => {
592
+ this.resizeClick( blockContent.getElementsByClassName( 'cdx-block' )[ 0 ], resizerTopRight, e );
593
+ } );
594
+
595
+
596
+ const resizerBottomLeft = document.createElement( 'div' );
597
+ resizerBottomLeft.classList.add( 'resizer', 'bottom-left' );
598
+ resizerBottomLeft.addEventListener( 'mousedown', e => {
599
+ this.resizeClick( blockContent.getElementsByClassName( 'cdx-block' )[ 0 ], resizerBottomLeft, e );
600
+ } );
601
+
602
+ const resizerBottomRight = document.createElement( 'div' );
603
+ resizerBottomRight.classList.add( 'resizer', 'bottom-right' );
604
+ resizerBottomRight.addEventListener( 'mousedown', e => {
605
+ this.resizeClick( blockContent.getElementsByClassName( 'cdx-block' )[ 0 ], resizerBottomRight, e );
606
+ } );
607
+
608
+ resizers.appendChild( resizerTopLeft );
609
+ resizers.appendChild( resizerTopRight );
610
+ resizers.appendChild( resizerBottomLeft );
611
+ resizers.appendChild( resizerBottomRight );
612
+ resizable.appendChild( resizers );
613
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].appendChild( resizable );
614
+ }
615
+
616
+
617
+
618
+ /**
619
+ * click event to resize handles
620
+ * preserve aspect ratio
621
+ * prevent block from moving when dragging resize handles
622
+ * max size = 100%
623
+ * min size = 50px
624
+ * @param {HTMLElement} blockContent - wrapper for block content
625
+ * @public
626
+ * @return {void}
627
+ * */
628
+ resizeClick( blockContent, handle, e ) {
629
+
630
+ let startX = 0;
631
+ let startY = 0;
632
+ let startWidth = 0;
633
+ let startHeight = 0;
634
+
635
+ const mouseMoveHandler = e => {
636
+
637
+ const dx = e.clientX - startX;
638
+ const dy = e.clientY - startY;
639
+
640
+ const newWidth = ( startWidth + dx );
641
+
642
+ if ( newWidth > 50 ) {
643
+ blockContent.style.width = newWidth + 'px';
644
+ }
645
+
646
+
647
+
648
+
649
+
650
+ };
651
+
652
+ const mouseUpHandler = () => {
653
+
654
+ let blockWidth = parseInt( document.defaultView.getComputedStyle( blockContent ).width, 10 );
655
+
656
+ if ( blockWidth > 0 ) {
657
+ this.data.resizeSize = blockWidth;
658
+ }
659
+
660
+ document.removeEventListener( 'mousemove', mouseMoveHandler );
661
+ document.removeEventListener( 'mouseup', mouseUpHandler );
662
+
663
+ this.block.dispatchChange();
664
+
665
+ };
666
+
667
+ document.addEventListener( 'mousemove', mouseMoveHandler );
668
+ document.addEventListener( 'mouseup', mouseUpHandler );
669
+
670
+ startX = e.clientX;
671
+ startY = e.clientY;
672
+
673
+ startWidth = parseInt( document.defaultView.getComputedStyle( blockContent ).width, 10 );
674
+
675
+ startHeight = parseInt( document.defaultView.getComputedStyle( blockContent ).height, 10 );
676
+
677
+
678
+ }
679
+
680
+
681
+ /**
682
+ * Remove resize handles from block
683
+ * @param {HTMLElement} blockContent - wrapper for block content
684
+ * @public
685
+ * @return {void}
686
+ */
687
+ unresize( blockContent ) {
688
+ const unresizable = blockContent.getElementsByClassName( 'resizable' )[ 0 ];
689
+ if ( unresizable ) {
690
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].removeChild( unresizable );
691
+ }
692
+
693
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.width = 'auto';
694
+
695
+
696
+ }
697
+
698
+
699
+
700
+
701
+ /**
702
+ * Remove tunes from block wrapper
703
+ * @param {HTMLElement} blockContent - wrapper for block content
704
+ * @public
705
+ * @return {HTMLElement}
706
+ */
707
+ unwrap( blockContent ) {
708
+ console.log( 'unwrap' );
709
+ //remove tunes from block
710
+ this.buttons.forEach( button => {
711
+ button.classList.remove( this.CSS.buttonActive );
712
+ } );
713
+
714
+ //remove isFloatLeft class
715
+ blockContent.classList.remove( this.CSS.isFloatLeft );
716
+
717
+ //remove isFloatRight class
718
+ blockContent.classList.remove( this.CSS.isFloatRight );
719
+
720
+ //remove isCenter class
721
+ blockContent.classList.remove( this.CSS.isCenter );
722
+
723
+ //remove isSizeSmall class
724
+ blockContent.classList.remove( this.CSS.isSizeSmall );
725
+
726
+ //remove isSizeMiddle class
727
+ blockContent.classList.remove( this.CSS.isSizeMiddle );
728
+
729
+ //remove isSizeLarge class
730
+ blockContent.classList.remove( this.CSS.isSizeLarge );
731
+
732
+ //remove isResize class
733
+ blockContent.classList.remove( this.CSS.isResize );
734
+
735
+ //remove isCrop class
736
+ blockContent.classList.remove( this.CSS.isCrop );
737
+
738
+ //remove isCropped class
739
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].classList.remove( 'isCropped' );
740
+
741
+ //remove isCropping class
742
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].classList.remove( 'isCropping' );
743
+
744
+ //remove min and max width
745
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.minWidth = '';
746
+ blockContent.getElementsByClassName( 'cdx-block' )[ 0 ].style.maxWidth = '';
747
+
748
+ //remove image width and height
749
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].style.width = '';
750
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].style.height = '';
751
+
752
+ //remove image left and top
753
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.left = '';
754
+
755
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.top = '';
756
+
757
+ //remove image width and height
758
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.width = '';
759
+
760
+ blockContent.getElementsByClassName( 'image-tool__image' )[ 0 ].getElementsByTagName( 'img' )[ 0 ].style.height = '';
761
+
762
+ //remove resize handles
763
+ this.unresize( blockContent );
764
+
765
+ //remove crop handles
766
+ this.uncrop( blockContent );
767
+
768
+ //remove cropper interface
769
+ if ( this.cropperInterface ) {
770
+ this.cropperInterface.destroy();
771
+ }
772
+
773
+ //remove crop data
774
+ this.data.cropperFrameHeight = 0;
775
+ this.data.cropperFrameWidth = 0;
776
+ this.data.cropperFrameLeft = 0;
777
+ this.data.cropperFrameTop = 0;
778
+ this.data.cropperImageHeight = 0;
779
+ this.data.cropperImageWidth = 0;
780
+
781
+ return blockContent;
782
+ }
783
+
784
+
785
+
786
+ /**
787
+ * Add tune to block data
788
+ * @private
789
+ * @return {void}
790
+ * */
791
+ save() {
792
+ return this.data;
793
+ }
794
+
795
+ /**
796
+ * Append tunes to block wrapper
797
+ * @param {HTMLElement} blockContent - wrapper for block content
798
+ * @public
799
+ * @return {HTMLElement}
800
+ * */
801
+ wrap( blockContent ) {
802
+
803
+ //createview if not exists
804
+ if ( !this.wrapper ) {
805
+ this.wrapper = this.createView();
806
+ }
807
+
808
+
809
+ this.apply( blockContent );
810
+
811
+
812
+
813
+
814
+ return blockContent;
815
+ }
816
+
817
+
818
+ /**
819
+ * Creates a view for tunes
820
+ * @return {HTMLElement}
821
+ * @private
822
+ * */
823
+ createView() {
824
+ this.buttons = this.settings.map( tune => {
825
+ const el = document.createElement( 'div' );
826
+ const buttonIco = document.createElement( 'span' );
827
+ const buttonTxt = document.createElement( 'span' );
828
+ el.classList.add( this.CSS.button );
829
+ buttonTxt.style.fontSize = '8px';
830
+ buttonIco.innerHTML = tune.icon;
831
+ buttonTxt.innerHTML = tune.label;
832
+ el.appendChild( buttonIco );
833
+ el.appendChild( buttonTxt );
834
+ el.dataset.tune = tune.name;
835
+ el.title = tune.label;
836
+
837
+ el.addEventListener( 'click', e => this.tuneClicked( e, el ) );
838
+
839
+ return el;
840
+ } );
841
+ const wrapper = document.createElement( 'div' );
842
+ this.buttons.forEach( button => {
843
+ wrapper.appendChild
844
+ ( button );
845
+ } );
846
+ wrapper.classList.add( this.CSS.wrapper );
847
+ return wrapper;
848
+
849
+ }
850
+
851
+ /**
852
+ * Checks if tune is active
853
+ * @param {string} tune - tune name
854
+ * @return {boolean}
855
+ * @private
856
+ * */
857
+ isTuneActive( tune ) {
858
+ return !!this.data[ tune ];
859
+ }
860
+
861
+ /**
862
+ * Makes buttons with tunes
863
+ * @return {HTMLElement}
864
+ * @public
865
+ * */
866
+ render() {
867
+ //when editor is ready
868
+ this.buttons.forEach( button => {
869
+ button.classList.toggle( this.CSS.buttonActive, this.isTuneActive( button.dataset.tune ) );
870
+ }
871
+ );
872
+
873
+ return this.view;
874
+ }
875
+
876
+
877
+ /**
878
+ * Destroys the plugin
879
+ * @public
880
+ * @return {void}
881
+ * */
882
+ destroy() {
883
+ this.wrapper = null;
884
+ this.buttons = null;
885
+ }
886
+ }