Socket
Socket
Sign inDemoInstall

tui-image-editor

Package Overview
Dependencies
3
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.2.2 to 3.3.0

1

.eslintrc.js

@@ -20,2 +20,3 @@ module.exports = {

'rules': {
indent: [2, 4, {SwitchCase: 1, ignoreComments: false, ImportDeclaration: 1, flatTernaryExpressions: false}],
'prefer-destructuring': ['error', {

@@ -22,0 +23,0 @@ VariableDeclarator: {array: true, object: true},

2

bower.json

@@ -40,5 +40,5 @@ {

"resolutions": {
"tui-code-snippet": "^1.4.0",
"tui-code-snippet": "^1.5.0",
"tui-color-picker": "^2.2.0"
}
}
{
"name": "tui-image-editor",
"author": "NHNEnt FE Development Lab <dl_javascript@nhnent.com>",
"version": "3.2.2",
"version": "3.3.0",
"license": "MIT",

@@ -70,5 +70,5 @@ "repository": {

"core-js": "2.4.1",
"tui-code-snippet": "^1.4.0",
"tui-code-snippet": "^1.5.0",
"tui-color-picker": "^2.2.0"
}
}

@@ -7,2 +7,5 @@ # ![Toast UI ImageEditor](https://user-images.githubusercontent.com/35218826/40895380-0b9f4cd6-67ea-11e8-982f-18121daa3a04.png)

## Wrappers
- [toast-ui.vue-image-editor](https://github.com/nhnent/toast-ui.vue-image-editor): Vue wrapper component is powered by [NHN Entertainment](https://github.com/nhnent).
![6 -20-2018 17-45-54](https://user-images.githubusercontent.com/35218826/41647896-7b218ae0-74b2-11e8-90db-d7805cc23e8c.gif)

@@ -36,2 +39,3 @@

* [TOAST UI Family](#-toast-ui-family)
* [Used By](#-used-by)
* [License](#-license)

@@ -333,3 +337,6 @@

## 🚀 Used By
* [TOAST Dooray! - Collaboration Service (Project, Messenger, Mail, Calendar, Drive, Wiki, Contacts)](https://dooray.com/home/)
## 📜 License
[MIT LICENSE](https://github.com/nhnent/tui.image-editor/blob/master/LICENSE)

@@ -323,2 +323,28 @@ import {extend} from 'tui-code-snippet';

this.ui.changeMenu('crop');
},
preset: presetType => {
switch (presetType) {
case 'preset-square':
this.setCropzoneRect(1 / 1);
break;
case 'preset-3-2':
this.setCropzoneRect(3 / 2);
break;
case 'preset-4-3':
this.setCropzoneRect(4 / 3);
break;
case 'preset-5-4':
this.setCropzoneRect(5 / 4);
break;
case 'preset-7-5':
this.setCropzoneRect(7 / 5);
break;
case 'preset-16-9':
this.setCropzoneRect(16 / 9);
break;
default:
this.setCropzoneRect();
this.ui.crop.changeApplyButtonStatus(false);
break;
}
}

@@ -325,0 +351,0 @@ }, this._commonAction());

@@ -26,7 +26,12 @@ /**

const prevImageHeight = prevImage ? prevImage.height : 0;
const objects = graphics.removeAll(true).filter(objectItem => objectItem.type !== 'cropzone');
objects.forEach(objectItem => {
objectItem.evented = true;
});
this.undoData = {
name: loader.getImageName(),
image: prevImage,
objects: graphics.removeAll(true)
objects
};

@@ -41,2 +46,3 @@

},
/**

@@ -43,0 +49,0 @@ * @param {Graphics} graphics - Graphics instance

@@ -5,2 +5,3 @@ /**

*/
import snippet from 'tui-code-snippet';
import fabric from 'fabric/dist/fabric.require';

@@ -13,2 +14,8 @@ import Component from '../interface/component';

const MOUSE_MOVE_THRESHOLD = 10;
const DEFAULT_OPTION = {
top: -10,
left: -10,
height: 1,
width: 1
};

@@ -283,2 +290,55 @@ /**

/**
* Set a cropzone square
* @param {number} [presetRatio] - preset ratio
*/
setCropzoneRect(presetRatio) {
const canvas = this.getCanvas();
const cropzone = this._cropzone;
canvas.deactivateAll();
canvas.selection = false;
cropzone.remove();
cropzone.set(presetRatio ? this._getPresetCropSizePosition(presetRatio) : DEFAULT_OPTION);
canvas.add(cropzone);
canvas.selection = true;
if (presetRatio) {
canvas.setActiveObject(cropzone);
}
}
/**
* Set a cropzone square
* @param {number} presetRatio - preset ratio
* @returns {{left: number, top: number, width: number, height: number}}
* @private
*/
_getPresetCropSizePosition(presetRatio) {
const canvas = this.getCanvas();
const originalWidth = canvas.getWidth();
const originalHeight = canvas.getHeight();
const standardSize = (originalWidth >= originalHeight) ? originalWidth : originalHeight;
const getScale = (value, orignalValue) => (value > orignalValue) ? orignalValue / value : 1;
let width = standardSize * presetRatio;
let height = standardSize;
const scaleWidth = getScale(width, originalWidth);
[width, height] = snippet.map([width, height], sizeValue => sizeValue * scaleWidth);
const scaleHeight = getScale(height, originalHeight);
[width, height] = snippet.map([width, height], sizeValue => sizeValue * scaleHeight);
return {
top: (originalHeight - height) / 2,
left: (originalWidth - width) / 2,
width,
height
};
}
/**
* Keydown event handler

@@ -285,0 +345,0 @@ * @param {KeyboardEvent} e - Event object

@@ -556,2 +556,11 @@ /**

/**
* Get cropped rect
* @param {Object} mode cropzone rect mode
* @returns {Object} rect
*/
setCropzoneRect(mode) {
return this.getComponent(components.CROPPER).setCropzoneRect(mode);
}
/**
* Get cropped image data

@@ -558,0 +567,0 @@ * @param {Object} cropRect cropzone rect

@@ -670,2 +670,11 @@ /**

/**
* Set the cropping rect
* @param {Object} mode crop rect mode [1, 1.5, 1.3333333333333333, 1.25, 1.7777777777777777]
* @returns {Object} {{left: number, top: number, width: number, height: number}} rect
*/
setCropzoneRect(mode) {
return this._graphics.setCropzoneRect(mode);
}
/**
* Flip

@@ -672,0 +681,0 @@ * @returns {Promise}

@@ -0,1 +1,2 @@

import snippet from 'tui-code-snippet';
import Submenu from './submenuBase';

@@ -19,6 +20,10 @@ import templateHtml from './template/submenu/crop';

this.status = 'active';
this._els = {
apply: this.selector('#tie-crop-button .apply'),
cancel: this.selector('#tie-crop-button .cancel')
cancel: this.selector('#tie-crop-button .cancel'),
preset: this.selector('#tie-crop-preset-button')
};
this.defaultPresetButton = this._els.preset.querySelector('.preset-none');
}

@@ -31,2 +36,3 @@

* @param {Function} actions.cancel - cancel action
* @param {Function} actions.preset - draw rectzone at a predefined ratio
*/

@@ -44,2 +50,12 @@ addEvent(actions) {

});
this._els.preset.addEventListener('click', event => {
const button = event.target.closest('.tui-image-editor-button.preset');
if (button) {
const [presetType] = button.className.match(/preset-[^\s]+/);
this._setPresetButtonActive(button);
this.actions.preset(presetType);
}
});
}

@@ -59,2 +75,3 @@

this.actions.stopDrawingMode();
this._setPresetButtonActive();
}

@@ -73,4 +90,19 @@

}
/**
* Set preset button to active status
* @param {HTMLElement} button - event target element
* @private
*/
_setPresetButtonActive(button = this.defaultPresetButton) {
snippet.forEach([].slice.call(this._els.preset.querySelectorAll('.preset')), presetButton => {
presetButton.classList.remove('active');
});
if (button) {
button.classList.add('active');
}
}
}
export default Crop;

@@ -36,2 +36,3 @@ export default ({

#tie-crop-button .tui-image-editor-button.apply.active label,
#tie-crop-preset-button .tui-image-editor-button.preset.active label,
#tie-shape-button.rect .tui-image-editor-button.rect label,

@@ -38,0 +39,0 @@ #tie-shape-button.circle .tui-image-editor-button.circle label,

@@ -0,4 +1,89 @@

export default ({iconStyle: {normal, active}}) => (`
<ul class="tui-image-editor-submenu-item">
<li id="tie-crop-button" class="apply">
<li id="tie-crop-preset-button">
<div class="tui-image-editor-button preset preset-none active">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-shape-rectangle"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-shape-rectangle"
class="active"/>
</svg>
</div>
<label> Custom </label>
</div>
<div class="tui-image-editor-button preset preset-square">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-crop"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-crop"
class="active"/>
</svg>
</div>
<label> Square </label>
</div>
<div class="tui-image-editor-button preset preset-3-2">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-crop"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-crop"
class="active"/>
</svg>
</div>
<label> 3:2 </label>
</div>
<div class="tui-image-editor-button preset preset-4-3">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-crop"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-crop"
class="active"/>
</svg>
</div>
<label> 4:3 </label>
</div>
<div class="tui-image-editor-button preset preset-5-4">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-crop"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-crop"
class="active"/>
</svg>
</div>
<label> 5:4 </label>
</div>
<div class="tui-image-editor-button preset preset-7-5">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-crop"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-crop"
class="active"/>
</svg>
</div>
<label> 7:5 </label>
</div>
<div class="tui-image-editor-button preset preset-16-9">
<div>
<svg class="svg_ic-submenu">
<use xlink:href="${normal.path}#${normal.name}-ic-crop"
class="normal"/>
<use xlink:href="${active.path}#${active.name}-ic-crop"
class="active"/>
</svg>
</div>
<label> 16:9 </label>
</div>
</li>
<li class="tui-image-editor-partition tui-image-editor-newline">
</li>
<li class="tui-image-editor-partition only-left-right">
<div></div>
</li>
<li id="tie-crop-button" class="action">
<div class="tui-image-editor-button apply">

@@ -5,0 +90,0 @@ <svg class="svg_ic-menu">

@@ -132,3 +132,3 @@ /**

sendHostname('image-editor');
sendHostname('image-editor', 'UA-129999381-1');
},

@@ -135,0 +135,0 @@

@@ -91,5 +91,5 @@ /**

invoker.execute('testCommand', graphics, 1, 2, 3).then(() =>
invoker.execute('testCommand', graphics, 1, 2, 3).then(() => (
invoker.undo()
).then(() => done()
)).then(() => done()
)['catch'](message => {

@@ -157,6 +157,37 @@ fail(message);

it('After running the LOAD_IMAGE command, existing objects should not include cropzone.', done => {
const objCropzone = new fabric.Object({type: 'cropzone'});
invoker.execute(commands.ADD_OBJECT, graphics, objCropzone).then(() => {
invoker.execute(commands.LOAD_IMAGE, graphics, 'image', imageURL).then(() => {
const lastUndoIndex = invoker._undoStack.length - 1;
const savedObjects = invoker._undoStack[lastUndoIndex].undoData.objects;
expect(savedObjects.length).toBe(0);
done();
});
});
});
it('`evented` attribute of the saved object must be true after LOAD_IMAGE.', done => {
const objCircle = new fabric.Object({
type: 'circle',
evented: false
});
invoker.execute(commands.ADD_OBJECT, graphics, objCircle).then(() => {
invoker.execute(commands.LOAD_IMAGE, graphics, 'image', imageURL).then(() => {
const lastUndoIndex = invoker._undoStack.length - 1;
const [savedObject] = invoker._undoStack[lastUndoIndex].undoData.objects;
expect(savedObject.evented).toBe(true);
done();
});
});
});
it('"undo()" should clear image if not exists prev image', done => {
invoker.execute(commands.LOAD_IMAGE, graphics, 'image', imageURL).then(() =>
invoker.execute(commands.LOAD_IMAGE, graphics, 'image', imageURL).then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(graphics.getCanvasImage()).toBe(null);

@@ -171,5 +202,5 @@ expect(graphics.getImageName()).toBe('');

invoker.execute(commands.LOAD_IMAGE, graphics, 'image', imageURL).then(() =>
invoker.execute(commands.LOAD_IMAGE, graphics, 'image', imageURL).then(() => (
invoker.execute(commands.LOAD_IMAGE, graphics, 'newImage', newImageURL)
).then(() => {
)).then(() => {
expect(graphics.getImageName()).toBe('newImage');

@@ -217,5 +248,5 @@ expect(graphics.getCanvasImage().getSrc()).toContain(newImageURL);

invoker.execute(commands.FLIP_IMAGE, graphics, 'flipX').then(() =>
invoker.execute(commands.FLIP_IMAGE, graphics, 'flipX').then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(mockImage.flipX).toBe(originFlipX);

@@ -229,5 +260,5 @@ done();

invoker.execute(commands.FLIP_IMAGE, graphics, 'flipY').then(() =>
invoker.execute(commands.FLIP_IMAGE, graphics, 'flipY').then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(mockImage.flipY).toBe(originFlipY);

@@ -258,5 +289,5 @@ done();

invoker.execute(commands.ROTATE_IMAGE, graphics, 'setAngle', 100).then(() =>
invoker.execute(commands.ROTATE_IMAGE, graphics, 'setAngle', 100).then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(mockImage.angle).toBe(originalAngle);

@@ -296,5 +327,5 @@ done();

canvas.add.apply(canvasContext, objects);
invoker.execute(commands.CLEAR_OBJECTS, graphics).then(() =>
invoker.execute(commands.CLEAR_OBJECTS, graphics).then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(canvas.contains(objects[0])).toBe(true);

@@ -340,5 +371,5 @@ expect(canvas.contains(objects[1])).toBe(true);

invoker.execute(commands.REMOVE_OBJECT, graphics, snippet.stamp(object)).then(() =>
invoker.execute(commands.REMOVE_OBJECT, graphics, snippet.stamp(object)).then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(canvas.contains(object)).toBe(true);

@@ -351,5 +382,5 @@ done();

canvas.setActiveObject(group);
invoker.execute(commands.REMOVE_OBJECT, graphics, snippet.stamp(group)).then(() =>
invoker.execute(commands.REMOVE_OBJECT, graphics, snippet.stamp(group)).then(() => (
invoker.undo()
).then(() => {
)).then(() => {
expect(canvas.contains(object)).toBe(true);

@@ -356,0 +387,0 @@ expect(canvas.contains(object2)).toBe(true);

@@ -243,2 +243,70 @@ /**

describe('"presets - setCropzoneRect()"', () => {
beforeEach(() => {
cropper.start();
});
afterEach(() => {
cropper.end();
});
it('should return cropzone rect as a square', () => {
spyOn(cropper._cropzone, 'isValid').and.returnValue(true);
cropper.setCropzoneRect(1 / 1);
expect(cropper.getCropzoneRect().width).toBe(cropper.getCropzoneRect().height);
});
it('should return cropzone rect as a 3:2 aspect box', () => {
spyOn(cropper._cropzone, 'isValid').and.returnValue(true);
cropper.setCropzoneRect(3 / 2);
expect((cropper.getCropzoneRect().width / cropper.getCropzoneRect().height).toFixed(1))
.toBe((3 / 2).toFixed(1));
});
it('should return cropzone rect as a 4:3 aspect box', () => {
spyOn(cropper._cropzone, 'isValid').and.returnValue(true);
cropper.setCropzoneRect(4 / 3);
expect((cropper.getCropzoneRect().width / cropper.getCropzoneRect().height).toFixed(1))
.toBe((4 / 3).toFixed(1));
});
it('should return cropzone rect as a 5:4 aspect box', () => {
spyOn(cropper._cropzone, 'isValid').and.returnValue(true);
cropper.setCropzoneRect(5 / 4);
expect((cropper.getCropzoneRect().width / cropper.getCropzoneRect().height).toFixed(1))
.toBe((5 / 4).toFixed(1));
});
it('should return cropzone rect as a 7:5 aspect box', () => {
spyOn(cropper._cropzone, 'isValid').and.returnValue(true);
cropper.setCropzoneRect(7 / 5);
expect((cropper.getCropzoneRect().width / cropper.getCropzoneRect().height).toFixed(1))
.toBe((7 / 5).toFixed(1));
});
it('should return cropzone rect as a 16:9 aspect box', () => {
spyOn(cropper._cropzone, 'isValid').and.returnValue(true);
cropper.setCropzoneRect(16 / 9);
expect((cropper.getCropzoneRect().width / cropper.getCropzoneRect().height).toFixed(1))
.toBe((16 / 9).toFixed(1));
});
it('should remove cropzone of cropper when falsy is passed', () => {
cropper.setCropzoneRect();
expect(cropper.getCropzoneRect()).toBeFalsy();
cropper.setCropzoneRect(0);
expect(cropper.getCropzoneRect()).toBeFalsy();
cropper.setCropzoneRect(null);
expect(cropper.getCropzoneRect()).toBeFalsy();
});
});
describe('"end()"', () => {

@@ -245,0 +313,0 @@ it('should set cropzone of cropper to null', () => {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc