tui-image-editor
Advanced tools
Comparing version 3.1.0 to 3.2.0
@@ -18,3 +18,9 @@ module.exports = { | ||
"sourceType": "module" | ||
}, | ||
'rules': { | ||
'prefer-destructuring': ['error', { | ||
VariableDeclarator: {array: true, object: true}, | ||
AssignmentExpression: {array: false, object: false} | ||
}] | ||
} | ||
}; |
@@ -32,3 +32,4 @@ { | ||
"fabric": "~1.6.4", | ||
"tui-code-snippet": "^1.3.0" | ||
"tui-code-snippet": "^1.3.0", | ||
"tui-color-picker": "^2.2.0" | ||
}, | ||
@@ -40,4 +41,5 @@ "devDependencies": { | ||
"resolutions": { | ||
"tui-code-snippet": "^1.3.0" | ||
"tui-code-snippet": "^1.3.0", | ||
"tui-color-picker": "^2.2.0" | ||
} | ||
} |
{ | ||
"example01-basic": { | ||
"title": "1. Basic" | ||
"example01-includeUi": { | ||
"title": "1. Include ui" | ||
}, | ||
"example02-mobile": { | ||
"title": "2. Mobile" | ||
"example02-useApiDirect": { | ||
"title": "2. Use api direct (basic)" | ||
}, | ||
"example03-mobile": { | ||
"title": "3. Mobile" | ||
} | ||
} |
@@ -674,3 +674,3 @@ /** | ||
}).then(objectProps => { | ||
console.log(objectProps); | ||
// console.log(objectProps); | ||
}); | ||
@@ -677,0 +677,0 @@ }); |
@@ -17,3 +17,3 @@ { | ||
"logo": { | ||
"url": "https://cloud.githubusercontent.com/assets/12269563/20029815/01133928-a39a-11e6-80f3-12500a91c755.png", | ||
"url": "https://user-images.githubusercontent.com/35218826/40895380-0b9f4cd6-67ea-11e8-982f-18121daa3a04.png", | ||
"width": "150px", | ||
@@ -20,0 +20,0 @@ "height": "13px", |
@@ -146,2 +146,6 @@ /* eslint-disable consts-on-top, no-process-env, require-jsdoc */ | ||
loader: 'babel' | ||
}, | ||
{ | ||
test: /\.styl$/, | ||
loader: 'css-loader!stylus-loader?paths=src/css/' | ||
} | ||
@@ -148,0 +152,0 @@ ] |
{ | ||
"name": "tui-image-editor", | ||
"author": "NHNEnt FE Development Lab <dl_javascript@nhnent.com>", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"license": "MIT", | ||
"repository": "https://github.com/nhnent/tui.image-editor", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/nhnent/tui.image-editor.git" | ||
}, | ||
"main": "dist/tui-image-editor.js", | ||
@@ -23,2 +26,3 @@ "description": "TOAST UI Component: ImageEditor", | ||
"babel-preset-es2015": "^6.18.0", | ||
"css-loader": "^0.28.11", | ||
"es5-shim": "^4.5.9", | ||
@@ -28,2 +32,3 @@ "eslint": "^4.5.0", | ||
"eslint-loader": "^1.6.1", | ||
"extract-text-webpack-plugin": "^1.0.1", | ||
"file-saver": "^1.3.3", | ||
@@ -39,2 +44,3 @@ "istanbul-instrumenter-loader": "^1.0.0", | ||
"karma-es5-shim": "0.0.4", | ||
"karma-firefox-launcher": "^1.1.0", | ||
"karma-jasmine": "^1.0.2", | ||
@@ -48,2 +54,5 @@ "karma-jquery": "^0.2.2", | ||
"simulant": "^0.2.2", | ||
"stylus": "^0.54.5", | ||
"stylus-loader": "^3.0.2", | ||
"svgstore": "^2.0.3", | ||
"tui-jsdoc-template": "^1.0.4", | ||
@@ -56,3 +65,4 @@ "webpack": "^1.13.3", | ||
"test:ne": "KARMA_SERVER=ne karma start", | ||
"bundle": "webpack && webpack -p", | ||
"bundle": "webpack && webpack -p && npm run bundle:svg", | ||
"bundle:svg": "mkdir -p dist/svg && node makesvg.js", | ||
"serve": "webpack-dev-server --inline --hot -d", | ||
@@ -65,4 +75,5 @@ "cpy-dist2doc": "mkdir -p doc/dist && cp -f -r dist doc", | ||
"core-js": "2.4.1", | ||
"tui-code-snippet": "^1.3.0" | ||
"tui-code-snippet": "^1.3.0", | ||
"tui-color-picker": "^2.2.0" | ||
} | ||
} |
317
README.md
@@ -1,7 +0,137 @@ | ||
# Image Editor | ||
Canvas image editor | ||
# ![Toast UI ImageEditor](https://user-images.githubusercontent.com/35218826/40895380-0b9f4cd6-67ea-11e8-982f-18121daa3a04.png) | ||
> Full-featured photo image editor with use canvas, easy to apply, and great filter function | ||
Full-featured photo image editor using canvas. It is really easy, and it comes with great filters. | ||
![image](https://cloud.githubusercontent.com/assets/26706716/26335518/84f041e2-3fa7-11e7-8892-155a95c6d5c3.png) | ||
## Feature | ||
[![github version](https://img.shields.io/github/release/nhnent/tui.image-editor.svg)](https://github.com/nhnent/tui.image-editor/releases/latest) [![npm version](https://img.shields.io/npm/v/tui-image-editor.svg)](https://www.npmjs.com/package/tui-image-editor) [![bower version](https://img.shields.io/bower/v/tui.image-editor.svg)](https://github.com/nhnent/tui.image-editor/releases/latest) [![license](https://img.shields.io/github/license/nhnent/tui.image-editor.svg)](https://github.com/nhnent/tui.image-editor/blob/master/LICENSE) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-ff69b4.svg)](https://github.com/nhnent/tui.image-editor/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)[![code with hearth by NHN Entertainment](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-NHN%20Entertainment-ff1414.svg)](https://github.com/nhnent) | ||
![6 -20-2018 17-45-54](https://user-images.githubusercontent.com/35218826/41647896-7b218ae0-74b2-11e8-90db-d7805cc23e8c.gif) | ||
## 🚩 Table of Contents | ||
* [Browser Support](#-browser-support) | ||
* [Has full features that stick to the basic.](#-has-full-features-that-stick-to-the-basic) | ||
* [Photo manipulation](#photo-manipulation) | ||
* [Integration function](#integration-function) | ||
* [Powerful filter function](#powerful-filter-function) | ||
* [Select only the desired function](#select-only-the-desired-function) | ||
* [Easy to apply the size and design you want](#-easy-to-apply-the-size-and-design-you-want) | ||
* [Can be used everywhere](#can-be-used-everywhere) | ||
* [Nice default & Fully customizable Themes](#nice-default--fully-customizable-themes) | ||
* [Features](#-features) | ||
* [Install](#-install) | ||
* [Via Package Manager](#via-package-manager) | ||
* [Via Contents Delivery Network (CDN)](#via-contents-delivery-network-cdn) | ||
* [Download Source Files](#download-source-files) | ||
* [Usage](#-usage) | ||
* [HTML](#html) | ||
* [JavaScript](#javascript) | ||
* [Development](#develop) | ||
* [Setup](#setup) | ||
* [Run webpack-dev-server](#run-webpack-dev-server) | ||
* [Documents](#-documents) | ||
* [Contributing](#-contributing) | ||
* [Dependency](#-dependency) | ||
* [TOAST UI Family](#-toast-ui-family) | ||
* [License](#-license) | ||
## 🌏 Browser Support | ||
| <img src="https://user-images.githubusercontent.com/1215767/34348387-a2e64588-ea4d-11e7-8267-a43365103afe.png" alt="Chrome" width="16px" height="16px" /> Chrome | <img src="https://user-images.githubusercontent.com/1215767/34348590-250b3ca2-ea4f-11e7-9efb-da953359321f.png" alt="IE" width="16px" height="16px" /> Internet Explorer | <img src="https://user-images.githubusercontent.com/1215767/34348380-93e77ae8-ea4d-11e7-8696-9a989ddbbbf5.png" alt="Edge" width="16px" height="16px" /> Edge | <img src="https://user-images.githubusercontent.com/1215767/34348394-a981f892-ea4d-11e7-9156-d128d58386b9.png" alt="Safari" width="16px" height="16px" /> Safari | <img src="https://user-images.githubusercontent.com/1215767/34348383-9e7ed492-ea4d-11e7-910c-03b39d52f496.png" alt="Firefox" width="16px" height="16px" /> Firefox | | ||
| :---------: | :---------: | :---------: | :---------: | :---------: | | ||
| Yes | 9+ | Yes | Yes | Yes | | ||
## 💪 Has full features that stick to the basic. | ||
### Photo manipulation | ||
- Crop, Flip, Rotation, Drawing, Shape, Icon, Text, Mask Filter, Image Filter | ||
### Integration function | ||
- Download, Image Load, Undo, Redo, Reset, Delete Object(Shape, Line, Mask Image...) | ||
<table> | ||
<tr> | ||
<th width="20%">Crop</th> | ||
<th width="20%">Flip</th> | ||
<th width="20%">Rotation</th> | ||
<th width="20%">Drawing</th> | ||
<th width="20%">Shape</th> | ||
</tr> | ||
<tr> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40904241-0c28ec68-6815-11e8-8296-89a1716b22d8.png" alt="2018-06-04 4 33 16" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40904521-f7c6e184-6815-11e8-8ba3-c94664da69a2.png" alt="2018-06-04 4 40 06" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40904664-656aa748-6816-11e8-9943-6607c209deac.png" alt="2018-06-04 4 43 02" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40904850-0f26ebde-6817-11e8-97d0-d3a7e4bc02da.png" alt="2018-06-04 4 47 40" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40905037-a026296a-6817-11e8-9d28-9e1ca7bc58c4.png" alt="2018-06-04 4 51 45" style="max-width:100%;"></td> | ||
</tr> | ||
<tr> | ||
<th>Icon</th> | ||
<th>Text</th> | ||
<th>Mask</th> | ||
<th>Filter</th> | ||
<th></th> | ||
</tr> | ||
<tr> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40931205-2d255db6-6865-11e8-98af-ad34c5a01da1.png" alt="2018-06-05 2 06 29" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40931484-46253948-6866-11e8-8a04-fa042920e457.png" alt="2018-06-05 2 14 36" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40931743-21eeb346-6867-11e8-8e31-a59f7a43482b.png" alt="2018-06-05 2 20 46" style="max-width:100%;"></td> | ||
<td><img src="https://user-images.githubusercontent.com/35218826/40932016-093ed1f4-6868-11e8-8224-a048c3ee8a09.png" alt="2018-06-05 2 27 10" style="max-width:100%;"></td> | ||
<td></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
### Powerful filter function | ||
- Grayscale, Invert, Sepia, Blur Sharpen, Emboss, RemoveWhite, GradientTransparency, Brightness, Noise, Pixelate, ColorFilter, Tint, Multiply, Blend | ||
| Grayscale | Noise | Gradient | Emboss | Pixelate | | ||
| --- | --- | --- | --- | --- | | ||
| ![grayscale](https://user-images.githubusercontent.com/35218826/41753470-930fb7b0-7608-11e8-9966-1c890e73d131.png) | ![noise](https://user-images.githubusercontent.com/35218826/41753458-9013bc82-7608-11e8-91d9-74dcc3ffce31.png) | ![gradient-transparency](https://user-images.githubusercontent.com/35218826/41753459-903fe640-7608-11e8-87f4-cc0bff43b4ee.png) | ![emboss](https://user-images.githubusercontent.com/35218826/41753460-906c018a-7608-11e8-8861-c135c0117cea.png) | ![pixelate](https://user-images.githubusercontent.com/35218826/41753461-90a614a6-7608-11e8-97a7-0d3b7bb4aec4.png) | | ||
| Sepia | Sepia2 | Blend-righten | Blend-diff | Invert | | ||
| --- | --- | --- | --- | --- | | ||
| ![sepia](https://user-images.githubusercontent.com/35218826/41753464-91acc41c-7608-11e8-8652-572f935ea704.png) | ![sepia2](https://user-images.githubusercontent.com/35218826/41753640-91e57248-7609-11e8-8960-145e0de57e39.png) | ![blend-righten](https://user-images.githubusercontent.com/35218826/41753462-9114bc3a-7608-11e8-9ab4-16ce20a34321.png) | ![blend-diff](https://user-images.githubusercontent.com/35218826/41753465-91e4baf2-7608-11e8-9b8f-79e1b956d387.png) | ![invert](https://user-images.githubusercontent.com/35218826/41753466-9260b224-7608-11e8-848a-73231a02ae3a.png) | | ||
| Multifly | Tint | Brightness | Remove-white | Sharpen | | ||
| --- | --- | --- | --- | --- | | ||
| ![multifly](https://user-images.githubusercontent.com/35218826/41753467-92baae28-7608-11e8-80d2-187a310213f5.png) | ![tint](https://user-images.githubusercontent.com/35218826/41753468-92e6391c-7608-11e8-8977-651366ebe693.png) | ![brightness](https://user-images.githubusercontent.com/35218826/41753457-8fb3d3c6-7608-11e8-9e1d-10c6e4aeba68.png) | ![remove-white](https://user-images.githubusercontent.com/35218826/41753463-917feeb0-7608-11e8-862d-eb3af84e120a.png) | ![sharpen](https://user-images.githubusercontent.com/35218826/41753639-91b8470a-7609-11e8-8d13-48ac3232365b.png) | | ||
### Select only the desired function | ||
```javascripot | ||
var imageEditor = new tui.ImageEditor('#tui-image-editor-container', { | ||
includeUI: { | ||
menu: ['shape', 'crop'] | ||
... | ||
}, | ||
... | ||
``` | ||
## 🙆 Easy to apply the size and design you want | ||
### Can be used everywhere. | ||
- Widely supported in browsers including IE9, which is the minimum requirement to support canvas. | ||
- Option to support various display sizes. | ||
(allows you to use the editor features on your web pages at least over **550 * 450 sizes**) | ||
![2018-06-04 5 35 25](https://user-images.githubusercontent.com/35218826/40907369-9221f482-681e-11e8-801c-78d6f2e246a8.png) | ||
### Nice default & Fully customizable Themes | ||
- Has a white and black theme, and you can modify the theme file to customize it. | ||
- Has an API so that you can create your own instead of the built-in. | ||
| black - top | black - bottom | white - left | white - right | | ||
| --- | --- | --- | --- | | ||
| ![2018-06-05 1 41 13](https://user-images.githubusercontent.com/35218826/40930753-8b72502e-6863-11e8-9cff-1719aee9aef0.png) | ![2018-06-05 1 40 24](https://user-images.githubusercontent.com/35218826/40930755-8bcee136-6863-11e8-8e28-0a6722d38c59.png) | ![2018-06-05 1 41 48](https://user-images.githubusercontent.com/35218826/40930756-8bfe0b50-6863-11e8-8682-bab11a0a2289.png) | ![2018-06-05 1 42 27](https://user-images.githubusercontent.com/35218826/40930754-8ba1dba0-6863-11e8-9439-cc059241b675.png) | | ||
## 🎨 Features | ||
* Load image to canvas | ||
@@ -12,3 +142,3 @@ * Undo/Redo (With shortcut) | ||
* Rotation | ||
* Free Drawing | ||
* Free drawing | ||
* Line drawing | ||
@@ -21,42 +151,89 @@ * Shape | ||
## Documentation | ||
* **API** : [http://nhnent.github.io/tui.image-editor/latest/](http://nhnent.github.io/tui.image-editor/latest/) | ||
* **Tutorial** : [https://github.com/nhnent/tui.image-editor/wiki/Tutorial](https://github.com/nhnent/tui.image-editor/wiki/Tutorial) | ||
* **Example** : [http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-basic.html](http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-basic.html) | ||
## 💾 Install | ||
## Dependency | ||
* [fabric.js](https://github.com/kangax/fabric.js/releases/tag/v1.6.7) >=1.6.7 | ||
* [tui.code-snippet](https://github.com/nhnent/tui.code-snippet/releases/tag/v1.2.5) >=1.3.0 | ||
The TOAST UI products can be installed by using the package manager or downloading the source directly. | ||
However, we highly recommend using the package manager. | ||
## Test Environment | ||
### PC | ||
* IE9~11 | ||
* Edge | ||
* Chrome | ||
* Firefox | ||
* Safari | ||
### Mobile | ||
* iOS 9.3.x | ||
* Android 4.4.x | ||
### Via Package Manager | ||
## Usage | ||
### Use `npm` | ||
You can find TOAST UI producs via [npm](https://www.npmjs.com/) and [bower](https://bower.io/) package managers. | ||
Install by using the commands provided by each package manager. | ||
When using npm, be sure [Node.js](https://nodejs.org) is installed in the environment. | ||
Install the latest version using `npm` command: | ||
#### npm | ||
```sh | ||
$ npm install --save tui-image-editor # Latest version | ||
$ npm install --save tui-image-editor@<version> # Specific version | ||
``` | ||
$ npm install tui-image-editor --save | ||
#### bower | ||
```sh | ||
$ bower install tui-image-editor # Latest version | ||
$ bower install tui-image-editor#<tag> # Specific version | ||
``` | ||
or want to install the each version: | ||
### Via Contents Delivery Network (CDN) | ||
TOAST UI products are available over the CDN powered by [TOAST Cloud](https://www.toast.com). | ||
You can use the CDN as below. | ||
```html | ||
<link rel="stylesheet" href="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.css"> | ||
<script src="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.js"></script> | ||
``` | ||
$ npm install tui-image-editor@<version> --save | ||
If you want to use a specific version, use the tag name instead of `latest` in the URL. | ||
The CDN directory has the following structure. | ||
``` | ||
tui-image-editor/ | ||
├─ latest/ | ||
│ ├─ tui-image-editor.js | ||
│ ├─ tui-image-editor.min.js | ||
│ └─ tui-image-editor.css | ||
├─ v3.1.0/ | ||
│ ├─ ... | ||
``` | ||
To access as module format in your code: | ||
### Download Source Files | ||
* [Download bundle files from `dist` folder](https://github.com/nhnent/tui.image-editor/tree/production/dist) | ||
* [Download all sources for each version](https://github.com/nhnent/tui.image-editor/releases) | ||
## 🔨 Usage | ||
### HTML | ||
Add the container element where TOAST UI ImageEditor will be created. | ||
``` html | ||
<body> | ||
... | ||
<div id="tui-image-editor"></div> | ||
... | ||
</body> | ||
``` | ||
### javascript | ||
Add dependencies & initialize ImageEditor class with given element to make an image editor. | ||
```javascript | ||
var ImageEditor = require('tui-image-editor'); | ||
var instance = new ImageEditor('.tui-image-editor', { | ||
var blackTheme = require('./js/theme/black-theme.js'); | ||
var instance = new ImageEditor(document.querySelector('#tui-image-editor'), { | ||
includeUI: { | ||
loadImage: { | ||
path: 'img/sampleImage.jpg', | ||
name: 'SampleImage' | ||
}, | ||
theme: blackTheme, // or whiteTheme | ||
initMenu: 'filter', | ||
menuBarPosition: 'bottom' | ||
}, | ||
cssMaxWidth: 700, | ||
@@ -71,19 +248,7 @@ cssMaxHeight: 500, | ||
### Use `bower` | ||
Install the latest version using `bower` command: | ||
Or ~ UI | ||
``` | ||
$ bower install tui-image-editor | ||
``` | ||
or want to install the each version: | ||
``` | ||
$ bower install tui-image-editor#<tag> | ||
``` | ||
To access as namespace format in your code: | ||
```javascript | ||
var imageEditor = new tui.ImageEditor('.tui-image-editor', { | ||
var ImageEditor = require('tui-image-editor'); | ||
var instance = new ImageEditor(document.querySelector('#tui-image-editor'), { | ||
cssMaxWidth: 700, | ||
@@ -98,29 +263,55 @@ cssMaxHeight: 500, | ||
### Via Contents Delivery Network (CDN) | ||
TOAST UI products are available over the CDN powered by [TOAST Cloud](https://www.toast.com). | ||
See [details](https://nhnent.github.io/tui.image-editor/latest) for additional informations. | ||
You can use the CDN as below. | ||
## 🔧 Development | ||
```html | ||
<script src="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.js"></script> | ||
``` | ||
The TOAST UI products are open-source. | ||
After fixing issues, create a pull request(PR). | ||
Run npm scripts and develop with the following process. | ||
If you want to use a specific version, use the tag name instead of `latest` in the url's path. | ||
### Setup | ||
The CDN directory has the following structure. | ||
Fork `master` branch into your personal repository. | ||
Clone to local computer. | ||
Install node modules. | ||
Before starting development, check for any errors. | ||
```sh | ||
$ git clone https://github.com/{username}/tui.image-editor.git | ||
$ cd tui.image-editor | ||
$ npm install | ||
$ npm run test | ||
``` | ||
tui-image-editor/ | ||
├─ latest/ | ||
│ ├─ tui-image-editor.js | ||
│ └─ tui-image-editor.min.js | ||
├─ v3.1.0/ | ||
│ ├─ ... | ||
### Run webpack-dev-server | ||
```sh | ||
$ npm run serve | ||
``` | ||
### Download | ||
* [Download bundle files from `dist` folder](https://github.com/nhnent/tui.image-editor/tree/production/dist) | ||
* [Download all sources for each version](https://github.com/nhnent/tui.image-editor/releases) | ||
## 📙 Documents | ||
* **Tutorial** : [https://github.com/nhnent/tui.image-editor/tree/master/docs](https://github.com/nhnent/tui.image-editor/tree/master/docs) | ||
* **Example** : [http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-includeUi.html](http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-includeUi.html) | ||
* **API** : [http://nhnent.github.io/tui.image-editor/latest/](http://nhnent.github.io/tui.image-editor/latest/) | ||
## License | ||
## 💬 Contributing | ||
* [Code of Conduct](CODE_OF_CONDUCT.md) | ||
* [Contributing guideline](CONTRIBUTING.md) | ||
* [Issue guideline](ISSUE_TEMPLATE.md) | ||
* [Commit convention](https://github.com/nhnent/tui.image-editor/blob/production/docs/COMMIT_MESSAGE_CONVENTION.md) | ||
## 🔩 Dependency | ||
* [fabric.js](https://github.com/kangax/fabric.js/releases/tag/v1.6.7) >=1.6.7 | ||
* [tui.code-snippet](https://github.com/nhnent/tui.code-snippet/releases/tag/v1.2.5) >=1.3.0 | ||
* [tui.color-picker](https://github.com/nhnent/tui.color-picker/releases/tag/v2.2.0) >=2.2.0 | ||
## 🍞 TOAST UI Family | ||
* [TOAST UI Editor](https://github.com/nhnent/tui.editor) | ||
* [TOAST UI Grid](https://github.com/nhnent/tui.grid) | ||
* [TOAST UI Chart](https://github.com/nhnent/tui.chart) | ||
* [TOAST UI Calendar](https://github.com/nhnent/tui.calendar) | ||
* [TOAST UI Components](https://github.com/nhnent) | ||
## 📜 License | ||
[MIT LICENSE](https://github.com/nhnent/tui.image-editor/blob/master/LICENSE) |
@@ -0,2 +1,4 @@ | ||
import './js/polyfill'; | ||
import ImageEditor from './js/imageEditor'; | ||
import './css/index.styl'; | ||
@@ -3,0 +5,0 @@ // commands |
@@ -74,5 +74,7 @@ /** | ||
const canvas = this.getCanvas(); | ||
canvas.forEachObject(obj => { // {@link http://fabricjs.com/docs/fabric.Object.html#evented} | ||
obj.evented = false; | ||
}); | ||
this._cropzone = new Cropzone({ | ||
@@ -91,3 +93,4 @@ left: -10, | ||
lockRotation: true | ||
}); | ||
}, this.graphics.cropSelectionStyle); | ||
canvas.deactivateAll(); | ||
@@ -94,0 +97,0 @@ canvas.add(this._cropzone); |
@@ -11,2 +11,3 @@ /** | ||
const events = consts.eventNames; | ||
const {rejectMessages} = consts; | ||
@@ -42,2 +43,8 @@ | ||
this._pathMap = pathMap; | ||
/** | ||
* Option to add icon to drag. | ||
* @type {boolean} | ||
*/ | ||
this.useDragAddIcon = graphics.useDragAddIcon; | ||
} | ||
@@ -69,5 +76,29 @@ | ||
fill: this._oColor | ||
}, selectionStyle, options)); | ||
}, selectionStyle, options, this.graphics.controlStyle)); | ||
canvas.add(icon).setActiveObject(icon); | ||
if (this.useDragAddIcon) { | ||
canvas.add(icon).setActiveObject(icon); | ||
canvas.on({ | ||
'mouse:move': fEvent => { | ||
canvas.selection = false; | ||
this.fire(events.ICON_CREATE_RESIZE, { | ||
moveOriginPointer: canvas.getPointer(fEvent.e) | ||
}); | ||
}, | ||
'mouse:up': fEvent => { | ||
this.fire(events.ICON_CREATE_END, { | ||
moveOriginPointer: canvas.getPointer(fEvent.e) | ||
}); | ||
canvas.defaultCursor = 'default'; | ||
canvas.off('mouse:up'); | ||
canvas.off('mouse:move'); | ||
canvas.selection = true; | ||
} | ||
}); | ||
} else { | ||
canvas.add(icon).setActiveObject(icon); | ||
} | ||
resolve(this.graphics.createObjectProperties(icon)); | ||
@@ -74,0 +105,0 @@ }); |
@@ -14,2 +14,3 @@ /** | ||
const KEY_CODES = consts.keyCodes; | ||
const DEFAULT_TYPE = 'rect'; | ||
@@ -130,2 +131,3 @@ const DEFAULT_OPTIONS = { | ||
canvas.defaultCursor = 'default'; | ||
canvas.selection = true; | ||
@@ -313,2 +315,7 @@ canvas.uniScaleTransform = false; | ||
_onFabricMouseDown(fEvent) { | ||
if (!fEvent.target) { | ||
this._isSelected = false; | ||
this._shapeObj = false; | ||
} | ||
if (!this._isSelected && !this._shapeObj) { | ||
@@ -369,2 +376,4 @@ const canvas = this.getCanvas(); | ||
this.fire(eventNames.ADD_OBJECT_AFTER, this.graphics.createObjectProperties(shape)); | ||
canvas.off({ | ||
@@ -371,0 +380,0 @@ 'mouse:move': this._handlers.mousemove, |
@@ -121,2 +121,8 @@ /** | ||
this.isPrevEditing = false; | ||
/** | ||
* use itext | ||
* @type {boolean} | ||
*/ | ||
this.useItext = graphics.useItext; | ||
} | ||
@@ -136,6 +142,20 @@ | ||
'before:selection:cleared': this._listeners.selectClear, | ||
'object:scaling': this._listeners.scaling | ||
'object:scaling': this._listeners.scaling, | ||
'text:editing': this._listeners.modify | ||
}); | ||
this._createTextarea(); | ||
if (this.useItext) { | ||
canvas.forEachObject(obj => { | ||
if (obj.type === 'i-text') { | ||
obj.set({ | ||
left: obj.left - (obj.width / 2), | ||
top: obj.top - (obj.height / 2), | ||
originX: 'left', | ||
originY: 'top' | ||
}); | ||
} | ||
}); | ||
} else { | ||
this._createTextarea(); | ||
} | ||
@@ -153,3 +173,23 @@ this.setCanvasRatio(); | ||
canvas.defaultCursor = 'default'; | ||
canvas.deactivateAllWithDispatch(); // action for undo stack | ||
if (this.useItext) { | ||
canvas.forEachObject(obj => { | ||
if (obj.type === 'i-text') { | ||
if (obj.text === '') { | ||
obj.remove(); | ||
} else { | ||
obj.set({ | ||
left: obj.left + (obj.width / 2), | ||
top: obj.top + (obj.height / 2), | ||
originX: 'center', | ||
originY: 'center' | ||
}); | ||
} | ||
} | ||
}); | ||
} else { | ||
canvas.deactivateAllWithDispatch(); | ||
this._removeTextarea(); | ||
} | ||
canvas.off({ | ||
@@ -159,6 +199,5 @@ 'mouse:down': this._listeners.mousedown, | ||
'before:selection:cleared': this._listeners.selectClear, | ||
'object:scaling': this._listeners.scaling | ||
'object:scaling': this._listeners.scaling, | ||
'text:editing': this._listeners.modify | ||
}); | ||
this._removeTextarea(); | ||
} | ||
@@ -184,2 +223,4 @@ | ||
const canvas = this.getCanvas(); | ||
let newText = null; | ||
let selectionStyle = consts.fObjectOptions.SELECTION_STYLE; | ||
let styles = this._defaultStyles; | ||
@@ -190,7 +231,16 @@ | ||
if (options.styles) { | ||
styles = snippet.extend(options.styles, styles); | ||
styles = snippet.extend(styles, options.styles); | ||
} | ||
const newText = new fabric.Text(text, styles); | ||
newText.set(consts.fObjectOptions.SELECTION_STYLE); | ||
if (this.useItext) { | ||
newText = new fabric.IText(text, styles); | ||
selectionStyle = snippet.extend({}, selectionStyle, { | ||
originX: 'left', | ||
originY: 'top' | ||
}); | ||
} else { | ||
newText = new fabric.Text(text, styles); | ||
} | ||
newText.set(selectionStyle); | ||
newText.on({ | ||
@@ -502,2 +552,3 @@ mouseup: this._onFabricMouseUp.bind(this) | ||
const obj = fEvent.target; | ||
if (obj && !obj.isType('text')) { | ||
@@ -549,3 +600,5 @@ return; | ||
if (this._isDoubleClick(newClickTime)) { | ||
this._changeToEditingMode(fEvent.target); | ||
if (!this.useItext) { | ||
this._changeToEditingMode(fEvent.target); | ||
} | ||
this.fire(events.TEXT_EDITING); // fire editing text event | ||
@@ -552,0 +605,0 @@ } |
@@ -59,10 +59,18 @@ /** | ||
OBJECT_SCALED: 'objectScaled', | ||
OBJECT_CREATED: 'objectCreated', | ||
TEXT_EDITING: 'textEditing', | ||
TEXT_CHANGED: 'textChanged', | ||
ICON_CREATE_RESIZE: 'iconCreateResize', | ||
ICON_CREATE_END: 'iconCreateEnd', | ||
ADD_TEXT: 'addText', | ||
ADD_OBJECT: 'addObject', | ||
ADD_OBJECT_AFTER: 'addObjectAfter', | ||
MOUSE_DOWN: 'mousedown', | ||
MOUSE_UP: 'mouseup', | ||
MOUSE_MOVE: 'mousemove', | ||
// UNDO/REDO Events | ||
REDO_STACK_CHANGED: 'redoStackChanged', | ||
UNDO_STACK_CHANGED: 'undoStackChanged' | ||
UNDO_STACK_CHANGED: 'undoStackChanged', | ||
SELECTION_CLEARED: 'selectionCleared', | ||
SELECTION_CREATED: 'selectionCreated' | ||
}, | ||
@@ -127,3 +135,89 @@ | ||
addedObject: 'The object is already added.' | ||
}, | ||
/** | ||
* Default icon menu svg path | ||
* @type {Object.<string, string>} | ||
*/ | ||
defaultIconPath: { | ||
'icon-arrow': 'M40 12V0l24 24-24 24V36H0V12h40z', | ||
'icon-arrow-2': 'M49,32 H3 V22 h46 l-18,-18 h12 l23,23 L43,50 h-12 l18,-18 z ', | ||
'icon-arrow-3': 'M43.349998,27 L17.354,53 H1.949999 l25.996,-26 L1.949999,1 h15.404 L43.349998,27 z ', | ||
'icon-star': 'M35,54.557999 l-19.912001,10.468 l3.804,-22.172001 l-16.108,-15.7 l22.26,-3.236 L35,3.746 l9.956,20.172001 l22.26,3.236 l-16.108,15.7 l3.804,22.172001 z ', | ||
'icon-star-2': 'M17,31.212 l-7.194,4.08 l-4.728,-6.83 l-8.234,0.524 l-1.328,-8.226 l-7.644,-3.14 l2.338,-7.992 l-5.54,-6.18 l5.54,-6.176 l-2.338,-7.994 l7.644,-3.138 l1.328,-8.226 l8.234,0.522 l4.728,-6.83 L17,-24.312 l7.194,-4.08 l4.728,6.83 l8.234,-0.522 l1.328,8.226 l7.644,3.14 l-2.338,7.992 l5.54,6.178 l-5.54,6.178 l2.338,7.992 l-7.644,3.14 l-1.328,8.226 l-8.234,-0.524 l-4.728,6.83 z ', | ||
'icon-polygon': 'M3,31 L19,3 h32 l16,28 l-16,28 H19 z ', | ||
'icon-location': 'M24 62C8 45.503 0 32.837 0 24 0 10.745 10.745 0 24 0s24 10.745 24 24c0 8.837-8 21.503-24 38zm0-28c5.523 0 10-4.477 10-10s-4.477-10-10-10-10 4.477-10 10 4.477 10 10 10z', | ||
'icon-heart': 'M49.994999,91.349998 l-6.96,-6.333 C18.324001,62.606995 2.01,47.829002 2.01,29.690998 C2.01,14.912998 13.619999,3.299999 28.401001,3.299999 c8.349,0 16.362,5.859 21.594,12 c5.229,-6.141 13.242001,-12 21.591,-12 c14.778,0 26.390999,11.61 26.390999,26.390999 c0,18.138 -16.314001,32.916 -41.025002,55.374001 l-6.96,6.285 z ', | ||
'icon-bubble': 'M44 48L34 58V48H12C5.373 48 0 42.627 0 36V12C0 5.373 5.373 0 12 0h40c6.627 0 12 5.373 12 12v24c0 6.627-5.373 12-12 12h-8z' | ||
}, | ||
defaultRotateRangeValus: { | ||
realTimeEvent: true, | ||
min: -360, | ||
max: 360, | ||
value: 0 | ||
}, | ||
defaultDrawRangeValus: { | ||
min: 5, | ||
max: 30, | ||
value: 12 | ||
}, | ||
defaultShapeStrokeValus: { | ||
realTimeEvent: false, | ||
min: 2, | ||
max: 300, | ||
value: 3 | ||
}, | ||
defaultTextRangeValus: { | ||
realTimeEvent: true, | ||
min: 10, | ||
max: 100, | ||
value: 50 | ||
}, | ||
defaultFilterRangeValus: { | ||
tintOpacityRange: { | ||
min: 0, | ||
max: 1, | ||
value: 0.7 | ||
}, | ||
removewhiteThresholdRange: { | ||
min: 0, | ||
max: 255, | ||
value: 60 | ||
}, | ||
removewhiteDistanceRange: { | ||
min: 0, | ||
max: 255, | ||
value: 10 | ||
}, | ||
gradientTransparencyRange: { | ||
min: 0, | ||
max: 255, | ||
value: 100 | ||
}, | ||
brightnessRange: { | ||
min: -255, | ||
max: 255, | ||
value: 100 | ||
}, | ||
noiseRange: { | ||
min: 0, | ||
max: 1000, | ||
value: 100 | ||
}, | ||
pixelateRange: { | ||
min: 2, | ||
max: 20, | ||
value: 4 | ||
}, | ||
colorfilterThresholeRange: { | ||
min: 0, | ||
max: 255, | ||
value: 45 | ||
} | ||
} | ||
}; |
@@ -43,3 +43,3 @@ /** | ||
*/ | ||
applyTo(canvasEl) { | ||
applyTo(canvasEl) { // eslint-disable-line | ||
const context = canvasEl.getContext('2d'); | ||
@@ -46,0 +46,0 @@ const imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height); |
@@ -32,5 +32,10 @@ /** | ||
*/ | ||
initialize(options) { | ||
initialize(options, extendsOptions) { | ||
options = snippet.extend(options, extendsOptions); | ||
options.type = 'cropzone'; | ||
this.callSuper('initialize', options); | ||
this.options = options; | ||
this.on({ | ||
@@ -65,7 +70,16 @@ 'moving': this._onMoving, | ||
// Black dash line | ||
this._strokeBorder(ctx, 'rgb(0, 0, 0)', cropzoneDashLineWidth); | ||
if (this.options.lineWidth) { | ||
this._fillInnerRect(ctx); | ||
} else { | ||
// Black dash line | ||
this._strokeBorder(ctx, 'rgb(0, 0, 0)', { | ||
lineDashWidth: cropzoneDashLineWidth | ||
}); | ||
// White dash line | ||
this._strokeBorder(ctx, 'rgb(255, 255, 255)', cropzoneDashLineWidth, cropzoneDashLineOffset); | ||
// White dash line | ||
this._strokeBorder(ctx, 'rgb(255, 255, 255)', { | ||
lineDashWidth: cropzoneDashLineWidth, | ||
lineDashOffset: cropzoneDashLineOffset | ||
}); | ||
} | ||
@@ -113,3 +127,3 @@ // Reset scale | ||
ctx.lineTo(x[3] + 1, y[3] + 1); | ||
ctx.lineTo(x[0] - 1, y[3] - 1); | ||
ctx.lineTo(x[0] - 1, y[3] + 1); | ||
ctx.lineTo(x[0] - 1, y[0] - 1); | ||
@@ -131,2 +145,51 @@ ctx.closePath(); | ||
/** | ||
* Draw Inner grid line | ||
* @param {CanvasRenderingContext2D} ctx - Context | ||
* @private | ||
*/ | ||
_fillInnerRect(ctx) { | ||
const {x: outerX, y: outerY} = this._getCoordinates(ctx); | ||
const x = this._caculateInnerPosition(outerX, (outerX[2] - outerX[1]) / 3); | ||
const y = this._caculateInnerPosition(outerY, (outerY[2] - outerY[1]) / 3); | ||
ctx.save(); | ||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'; | ||
ctx.lineWidth = this.options.lineWidth; | ||
ctx.beginPath(); | ||
ctx.moveTo(x[0], y[1]); | ||
ctx.lineTo(x[3], y[1]); | ||
ctx.moveTo(x[0], y[2]); | ||
ctx.lineTo(x[3], y[2]); | ||
ctx.moveTo(x[1], y[0]); | ||
ctx.lineTo(x[1], y[3]); | ||
ctx.moveTo(x[2], y[0]); | ||
ctx.lineTo(x[2], y[3]); | ||
ctx.stroke(); | ||
ctx.closePath(); | ||
ctx.restore(); | ||
}, | ||
/** | ||
* Calculate Inner Position | ||
* @param {Array} outer - outer position | ||
* @param {number} size - interval for calcaulate | ||
* @returns {Array} - inner position | ||
* @private | ||
*/ | ||
_caculateInnerPosition(outer, size) { | ||
const position = []; | ||
position[0] = outer[1]; | ||
position[1] = outer[1] + size; | ||
position[2] = outer[1] + (size * 2); | ||
position[3] = outer[2]; | ||
return position; | ||
}, | ||
/** | ||
* Get coordinates | ||
@@ -170,3 +233,3 @@ * @param {CanvasRenderingContext2D} ctx - Context | ||
*/ | ||
_strokeBorder(ctx, strokeStyle, lineDashWidth, lineDashOffset) { | ||
_strokeBorder(ctx, strokeStyle, {lineDashWidth, lineDashOffset, lineWidth}) { | ||
const halfWidth = this.getWidth() / 2, | ||
@@ -177,2 +240,3 @@ halfHeight = this.getHeight() / 2; | ||
ctx.strokeStyle = strokeStyle; | ||
if (ctx.setLineDash) { | ||
@@ -184,2 +248,5 @@ ctx.setLineDash([lineDashWidth, lineDashWidth]); | ||
} | ||
if (lineWidth) { | ||
ctx.lineWidth = lineWidth; | ||
} | ||
@@ -186,0 +253,0 @@ ctx.beginPath(); |
@@ -28,2 +28,3 @@ /** | ||
const events = consts.eventNames; | ||
const {drawingModes, fObjectOptions} = consts; | ||
@@ -49,6 +50,13 @@ const {extend, stamp, isArray, isString, forEachArray, forEachOwnProperties, CustomEvents} = snippet; | ||
* @param {number} option.cssMaxHeight - Canvas css-max-height | ||
* @param {boolean} option.useItext - Use IText in text mode | ||
* @param {boolean} option.useDragAddIcon - Use dragable add in icon mode | ||
* @ignore | ||
*/ | ||
class Graphics { | ||
constructor(element, cssMaxWidth, cssMaxHeight) { | ||
constructor(element, { | ||
cssMaxWidth, | ||
cssMaxHeight, | ||
useItext = false, | ||
useDragAddIcon = false | ||
} = {}) { | ||
/** | ||
@@ -73,2 +81,20 @@ * Fabric image instance | ||
/** | ||
* Use Itext mode for text component | ||
* @type {boolean} | ||
*/ | ||
this.useItext = useItext; | ||
/** | ||
* Use add drag icon mode for icon component | ||
* @type {boolean} | ||
*/ | ||
this.useDragAddIcon = useDragAddIcon; | ||
/** | ||
* cropper Selection Style | ||
* @type {Object} | ||
*/ | ||
this.cropSelectionStyle = {}; | ||
/** | ||
* Image name | ||
@@ -126,3 +152,5 @@ * @type {string} | ||
onObjectSelected: this._onObjectSelected.bind(this), | ||
onPathCreated: this._onPathCreated.bind(this) | ||
onPathCreated: this._onPathCreated.bind(this), | ||
onSelectionCleared: this._onSelectionCleared.bind(this), | ||
onSelectionCreated: this._onSelectionCreated.bind(this) | ||
}; | ||
@@ -181,2 +209,3 @@ | ||
} | ||
/** | ||
@@ -285,2 +314,10 @@ * Removes the object or group | ||
/** | ||
* Gets an active group object | ||
* @returns {Object} active group object instance | ||
*/ | ||
getActiveGroupObject() { | ||
return this._canvas.getActiveGroup(); | ||
} | ||
/** | ||
* Activates an object or group | ||
@@ -294,2 +331,10 @@ * @param {Object} target - target object or group | ||
/** | ||
* Set Crop selection style | ||
* @param {Object} style - Selection styles | ||
*/ | ||
setCropSelectionStyle(style) { | ||
this.cropSelectionStyle = style; | ||
} | ||
/** | ||
* Get component | ||
@@ -336,2 +381,3 @@ * @param {string} name - Component name | ||
} | ||
/** | ||
@@ -568,2 +614,12 @@ * Stop the current drawing mode and back to the 'NORMAL' mode | ||
/** | ||
* Change cursor style | ||
* @param {string} cursorType - cursor type | ||
*/ | ||
changeCursor(cursorType) { | ||
const canvas = this.getCanvas(); | ||
canvas.defaultCursor = cursorType; | ||
canvas.renderAll(); | ||
} | ||
/** | ||
* Whether it has the filter or not | ||
@@ -843,3 +899,5 @@ * @param {string} type - Filter type | ||
'object:selected': handler.onObjectSelected, | ||
'path:created': handler.onPathCreated | ||
'path:created': handler.onPathCreated, | ||
'selection:cleared': handler.onSelectionCleared, | ||
'selection:created': handler.onSelectionCreated | ||
}); | ||
@@ -933,2 +991,39 @@ } | ||
/** | ||
* "selction:cleared" canvas event handler | ||
* @private | ||
*/ | ||
_onSelectionCleared() { | ||
this.fire(events.SELECTION_CLEARED); | ||
} | ||
/** | ||
* "selction:created" canvas event handler | ||
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event | ||
* @private | ||
*/ | ||
_onSelectionCreated(fEvent) { | ||
this.fire(events.SELECTION_CREATED, fEvent.target); | ||
} | ||
/** | ||
* Canvas discard selection all | ||
*/ | ||
discardSelection() { | ||
this._canvas.discardActiveGroup(); | ||
this._canvas.discardActiveObject(); | ||
this._canvas.renderAll(); | ||
} | ||
/** | ||
* Canvas Selectable status change | ||
* @param {boolean} selectable - expect status | ||
*/ | ||
changeSelectableAll(selectable) { | ||
this._canvas.forEachObject(obj => { | ||
obj.selectable = selectable; | ||
obj.hoverCursor = selectable ? 'move' : 'crosshair'; | ||
}); | ||
} | ||
/** | ||
* Return object's properties | ||
@@ -956,3 +1051,3 @@ * @param {fabric.Object} obj - fabric object | ||
if (obj.type === 'text') { | ||
if (['i-text', 'text'].indexOf(obj.type) > -1) { | ||
extend(props, this._createTextProperties(obj, props)); | ||
@@ -959,0 +1054,0 @@ } |
@@ -6,3 +6,6 @@ /** | ||
import snippet from 'tui-code-snippet'; | ||
import Promise from 'core-js/library/es6/promise'; | ||
import Invoker from './invoker'; | ||
import UI from './ui'; | ||
import action from './action'; | ||
import commandFactory from './factory/command'; | ||
@@ -22,14 +25,57 @@ import Graphics from './graphics'; | ||
* @param {string|jQuery|HTMLElement} wrapper - Wrapper's element or selector | ||
* @param {Object} [option] - Canvas max width & height of css | ||
* @param {number} option.cssMaxWidth - Canvas css-max-width | ||
* @param {number} option.cssMaxHeight - Canvas css-max-height | ||
* @param {Boolean} [option.usageStatistics=true] - Let us know the hostname. If you don't want to send the hostname, please set to false. | ||
* @param {Object} [options] - Canvas max width & height of css | ||
* @param {number} [options.includeUI] - Use the provided UI | ||
* @param {Object} [options.includeUI.loadImage] - Basic editing image | ||
* @param {string} options.includeUI.loadImage.path - image path | ||
* @param {string} options.includeUI.loadImage.name - image name | ||
* @param {Object} [options.includeUI.theme] - Theme object | ||
* @param {Array} [options.includeUI.menu] - It can be selected when only specific menu is used. [default all] | ||
* @param {string} [options.includeUI.initMenu] - The first menu to be selected and started. | ||
* @param {string} [options.includeUI.menuBarPosition=bottom] - Menu bar position [top | bottom | left | right] | ||
* @param {number} options.cssMaxWidth - Canvas css-max-width | ||
* @param {number} options.cssMaxHeight - Canvas css-max-height | ||
* @param {Boolean} [options.usageStatistics=true] - Let us know the hostname. If you don't want to send the hostname, please set to false. | ||
* @example | ||
* var ImageEditor = require('tui-image-editor'); | ||
* var blackTheme = require('./js/theme/black-theme.js'); | ||
* var instance = new ImageEditor(document.querySelector('#tui-image-editor'), { | ||
* includeUI: { | ||
* loadImage: { | ||
* path: 'img/sampleImage.jpg', | ||
* name: 'SampleImage' | ||
* }, | ||
* theme: blackTheme, // or whiteTheme | ||
* menu: ['shape', 'filter'], | ||
* initMenu: 'filter', | ||
* menuBarPosition: 'bottom' | ||
* }, | ||
* cssMaxWidth: 700, | ||
* cssMaxHeight: 500, | ||
* selectionStyle: { | ||
* cornerSize: 20, | ||
* rotatingPointOffset: 70 | ||
* } | ||
* }); | ||
*/ | ||
class ImageEditor { | ||
constructor(wrapper, option) { | ||
option = snippet.extend({ | ||
constructor(wrapper, options) { | ||
options = snippet.extend({ | ||
includeUI: false, | ||
usageStatistics: true | ||
}, option); | ||
}, options); | ||
this.mode = null; | ||
this.activeObjectId = null; | ||
/** | ||
* UI instance | ||
* @type {Ui} | ||
*/ | ||
if (options.includeUI) { | ||
this.ui = new UI(wrapper, options.includeUI, this.getActions()); | ||
options = this.ui.setUiDefaultSelectionStyle(options); | ||
} | ||
/** | ||
* Invoker | ||
@@ -46,3 +92,10 @@ * @type {Invoker} | ||
*/ | ||
this._graphics = new Graphics(wrapper, option.cssMaxWidth, option.cssMaxHeight); | ||
this._graphics = new Graphics( | ||
this.ui ? this.ui.getEditorArea() : wrapper, { | ||
cssMaxWidth: options.cssMaxWidth, | ||
cssMaxHeight: options.cssMaxHeight, | ||
useItext: !!this.ui, | ||
useDragAddIcon: !!this.ui | ||
} | ||
); | ||
@@ -63,4 +116,9 @@ /** | ||
addObject: this._onAddObject.bind(this), | ||
addObjectAfter: this._onAddObjectAfter.bind(this), | ||
textEditing: this._onTextEditing.bind(this), | ||
textChanged: this._onTextChanged.bind(this) | ||
textChanged: this._onTextChanged.bind(this), | ||
iconCreateResize: this._onIconCreateResize.bind(this), | ||
iconCreateEnd: this._onIconCreateEnd.bind(this), | ||
selectionCleared: this._selectionCleared.bind(this), | ||
selectionCreated: this._selectionCreated.bind(this) | ||
}; | ||
@@ -71,9 +129,14 @@ | ||
this._attachDomEvents(); | ||
this._setSelectionStyle(options.selectionStyle, { | ||
applyCropSelectionStyle: options.applyCropSelectionStyle, | ||
applyGroupSelectionStyle: options.applyGroupSelectionStyle | ||
}); | ||
if (option.selectionStyle) { | ||
this._setSelectionStyle(option.selectionStyle); | ||
if (options.usageStatistics) { | ||
sendHostName(); | ||
} | ||
if (option.usageStatistics) { | ||
sendHostName(); | ||
if (this.ui) { | ||
this.ui.initCanvas(); | ||
this.setReAction(); | ||
} | ||
@@ -96,3 +159,2 @@ } | ||
*/ | ||
/** | ||
@@ -139,7 +201,24 @@ * Rotation status | ||
* Set selection style by init option | ||
* @param {Object} styles - Selection styles | ||
* @param {Object} selectionStyle - Selection styles | ||
* @param {Object} applyTargets - Selection apply targets | ||
* @param {boolean} applyCropSelectionStyle - whether apply with crop selection style or not | ||
* @param {boolean} applyGroupSelectionStyle - whether apply with group selection style or not | ||
* @private | ||
*/ | ||
_setSelectionStyle(styles) { | ||
this._graphics.setSelectionStyle(styles); | ||
_setSelectionStyle(selectionStyle, {applyCropSelectionStyle, applyGroupSelectionStyle}) { | ||
if (selectionStyle) { | ||
this._graphics.setSelectionStyle(selectionStyle); | ||
} | ||
if (applyCropSelectionStyle) { | ||
this._graphics.setCropSelectionStyle(selectionStyle); | ||
} | ||
if (applyGroupSelectionStyle) { | ||
this.on('selectionCreated', eventTarget => { | ||
if (eventTarget.type === 'group') { | ||
eventTarget.set(selectionStyle); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -192,3 +271,8 @@ | ||
'textEditing': this._handlers.textEditing, | ||
'textChanged': this._handlers.textChanged | ||
'textChanged': this._handlers.textChanged, | ||
'iconCreateResize': this._handlers.iconCreateResize, | ||
'iconCreateEnd': this._handlers.iconCreateEnd, | ||
'selectionCleared': this._handlers.selectionCleared, | ||
'selectionCreated': this._handlers.selectionCreated, | ||
'addObjectAfter': this._handlers.addObjectAfter | ||
}); | ||
@@ -222,5 +306,2 @@ } | ||
_onKeyDown(e) { | ||
const activeObject = this._graphics.getActiveObject(); | ||
const activeObjectId = this._graphics.getObjectId(activeObject); | ||
if ((e.ctrlKey || e.metaKey) && e.keyCode === keyCodes.Z) { | ||
@@ -236,6 +317,5 @@ // There is no error message on shortcut when it's empty | ||
if ((e.keyCode === keyCodes.BACKSPACE || e.keyCode === keyCodes.DEL) && | ||
activeObject) { | ||
if ((e.keyCode === keyCodes.BACKSPACE || e.keyCode === keyCodes.DEL)) { | ||
e.preventDefault(); | ||
this.removeObject(activeObjectId); | ||
this.removeActiveObject(); | ||
} | ||
@@ -246,2 +326,37 @@ } | ||
/** | ||
* Remove Active Object | ||
*/ | ||
removeActiveObject() { | ||
const activeObject = this._graphics.getActiveObject(); | ||
const activeObjectGroup = this._graphics.getActiveGroupObject(); | ||
if (activeObjectGroup) { | ||
const objects = activeObjectGroup.getObjects(); | ||
this.discardSelection(); | ||
this._removeObjectStream(objects); | ||
} else if (activeObject) { | ||
const activeObjectId = this._graphics.getObjectId(activeObject); | ||
this.removeObject(activeObjectId); | ||
} | ||
} | ||
/** | ||
* RemoveObject Sequential processing for prevent invoke lock | ||
* @param {Array.<Object>} targetObjects - target Objects for remove | ||
* @returns {object} targetObjects | ||
* @private | ||
*/ | ||
_removeObjectStream(targetObjects) { | ||
if (!targetObjects.length) { | ||
return true; | ||
} | ||
const targetObject = targetObjects.pop(); | ||
return this.removeObject(this._graphics.getObjectId(targetObject)).then(() => ( | ||
this._removeObjectStream(targetObjects) | ||
)); | ||
} | ||
/** | ||
* mouse down event handler | ||
@@ -386,2 +501,21 @@ * @param {Event} event mouse down event | ||
/** | ||
* discard selction | ||
* @example | ||
* imageEditor.discardSelection(); | ||
*/ | ||
discardSelection() { | ||
this._graphics.discardSelection(); | ||
} | ||
/** | ||
* selectable status change | ||
* @param {boolean} selectable - selctable status | ||
* @example | ||
* imageEditor.changeSelectableAll(false); // or true | ||
*/ | ||
changeSelectableAll(selectable) { | ||
this._graphics.changeSelectableAll(selectable); | ||
} | ||
/** | ||
* Invoke command | ||
@@ -864,2 +998,13 @@ * @param {String} commandName - Command name | ||
/** | ||
* change text mode | ||
* @param {string} type - change type | ||
* @private | ||
*/ | ||
_changeActivateMode(type) { | ||
if (type !== 'ICON' && this.getDrawingMode() !== type) { | ||
this.startDrawingMode(type); | ||
} | ||
} | ||
/** | ||
* 'textChanged' event handler | ||
@@ -874,2 +1019,24 @@ * @param {Object} objectProps changed object properties | ||
/** | ||
* 'iconCreateResize' event handler | ||
* @param {Object} originPointer origin pointer | ||
* @param {Number} originPointer.x x position | ||
* @param {Number} originPointer.y y position | ||
* @private | ||
*/ | ||
_onIconCreateResize(originPointer) { | ||
this.fire(events.ICON_CREATE_RESIZE, originPointer); | ||
} | ||
/** | ||
* 'iconCreateEnd' event handler | ||
* @param {Object} originPointer origin pointer | ||
* @param {Number} originPointer.x x position | ||
* @param {Number} originPointer.y y position | ||
* @private | ||
*/ | ||
_onIconCreateEnd(originPointer) { | ||
this.fire(events.ICON_CREATE_END, originPointer); | ||
} | ||
/** | ||
* 'textEditing' event handler | ||
@@ -932,2 +1099,28 @@ * @private | ||
/** | ||
* 'addObjectAfter' event handler | ||
* @param {Object} objectProps added object properties | ||
* @private | ||
*/ | ||
_onAddObjectAfter(objectProps) { | ||
this.fire(events.ADD_OBJECT_AFTER, objectProps); | ||
} | ||
/** | ||
* 'selectionCleared' event handler | ||
* @private | ||
*/ | ||
_selectionCleared() { | ||
this.fire(events.SELECTION_CLEARED); | ||
} | ||
/** | ||
* 'selectionCreated' event handler | ||
* @param {Object} eventTarget - Fabric object | ||
* @private | ||
*/ | ||
_selectionCreated(eventTarget) { | ||
this.fire(events.SELECTION_CREATED, eventTarget); | ||
} | ||
/** | ||
* Register custom icons | ||
@@ -946,2 +1139,12 @@ * @param {{iconType: string, pathValue: string}} infos - Infos to register icons | ||
/** | ||
* Change canvas cursor type | ||
* @param {string} cursorType - cursor type | ||
* @example | ||
* imageEditor.changeCursor('crosshair'); | ||
*/ | ||
changeCursor(cursorType) { | ||
this._graphics.changeCursor(cursorType); | ||
} | ||
/** | ||
* Add icon on canvas | ||
@@ -1164,2 +1367,19 @@ * @param {string} type - Icon type ('arrow', 'cancel', custom icon name) | ||
/** | ||
* Set properties of active object, Do not leave an invoke history. | ||
* @param {number} id - object id | ||
* @param {Object} keyValue - key & value | ||
* @example | ||
* imageEditor.setObjectPropertiesQuietly(id, { | ||
* left:100, | ||
* top:100, | ||
* width: 200, | ||
* height: 200, | ||
* opacity: 0.5 | ||
* }); | ||
*/ | ||
setObjectPropertiesQuietly(id, keyValue) { | ||
this._graphics.setObjectProperties(id, keyValue); | ||
} | ||
/** | ||
* Get properties of active object corresponding key | ||
@@ -1270,3 +1490,5 @@ * @param {number} id - object id | ||
action.mixin(ImageEditor); | ||
CustomEvents.mixin(ImageEditor); | ||
module.exports = ImageEditor; |
@@ -78,2 +78,48 @@ /** | ||
/** | ||
* ParseInt simpliment | ||
* @param {number} value - Value | ||
* @returns {number} | ||
*/ | ||
toInteger(value) { | ||
return parseInt(value, 10); | ||
}, | ||
/** | ||
* String to camelcase string | ||
* @param {string} targetString - change target | ||
* @returns {string} | ||
* @private | ||
*/ | ||
toCamelCase(targetString) { | ||
return targetString.replace(/-([a-z])/g, ($0, $1) => $1.toUpperCase()); | ||
}, | ||
/** | ||
* Check browser file api support | ||
* @returns {boolean} | ||
* @private | ||
*/ | ||
isSupportFileApi() { | ||
return !!(window.File && window.FileList && window.FileReader); | ||
}, | ||
/** | ||
* hex to rgb | ||
* @param {string} color - hex color | ||
* @param {string} alpha - color alpha value | ||
* @returns {string} rgb expression | ||
*/ | ||
getRgb(color, alpha) { | ||
if (color.length === 4) { | ||
color = `${color}${color.slice(1, 4)}`; | ||
} | ||
const r = parseInt(color.slice(1, 3), 16); | ||
const g = parseInt(color.slice(3, 5), 16); | ||
const b = parseInt(color.slice(5, 7), 16); | ||
const a = alpha || 1; | ||
return `rgba(${r}, ${g}, ${b}, ${a})`; | ||
}, | ||
/** | ||
* send hostname | ||
@@ -96,3 +142,58 @@ */ | ||
}); | ||
}, | ||
/** | ||
* Apply css resource | ||
* @param {string} styleBuffer - serialized css text | ||
* @param {string} tagId - style tag id | ||
*/ | ||
styleLoad(styleBuffer, tagId) { | ||
const [head] = document.getElementsByTagName('head'); | ||
const linkElement = document.createElement('link'); | ||
const styleData = encodeURIComponent(styleBuffer); | ||
if (tagId) { | ||
linkElement.id = tagId; | ||
// linkElement.id = 'tui-image-editor-theme-style'; | ||
} | ||
linkElement.setAttribute('rel', 'stylesheet'); | ||
linkElement.setAttribute('type', 'text/css'); | ||
linkElement.setAttribute('href', `data:text/css;charset=UTF-8,${styleData}`); | ||
head.appendChild(linkElement); | ||
}, | ||
/** | ||
* Get selector | ||
* @param {HTMLElement} targetElement - target element | ||
* @returns {Function} selector | ||
*/ | ||
getSelector(targetElement) { | ||
return str => targetElement.querySelector(str); | ||
}, | ||
/** | ||
* Change base64 to blob | ||
* @param {String} data - base64 string data | ||
* @returns {Blob} Blob Data | ||
*/ | ||
base64ToBlob(data) { | ||
const rImageType = /data:(image\/.+);base64,/; | ||
let mimeString = ''; | ||
let raw, uInt8Array, i; | ||
raw = data.replace(rImageType, (header, imageType) => { | ||
mimeString = imageType; | ||
return ''; | ||
}); | ||
raw = atob(raw); | ||
const rawLength = raw.length; | ||
uInt8Array = new Uint8Array(rawLength); // eslint-disable-line | ||
for (i = 0; i < rawLength; i += 1) { | ||
uInt8Array[i] = raw.charCodeAt(i); | ||
} | ||
return new Blob([uInt8Array], {type: mimeString}); | ||
} | ||
}; |
@@ -20,3 +20,6 @@ /** | ||
beforeEach(() => { | ||
graphics = new Graphics($('<canvas>'), cssMaxWidth, cssMaxHeight); | ||
graphics = new Graphics($('<canvas>'), { | ||
cssMaxWidth, | ||
cssMaxHeight | ||
}); | ||
canvas = graphics.getCanvas(); | ||
@@ -23,0 +26,0 @@ }); |
@@ -7,2 +7,3 @@ /** | ||
import snippet from 'tui-code-snippet'; | ||
import Promise from 'core-js/library/es6/promise'; | ||
import ImageEditor from '../src/js/imageEditor'; | ||
@@ -19,2 +20,6 @@ | ||
spyOn(snippet, 'imagePing'); | ||
imageEditor = new ImageEditor(el, { | ||
usageStatistics: false | ||
}); | ||
}); | ||
@@ -39,3 +44,47 @@ | ||
}); | ||
it('removeObjectStream () must be executed as many times as the length of the Object array.', done => { | ||
const promise = new Promise(resolve => { | ||
resolve(); | ||
}); | ||
spyOn(imageEditor, '_removeObjectStream').and.callThrough(); | ||
spyOn(imageEditor, 'removeObject').and.returnValue(promise); | ||
const removeJobsSequens = [1, 2, 3, 4]; | ||
const expected = removeJobsSequens.length + 1; | ||
const removeObjectStremPromise = imageEditor._removeObjectStream(removeJobsSequens); | ||
removeObjectStremPromise.then(() => { | ||
expect(imageEditor._removeObjectStream.calls.count()).toBe(expected); | ||
done(); | ||
}); | ||
}); | ||
describe('removeActiveObject()', () => { | ||
it('_removeObjectStream should be executed when group exists.', () => { | ||
spyOn(imageEditor._graphics, 'getActiveObject'); | ||
spyOn(imageEditor._graphics, 'getActiveGroupObject').and.returnValue({ | ||
getObjects: () => [1, 2, 3] | ||
}); | ||
spyOn(imageEditor, '_removeObjectStream'); | ||
spyOn(imageEditor, 'discardSelection'); | ||
imageEditor.removeActiveObject(); | ||
expect(imageEditor.discardSelection).toHaveBeenCalled(); | ||
expect(imageEditor._removeObjectStream).toHaveBeenCalled(); | ||
}); | ||
it('removeObject must be executed when group does not exist.', () => { | ||
spyOn(imageEditor._graphics, 'getActiveGroupObject').and.returnValue(null); | ||
spyOn(imageEditor._graphics, 'getActiveObject').and.returnValue(jasmine.any(Object)); | ||
spyOn(imageEditor._graphics, 'getObjectId'); | ||
spyOn(imageEditor, 'removeObject'); | ||
imageEditor.removeActiveObject(); | ||
expect(imageEditor.removeObject).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -47,5 +47,6 @@ /** | ||
it('"rotate()" should add angle value modular 360(===2*PI)', () => { | ||
it('"rotate()" should add angle value modular 360(===2*PI)', done => { | ||
rotationModule.setAngle(10).then(() => rotationModule.rotate(380)).then(() => { | ||
expect(rotationModule.getCurrentAngle()).toBe(30); | ||
done(); | ||
}); | ||
@@ -52,0 +53,0 @@ }); |
@@ -9,8 +9,9 @@ /** | ||
const SafeUmdPlugin = require('safe-umd-webpack-plugin'); | ||
const ExtractTextPlugin = require('extract-text-webpack-plugin'); | ||
const isProduction = process.argv.indexOf('-p') > -1; | ||
const FILENAME = pkg.name + (isProduction ? '.min.js' : '.js'); | ||
const FILENAME = pkg.name + (isProduction ? '.min' : ''); | ||
const BANNER = [ | ||
FILENAME, | ||
`${FILENAME}.js`, | ||
`@version ${pkg.version}`, | ||
@@ -31,3 +32,3 @@ `@author ${pkg.author}`, | ||
publicPath: 'dist', | ||
filename: FILENAME | ||
filename: `${FILENAME}.js` | ||
}, | ||
@@ -61,3 +62,8 @@ externals: { | ||
loader: 'babel' | ||
}, | ||
{ | ||
test: /\.styl$/, | ||
loader: ExtractTextPlugin.extract('css-loader?sourceMap!stylus-loader?paths=src/css/') | ||
} | ||
] | ||
@@ -67,2 +73,3 @@ }, | ||
new webpack.BannerPlugin(BANNER), | ||
new ExtractTextPlugin(`${FILENAME}.css`), | ||
new SafeUmdPlugin() | ||
@@ -69,0 +76,0 @@ ], |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
4645958
346
36097
314
3
35
5
+ Addedtui-color-picker@^2.2.0
+ Addedtui-color-picker@2.2.8(transitive)