diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..ad390b6c7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +# Ignore polyfill +src/js/polyfill.js diff --git a/.eslintrc.js b/.eslintrc.js index 7810e3a07..18307f85e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,5 +15,11 @@ module.exports = { }, "parserOptions": { "sourceType": "module" + }, + 'rules': { + 'prefer-destructuring': ['error', { + VariableDeclarator: {array: true, object: true}, + AssignmentExpression: {array: false, object: false} + }] } }; diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..1e8ca1109 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at dl_javascript@nhnent.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3709dfc48 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing to TOAST UI + +First off, thanks for taking the time to contribute! πŸŽ‰ 😘 ✨ + +The following is a set of guidelines for contributing to TOAST UI. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. + +## Reporting Bugs +Bugs are tracked as GitHub issues. Search the list and try reproduce on [demo][demo] before you create an issue. When you create an issue, please provide the following information by filling in the template. + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. Don't just say what you did, but explain how you did it. For example, if you moved the cursor to the end of a line, explain if you used a mouse or a keyboard. +* **Provide specific examples to demonstrate the steps.** Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets on the issue, use Markdown code blocks. +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **Include screenshots and animated GIFs** which show you following the described steps and clearly demonstrate the problem. + +## Suggesting Enhancements +In case you want to suggest for TOAST UI ImageEditor, please follow this guideline to help maintainers and the community understand your suggestion. +Before creating suggestions, please check [issue list](../../labels/feature%20request) if there's already a request. + +Create an issue and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps.** Include copy/pasteable snippets which you use in those examples, as Markdown code blocks. +* **Include screenshots and animated GIFs** which helps demonstrate the steps or point out the part of TOAST UI ImageEditor which the suggestion is related to. +* **Explain why this enhancement would be useful** to most TOAST UI users. +* **List some other image editors or applications where this enhancement exists.** + +## First Code Contribution + +Unsure where to begin contributing to TOAST UI? You can start by looking through these `document`, `good first issue` and `help wanted` issues: + +* **document issues**: issues which should be reviewed or improved. +* **good first issues**: issues which should only require a few lines of code, and a test or two. +* **help wanted issues**: issues which should be a bit more involved than beginner issues. + +## Pull Requests + +### Development WorkFlow +- Set up your development environment +- Make change from a right branch +- Be sure the code passes `npm run lint`, `npm run test` +- Make a pull request + +### Development environment +- Prepare your machine node and it's packages installed. +- Checkout our repository +- Install dependencies by `npm install && bower install` +- Start webpack-dev-server by `npm run serve` + +### Make changes +#### Checkout a branch +- **develop**: PR base branch. merge features, updates for next minor or major release +- **master**: bug fix or document update for next patch release. develop branch will rebase every time master branch update. so keep code change to a minimum. +- **production**: lastest release branch with distribution files. never make a PR on this +- **gh-pages**: API docs, examples and demo + +#### Check Code Style +Run `npm run eslint` and make sure all the tests pass. + +#### Test +Run `npm run test` and verify all the tests pass. +If you are adding new commands or features, they must include tests. +If you are changing functionality, update the tests if you need to. + +#### Commit +Follow our [commit message conventions](./docs/COMMIT_MESSAGE_CONVENTION.md). + +### Yes! Pull request +Make your pull request, then describe your changes. +#### Title +Follow other PR title format on below. +``` + : Short Description (fix #111) + : Short Description (fix #123, #111, #122) + : Short Description (ref #111) +``` +* capitalize first letter of Type +* use present tense: 'change' not 'changed' or 'changes' + +#### Description +If it has related to issues, add links to the issues(like `#123`) in the description. +Fill in the [Pull Request Template](./docs/PULL_REQUEST_TEMPLATE.md) by check your case. + +## Code of Conduct +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to dl_javascript@nhnent.com. + +> This Guide is base on [atom contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md), [CocoaPods](http://guides.cocoapods.org/contributing/contribute-to-cocoapods.html) and [ESLint](http://eslint.org/docs/developer-guide/contributing/pull-requests) diff --git a/README.md b/README.md index a0c88a2fc..b7ba4f04b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,145 @@ -# 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 +| Chrome Chrome | IE Internet Explorer | Edge Edge | Safari Safari | Firefox 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...) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CropFlipRotationDrawingShape
2018-06-04 4 33 162018-06-04 4 40 062018-06-04 4 43 022018-06-04 4 47 402018-06-04 4 51 45
IconTextMaskFilter
2018-06-05 2 06 292018-06-05 2 14 362018-06-05 2 20 462018-06-05 2 27 10
+ +### 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 * Undo/Redo (With shortcut) * Crop * Flip * Rotation -* Free Drawing +* Free drawing * Line drawing * Shape * Icon @@ -17,46 +147,93 @@ Canvas image editor * Mask Filter * Image Filter -## 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. + +### Via Package Manager -## Test Environment -### PC - * IE9~11 - * Edge - * Chrome - * Firefox - * Safari -### Mobile - * iOS 9.3.x - * Android 4.4.x +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. -## Usage -### Use `npm` -Install the latest version using `npm` command: +#### npm +```sh +$ npm install --save tui-image-editor # Latest version +$ npm install --save tui-image-editor@ # Specific version ``` -$ npm install tui-image-editor --save + +#### bower + +```sh +$ bower install tui-image-editor # Latest version +$ bower install tui-image-editor# # 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 + + +``` + +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/ +β”‚ β”œβ”€ ... ``` -$ npm install tui-image-editor@ --save + +### 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 + +... +
+... + ``` -To access as module format in your code: +### 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, cssMaxHeight: 500, selectionStyle: { @@ -66,23 +243,11 @@ var instance = new ImageEditor('.tui-image-editor', { }); ``` -### Use `bower` -Install the latest version using `bower` command: - -``` -$ bower install tui-image-editor -``` - -or want to install the each version: - -``` -$ bower install tui-image-editor# -``` - -To access as namespace format in your code: +Or ~ UI ```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, cssMaxHeight: 500, selectionStyle: { @@ -92,31 +257,57 @@ var imageEditor = new tui.ImageEditor('.tui-image-editor', { }); ``` -### 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 - -``` +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/) + +## πŸ’¬ 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 +## πŸ“œ License [MIT LICENSE](https://github.com/nhnent/tui.image-editor/blob/master/LICENSE) diff --git a/docs/Apply-Mobile-Version-Image.md b/docs/Apply-Mobile-Version-Image.md new file mode 100644 index 000000000..9e6bc46c3 --- /dev/null +++ b/docs/Apply-Mobile-Version-Image.md @@ -0,0 +1,79 @@ +# Mobile version: image load & save issues + +## Load Image + +#### Issue +- You can load photos directly from your mobile device into the image editor, but images with too high a resolution are not suitable for use. +- For an action that includes a mouse gesture, such as cropping and drawing in the image editor, the action is determined by the aspect ratio relative to the original image size, so the higher the resolution, the less usable. +- Maximum resolution per device + * iPhone : `3264 * 2448` + * Galaxy4 : `4128 * 3096` (High resolution) / `3264 * 2448` (Normal) / `2048 * 1152` (Low resolution) +- The appropriate image size for usability is `3264 * 2448`. If you receive a file upload event when loading an image taken at high resolution on your Android device, do the following. + +#### How to handle high-resolution image uploads +```html + +``` +```js +var MAX_RESOLUTION = 3264 * 2448; + +$('input-image-file').on('change', function(event) { + var file; + var img; + var resolution; + + if (!supportingFileAPI) { + alert('This browser does not support file-api'); + } + + file = event.target.files[0]; + + if (file) { + img = new Image(); + + img.onload = function() { + resolution = this.width * this.height; + + if (resolution <= MAX_RESOLUTION) { + imageEditor.loadImageFromFile(file); + } else { + alert('Loaded image\'s resolution is too large!\nRecommended resolution is 3264 * 2448!'); + } + + URL.revokeObjectURL(file); + }; + + img.src = URL.createObjectURL(file); + } +}); +``` + +## Save Image + +#### Issue +- Saving an edited image does not appear in the current sample page, but the actual service must send the file to the server to save the image. +- Uses Ajax communication. + +#### How to Save a Server Image + +Step 1. Import image data to be saved in the image editor. +```js +var dataURL = imageEditor.toDataURL(); +``` + +Step 2. `base64` encoded image data is Ajax communicated and sent to the server. +```js +$.ajax({ + type: 'POST', + url: serverUrl, + data: { + imgBase64: dataURL // Data from Step 1. + } +}).done(function() { + console.log('saved!'); +}); +``` + +Step 3. The server processes the received data and stores it. +- [Using Java](https://sangupta.com/tech/saving-html5-canvas-to-java-server.html) +- [Using Php](http://permadi.com/2010/10/html5-saving-canvas-image-data-using-php-and-ajax/) diff --git a/docs/Apply-Mobile-Version.md b/docs/Apply-Mobile-Version.md new file mode 100644 index 000000000..511f22b13 --- /dev/null +++ b/docs/Apply-Mobile-Version.md @@ -0,0 +1,75 @@ +# How to apply the mobile version. + +## Image editor How to apply a mobile device + +- Some settings are required to use Image Editor components on mobile devices. +- Please refer to the [sample page](http://nhnent.github.io/tui.image-editor/latest/tutorial-example03-mobile.html) first to check the UI configuration and operation. + +#### Step 1. Include the dependency file on the page. (PC version same) +```html + + + + +``` + +#### Step 2. `body` Add markup to the tag to create an image editor. (PC version same) +```html +
+ +
+``` + +#### Step 3. `head` Add a meta tag for setting the viewport to the tag. +```html + +``` + +#### Step 4. Create an image editor by setting option values for mobile device optimization. +```js +// Create image editor +var imageEditor = new tui.component.ImageEditor('.tui-image-editor canvas', { + cssMaxWidth: document.documentElement.clientWidth, + cssMaxHeight: document.documentElement.clientHeight, + selectionStyle: { + cornerSize: 50, + rotatingPointOffset: 100 + } +}); +``` +- `cssMaxWidth`, `cssMaxHeight` : + * Sets maximum `width` and` height` values in the canvas area. + * Do not set it to a fixed value like the PC version because the value changes depending on the mobile device to be connected. +- `selectionStyle` : + * Selection style setting options that are displayed when an object such as an icon, text, etc. is selected. + * If the corner size is small, it is difficult to resize and rotate, so set the selection style. + * The selection style options are the same as those provided by `fabric.js` and can be set with the following option values: ([Reference](http://fabricjs.com/customization)) + + ```js + { + borderColor: 'red', // Selection line color + cornerColor: 'green', // Selection corner color + cornerSize: 6, // Selection corner size + rotatingPointOffset: 100 // Distance from selection area to rotation corner + transparentCorners: false, // Selection corner Transparency + } + ``` +![2016-08-18 4 52 29](https://cloud.githubusercontent.com/assets/18183560/17766120/86f7c3fc-6564-11e6-86d7-554e8e946843.png) + + +#### Step 5. Add a CSS file and markup for UI configuration. (PC version same) +```html + +``` +> +The CSS file is used on the sample page and should only refer to the UI configuration, +It is recommended to customize image, CSS, and markup files when applying the service. + + +#### Step 6. Apply the image editor API to the UI +- API : [http://nhnent.github.io/tui.image-editor/latest/](http://nhnent.github.io/tui.image-editor/latest/) +- Sample Page : [http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-includeUi.html](http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-includeUi.html) + +![all_feature_small](https://cloud.githubusercontent.com/assets/18183560/17803706/034ea17c-6633-11e6-914d-6602d12888f9.gif) +![text_feature_small](https://cloud.githubusercontent.com/assets/18183560/17803707/03530636-6633-11e6-8c03-cd5523716b9b.gif) + diff --git a/docs/Basic-Tutorial.md b/docs/Basic-Tutorial.md new file mode 100644 index 000000000..f8cc543fb --- /dev/null +++ b/docs/Basic-Tutorial.md @@ -0,0 +1,46 @@ +## Basic +Follow these 3steps to create image-editor. + +### 1. Load required files +Load first the dependencies, and then load `image-editor.js` or `image-editor.min.js`. +```html + + + + +``` + +### 2. HTML Markup +ImageEditor needs a division element having a canvas element.
+And **the division element must have own (css)height.** +```html + +
+ +
+``` + +### 3. Javascript +ImageEditor constructor needs two parameters. +* The canvas element selector +* Css max width & Css max height + * Set the max width according to the size of your page. + * The max height should be same the height of the division element (in this example, `#my-image-editor`). +```js +// Create image editor +var imageEditor = new tui.component.ImageEditor('#my-image-editor canvas', { + cssMaxWidth: 1000, // Component default value: 1000 + cssMaxHeight: 800 // Component default value: 800 +}); + +// Load image +imageEditor.loadImageFromURL('img/sampleImage.jpg', 'My sample image') +``` + +
+ +## More.. + +See the API page and the sample page +* API: http://nhnent.github.io/tui.image-editor/latest/ +* Sample: http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-includeUi.html diff --git a/docs/COMMIT_MESSAGE_CONVENTION.md b/docs/COMMIT_MESSAGE_CONVENTION.md new file mode 100644 index 000000000..c150e2e24 --- /dev/null +++ b/docs/COMMIT_MESSAGE_CONVENTION.md @@ -0,0 +1,49 @@ +# Commit Message Convention + +## Commit Message Format + +``` +: Short description (fix #1234) + +Logger description here if necessary + +BREAKING CHANGE: only contain breaking change +``` +* Any line of the commit message cannot be longer 100 characters! + +## Revert +``` +revert: commit + +This reverts commit +More description if needed +``` + +## Type +Must be one of the following: + +* **feat**: A new feature +* **fix**: A bug fix +* **docs**: Documentation only changes +* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) +* **refactor**: A code change that neither fixes a bug nor adds a feature +* **perf**: A code change that improves performance +* **test**: Adding missing or correcting existing tests +* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation + +## Subject +* use the imperative, __present__ tense: "change" not "changed" nor "changes" +* don't capitalize the first letter +* no dot (.) at the end +* reference GitHub issues at the end. If the commit doesn’t completely fix the issue, then use `(refs #1234)` instead of `(fixes #1234)`. + +## Body + +* use the imperative, __present__ tense: "change" not "changed" nor "changes". +* the motivation for the change and contrast this with previous behavior. + +## BREAKING CHANGE +* This commit contains breaking change(s). +* start with the word BREAKING CHANGE: with a space or two newlines. The rest of the commit message is then used for this. + +This convention is based on [AngularJS](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) and [ESLint](https://eslint.org/docs/developer-guide/contributing/pull-requests#step2) \ No newline at end of file diff --git a/docs/ISSUE_TEMPLATE.md b/docs/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..2f6fd0eea --- /dev/null +++ b/docs/ISSUE_TEMPLATE.md @@ -0,0 +1,24 @@ + + + + +## Version + + +## Development Environment + + +## Current Behavior + + +```js +// Write example code +``` + +## Expected Behavior + \ No newline at end of file diff --git a/docs/ImageEditor-2.0.0-Migration-guide.md b/docs/ImageEditor-2.0.0-Migration-guide.md new file mode 100644 index 000000000..ba9bb318e --- /dev/null +++ b/docs/ImageEditor-2.0.0-Migration-guide.md @@ -0,0 +1,118 @@ +There are a lot of changes for ImageEditor 2.0.0 including API changes and new features. This migration document will be nicely moving to v2.0.0. + +## New drawing mode change APIs + * New APIs + * `startDrawingMode(modeName)` starts a drawing mode + * `stopDrawingMode()` stops current drawing mode and back to 'NORMAL' mode + * `getDrawingMode()` returns current drawing mode name. + * `getCropzoneRect()` returns cropping rect in 'CROPPER' drawing mode. + * `crop(rect)` crops image given area + + * Removed APIs + * `startCropping`, `endCropping` + * `startDrawingShapeMode`, `endDrawingShapeMode` + * `startFreeDrawing`, `endFreeDrawing` + * `startLineDrawing`, `endLineDrawing` + * `startTextMode`, `endTextMode` + * `endAll` + * `endCropping` is divided into three APIs +```js +var rect = imageEditor.getCropzoneRect(); +imageEditor.crop(rect).then(() => { + imageEditor.stopDrawingMode(); +}); +``` + +## Changed APIs +* `removeActiveObject()` ==> `removeObject(id)` +* `getCurrentState()` ==> `getDrawingMode()` + +## Use object is with all drawing APIs + * In versions prior to 1.4.1, the users should select an object and manipulate it which is called 'active object'. There was no way to manipulate non-selected object. After 2.0.0 version, you can manipulate not only selected object, but also non-selected objects by receiving the Object Id. + * To get the Object Id, use the parameter.id in Promise.then() and the event callback. + +```js +/* +{ + id: number + type: type + left: number, + top: number, + width: number, + fill: string + stroke: string + strokeWidth: number + opacity: number, + + // text object + text: string, + fontFamily: string, + fontSize: number, + fontStyle: string, + fontWeight: string, + textAlign: string, + textDecoration: string +} +*/ +imageEditor.on('objectActivated', function(props) { + console.log(props); + console.log(props.type); + console.log(props.id); +}); +``` +```js +imageEditor.addShape('circle', { + fill: 'red', + stroke: 'blue', + strokeWidth: 3, + rx: 10, + ry: 100, + isRegular: false +}).then(function(props) { + console.log(props.id); + + imageEditor.changeShape(props.id, { // change circle + fill: '#FFFF00', + strokeWidth: 10 + }); +}); +``` + +## Support Promise API +* All drawing APIs returns Promise and supports Undo/Redo. +* List of related APIs + * `addIcon`, `addImageObject`, `addShape`, `changeIconColor` + * `changeShape`, `addText`, `changeText`, `changeTextStyle`, + * `resizeCanvasDimension`, `applyFilter`, `removeFilter`, + * `clearObjects`, `flipX`, `flipY`, `loadImageFromFile`, + * `loadImageFromURL`, `redo`, `undo`, `removeObject`, + * `resetFlip`, `rotate`, `setAngle`, `crop`, + * `setObjectPosition`, `setObjectProperties` + +## Changed event type +| As-Is | To-Be | Change | Why & Purpose | +| ----- | ----- | --- | --- | +| **~~_activateText_~~** | **addText** | renamed | when mousedown event occurs in 'TEXT' drawing mode | +| **_~~addObject~~_** | - | removed | unnecessary | +| **_~~adjustObject~~_** | **objectMoved** | renamed
changed | when user drags an object | +| **_~~adjustObject~~_** | **objectScaled** | renamed
changed | when object is being scaled | +| ~~applyFilter~~ | - | removed | Replace it to `applyFilter()` Promise API | +| ~~clearImage~~ | - | removed | Replace it to `loadImageFromFile()`, `loadImageFromURL()` Promise API | +| ~~clearObjects~~ | - | removed | Replace it to `clearObjects()` Promise API | +| **_~~editText~~_** | **textEditing** | renamed | when textbox is being edited | +| **~~_emptyRedoStack_~~** | **redoStackChanged** | renamed
changed | Replace it to `redoStackChanged` event with length `0` | +| **~~_emptyUndoStack_~~** | **undoStackChanged** | renamed
changed | Replace it to `undoStackChanged` event with length `0` | +| ~~endCropping~~ | - | removed | unnecessary | +| ~~endFreeDrawing~~ | - | removed | unnecessary | +| ~~endLineDrawing~~ | - | removed | unnecessary | +| ~~flipImage~~ | - | removed | Replace it to `flipX()`, `flipY()` Promise API | +| ~~loadImage~~ | - | removed | Replace it to `loadImageFromFile()`, `loadImageFromURL()` Promise API | +| **mousedown** | **mousedown** | remained | just mousedown | +| **_~~pushRedoStack~~_** | **redoStackChanged** | renamed
changed |redo change event | +| **_~~pushUndoStack~~_** | **undoStackChanged** | renamed
changed | undo change event | +| ~~removeObject~~ | - | removed | Replace it to `removeObject()` Promise API | +| ~~rotateImage~~ | - | removed | Replace it to `rotate()`, `setAngle()` Promise API | +| **_~~selectObject~~_** | **objectActivated** | renamed
changed | when user selects an object | +| ~~startCropping~~ | - | removed | unnecessary | +| ~~startFreeDrawing~~ | - | removed | unnecessary | +| ~~startLineDrawing~~ | - | removed | unnecessary | \ No newline at end of file diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..efd0e978b --- /dev/null +++ b/docs/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,42 @@ + + + + + + + +### Please check if the PR fulfills these requirements +- [ ] It's submitted to right branch according to our branching model +- [ ] It's right issue type on title +- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number) +- [ ] The commit message follows our guidelines +- [ ] Tests for the changes have been added (for bug fixes/features) +- [ ] Docs have been added/updated (for bug fixes/features) +- [ ] It does not introduce a breaking change or has description for the breaking change + +### Description + + + +--- +Thank you for your contribution to TOAST UI product. πŸŽ‰ 😘 ✨ \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..b05739743 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,17 @@ +## Tutorials +- [Getting Started](Basic-Tutorial.md) +- [Understand the structure](Structure.md) +- [Simple Reference](Reference.md) +- [Mobile Version](Apply-Mobile-Version.md) +- [Mobile Version - Image Load and Save](Apply-Mobile-Version-Image.md) + + +## Documents +- [Code of Conducting](../CODE_OF_CONDUCT.md) +- [Contributing Guide](../CONTRIBUTING.md) +- [Commit Message Convention](COMMIT_MESSAGE_CONVENTION.md) +- [API & Examples](http://nhnent.github.io/tui.image-editor/latest/) +- [v2.0.0 Migration Guide](ImageEditor-2.0.0-Migration-guide.md) + + + diff --git a/docs/Reference.md b/docs/Reference.md new file mode 100644 index 000000000..8d7e3b4f9 --- /dev/null +++ b/docs/Reference.md @@ -0,0 +1,70 @@ + +### Text +- Insert text on the canvas and modify text. +- Change text color, weight, align so on. + +>**Implementing to insert text on the canvas using the text palette** +- The user can make the specific text palette and edit some text using this palette. +- Call custom event API, it can insert text object and control the text palette. + * `ImageEditor#activateText` : It occurs when the canvas is clicked. + * `ImageEditor#adjustObject` : It occurs when any inserted text object is moved or resized. + +```js +imageEditor.on('activateText', function(obj) { + console.log(obj.type); // Whether the current text object is new or aleady created + console.log(obj.text); // Contents of the current text object + console.log(obj.styles); // Styles of the current text object + console.log(obj.originPosition); // Mouse position on the canvas + console.log(obj.clientPosition); // Mouse position on browser - set the text palette's position +}); +``` +```js +imageEditor.on('adjustObject', function(obj) { + console.log(obj.type); // Whether the selected object's type is "text" or others - control the the text palette's view state +}); +``` +![image](https://cloud.githubusercontent.com/assets/18183560/16838164/cd200920-4a02-11e6-9c5a-304d1a07d82a.png) + + +### Icon +- Insert the basic icon on the canvas. (type: _arrow_, _cancel_ icon) +- Register the custom icon. +- Change color of the icon. + +>**How to draw SVG path** +- [Link](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths) + +>**How to get SVG path value when registering the custom icon** +- [Link](https://css-tricks.com/using-svg/) + +![image](https://cloud.githubusercontent.com/assets/18183560/16838300/726f8a68-4a03-11e6-8703-6d0e36a7f3e3.png) + + +### Mask Filter +- Load the image for using mask filter. (This image is called the "mask image") +- When applying mask filter on the canvas image, the canvas image's areas matching the mask image's black areas should be transparent. + +![image](https://cloud.githubusercontent.com/assets/18183560/16837578/07444c46-49ff-11e6-99fc-2355a6777dc0.gif) + + +### Line Drawing +- Draw the straight line on the canvas. +- Change the color and width value of brush to draw line. + +![image](https://cloud.githubusercontent.com/assets/18183560/16837621/4beed348-49ff-11e6-8276-8e0f7e9e85e6.gif) + +### Shourtcut +- On the canvas + * `ctrl + z` : undo + * `ctrl + y` : redo +- Crop + * `shift` : making the cropzone of 1:1 ratio + +![image](https://cloud.githubusercontent.com/assets/18183560/16837645/73e7614e-49ff-11e6-9460-e596dd683724.gif) + + +## More +- Get started (Tutorial) : [https://github.com/nhnent/tui.component.image-editor/wiki/Tutorial](https://github.com/nhnent/tui.component.image-editor/wiki/Tutorial) +- API : [http://nhnent.github.io/tui.image-editor/latest/](http://nhnent.github.io/tui.image-editor/latest/) +- Sample : [http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-basic.html](http://nhnent.github.io/tui.image-editor/latest/tutorial-example01-basic.html) + diff --git a/docs/Structure.md b/docs/Structure.md new file mode 100644 index 000000000..7179aec8a --- /dev/null +++ b/docs/Structure.md @@ -0,0 +1,350 @@ + +# Introducation +- The image editor includes an implementation that includes the UI via the includeUI option. +However, you can express the implementation more freely without using the basic UI. + +# Modules +Internally, it is separated into two major layers. +* `ImageEditor` - The object responsible for the API has the having two layers, and communicates directly with the UI. + * `Middle Layer`Β - It is composed of `Invoker`, `Command`, `CommandFactory` which provides the function of the application independent of the drawing operation. + * `Graphics Layer`Β - Generally speaking, a canvas is composed of `Canvas` which consists of functions provided by ImageEditor. Drawing operation is abstracted and the actual implementation uses _fabric.js_. + * `Component` is a modularized drawing operation of a specific function and belongs to the Graphics Layer. + * Drawing mode is a feature that is essential in the Graphics Layer. + +## ImageEditor +The object responsible for the API, which has the Invoker and Graphics properties. + +## CommandFactory +It is a class to register and create `Command`. +Provide an interface to register a command and create an instance of the registered command where you need to draw with ImageEditor or Command. + +* register - Command registration +* create - Create Instance of Registered Command + +## Command +Command is a unit of execution for performing specific functions and is independent of other modules. In the image editor, it is used as an execution unit for Undo / Redo, and the Command instance is managed as a stack in the Invoker.
+Command registration receives objects actions and args that define `name`,` execute`, and `undo`. + +### Command Class +```js +class Command { + constructor(actions, args) { + this.name = actions.name; + this.args = args; + this.execute = actions.execute; + this.undo = actions.undo; + this.executeCallback = actions.executeCallback || null; + this.undoCallback = actions.undoCallback || null; + this.undoData = {}; + } +} +``` + +#### Command registration process. +CommandFactory.register is executed when import. The Command class gets the Component to use and passes the Graphics instance to perform the function. + + + ``` js +// commandName.js +import commandFactory from '../factory/command'; +const command = { + name: 'commandName', + execute(graphics, ...args) { + + }, + undo(graphics, ...args) { + + } +}; + +CommandFactory.register(command); +module.export = command; +``` + +#### Command creation and execution. +```js +const command = commandFactory.create('commandName', param1, param2); +command.execute(...command.args).then(value => { + // push undo stack + return value; +}).catch(message => { + // do something + return Promise.reject(message); +}) +``` + +## Invoker +Execute `Command` and manage Undo / Redo. + +* `Component` list management is removed here. Escalate to the Canvas to be specified below. + + +## Canvas +- As the drawing core module of ImageEditor, we have all of the drawing functions we provide. +- The underlying graphics module uses fabric.js. +- Below is a list of functions provided by the image editor. + +| **Icon** | **Image** | **Shape** | **Text** | **Flip** | **FreeDrawing** | **LineDrawing** | **Rotate** | Crop | **기타** | +| ---- | ----- | ----- | ---- | ---- | ----------- | ----------- | ------ | ---- | --- | +| addIcon | addImageObject | addShape | addText | flipX | setBrush | setBrush | rotate | crop | resizeCanvasDimension | +| registerIcons | loadImageFromFile | setDrawingShape | changeText | flipY | | | setAngle | getCropzoneRect | toDataURL | +| changeIconColor | loadImageFromURL | changeShape | changeTextStyle | resetFlip | | | | | getDrawingMode | +| | ApplyFilter | | | | | | | | setDrawingMode | +| | RemoveFilter | | | | | | | | getImageName | +| | hasFilter | | | | | | | | clearObjects | +| | | | | | | | | | removeActiveObject | +| | | | | | | | | | destroy | +| | | | | | | | | | setDefaultPathStyle | + + +## Component + +- `Component` is a module that implements a specific drawing operation. +- `Component` is a subset of the drawing set, so you can use it through` Canvas`. +- `Command` uses `Canvas` to manage various Components. +- The event that should be externally transmitted from the Component is passed through the Canvas. The Canvas passes the event back if it is registered outside Canvas. +- The component list is shown below, and the components that need to change modes such as start / end are displayed. + +| Name | Need mode | Usage | +| --- | ----- | --- | +| Cropper | O | Crop module, event handling for Crop. | +| filter | X | Image filter module | +| flip | X | Image flip Module. | +| freeDrawing | O | free drawing module | +| icon | X | Add Icon Module | +| imageLoader | X | Main image loading module | +| line | O | Straight line drawing module | +| rotation | X | Main image and objects rotation module | +| shape | O | Shape drawing module | +| text | O | Text object input module. | + +# The drawing mode is mutually exclusive, and Command operation is the user's part. +- Only one drawing mode should be activated at a time, because the events and UI used for each mode are different. Therefore, the drawing mode is mutually exclusive. +- On the other hand, `Command` is a command that defines the drawing operation, so it can be considered to be able to operate regardless of the drawing mode. +- Command` is is no dependency on the drawing mode, and the operation is delegated to the user. +- The image editor expects you to make the following general API calls: + + + +``` +editor.setDrawingMode("cropper"); +editor.crop(editor.getCropzoneRect()); +editor.setDrawingMode("normal"); +``` + + + +``` +editor.setDrawingMode("cropper"); +editor.rotate(90); +editor.setDrawingMode("normal"); +``` + +# Event handling +- Canvas Layer For modularity, all events that occur inside the canvas are passed through the Canvas. +- Events that occur on a Component managed by a Canvas are passed to the Canvas, and Canvas to the outside. +- All callbacks that need to be passed to the UI are passed through the ImageEditor. For example, events that need to be imported from Canvas and Component are registered with ImageEditor and registered with Canvas. +- If an event from the Canvas needs to be managed by an undo stack, the ImageEditor receives an event from the Canvas and calls the Invoker function. + +# UI delivery events. +- Most events that are passed to the UI are replaced by Promise, which conveys the completion of execution. The undo / redo related events pass events to the UI as they are for state value management. + +Name | Purpose +------ | -------- +addText | when mousedown event occurs in 'TEXT' drawing mode +objectActivated | when user selects an object +objectMoved | when user drags an object +objectScaled | when object is being scaled +textEditing | when textbox is being edited +mousedown | just mousedown +undoStackChanged | undo change event +redoStackChanged | redo change event + +### Example +```js +/* +{ + id: number + type: type + left: number, + top: number, + width: number, + fill: string + stroke: string + strokeWidth: number + opacity: number, + + // text object + text: string, + fontFamily: string, + fontSize: number, + fontStyle: string, + fontWeight: string, + textAlign: string, + textDecoration: string +} +*/ +imageEditor.on('objectActivated', function(props) { + console.log(props); + console.log(props.type); + console.log(props.id); +}); +``` + + +# Class Diagram + +``` uml +class ServiceUI { + -ImageEditor _imageEditor +} + +class ImageEditor { + -Invoker _invoker + -Graphics _graphics + +Β Β -void execute(commandName) +} + +package "Middle Layer" #DDDDDD { + class Invoker + class Command + class CommandFactory +} + +together { + class Invoker + class Command + class CommandFactory +} + +package "Graphics Layer" #DDDDDD { + class Graphics + +} + +package "Component" { + class Component + class Cropper + class Filter + class Flip + class FreeDrawing + class Icon + class ImageLoader + class Line + class Rotation + class Shape + class Text +} + +package "DrawingMode" { + class DrawingMode + class CropperDrawingMode + class FreeDrawingMode + class LineDrawingMode + class ShapeDrawingMode + class TextDrawingMode +} + +class Invoker { + -Array _undoStack + -Array _redoStack + + +Promise execute("commandName") + +Promise undo() + +Promise redo() + +void pushUndoStack(command, isSilent) + +void pushRedoStack(command, isSilent) +} + +class CommandFactory { + -Map _commands + + +void register(commandObject) + +Command create("commandName", ...args) +} + +class Command { + +string name +Β Β +Array args + +Object _undoData + + +Promise execute() + +Promise undo() + +Command setExecuteCallback(callback) + +Command setUndoCallback(callback) +} + +class Graphics { + -DrawingMode[] _drawingModeInstances + -Component[] _components + -Fabric.Canvas _canvas + + +setDrawingMode("modeName") + +setDefaultPathStyle(style) + +on("eventName", callback) +} + +class Component { + +} + +class DrawingMode { + +} + +ServiceUI o-- ImageEditor +ImageEditor o-- Graphics +ImageEditor o-- Invoker +ImageEditor <|-- tui.util.CustomEvents +ImageEditor --> CommandFactory +ImageEditor --> Command + +Invoker <|-- tui.util.CustomEvents +Invoker --> CommandFactory +Invoker o-- Command + +Command o-- Graphics +Command o-- Component + +Graphics o-- DrawingMode +Graphics o-- Component +Graphics o-- Fabric.Canvas +Graphics <|-- tui.util.CustomEvents + +Component <|-- Cropper +Component <|-- Filter +Component <|-- Flip +Component <|-- FreeDrawing +Component <|-- Icon +Component <|-- ImageLoader +Component <|-- Line +Component <|-- Rotation +Component <|-- Shape +Component <|-- Text + +Cropper <-- Fabric.Canvas +Filter <-- Fabric.Canvas +Flip <-- Fabric.Canvas +FreeDrawing <-- Fabric.Canvas +Icon <-- Fabric.Canvas +ImageLoader <-- Fabric.Canvas +Line <-- Fabric.Canvas +Rotation <-- Fabric.Canvas +Shape <-- Fabric.Canvas +Text <-- Fabric.Canvas + +DrawingMode --> Component +DrawingMode <|-- CropperDrawingMode +DrawingMode <|-- FreeDrawingMode +DrawingMode <|-- LineDrawingMode +DrawingMode <|-- ShapeDrawingMode +DrawingMode <|-- TextDrawingMode + +CropperDrawingMode <-- Cropper +FreeDrawingMode <-- FreeDrawing +LineDrawingMode <-- Line +ShapeDrawingMode <-- Shape +TextDrawingMode <-- Text +``` + diff --git a/examples/example01-includeUi.html b/examples/example01-includeUi.html new file mode 100644 index 000000000..35f816c22 --- /dev/null +++ b/examples/example01-includeUi.html @@ -0,0 +1,49 @@ + + + + + 0. Design + + + + + + +
+ + + + + + + + + + + diff --git a/examples/example01-basic.html b/examples/example02-useApiDirect.html similarity index 100% rename from examples/example01-basic.html rename to examples/example02-useApiDirect.html diff --git a/examples/example02-mobile.html b/examples/example03-mobile.html similarity index 100% rename from examples/example02-mobile.html rename to examples/example03-mobile.html diff --git a/examples/examples.json b/examples/examples.json index 834f75ff2..e36a72f9c 100644 --- a/examples/examples.json +++ b/examples/examples.json @@ -1,8 +1,11 @@ { - "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" } } diff --git a/examples/img/bg.png b/examples/img/bg.png new file mode 100644 index 000000000..55e234d40 Binary files /dev/null and b/examples/img/bg.png differ diff --git a/examples/img/sampleImage2.png b/examples/img/sampleImage2.png new file mode 100644 index 000000000..331e68228 Binary files /dev/null and b/examples/img/sampleImage2.png differ diff --git a/examples/js/service-basic.js b/examples/js/service-basic.js index 455e2a72a..771de8ee2 100644 --- a/examples/js/service-basic.js +++ b/examples/js/service-basic.js @@ -671,7 +671,7 @@ function onClickIconSubMenu(event) { left: originPointer.x, top: originPointer.y }).then(objectProps => { - console.log(objectProps); + // console.log(objectProps); }); }); } diff --git a/examples/js/theme/black-theme.js b/examples/js/theme/black-theme.js new file mode 100644 index 000000000..742e559dc --- /dev/null +++ b/examples/js/theme/black-theme.js @@ -0,0 +1,73 @@ +var blackTheme = { + 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png', + 'common.bisize.width': '251px', + 'common.bisize.height': '21px', + 'common.backgroundImage': 'none', + 'common.backgroundColor': '#1e1e1e', + 'common.border': '0px', + + // header + 'header.backgroundImage': 'none', + 'header.backgroundColor': 'transparent', + 'header.border': '0px', + + // load button + 'loadButton.backgroundColor': '#fff', + 'loadButton.border': '1px solid #ddd', + 'loadButton.color': '#222', + 'loadButton.fontFamily': 'NotoSans, sans-serif', + 'loadButton.fontSize': '12px', + + // download button + 'downloadButton.backgroundColor': '#fdba3b', + 'downloadButton.border': '1px solid #fdba3b', + 'downloadButton.color': '#fff', + 'downloadButton.fontFamily': 'NotoSans, sans-serif', + 'downloadButton.fontSize': '12px', + + // main icons + 'menu.normalIcon.path': '../dist/svg/icon-b.svg', + 'menu.normalIcon.name': 'icon-b', + 'menu.activeIcon.path': '../dist/svg/icon-a.svg', + 'menu.activeIcon.name': 'icon-a', + 'menu.iconSize.width': '24px', + 'menu.iconSize.height': '24px', + + // submenu primary color + 'submenu.backgroundColor': '#1e1e1e', + 'submenu.partition.color': '#858585', + + // submenu icons + 'submenu.normalIcon.path': '../dist/svg/icon-a.svg', + 'submenu.normalIcon.name': 'icon-a', + 'submenu.activeIcon.path': '../dist/svg/icon-c.svg', + 'submenu.activeIcon.name': 'icon-c', + 'submenu.iconSize.width': '32px', + 'submenu.iconSize.height': '32px', + + // submenu labels + 'submenu.normalLabel.color': '#858585', + 'submenu.normalLabel.fontWeight': 'lighter', + 'submenu.activeLabel.color': '#fff', + 'submenu.activeLabel.fontWeight': 'lighter', + + // checkbox style + 'checkbox.border': '1px solid #ccc', + 'checkbox.backgroundColor': '#fff', + + // rango style + 'range.pointer.color': '#fff', + 'range.bar.color': '#666', + 'range.subbar.color': '#d1d1d1', + 'range.value.color': '#fff', + 'range.value.fontWeight': 'lighter', + 'range.value.fontSize': '11px', + 'range.value.border': '1px solid #353535', + 'range.value.backgroundColor': '#151515', + 'range.title.color': '#fff', + 'range.title.fontWeight': 'lighter', + + // colorpicker style + 'colorpicker.button.border': '1px solid #1e1e1e', + 'colorpicker.title.color': '#fff' +}; diff --git a/examples/js/theme/white-theme.js b/examples/js/theme/white-theme.js new file mode 100644 index 000000000..d1db0a84f --- /dev/null +++ b/examples/js/theme/white-theme.js @@ -0,0 +1,73 @@ +var whiteTheme = { + 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png', + 'common.bisize.width': '251px', + 'common.bisize.height': '21px', + 'common.backgroundImage': './img/bg.png', + 'common.backgroundColor': '#fff', + 'common.border': '1px solid #c1c1c1', + + // header + 'header.backgroundImage': 'none', + 'header.backgroundColor': 'transparent', + 'header.border': '0px', + + // load button + 'loadButton.backgroundColor': '#fff', + 'loadButton.border': '1px solid #ddd', + 'loadButton.color': '#222', + 'loadButton.fontFamily': 'NotoSans, sans-serif', + 'loadButton.fontSize': '12px', + + // download button + 'downloadButton.backgroundColor': '#fdba3b', + 'downloadButton.border': '1px solid #fdba3b', + 'downloadButton.color': '#fff', + 'downloadButton.fontFamily': 'NotoSans, sans-serif', + 'downloadButton.fontSize': '12px', + + // main icons + 'menu.normalIcon.path': '../dist/svg/icon-b.svg', + 'menu.normalIcon.name': 'icon-b', + 'menu.activeIcon.path': '../dist/svg/icon-a.svg', + 'menu.activeIcon.name': 'icon-a', + 'menu.iconSize.width': '24px', + 'menu.iconSize.height': '24px', + + // submenu primary color + 'submenu.backgroundColor': 'transparent', + 'submenu.partition.color': '#858585', + + // submenu icons + 'submenu.normalIcon.path': '../dist/svg/icon-a.svg', + 'submenu.normalIcon.name': 'icon-a', + 'submenu.activeIcon.path': '../dist/svg/icon-d.svg', + 'submenu.activeIcon.name': 'icon-d', + 'submenu.iconSize.width': '32px', + 'submenu.iconSize.height': '32px', + + // submenu labels + 'submenu.normalLabel.color': '#858585', + 'submenu.normalLabel.fontWeight': 'normal', + 'submenu.activeLabel.color': '#000', + 'submenu.activeLabel.fontWeight': 'normal', + + // checkbox style + 'checkbox.border': '1px solid #ccc', + 'checkbox.backgroundColor': '#fff', + + // rango style + 'range.pointer.color': '#333', + 'range.bar.color': '#ccc', + 'range.subbar.color': '#606060', + 'range.value.color': '#000', + 'range.value.fontWeight': 'normal', + 'range.value.fontSize': '11px', + 'range.value.border': '0', + 'range.value.backgroundColor': '#f5f5f5', + 'range.title.color': '#000', + 'range.title.fontWeight': 'lighter', + + // colorpicker style + 'colorpicker.button.border': '1px solid #cbcbcb', + 'colorpicker.title.color': '#000' +}; diff --git a/jsdoc.conf.json b/jsdoc.conf.json index 66495dc15..ae1d10724 100644 --- a/jsdoc.conf.json +++ b/jsdoc.conf.json @@ -14,7 +14,7 @@ "templates": { "name": "ImageEditor", "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", "height": "13px", "link": "https://github.com/nhnent/tui.jsdoc-template" diff --git a/karma.conf.js b/karma.conf.js index 86a85542a..bb0294b8b 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -143,6 +143,10 @@ module.exports = function(config) { test: /\.js$/, exclude: /(node_modules|bower_components)/, loader: 'babel' + }, + { + test: /\.styl$/, + loader: 'css-loader!stylus-loader?paths=src/css/' } ] } diff --git a/makesvg.js b/makesvg.js new file mode 100644 index 000000000..0560f0466 --- /dev/null +++ b/makesvg.js @@ -0,0 +1,25 @@ +const fs = require('fs'); +const svgstore = require('svgstore'); +const svgDir = './src/svg'; + +function getFileList(dir) { + const targetDir = `${svgDir}/${dir}`; + const sprites = svgstore(); + fs.readdir(targetDir, (err, files) => { + if (!files) return; + files.forEach(file => { + if (file.match(/^\./)) return; + const id = `${dir}-${file.replace(/\.svg$/, '')}`; + const svg = fs.readFileSync(`${targetDir}/${file}`); + sprites.add(id, svg); + }); + fs.writeFileSync(`./dist/svg/${dir}.svg`, sprites); + }); +} + +fs.readdir('./src/svg', (err, dirs) => { + dirs.forEach(dir => { + getFileList(dir); + }); +}) + diff --git a/package-lock.json b/package-lock.json index 78c02998b..40af19352 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,14 +16,14 @@ "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", "dev": true, "requires": { - "mime-types": "2.1.17", + "mime-types": "2.1.18", "negotiator": "0.6.1" } }, "acorn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", - "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", "dev": true }, "acorn-jsx": { @@ -56,7 +56,7 @@ "dev": true, "requires": { "co": "4.6.0", - "fast-deep-equal": "1.0.0", + "fast-deep-equal": "1.1.0", "fast-json-stable-stringify": "2.0.0", "json-schema-traverse": "0.3.1" } @@ -78,6 +78,12 @@ "repeat-string": "1.6.1" } }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -85,9 +91,9 @@ "dev": true }, "ansi-escapes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", - "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, "ansi-regex": { @@ -164,7 +170,7 @@ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "readable-stream": { @@ -188,9 +194,9 @@ } }, "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "1.0.3" @@ -295,6 +301,20 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000846", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, "aws-sign2": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", @@ -313,13 +333,13 @@ } }, "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", "dev": true, "requires": { "babel-code-frame": "6.26.0", - "babel-generator": "6.26.0", + "babel-generator": "6.26.1", "babel-helpers": "6.24.1", "babel-messages": "6.23.0", "babel-register": "6.26.0", @@ -331,7 +351,7 @@ "convert-source-map": "1.5.1", "debug": "2.6.9", "json5": "0.5.1", - "lodash": "4.17.4", + "lodash": "4.17.10", "minimatch": "3.0.4", "path-is-absolute": "1.0.1", "private": "0.1.8", @@ -352,9 +372,9 @@ } }, "babel-generator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { "babel-messages": "6.23.0", @@ -362,7 +382,7 @@ "babel-types": "6.26.0", "detect-indent": "4.0.0", "jsesc": "1.3.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "source-map": "0.5.7", "trim-right": "1.0.1" } @@ -388,7 +408,7 @@ "babel-helper-function-name": "6.24.1", "babel-runtime": "6.26.0", "babel-types": "6.26.0", - "lodash": "4.17.4" + "lodash": "4.17.10" } }, "babel-helper-function-name": { @@ -442,7 +462,7 @@ "requires": { "babel-runtime": "6.26.0", "babel-types": "6.26.0", - "lodash": "4.17.4" + "lodash": "4.17.10" } }, "babel-helper-replace-supers": { @@ -527,7 +547,7 @@ "babel-template": "6.26.0", "babel-traverse": "6.26.0", "babel-types": "6.26.0", - "lodash": "4.17.4" + "lodash": "4.17.10" } }, "babel-plugin-transform-es2015-classes": { @@ -611,15 +631,15 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", "babel-runtime": "6.26.0", "babel-template": "6.26.0" } }, "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", "dev": true, "requires": { "babel-plugin-transform-strict-mode": "6.24.1", @@ -770,7 +790,7 @@ "babel-plugin-transform-es2015-function-name": "6.24.1", "babel-plugin-transform-es2015-literals": "6.22.0", "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", "babel-plugin-transform-es2015-modules-umd": "6.24.1", "babel-plugin-transform-es2015-object-super": "6.24.1", @@ -790,19 +810,19 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "6.26.0", + "babel-core": "6.26.3", "babel-runtime": "6.26.0", - "core-js": "2.5.3", + "core-js": "2.5.6", "home-or-tmp": "2.0.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "mkdirp": "0.5.1", "source-map-support": "0.4.18" }, "dependencies": { "core-js": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", - "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.6.tgz", + "integrity": "sha512-lQUVfQi0aLix2xpyjrrJEvfuYCqPc/HwmTKsC/VNf8q0zsjX7SQZtp4+oRONN5Tsur9GDETPjj+Ub2iDiGZfSQ==", "dev": true } } @@ -827,7 +847,7 @@ "babel-traverse": "6.26.0", "babel-types": "6.26.0", "babylon": "6.18.0", - "lodash": "4.17.4" + "lodash": "4.17.10" } }, "babel-traverse": { @@ -843,8 +863,8 @@ "babylon": "6.18.0", "debug": "2.6.9", "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" + "invariant": "2.2.4", + "lodash": "4.17.10" } }, "babel-types": { @@ -855,7 +875,7 @@ "requires": { "babel-runtime": "6.26.0", "esutils": "2.0.2", - "lodash": "4.17.4", + "lodash": "4.17.10", "to-fast-properties": "1.0.3" } }, @@ -884,9 +904,9 @@ "dev": true }, "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, "base64id": { @@ -970,21 +990,21 @@ "dev": true }, "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", "dev": true, "requires": { "bytes": "3.0.0", "content-type": "1.0.4", "debug": "2.6.9", - "depd": "1.1.1", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", + "depd": "1.1.2", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.15" + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "1.6.16" } }, "boolbase": { @@ -1003,9 +1023,9 @@ } }, "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "1.0.0", @@ -1041,14 +1061,24 @@ "pako": "0.2.9" } }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "1.0.30000846", + "electron-to-chromium": "1.3.48" + } + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", + "base64-js": "1.3.0", + "ieee754": "1.1.11", "isarray": "1.0.0" } }, @@ -1058,6 +1088,12 @@ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", "dev": true }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -1113,6 +1149,24 @@ "map-obj": "1.0.1" } }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-db": "1.0.30000846", + "lodash.memoize": "4.1.2", + "lodash.uniq": "4.5.0" + } + }, + "caniuse-db": { + "version": "1.0.30000846", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000846.tgz", + "integrity": "sha1-2chvkUc4202gmO7e2ZdBPERWG9I=", + "dev": true + }, "caseless": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz", @@ -1174,7 +1228,7 @@ "lodash.flatten": "4.4.0", "lodash.foreach": "4.5.0", "lodash.map": "4.6.0", - "lodash.merge": "4.6.0", + "lodash.merge": "4.6.1", "lodash.pick": "4.4.0", "lodash.reduce": "4.6.0", "lodash.reject": "4.6.0", @@ -1189,7 +1243,7 @@ "requires": { "anymatch": "1.3.2", "async-each": "1.0.1", - "fsevents": "1.1.3", + "fsevents": "1.2.4", "glob-parent": "2.0.0", "inherits": "2.0.3", "is-binary-path": "1.0.1", @@ -1204,6 +1258,15 @@ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "dev": true, + "requires": { + "chalk": "1.1.3" + } + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -1239,9 +1302,9 @@ } }, "clone": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, "co": { @@ -1250,6 +1313,26 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "requires": { + "q": "1.4.1" + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "requires": { + "clone": "1.0.4", + "color-convert": "1.9.1", + "color-string": "0.3.0" + } + }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", @@ -1265,10 +1348,30 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colors": { + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "colormin": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "requires": { + "color": "0.11.4", + "css-color-names": "0.0.4", + "has": "1.0.1" + } + }, + "colors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.0.tgz", + "integrity": "sha512-EDpX3a7wHMWFA7PUHWPHNWqOxIIRSJetuwl0AS5Oi/5FMV8kWm69RTlgm00GKjBO1xFHMtBbL49yRtMMdticBw==", "dev": true }, "combine-lists": { @@ -1277,7 +1380,7 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "4.17.4" + "lodash": "4.17.10" } }, "combined-stream": { @@ -1358,23 +1461,23 @@ } }, "compressible": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz", - "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", + "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", "dev": true, "requires": { - "mime-db": "1.30.0" + "mime-db": "1.33.0" } }, "compression": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz", - "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=", + "version": "1.7.2", + "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", + "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", "dev": true, "requires": { - "accepts": "1.3.4", + "accepts": "1.3.5", "bytes": "3.0.0", - "compressible": "2.0.12", + "compressible": "2.0.13", "debug": "2.6.9", "on-headers": "1.0.1", "safe-buffer": "5.1.1", @@ -1382,14 +1485,20 @@ }, "dependencies": { "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.17", + "mime-types": "2.1.18", "negotiator": "0.6.1" } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true } } }, @@ -1400,24 +1509,25 @@ "dev": true }, "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { + "buffer-from": "1.0.0", "inherits": "2.0.3", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "typedarray": "0.0.6" } }, "connect": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", - "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", "dev": true, "requires": { "debug": "2.6.9", - "finalhandler": "1.0.6", + "finalhandler": "1.1.0", "parseurl": "1.3.2", "utils-merge": "1.0.1" } @@ -1526,7 +1636,7 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.1", + "lru-cache": "4.1.3", "shebang-command": "1.2.0", "which": "1.3.0" } @@ -1552,6 +1662,59 @@ "sha.js": "2.2.6" } }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-loader": { + "version": "0.28.11", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "css-selector-tokenizer": "0.7.0", + "cssnano": "3.10.0", + "icss-utils": "2.1.0", + "loader-utils": "1.1.0", + "lodash.camelcase": "4.3.0", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-modules-extract-imports": "1.2.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0", + "postcss-value-parser": "3.3.0", + "source-list-map": "2.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + }, + "source-list-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", + "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "dev": true + } + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", @@ -1564,12 +1727,92 @@ "nth-check": "1.0.1" } }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "dev": true, + "requires": { + "cssesc": "0.1.0", + "fastparse": "1.1.1", + "regexpu-core": "1.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "1.4.0", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" + } + } + } + }, "css-what": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", "dev": true }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "requires": { + "autoprefixer": "6.7.7", + "decamelize": "1.2.0", + "defined": "1.0.0", + "has": "1.0.1", + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-calc": "5.3.1", + "postcss-colormin": "2.2.2", + "postcss-convert-values": "2.6.1", + "postcss-discard-comments": "2.0.4", + "postcss-discard-duplicates": "2.1.0", + "postcss-discard-empty": "2.1.0", + "postcss-discard-overridden": "0.1.1", + "postcss-discard-unused": "2.2.3", + "postcss-filter-plugins": "2.0.3", + "postcss-merge-idents": "2.1.7", + "postcss-merge-longhand": "2.0.2", + "postcss-merge-rules": "2.1.2", + "postcss-minify-font-values": "1.0.5", + "postcss-minify-gradients": "1.0.5", + "postcss-minify-params": "1.2.2", + "postcss-minify-selectors": "2.1.1", + "postcss-normalize-charset": "1.1.1", + "postcss-normalize-url": "3.0.8", + "postcss-ordered-values": "2.2.3", + "postcss-reduce-idents": "2.4.0", + "postcss-reduce-initial": "1.0.1", + "postcss-reduce-transforms": "1.0.4", + "postcss-svgo": "2.1.6", + "postcss-unique-selectors": "2.0.2", + "postcss-value-parser": "3.3.0", + "postcss-zindex": "2.2.0" + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "requires": { + "clap": "1.2.3", + "source-map": "0.5.7" + } + }, "ctype": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", @@ -1628,6 +1871,12 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, "del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -1636,7 +1885,7 @@ "requires": { "globby": "5.0.0", "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", + "is-path-in-cwd": "1.0.1", "object-assign": "4.1.1", "pify": "2.3.0", "pinkie-promise": "2.0.1", @@ -1650,9 +1899,9 @@ "dev": true }, "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, "destroy": { @@ -1716,9 +1965,9 @@ } }, "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, "domelementtype": { @@ -1728,9 +1977,9 @@ "dev": true }, "domhandler": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", - "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dev": true, "requires": { "domelementtype": "1.3.0" @@ -1752,6 +2001,12 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "electron-to-chromium": { + "version": "1.3.48", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", + "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", + "dev": true + }, "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", @@ -1759,9 +2014,9 @@ "dev": true }, "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, "end-of-stream": { @@ -1893,9 +2148,9 @@ "dev": true }, "errno": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.6.tgz", - "integrity": "sha512-IsORQDpaaSwcDP4ZZnHxgE85werpo34VYn1Ud3mq+eUsF593faR8oCZNXrROVkpFu2TsbrNhHin0aUrTsQ9vNw==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { "prr": "1.0.1" @@ -1966,35 +2221,35 @@ } }, "eslint": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.15.0.tgz", - "integrity": "sha512-zEO/Z1ZUxIQ+MhDVKkVTUYpIPDTEJLXGMrkID+5v1NeQHtCz6FZikWuFRgxE1Q/RV2V4zVl1u3xmpPADHhMZ6A==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { "ajv": "5.5.2", "babel-code-frame": "6.26.0", - "chalk": "2.3.0", - "concat-stream": "1.6.0", + "chalk": "2.4.1", + "concat-stream": "1.6.2", "cross-spawn": "5.1.0", "debug": "3.1.0", "doctrine": "2.1.0", "eslint-scope": "3.7.1", "eslint-visitor-keys": "1.0.0", - "espree": "3.5.2", - "esquery": "1.0.0", + "espree": "3.5.4", + "esquery": "1.0.1", "esutils": "2.0.2", "file-entry-cache": "2.0.0", "functional-red-black-tree": "1.0.1", "glob": "7.1.2", - "globals": "11.1.0", - "ignore": "3.3.7", + "globals": "11.5.0", + "ignore": "3.3.8", "imurmurhash": "0.1.4", "inquirer": "3.3.0", - "is-resolvable": "1.0.1", - "js-yaml": "3.10.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.11.0", "json-stable-stringify-without-jsonify": "1.0.1", "levn": "0.3.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "minimatch": "3.0.4", "mkdirp": "0.5.1", "natural-compare": "1.4.0", @@ -2002,8 +2257,9 @@ "path-is-inside": "1.0.2", "pluralize": "7.0.0", "progress": "2.0.0", + "regexpp": "1.1.0", "require-uncached": "1.0.3", - "semver": "5.4.1", + "semver": "5.5.0", "strip-ansi": "4.0.0", "strip-json-comments": "2.0.1", "table": "4.0.2", @@ -2017,23 +2273,23 @@ "dev": true }, "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "1.9.1" } }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "supports-color": "5.4.0" } }, "debug": { @@ -2046,9 +2302,9 @@ } }, "globals": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.1.0.tgz", - "integrity": "sha512-uEuWt9mqTlPDwSqi+sHjD4nWU/1N+q0fiWI9T1mZpD2UENqX20CFD5T/ziLZvztPaBKl7ZylUi1q6Qfm7E2CiQ==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz", + "integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==", "dev": true }, "strip-ansi": { @@ -2061,20 +2317,20 @@ } }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "3.0.0" } } } }, "eslint-config-tui": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/eslint-config-tui/-/eslint-config-tui-1.0.2.tgz", - "integrity": "sha512-4x8Ic5b4L3isL684m+oZjT+l3pb1YetdIfykILggP+R35+vxNAWHzxriT/91RMMhuIY8Ubq6tkTjtio58CT/8A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-tui/-/eslint-config-tui-1.0.3.tgz", + "integrity": "sha512-81DMKKSVccvkG9YobCBBCIJT+ewYNINo2mIdfeYMvtA/P3g0XEfEFlDix5xN3HP57rVM30Z/Wnzt7q2Z7M4xQw==", "dev": true }, "eslint-loader": { @@ -2086,7 +2342,7 @@ "loader-fs-cache": "1.0.1", "loader-utils": "1.1.0", "object-assign": "4.1.1", - "object-hash": "1.2.0", + "object-hash": "1.3.0", "rimraf": "2.6.2" }, "dependencies": { @@ -2109,7 +2365,7 @@ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "esrecurse": "4.2.0", + "esrecurse": "4.2.1", "estraverse": "4.2.0" } }, @@ -2120,12 +2376,12 @@ "dev": true }, "espree": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", - "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { - "acorn": "5.3.0", + "acorn": "5.5.3", "acorn-jsx": "3.0.1" } }, @@ -2136,22 +2392,21 @@ "dev": true }, "esquery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", - "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { "estraverse": "4.2.0" } }, "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" + "estraverse": "4.2.0" } }, "estraverse": { @@ -2173,9 +2428,9 @@ "dev": true }, "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", "dev": true }, "events": { @@ -2190,7 +2445,7 @@ "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", "dev": true, "requires": { - "original": "1.0.0" + "original": "1.0.1" } }, "expand-braces": { @@ -2252,16 +2507,16 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.3" + "fill-range": "2.2.4" } }, "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "dev": true, "requires": { - "accepts": "1.3.4", + "accepts": "1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", @@ -2269,65 +2524,133 @@ "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.1", - "encodeurl": "1.0.1", + "depd": "1.1.2", + "encodeurl": "1.0.2", "escape-html": "1.0.3", "etag": "1.8.1", - "finalhandler": "1.1.0", + "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", "methods": "1.1.2", "on-finished": "2.3.0", "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", + "proxy-addr": "2.0.3", "qs": "6.5.1", "range-parser": "1.2.0", "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", + "send": "0.16.2", + "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", + "statuses": "1.4.0", + "type-is": "1.6.16", "utils-merge": "1.0.1", "vary": "1.1.2" }, "dependencies": { "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.17", + "mime-types": "2.1.18", "negotiator": "0.6.1" } }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.2", + "http-errors": "1.6.3", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.16" + } + }, "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.1", + "encodeurl": "1.0.2", "escape-html": "1.0.3", "on-finished": "2.3.0", "parseurl": "1.3.2", - "statuses": "1.3.1", + "statuses": "1.4.0", "unpipe": "1.0.0" } }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "dev": true + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true, + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.4.0" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", "dev": true } } @@ -2339,13 +2662,13 @@ "dev": true }, "external-editor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", - "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { "chardet": "0.4.2", - "iconv-lite": "0.4.19", + "iconv-lite": "0.4.23", "tmp": "0.0.33" } }, @@ -2358,10 +2681,21 @@ "is-extglob": "1.0.0" } }, + "extract-text-webpack-plugin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-1.0.1.tgz", + "integrity": "sha1-yVvzy6rEnclvHcbgclSfu2VMzSw=", + "dev": true, + "requires": { + "async": "1.5.2", + "loader-utils": "0.2.17", + "webpack-sources": "0.1.5" + } + }, "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, "fast-json-stable-stringify": { @@ -2376,6 +2710,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", + "dev": true + }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -2405,9 +2745,9 @@ } }, "file-saver": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz", - "integrity": "sha1-zdTETTqiZOrC9o7BZbx5HDSvEjI=", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", + "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==", "dev": true }, "filename-regex": { @@ -2417,26 +2757,26 @@ "dev": true }, "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "2.1.0", "isobject": "2.1.0", - "randomatic": "1.1.7", + "randomatic": "3.0.0", "repeat-element": "1.1.2", "repeat-string": "1.6.1" } }, "finalhandler": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", - "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.1", + "encodeurl": "1.0.2", "escape-html": "1.0.3", "on-finished": "2.3.0", "parseurl": "1.3.2", @@ -2485,6 +2825,32 @@ "write": "0.2.1" } }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "dev": true + }, + "follow-redirects": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.0.tgz", + "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==", + "dev": true, + "requires": { + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2568,195 +2934,99 @@ "dev": true }, "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", + "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", "dev": true, "optional": true, "requires": { - "nan": "2.8.0", - "node-pre-gyp": "0.6.39" + "nan": "2.10.0", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { - "version": "1.1.0", - "bundled": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "aproba": { - "version": "1.1.1", - "bundled": true, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, "requires": { "delegates": "1.0.0", - "readable-stream": "2.2.9" + "readable-stream": "2.3.6" } }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } }, - "asynckit": { - "version": "0.4.0", - "bundled": true, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, "debug": { - "version": "2.6.8", - "bundled": true, + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, "requires": { @@ -2764,99 +3034,51 @@ } }, "deep-extend": { - "version": "0.4.2", - "bundled": true, + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, - "form-data": { - "version": "2.1.4", - "bundled": true, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" + "minipass": "2.2.4" } }, "fs.realpath": { "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } + "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { - "aproba": "1.1.1", + "aproba": "1.2.0", "console-control-strings": "1.1.0", "has-unicode": "2.0.1", "object-assign": "4.1.1", @@ -2866,27 +3088,12 @@ "wide-align": "1.1.2" } }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, + "optional": true, "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -2896,64 +3103,39 @@ "path-is-absolute": "1.0.1" } }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, - "hawk": { - "version": "3.1.3", - "bundled": true, + "iconv-lite": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, + "optional": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "safer-buffer": "2.1.2" } }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" + "minimatch": "3.0.4" } }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, + "optional": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -2961,131 +3143,72 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ini": { - "version": "1.3.4", - "bundled": true, + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { "number-is-nan": "1.0.1" } }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, "isarray": { "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } + "brace-expansion": "1.1.11" } }, - "mime-db": { - "version": "1.27.0", - "bundled": true, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, - "mime-types": { - "version": "2.1.15", - "bundled": true, + "minipass": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { - "mime-db": "1.27.0" + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, - "minimatch": { - "version": "3.0.4", - "bundled": true, + "minizlib": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, + "optional": true, "requires": { - "brace-expansion": "1.1.7" + "minipass": "2.2.4" } }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, "mkdirp": { "version": "0.5.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -3093,42 +3216,75 @@ }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, + "needle": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.21", + "sax": "1.2.4" + } + }, "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", + "detect-libc": "1.0.3", "mkdirp": "0.5.1", + "needle": "2.2.0", "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" + "npm-packlist": "1.1.10", + "npmlog": "4.1.2", + "rc": "1.2.7", + "rimraf": "2.6.2", + "semver": "5.5.0", + "tar": "4.4.1" } }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.3" } }, "npmlog": { - "version": "4.1.0", - "bundled": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -3140,24 +3296,21 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "optional": true - }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1.0.2" @@ -3165,19 +3318,22 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { - "version": "0.1.4", - "bundled": true, + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -3187,163 +3343,111 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { - "version": "1.2.1", - "bundled": true, + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", + "deep-extend": "0.5.1", + "ini": "1.3.5", "minimist": "1.2.0", "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } } }, "readable-stream": { - "version": "2.2.9", - "bundled": true, + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, + "optional": true, "requires": { - "buffer-shims": "1.0.0", "core-util-is": "1.0.2", "inherits": "2.0.3", "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, "rimraf": { - "version": "2.6.1", - "bundled": true, + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, + "optional": true, "requires": { "glob": "7.1.2" } }, "safe-buffer": { - "version": "5.0.1", - "bundled": true, + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "optional": true + }, "semver": { - "version": "5.3.0", - "bundled": true, + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { "code-point-at": "1.1.0", @@ -3352,22 +3456,19 @@ } }, "string_decoder": { - "version": "1.0.1", - "bundled": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "optional": true, "requires": { - "safe-buffer": "5.0.1" + "safe-buffer": "5.1.1" } }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { "ansi-regex": "2.1.1" @@ -3375,102 +3476,64 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { - "version": "2.2.1", - "bundled": true, + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, + "optional": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.2.4", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.1", + "yallist": "3.0.2" } }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } + "optional": true }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, "requires": { - "punycode": "1.4.1" + "string-width": "1.0.2" } }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { + "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "dev": true } } }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -3600,6 +3663,15 @@ } } }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -3633,9 +3705,9 @@ "dev": true }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "hawk": { @@ -3667,9 +3739,15 @@ } }, "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", "dev": true }, "htmlparser2": { @@ -3679,38 +3757,39 @@ "dev": true, "requires": { "domelementtype": "1.3.0", - "domhandler": "2.4.1", + "domhandler": "2.4.2", "domutils": "1.5.1", "entities": "1.1.1", "inherits": "2.0.3", - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "1.1.1", + "depd": "1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "setprototypeof": "1.1.0", + "statuses": "1.5.0" } }, "http-parser-js": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", - "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=", + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", + "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", "dev": true }, "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "dev": true, "requires": { - "eventemitter3": "1.2.0", + "eventemitter3": "3.1.0", + "follow-redirects": "1.5.0", "requires-port": "1.0.0" } }, @@ -3720,9 +3799,9 @@ "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", "dev": true, "requires": { - "http-proxy": "1.16.2", + "http-proxy": "1.17.0", "is-glob": "3.1.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "micromatch": "2.3.11" }, "dependencies": { @@ -3761,21 +3840,87 @@ "dev": true }, "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", "dev": true }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "requires": { + "postcss": "6.0.22" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", + "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", "dev": true }, "ignore": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", - "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", + "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", "dev": true }, "imurmurhash": { @@ -3793,6 +3938,12 @@ "repeating": "2.0.1" } }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -3821,13 +3972,13 @@ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "3.0.0", - "chalk": "2.3.0", + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", "cli-cursor": "2.1.0", "cli-width": "2.2.0", - "external-editor": "2.1.0", + "external-editor": "2.2.0", "figures": "2.0.0", - "lodash": "4.17.4", + "lodash": "4.17.10", "mute-stream": "0.0.7", "run-async": "2.3.0", "rx-lite": "4.0.8", @@ -3844,23 +3995,23 @@ "dev": true }, "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "1.9.1" } }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "supports-color": "5.4.0" } }, "strip-ansi": { @@ -3873,12 +4024,12 @@ } }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "3.0.0" } } } @@ -3890,9 +4041,9 @@ "dev": true }, "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { "loose-envify": "1.3.1" @@ -3905,9 +4056,15 @@ "dev": true }, "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", "dev": true }, "is-arrayish": { @@ -4026,9 +4183,9 @@ "dev": true }, "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { "is-path-inside": "1.0.1" @@ -4043,6 +4200,12 @@ "path-is-inside": "1.0.2" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, "is-posix-bracket": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", @@ -4068,11 +4231,20 @@ "dev": true }, "is-resolvable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.1.tgz", - "integrity": "sha512-y5CXYbzvB3jTnWAZH1Nl7ykUWb6T3BcTs56HUruwBf8MhF56n1HWqhDWnVFo8GHrUPDgvUUNVhrc2U8W7iqz5g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "requires": { + "html-comment-regex": "1.1.1" + } + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -4124,7 +4296,7 @@ "esprima": "2.7.3", "glob": "5.0.15", "handlebars": "4.0.11", - "js-yaml": "3.10.0", + "js-yaml": "3.11.0", "mkdirp": "0.5.1", "nopt": "3.0.6", "once": "1.4.0", @@ -4177,36 +4349,36 @@ "dev": true, "requires": { "convert-source-map": "1.5.1", - "istanbul-lib-instrument": "1.9.1", + "istanbul-lib-instrument": "1.10.1", "loader-utils": "0.2.17", "object-assign": "4.1.1" } }, "istanbul-lib-coverage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz", - "integrity": "sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", + "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", "dev": true }, "istanbul-lib-instrument": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz", - "integrity": "sha512-RQmXeQ7sphar7k7O1wTNzVczF9igKpaeGQAG9qR2L+BS4DCJNTI9nytRmIVYevwO0bbq+2CXvJmYDuz0gMrywA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz", + "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "6.26.0", + "babel-generator": "6.26.1", "babel-template": "6.26.0", "babel-traverse": "6.26.0", "babel-types": "6.26.0", "babylon": "6.18.0", - "istanbul-lib-coverage": "1.1.1", - "semver": "5.4.1" + "istanbul-lib-coverage": "1.2.0", + "semver": "5.5.0" } }, "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", "dev": true }, "jasmine-jquery": { @@ -4216,9 +4388,15 @@ "dev": true }, "jquery": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", - "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==", + "dev": true + }, + "js-base64": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz", + "integrity": "sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ==", "dev": true }, "js-tokens": { @@ -4228,12 +4406,12 @@ "dev": true }, "js-yaml": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", - "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", "dev": true, "requires": { - "argparse": "1.0.9", + "argparse": "1.0.10", "esprima": "4.0.0" } }, @@ -4258,7 +4436,7 @@ "escape-string-regexp": "1.0.5", "js2xmlparser": "3.0.0", "klaw": "2.0.0", - "marked": "0.3.12", + "marked": "0.3.19", "mkdirp": "0.5.1", "requizzle": "0.2.1", "strip-json-comments": "2.0.1", @@ -4323,32 +4501,32 @@ "dev": true, "requires": { "bluebird": "3.5.1", - "body-parser": "1.18.2", + "body-parser": "1.18.3", "chokidar": "1.7.0", - "colors": "1.1.2", + "colors": "1.3.0", "combine-lists": "1.0.1", - "connect": "3.6.5", + "connect": "3.6.6", "core-js": "2.4.1", "di": "0.0.1", "dom-serialize": "2.2.1", "expand-braces": "0.1.2", "glob": "7.1.2", "graceful-fs": "4.1.11", - "http-proxy": "1.16.2", + "http-proxy": "1.17.0", "isbinaryfile": "3.0.2", "lodash": "3.10.1", "log4js": "0.6.38", "mime": "1.6.0", "minimatch": "3.0.4", "optimist": "0.6.1", - "qjobs": "1.1.5", + "qjobs": "1.2.0", "range-parser": "1.2.0", "rimraf": "2.6.2", - "safe-buffer": "5.1.1", + "safe-buffer": "5.1.2", "socket.io": "1.7.3", "source-map": "0.5.7", "tmp": "0.0.31", - "useragent": "2.2.1" + "useragent": "2.3.0" }, "dependencies": { "lodash": { @@ -4379,24 +4557,16 @@ } }, "karma-coverage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-1.1.1.tgz", - "integrity": "sha1-Wv+LOc9plNwi3kyENix2ABtjfPY=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-1.1.2.tgz", + "integrity": "sha512-eQawj4Cl3z/CjxslYy9ariU4uDh7cCNFZHNWXWRpl0pNeblY/4wHR7M7boTYXWrn9bY0z2pZmr11eKje/S/hIw==", "dev": true, "requires": { "dateformat": "1.0.12", "istanbul": "0.4.5", - "lodash": "3.10.1", + "lodash": "4.17.10", "minimatch": "3.0.4", "source-map": "0.5.7" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } } }, "karma-es5-shim": { @@ -4408,10 +4578,16 @@ "es5-shim": "4.5.10" } }, + "karma-firefox-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz", + "integrity": "sha512-LbZ5/XlIXLeQ3cqnCbYLn+rOVhuMIK9aZwlP6eOLGzWdo1UVp7t6CN3DP4SafiRLjexKwHeKHDm0c38Mtd3VxA==", + "dev": true + }, "karma-jasmine": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz", - "integrity": "sha1-b+hA51oRYAydkehLM8RY4cRqNSk=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", + "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", "dev": true }, "karma-jquery": { @@ -4588,9 +4764,9 @@ } }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, "lodash.assignin": { @@ -4605,6 +4781,18 @@ "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", "dev": true }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -4635,10 +4823,16 @@ "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", "dev": true }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, "lodash.merge": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.0.tgz", - "integrity": "sha1-aYhLoUSsM/5plzemCG3v+t0PicU=", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==", "dev": true }, "lodash.pick": { @@ -4665,6 +4859,12 @@ "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", "dev": true }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, "log4js": { "version": "0.6.38", "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", @@ -4733,9 +4933,9 @@ } }, "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { "pseudomap": "1.0.2", @@ -4749,9 +4949,21 @@ "dev": true }, "marked": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz", - "integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", + "dev": true + }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", + "dev": true + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", "dev": true }, "media-typer": { @@ -4766,8 +4978,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "0.1.6", - "readable-stream": "2.3.3" + "errno": "0.1.7", + "readable-stream": "2.3.6" } }, "meow": { @@ -4836,24 +5048,24 @@ "dev": true }, "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", "dev": true }, "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "1.30.0" + "mime-db": "1.33.0" } }, "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { @@ -4862,7 +5074,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.8" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -4893,9 +5105,9 @@ "dev": true }, "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", "dev": true, "optional": true }, @@ -4929,7 +5141,7 @@ "console-browserify": "1.1.0", "constants-browserify": "1.0.0", "crypto-browserify": "3.3.0", - "domain-browser": "1.1.7", + "domain-browser": "1.2.0", "events": "1.1.1", "https-browserify": "0.0.1", "os-browserify": "0.2.1", @@ -4937,11 +5149,11 @@ "process": "0.11.10", "punycode": "1.4.1", "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "stream-browserify": "2.0.1", - "stream-http": "2.7.2", + "stream-http": "2.8.2", "string_decoder": "0.10.31", - "timers-browserify": "2.0.4", + "timers-browserify": "2.0.10", "tty-browserify": "0.0.0", "url": "0.11.0", "util": "0.10.3", @@ -4977,10 +5189,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.5.0", + "hosted-git-info": "2.6.0", "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" } }, "normalize-path": { @@ -4992,6 +5204,24 @@ "remove-trailing-separator": "1.1.0" } }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "prepend-http": "1.0.4", + "query-string": "4.3.4", + "sort-keys": "1.1.2" + } + }, "nth-check": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", @@ -5007,6 +5237,12 @@ "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", "dev": true }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -5032,9 +5268,9 @@ "dev": true }, "object-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.2.0.tgz", - "integrity": "sha512-smRWXzkvxw72VquyZ0wggySl7PFUtoDhvhpdwgESXxUrH7vVhhp9asfup1+rVLrhsl7L45Ee1Q/l5R2Ul4MwUg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.0.tgz", + "integrity": "sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ==", "dev": true }, "object.omit": { @@ -5077,7 +5313,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "1.1.0" + "mimic-fn": "1.2.0" } }, "open": { @@ -5125,24 +5361,12 @@ "dev": true }, "original": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", - "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz", + "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==", "dev": true, "requires": { - "url-parse": "1.0.5" - }, - "dependencies": { - "url-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", - "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", - "dev": true, - "requires": { - "querystringify": "0.0.4", - "requires-port": "1.0.0" - } - } + "url-parse": "1.4.0" } }, "os-browserify": { @@ -5279,42 +5503,591 @@ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "1.1.2" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.5", + "source-map": "0.5.7", + "supports-color": "3.2.3" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-message-helpers": "2.0.0", + "reduce-css-calc": "1.3.0" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "requires": { + "colormin": "1.1.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "uniqs": "2.0.0" + } + }, + "postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "requires": { + "browserslist": "1.7.7", + "caniuse-api": "1.6.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3", + "vendors": "1.0.2" + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "requires": { + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "uniqs": "2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "requires": { + "alphanum-sort": "1.0.2", + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-selector-parser": "2.2.3" + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", + "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", + "dev": true, + "requires": { + "postcss": "6.0.22" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.22" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "0.7.0", + "postcss": "6.0.22" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.22" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "requires": { + "is-absolute-url": "2.1.0", + "normalize-url": "1.9.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "requires": { + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0" + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "requires": { + "flatten": "1.0.2", + "indexes-of": "1.0.1", + "uniq": "1.0.1" + } }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", "dev": true, "requires": { - "pinkie": "2.0.4" + "is-svg": "2.1.0", + "postcss": "5.2.18", + "postcss-value-parser": "3.3.0", + "svgo": "0.7.2" } }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", "dev": true, "requires": { - "find-up": "1.1.2" + "alphanum-sort": "1.0.2", + "postcss": "5.2.18", + "uniqs": "2.0.0" } }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", "dev": true }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "requires": { + "has": "1.0.1", + "postcss": "5.2.18", + "uniqs": "2.0.0" + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", @@ -5334,9 +6107,9 @@ "dev": true }, "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, "progress": { @@ -5346,13 +6119,13 @@ "dev": true }, "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", "dev": true, "requires": { "forwarded": "0.1.2", - "ipaddr.js": "1.5.2" + "ipaddr.js": "1.6.0" } }, "prr": { @@ -5380,17 +6153,27 @@ "dev": true }, "qjobs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", - "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", "dev": true }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" + } + }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -5404,49 +6187,33 @@ "dev": true }, "querystringify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", - "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", + "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==", "dev": true }, "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", + "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "4.0.0", + "kind-of": "6.0.2", + "math-random": "1.0.1" }, "dependencies": { "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true }, "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true } } }, @@ -5457,14 +6224,14 @@ "dev": true }, "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", "dev": true, "requires": { "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", "unpipe": "1.0.0" } }, @@ -5490,17 +6257,17 @@ } }, "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, @@ -5512,7 +6279,7 @@ "requires": { "graceful-fs": "4.1.11", "minimatch": "3.0.4", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "set-immediate-shim": "1.0.1" } }, @@ -5526,10 +6293,46 @@ "strip-indent": "1.0.1" } }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "math-expression-evaluator": "1.2.17", + "reduce-function-call": "1.0.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", + "dev": true, + "requires": { + "balanced-match": "0.4.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, "regenerator-runtime": { @@ -5558,13 +6361,19 @@ "is-equal-shallow": "0.1.3" } }, + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "1.3.3", + "regenerate": "1.4.0", "regjsgen": "0.2.0", "regjsparser": "0.1.5" } @@ -5640,7 +6449,7 @@ "node-uuid": "1.4.8", "oauth-sign": "0.6.0", "qs": "2.4.2", - "stringstream": "0.0.5", + "stringstream": "0.0.6", "tough-cookie": "2.3.4", "tunnel-agent": "0.4.3" }, @@ -5772,9 +6581,9 @@ } }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, "safe-umd-webpack-plugin": { @@ -5786,31 +6595,43 @@ "webpack-core": "0.6.9" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "1.1.1", + "depd": "1.1.2", "destroy": "1.0.4", - "encodeurl": "1.0.1", + "encodeurl": "1.0.2", "escape-html": "1.0.3", "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.2", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", "on-finished": "2.3.0", "range-parser": "1.2.0", - "statuses": "1.3.1" + "statuses": "1.4.0" }, "dependencies": { "mime": { @@ -5820,9 +6641,9 @@ "dev": true }, "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", "dev": true } } @@ -5833,37 +6654,37 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "1.3.4", + "accepts": "1.3.5", "batch": "0.6.1", "debug": "2.6.9", "escape-html": "1.0.3", - "http-errors": "1.6.2", - "mime-types": "2.1.17", + "http-errors": "1.6.3", + "mime-types": "2.1.18", "parseurl": "1.3.2" }, "dependencies": { "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.17", + "mime-types": "2.1.18", "negotiator": "0.6.1" } } } }, "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "requires": { - "encodeurl": "1.0.1", + "encodeurl": "1.0.2", "escape-html": "1.0.3", "parseurl": "1.3.2", - "send": "0.16.1" + "send": "0.16.2" } }, "set-immediate-shim": { @@ -5879,9 +6700,9 @@ "dev": true }, "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, "sha.js": { @@ -6090,13 +6911,13 @@ "dev": true, "requires": { "faye-websocket": "0.10.0", - "uuid": "3.1.0" + "uuid": "3.2.1" }, "dependencies": { "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true } } @@ -6112,7 +6933,7 @@ "faye-websocket": "0.11.1", "inherits": "2.0.3", "json3": "3.3.2", - "url-parse": "1.2.0" + "url-parse": "1.4.0" }, "dependencies": { "faye-websocket": { @@ -6126,6 +6947,15 @@ } } }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "1.1.0" + } + }, "source-list-map": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", @@ -6148,24 +6978,35 @@ } }, "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { - "spdx-license-ids": "1.2.2" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" + } + }, "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", "dev": true }, "sprintf-js": { @@ -6175,9 +7016,9 @@ "dev": true }, "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", "dev": true }, "stream-browserify": { @@ -6187,7 +7028,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.3" + "readable-stream": "2.3.6" } }, "stream-cache": { @@ -6197,18 +7038,24 @@ "dev": true }, "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.2.tgz", + "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==", "dev": true, "requires": { "builtin-status-codes": "3.0.0", "inherits": "2.0.3", - "readable-stream": "2.3.3", + "readable-stream": "2.3.6", "to-arraybuffer": "1.0.1", "xtend": "4.0.1" } }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6237,18 +7084,18 @@ } }, "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "5.1.2" } }, "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", "dev": true }, "strip-ansi": { @@ -6284,12 +7131,130 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.0", + "debug": "2.6.9", + "glob": "7.0.6", + "mkdirp": "0.5.1", + "sax": "0.5.8", + "source-map": "0.1.43" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": "1.0.1" + } + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "lodash.clonedeep": "4.5.0", + "when": "3.6.4" + }, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "3.2.0", + "emojis-list": "2.1.0", + "json5": "0.5.1" + } + } + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "requires": { + "coa": "1.0.4", + "colors": "1.1.2", + "csso": "2.3.2", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "sax": "1.2.4", + "whet.extend": "0.9.9" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "2.7.3" + } + } + } + }, + "svgstore": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/svgstore/-/svgstore-2.0.3.tgz", + "integrity": "sha512-K5GcfdH/lZbLyQv2Vi9pDAKUXBLZAn7eRoPGCzV+7gk7Fv/LOB1gLsNfevNSrcapX/q45M8x0XMct9ZjWRFImQ==", + "dev": true, + "requires": { + "cheerio": "0.22.0", + "object-assign": "4.1.1" + } + }, "table": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", @@ -6298,39 +7263,39 @@ "requires": { "ajv": "5.5.2", "ajv-keywords": "2.1.1", - "chalk": "2.3.0", - "lodash": "4.17.4", + "chalk": "2.4.1", + "lodash": "4.17.10", "slice-ansi": "1.0.0", "string-width": "2.1.1" }, "dependencies": { "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "1.9.1" } }, "chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.0", + "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "supports-color": "5.4.0" } }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "3.0.0" } } } @@ -6404,9 +7369,9 @@ "dev": true }, "timers-browserify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", - "integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", "dev": true, "requires": { "setimmediate": "1.0.5" @@ -6467,9 +7432,17 @@ "dev": true }, "tui-code-snippet": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tui-code-snippet/-/tui-code-snippet-1.3.0.tgz", - "integrity": "sha512-OJUibhjdfq6jsav8ss6bg3qB8Mi7ugXprXZBOSAaNLy8GRUD3tt9TP9tF154419CCfo2tIIYl9KEJqznJg4+rA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tui-code-snippet/-/tui-code-snippet-1.4.0.tgz", + "integrity": "sha512-a7XzHRbRDi6Tt4lGcopq6ctQjVrzmnw9JMoTFqur5gczgtw5tmgUqXHjg8D9IonDkzZNq5gYLhkzykx4fmn+GA==" + }, + "tui-color-picker": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tui-color-picker/-/tui-color-picker-2.2.0.tgz", + "integrity": "sha512-oAzMxF19bDExbvv7jVWQBPlrJ8sO3jQe+1rHqKkM4FtpvtGNlJO/ty19LW6pk9CCi1y43cgoG3QUt41ctGmygQ==", + "requires": { + "tui-code-snippet": "1.4.0" + } }, "tui-jsdoc-template": { "version": "1.2.2", @@ -6496,13 +7469,13 @@ } }, "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.17" + "mime-types": "2.1.18" } }, "typedarray": { @@ -6564,6 +7537,18 @@ "integrity": "sha1-Rhe4waJQz25QZPu7Nj0PqWzxRVI=", "dev": true }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -6589,39 +7574,23 @@ } }, "url-parse": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.2.0.tgz", - "integrity": "sha512-DT1XbYAfmQP65M/mE6OALxmXzZ/z1+e5zk2TcSKe/KiYbNGZxgtttzC0mR/sjopbpOXcbniq7eIKmocJnUWlEw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.0.tgz", + "integrity": "sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg==", "dev": true, "requires": { - "querystringify": "1.0.0", + "querystringify": "2.0.0", "requires-port": "1.0.0" - }, - "dependencies": { - "querystringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", - "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", - "dev": true - } } }, "useragent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", - "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", "dev": true, "requires": { - "lru-cache": "2.2.4", + "lru-cache": "4.1.3", "tmp": "0.0.33" - }, - "dependencies": { - "lru-cache": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", - "dev": true - } } }, "util": { @@ -6654,13 +7623,13 @@ "dev": true }, "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "vargs": { @@ -6675,6 +7644,12 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, + "vendors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", + "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", + "dev": true + }, "vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -6746,7 +7721,7 @@ "requires": { "acorn": "3.3.0", "async": "1.5.2", - "clone": "1.0.3", + "clone": "1.0.4", "enhanced-resolve": "0.9.1", "interpret": "0.6.6", "loader-utils": "0.2.17", @@ -6779,8 +7754,8 @@ "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", "dev": true, "requires": { - "errno": "0.1.6", - "readable-stream": "2.3.3" + "errno": "0.1.7", + "readable-stream": "2.3.6" } }, "supports-color": { @@ -6854,9 +7829,9 @@ "integrity": "sha1-DL1fLSrI1OWTqs1clwLnu9XlmJI=", "dev": true, "requires": { - "compression": "1.7.1", + "compression": "1.7.2", "connect-history-api-fallback": "1.5.0", - "express": "4.16.2", + "express": "4.16.3", "http-proxy-middleware": "0.17.4", "open": "0.0.5", "optimist": "0.6.1", @@ -6886,13 +7861,23 @@ } } }, + "webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", + "dev": true, + "requires": { + "source-list-map": "0.1.8", + "source-map": "0.5.7" + } + }, "websocket-driver": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": "0.4.9", + "http-parser-js": "0.4.13", "websocket-extensions": "0.1.3" } }, @@ -6902,6 +7887,18 @@ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", "dev": true }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true + }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", diff --git a/package.json b/package.json index b7e2d625a..39b12cc01 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,12 @@ "babel-eslint": "^7.1.0", "babel-loader": "^6.2.7", "babel-preset-es2015": "^6.18.0", + "css-loader": "^0.28.11", "es5-shim": "^4.5.9", "eslint": "^4.5.0", "eslint-config-tui": "^1.0.1", "eslint-loader": "^1.6.1", + "extract-text-webpack-plugin": "^1.0.1", "file-saver": "^1.3.3", "istanbul-instrumenter-loader": "^1.0.0", "jasmine-core": "^2.4.1", @@ -37,6 +39,7 @@ "karma-chrome-launcher": "^2.0.0", "karma-coverage": "^1.1.1", "karma-es5-shim": "0.0.4", + "karma-firefox-launcher": "^1.1.0", "karma-jasmine": "^1.0.2", "karma-jquery": "^0.2.2", "karma-junit-reporter": "^1.2.0", @@ -45,6 +48,9 @@ "karma-webpack": "^1.8.0", "safe-umd-webpack-plugin": "0.0.2", "simulant": "^0.2.2", + "stylus": "^0.54.5", + "stylus-loader": "^3.0.2", + "svgstore": "^2.0.3", "tui-jsdoc-template": "^1.0.4", "webpack": "^1.13.3", "webpack-dev-server": "^1.11.0" @@ -52,7 +58,8 @@ "scripts": { "test": "karma start --no-single-run", "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", "cpy-dist2doc": "mkdir -p doc/dist && cp -f -r dist doc", "doc": "jsdoc -c jsdoc.conf.json && npm run cpy-dist2doc", @@ -60,6 +67,7 @@ }, "dependencies": { "core-js": "2.4.1", - "tui-code-snippet": "^1.3.0" + "tui-code-snippet": "^1.3.0", + "tui-color-picker": "^2.2.0" } } diff --git a/src/css/buttons.styl b/src/css/buttons.styl new file mode 100644 index 000000000..a4cb0d904 --- /dev/null +++ b/src/css/buttons.styl @@ -0,0 +1,77 @@ +/* ICON BUTTON */ +#tie-icon-add-button + &.icon-bubble .{prefix}-button[data-icontype="icon-bubble"] svg > use.active, + &.icon-heart .{prefix}-button[data-icontype="icon-heart"] svg > use.active, + &.icon-location .{prefix}-button[data-icontype="icon-location"] svg > use.active, + &.icon-polygon .{prefix}-button[data-icontype="icon-polygon"] svg > use.active, + &.icon-star .{prefix}-button[data-icontype="icon-star"] svg > use.active, + &.icon-arrow-3 .{prefix}-button[data-icontype="icon-arrow-3"] svg > use.active, + &.icon-arrow-2 .{prefix}-button[data-icontype="icon-arrow-2"] svg > use.active, + &.icon-arrow .{prefix}-button[data-icontype="icon-arrow"] svg > use.active, + &.icon-bubble .{prefix}-button[data-icontype="icon-bubble"] svg > use.active + display: block; + +/* DRAW BUTTON */ +#tie-draw-line-select-button + &.line .{prefix}-button.line svg > use.normal, + &.free .{prefix}-button.free svg > use.normal + display: none; + + &.line .{prefix}-button.line svg > use.active, + &.free .{prefix}-button.free svg > use.active + display: block; + +/* FLIP BUTTON */ +#tie-flip-button + &.resetFlip .{prefix}-button.resetFlip, + &.flipX .{prefix}-button.flipX, + &.flipY .{prefix}-button.flipY + svg > use.normal + display: none; + svg > use.active + display: block; + +/* MASK BUTTON */ +#tie-mask-apply.apply.active .{prefix}-button.apply + label + color: #fff; + svg > use.active + display: block; + +/* CROP BUTTON */ +#tie-crop-button + .{prefix}-button.apply + margin-right: 24px; + .{prefix}-button.apply.active svg > use.active + display: block; + + +/* SHAPE BUTTON */ +#tie-shape-button + &.rect .{prefix}-button.rect, + &.circle .{prefix}-button.circle, + &.triangle .{prefix}-button.triangle + svg > use.normal + display: none; + svg > use.active + display: block; + +/* TEXT BUTTON */ +#tie-text-effect-button + .{prefix}-button.active svg > use.active + display: block; +#tie-text-align-button + &.left .{prefix}-button.left svg > use.active, + &.center .{prefix}-button.center svg > use.active, + &.right .{prefix}-button.right svg > use.active + display: block; +#tie-mask-image-file, +#tie-icon-image-file + opacity: 0; + position: absolute; + width: 100%; + height: 100%; + border: 1px solid green; + cursor: inherit; + left: 0; + top: 0; \ No newline at end of file diff --git a/src/css/checkbox.styl b/src/css/checkbox.styl new file mode 100644 index 000000000..3ad2b095b --- /dev/null +++ b/src/css/checkbox.styl @@ -0,0 +1,70 @@ +/* VIRTUAL CHECKBOX */ +.{prefix}-container + .filter-color-item + display: inline-block; + .tui-image-editor-checkbox + display: block; + .{prefix}-checkbox-wrap + display: inline-block !important; + text-align: left; + .{prefix}-checkbox-wrap.fixed-width + width: 187px; + white-space: normal; + .{prefix}-checkbox + display: inline-block; + margin: 1px 0 1px 0; + input + width: 14px; + height: 14px; + opacity: 0; + input + label + color: #fff; + height: 14px; + position: relative; + input + label:before + content: ''; + position: absolute; + width: 14px; + height: 14px; + background-color: #fff; + top: 4px; + left: -19px; + display: inline-block; + margin: 0; + text-align: center; + font-size: 11px; + border: 0; + border-radius: 2px; + padding-top: 1px; + box-sizing: border-box; + input[type='checkbox']:checked + label:before + background-size: cover; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAMBJREFUKBWVkjEOwjAMRe2WgZW7IIHEDdhghhuwcQ42rlJugAQS54Cxa5cq1QM5TUpByZfS2j9+dlJVt/tX5ZxbS4ZU9VLkQvSHKTIGRaVJYFmKrBbTCJxE2UgCdDzMZDkHrOV6b95V0US6UmgKodujEZbJg0B0ZgEModO5lrY1TMQf1TpyJGBEjD+E2NPN7ukIUDiF/BfEXgRiGEw8NgkffYGYwCi808fpn/6OvfUfsDr/Vc1IfRf8sKnFVqeiVQfDu0tf/nWH9gAAAABJRU5ErkJggg=='); + + .{prefix}-selectlist-wrap + position: relative; + select + width: 100%; + height: 25px; + margin-top: 9px; + border: 0; + outline: 0; + border-radius: 0; + border: 1px solid #cbdbdb; + background-color: #fff; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0 7px 0 7px; + .{prefix}-selectlist-wrap:after + content: ''; + position: absolute; + display: inline-block; + width: 14px; + height: 14px; + right: 5px; + bottom: 5px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHlJREFUKBVjYBgFOEOAEVkmPDxc89+/f6eAYjzI4kD2FyYmJrOVK1deh4kzwRggGiQBVJCELAZig8SQNYHEmEEEMrh69eo1HR0dfqCYJUickZGxf9WqVf3IakBsFBthklpaWmVA9mEQhrJhUoTp0NBQCRAmrHL4qgAAuu4cWZOZIGsAAAAASUVORK5CYII='); + background-size: cover; + .{prefix}-selectlist-wrap select::-ms-expand + display:none; \ No newline at end of file diff --git a/src/css/colorpicker.styl b/src/css/colorpicker.styl new file mode 100644 index 000000000..c7ca00d6a --- /dev/null +++ b/src/css/colorpicker.styl @@ -0,0 +1,80 @@ +/* COLOR PICKER */ +.{prefix}-container + div.tui-colorpicker-clearfix + width: 159px; + height: 28px; + border: 1px solid #d5d5d5; + border-radius: 2px; + background-color: #f5f5f5; + margin-top: 6px; + padding: 4px 7px 4px 7px; + .tui-colorpicker-palette-hex + width: 114px; + background-color: #f5f5f5; + border: 0; + font-size: 11px; + margin-top: 2px; + + .tui-colorpicker-palette-preview + border-radius: 100%; + float: left; + width: 16px; + height: 16px; + + .color-picker-control + position: absolute; + display: none; + z-index: 99; + width: 188px; + height: 112px; + background-color: #fff; + box-shadow: 0 3px 22px 0px #000; + padding: 14px; + border-radius: 2px; + .tui-colorpicker-palette-toggle-slider + display: none; + .tui-colorpicker-palette-button + border-radius: 100%; + margin: 2px; + .triangle + width: 0; + height: 0; + border-right: 12px solid transparent; + border-top: 12px solid #fff; + border-left: 12px solid transparent; + position: absolute; + bottom: -11px; + left: 77px; + .tui-colorpicker-container, + .tui-colorpicker-palette-container ul, + .tui-colorpicker-palette-container + width: 100%; + height: auto; + + + .filter-color-item .color-picker-control label + font-color: #333; + font-weight: normal; + margin-right: 7pxleft + .color-picker + width: 100%; + height: auto; + .color-picker-value + width: 30px; + height: 30px; + border: 0px; + border-radius: 100%; + margin: auto; + margin-bottom: 4px; + &.transparent + border: 1px solid #cbcbcb; + background-size: cover; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAdBJREFUWAnFl0FuwjAQRZ0ukiugHqFSOQNdseuKW3ALzkA4BateICvUGyCxrtRFd4WuunH/TzykaYJrnLEYaTJJsP2+x8GZZCbQrLU5mj7Bn+EP8HvnCObd+R7xBV5lWfaNON4AnsA38E94qLEt+0yiFaBzAV/Bv+Cxxr4co7hKCDpw1q9wLeNYYdlAwyn8TYt8Hme3+8D5ozcTaMCZ68PXa2tnM2sbEcOZAJhrrpl2DAcTOGNjZPSfCdzkw6JrfbiMv+osBe4y9WOedhm4jZfhbENWuxS44H9Wz/xw4WzqLOAqh1+zycgAwzEMzr5k5gaHOa9ULBwuuDkFlHI1Kl4PJ66kgIpnoywOTmRFAYcbwYk9UMApWkD8zAV5ihcwHk4Rx7gl0IFTQL0EFc+CTQ9OZHWH3YhlVJiVpTHbrTGLhTHLZVgff6s9lyBsI9KduSS83oj+34rTwJutmBmCnMsvozRwZqB5GTkBw6/jdPDu69iJ6BYk6eCcfbcgcQIK/MByaaiMqm8rHcjol2TnpWDhyAKSGdA3FrxtJUToX0ODqatetfGE+8tyEUOV8GY5dGRwLP/MBS4RHQr4bT7NRAQjlcOTfZxmv2G+c4hI8nn+Ax5PG/zhI393AAAAAElFTkSuQmCC'); + + .color-picker-value + label + color: #fff; + + .{prefix}-submenu svg > use + display: none; + .{prefix}-submenu svg > use.normal + display: block; \ No newline at end of file diff --git a/src/css/gridtable.styl b/src/css/gridtable.styl new file mode 100644 index 000000000..dcbe1a5dc --- /dev/null +++ b/src/css/gridtable.styl @@ -0,0 +1,43 @@ +/* GRID VISUAL OF FLIP AND ROTATE MENU */ +.{prefix}-container + .{prefix}-grid-visual + display: none; + position: absolute; + width: 100%; + height: 100%; + .{prefix}-main.{prefix}-menu-flip, + .{prefix}-main.{prefix}-menu-rotate + .tui-image-editor + transition: none; + .{prefix}-main.{prefix}-menu-flip .{prefix}-grid-visual, + .{prefix}-main.{prefix}-menu-rotate .{prefix}-grid-visual + display: block; + .{prefix}-grid-visual + table + width: 100%; + height: 100%; + border-collapse: collapse; + box-shadow: 0 0 1px 0 rgba(0,0,0,0.3); + td + border: 1px solid #fff; + box-shadow: 0 0 1px 0 rgba(0,0,0,0.3); + td.dot:before + content: ''; + position: absolute; + width: 8px; + height: 8px; + border: 1px solid #cdcdcd; + border-radius: 100%; + background-color: #fff; + td.dot.left-top:before + top: -4px; + left: -4px; + td.dot.right-top:before + top: -4px; + right: -4px; + td.dot.left-bottom:before + bottom: -4px; + left: -4px; + td.dot.right-bottom:before + bottom: -4px; + right: -4px; \ No newline at end of file diff --git a/src/css/icon.styl b/src/css/icon.styl new file mode 100644 index 000000000..d2a7fbdfc --- /dev/null +++ b/src/css/icon.styl @@ -0,0 +1,24 @@ +/* ICON */ +.{prefix}-container + .svg_ic-menu + width: 24px; + height: 24px; + .svg_ic-submenu + width: 32px; + height: 32px; + .svg_img-bi + width: 257px; + height: 26px; + + .{prefix}-controls + svg > use + display: none; + svg > use.normal + display: block; + .active svg > use.active + display: block; + .enabled svg > use.enabled + display: block; + .active svg > use.normal, + .enabled svg > use.normal + display: none; \ No newline at end of file diff --git a/src/css/index.styl b/src/css/index.styl new file mode 100644 index 000000000..50c6e0efb --- /dev/null +++ b/src/css/index.styl @@ -0,0 +1,17 @@ +prefix = 'tui-image-editor' + +@import 'main.styl' +@import 'gridtable.styl' +@import 'submenu.styl' +@import 'checkbox.styl' +@import 'range.styl' +@import 'position.styl' +@import 'icon.styl' +@import 'colorpicker.styl' +@import 'buttons.styl' +.{prefix}-container.top + &.{prefix}-top-optimization + .{prefix}-controls ul + text-align: right; + .{prefix}-controls-logo + display: none; diff --git a/src/css/main.styl b/src/css/main.styl new file mode 100644 index 000000000..182424d65 --- /dev/null +++ b/src/css/main.styl @@ -0,0 +1,157 @@ +body > textarea + position: fixed !important; + ++prefix-classes(prefix) + .-container + marign: 0; + padding: 0; + box-sizing: border-box; + min-height: 300px; + height: 100%; + position: relative; + background-color: #282828; + overflow: hidden; + + div, ul, label, input, li + box-sizing: border-box; + margin: 0; + padding: 0; + -ms-user-select: none; + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + user-select: none; + + .-header + /* BUTTON AND LOGO */ + min-width: 533px; + position: absolute; + background-color: #151515; + top: 0; + width: 100%; + .-header-buttons, + .-controls-buttons + float: right; + margin: 8px; + + .-header-logo, + .-controls-logo + float: left; + width: 30%; + padding: 17px; + + .-controls-logo, + .-controls-buttons + width: 270px; + height: 100%; + display: none; + + .-header-buttons button, + .-controls-buttons button + position: relative; + width: 120px; + height: 40px; + outline: none; + border-radius: 20px; + border: 1px solid #ddd; + padding-top: 5px; + font-family: NotoSans, sans-serif; + font-size: 12px; + font-weight: bold; + cursor: pointer; + + .-download-btn + background-color: #fdba3b; + border-color: #fdba3b; + color: #fff; + .-load-btn + position: absolute; + left: 0; + right: 0; + display: inline-block; + top: 0; + bottom: 0; + width: 100%; + cursor: pointer; + opacity: 0; + .-main-container + position: absolute; + width: 100%; + top: 0; + bottom: 64px; + .-main + position: absolute; + text-align: center; + top: 64px; + bottom: 0; + right: 0; + left: 0; + .-wrap + position: absolute; + bottom: 150px; + width: 100%; + overflow: auto; + .-size-wrap + display: table; + width: 100%; + height: 100% + .-align-wrap + display: table-cell; + vertical-align: middle; + + . + position: static; + display: inline-block; + + +/* BIG MENU */ +.{prefix}-container + .{prefix}-menu + width: auto; + list-style: none; + padding: 0; + margin: 0 auto; + display: table-cell; + text-align: center; + vertical-align: middle; + white-space: nowrap; + > .{prefix}-item + position: relative; + display: inline-block; + border-radius: 2px; + padding: 7px 8px 3px 8px; + cursor: pointer; + margin: 0 4px; + > .{prefix}-item[title]:hover + &:before + content: ''; + position: absolute; + display: inline-block; + margin: 0 auto 0; + width: 0; + height: 0; + border-right: 7px solid transparent; + border-top: 7px solid #2f2f2f; + border-left: 7px solid transparent; + position: absolute; + left: 13px; + top: -2px; + &:after + content: attr(title); + position: absolute; + display: inline-block; + background-color: #2f2f2f; + color: #fff; + padding: 5px 8px; + font-size: 11px; + font-weight: lighter; + border-radius: 3px; + max-height: 23px; + top: -22px; + left: 0; + min-width: 24px; + > .{prefix}-item.active + background-color: #fff; + transition: all .3s ease; + .{prefix}-wrap + position: absolute; \ No newline at end of file diff --git a/src/css/position.styl b/src/css/position.styl new file mode 100644 index 000000000..3bbc8cbf4 --- /dev/null +++ b/src/css/position.styl @@ -0,0 +1,160 @@ +/* POSITION LEFT */ +.{prefix}-container + &.left + .{prefix}-menu + > .{prefix}-item[title] + &:before + left: 28px; + top: 11px; + border-right: 7px solid #2f2f2f; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + &:after + top: 7px; + left: 39px; + width: 27px; + .{prefix}-submenu + left: 0; + height: 100%; + width: 248px; + .{prefix}-main-container + left: 64px; + width: calc(100% - 64px); + height: 100%; + .{prefix}-controls + width: 64px; + height: 100%; + display: table; + +/* POSITION LEFT & RIGHT */ +.{prefix}-container + &.left, &.right + .{prefix}-menu + white-space: initial; + .{prefix}-submenu + white-space: normal; + > div + vertical-align: middle; + .{prefix}-controls li + display: inline-block; + margin: 4px auto; + .{prefix}-icpartition + position: relative; + top: -7px; + width: 24px; + height: 1px; + .{prefix}-submenu + .{prefix}-partition + display: block; + width: 75%; + margin: auto; + > div + border-left: 0; + height:10px; + border-bottom: 1px solid #3c3c3c; + width: 100%; + margin: 0; + .{prefix}-submenu-align + margin-right: 0; + .{prefix}-submenu-item + li + margin-top: 15px; + .tui-colorpicker-clearfix li + margin-top: 0; + + .{prefix}-checkbox-wrap.fixed-width + width: 182px; + white-space: normal; + .{prefix}-range-wrap.{prefix}-newline label.range + display: block; + text-align: left; + width: 75%; + margin: auto; + .{prefix}-range + width: 136px; + #tie-icon-add-button .{prefix}-button + width: 45px; + + +/* POSITION RIGIHT */ +.{prefix}-container + &.right + .{prefix}-menu + > .{prefix}-item[title] + &:before + left: -3px; + top: 11px; + border-left: 7px solid #2f2f2f; + border-top: 7px solid transparent; + border-bottom: 7px solid transparent; + &:after + top: 7px; + left: -44px; + width: 27px; + .{prefix}-submenu + right: 0; + height: 100%; + width: 248px; + .{prefix}-main-container + right: 64px; + width: calc(100% - 64px); + height: 100%; + .{prefix}-controls + right: 0; + width: 64px; + height: 100%; + display: table; + + +/* POSITION TOP & BOTTOM */ +.{prefix}-container + &.top, &.bottom + .{prefix}-submenu + .{prefix}-partition.only-left-right + display: none; + + +/* POSITION BOTTOM */ +.{prefix}-container + &.bottom .tui-image-editor-submenu > div + padding-bottom: 24px; + +/* POSITION TOP */ +.{prefix}-container + &.top + .color-picker-control .triangle + top: -11px; + border-right: 12px solid transparent; + border-top: 0px; + border-left: 12px solid transparent; + border-bottom: 12px solid #fff; + .{prefix}-size-wrap + height: calc(100% - 64px); + .{prefix}-main-container + bottom: 0; + .{prefix}-menu + > .{prefix}-item[title] + &:before + left: 13px; + border-top: 0; + border-bottom: 7px solid #2f2f2f; + top: 33px; + &:after + top: 38px; + .{prefix}-submenu + top: 0; + bottom: inherit; + > div + padding-top: 24px; + vertical-align: top; + .{prefix}-controls-logo + display: table-cell; + .{prefix}-controls-buttons + display: table-cell; + .{prefix}-main + top: 64px; + height: 100%; + .{prefix}-controls + top: 0; + bottom: inherit; + diff --git a/src/css/range.styl b/src/css/range.styl new file mode 100644 index 000000000..9017623eb --- /dev/null +++ b/src/css/range.styl @@ -0,0 +1,81 @@ +/* VIRTUAL RANGE */ +.{prefix}-container + .{prefix}-range + position: relative; + top: 7px; + width: 166px; + height: 17px; + display: inline-block; + .{prefix}-virtual-range-bar + top: 7px; + position: absolute; + width: 100%; + height: 2px; + background-color: #666666; + .{prefix}-virtual-range-subbar + position: absolute; + height: 100%; + left: 0; + right: 0; + background-color: #d1d1d1; + .{prefix}-virtual-range-pointer + position: absolute; + cursor: pointer; + top: -5px; + left: 0; + width: 12px; + height: 12px; + background-color: #fff; + border-radius: 100%; + .{prefix}-range-wrap + display: inline-block; + &.short .tui-image-editor-range + width: 100px; + .color-picker-control + .{prefix}-range + width: 108px; + .{prefix}-virtual-range-pointer + background-color: #333; + .{prefix}-virtual-range-bar + background-color: #ccc; + .{prefix}-virtual-range-subbar + background-color: #606060; + .{prefix}-range-wrap.{prefix}-newline.short + margin-top: 0; + margin-left: 17px; + label + color: #8e8e8e; + font-weight: normal; + .{prefix}-range-wrap label + vertical-align: baseline; + font-size: 11px; + margin-right: 7px; + color: #fff; + .{prefix}-range-value + cursor: default; + width: 40px; + height: 24px; + outline: none; + border-radius: 2px; + box-shadow: none; + border: 1px solid #d5d5d5; + text-align: center; + background-color: #1c1c1c; + color: #fff; + font-weight: lighter; + vertical-align: baseline; + font-family: NotoSans, sans-serif; + padding-top: 2px; + .{prefix}-controls + position: absolute; + background-color: #151515; + width: 100%; + height: 64px; + display: table; + bottom: 0; + z-index: 1; + .{prefix}-icpartition + display: inline-block; + background-color: #282828; + width: 1px; + height: 24px; \ No newline at end of file diff --git a/src/css/submenu.styl b/src/css/submenu.styl new file mode 100644 index 000000000..f03577810 --- /dev/null +++ b/src/css/submenu.styl @@ -0,0 +1,79 @@ +/* SUBMENU */ +.{prefix}-container + .{prefix}-submenu + display: none; + position: absolute; + bottom: 0; + width:100%; + height: 150px; + white-space: nowrap; + .{prefix}-button:hover svg > use.active + display: block; + .{prefix}-submenu-item + li + display: inline-block; + vertical-align: top; + .{prefix}-newline + display: block; + margin-top: 14px; + .{prefix}-button + position: relative; + cursor: pointer; + display: inline-block; + font-weight: normal; + font-size: 11px; + margin: 0 7px 0 7px; + label + display: inline-block; + cursor: pointer; + padding-top: 5px; + font-family: NotoSans, sans-serif; + font-size: 11px; + letter-spacing: 0.7px; + .{prefix}-button.apply label, + .{prefix}-button.cancel label + vertical-align: 7px; + > div + display: none; + vertical-align: bottom; + + .{prefix}-partition > div + width: 1px; + height: 52px; + border-left: 1px solid #3c3c3c; + margin: 0 12px 0 14px; + .{prefix}-main.{prefix}-menu-filter .{prefix}-partition > div + height: 108px; + margin: 0 29px 0 0px; + .{prefix}-submenu-align + text-align: left; + margin-right: 30px; + label + width: 55px; + white-space: nowrap; + .{prefix}-submenu-align:first-child + margin-right: 0; + label + width: 70px; + .{prefix}-main.{prefix}-menu-crop .{prefix}-submenu > div.{prefix}-menu-crop, + .{prefix}-main.{prefix}-menu-flip .{prefix}-submenu > div.{prefix}-menu-flip, + .{prefix}-main.{prefix}-menu-rotate .{prefix}-submenu > div.{prefix}-menu-rotate, + .{prefix}-main.{prefix}-menu-shape .{prefix}-submenu > div.{prefix}-menu-shape, + .{prefix}-main.{prefix}-menu-text .{prefix}-submenu > div.{prefix}-menu-text, + .{prefix}-main.{prefix}-menu-mask .{prefix}-submenu > div.{prefix}-menu-mask, + .{prefix}-main.{prefix}-menu-icon .{prefix}-submenu > div.{prefix}-menu-icon, + .{prefix}-main.{prefix}-menu-draw .{prefix}-submenu > div.{prefix}-menu-draw, + .{prefix}-main.{prefix}-menu-filter .{prefix}-submenu > div.{prefix}-menu-filter + display: table-cell; + .{prefix}-main.{prefix}-menu-crop, + .{prefix}-main.{prefix}-menu-flip, + .{prefix}-main.{prefix}-menu-rotate, + .{prefix}-main.{prefix}-menu-shape, + .{prefix}-main.{prefix}-menu-text, + .{prefix}-main.{prefix}-menu-mask, + .{prefix}-main.{prefix}-menu-icon, + .{prefix}-main.{prefix}-menu-draw, + .{prefix}-main.{prefix}-menu-filter + .{prefix}-submenu + display: table; + diff --git a/src/index.js b/src/index.js index cc3a028bd..5c8afe0fb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,6 @@ +import './js/polyfill'; import ImageEditor from './js/imageEditor'; +import './css/index.styl'; // commands import './js/command/addIcon'; diff --git a/src/js/action.js b/src/js/action.js new file mode 100644 index 000000000..ae6a20f50 --- /dev/null +++ b/src/js/action.js @@ -0,0 +1,496 @@ +import {extend} from 'tui-code-snippet'; +import util from './util'; +import Imagetracer from './helper/imagetracer'; + +export default { + + /** + * Get ui actions + * @returns {Object} actions for ui + * @private + */ + getActions() { + return { + main: this._mainAction(), + shape: this._shapeAction(), + crop: this._cropAction(), + flip: this._flipAction(), + rotate: this._rotateAction(), + text: this._textAction(), + mask: this._maskAction(), + draw: this._drawAction(), + icon: this._iconAction(), + filter: this._filterAction() + }; + }, + + /** + * Main Action + * @returns {Object} actions for ui main + * @private + */ + _mainAction() { + const exitCropOnAction = () => { + if (this.ui.submenu === 'crop') { + this.stopDrawingMode(); + this.ui.changeMenu('crop'); + } + }; + + return extend({ + initLoadImage: (imagePath, imageName) => ( + this.loadImageFromURL(imagePath, imageName).then(sizeValue => { + exitCropOnAction(); + this.ui.initializeImgUrl = imagePath; + this.ui.resizeEditor({imageSize: sizeValue}); + this.clearUndoStack(); + }) + ), + undo: () => { + if (!this.isEmptyUndoStack()) { + exitCropOnAction(); + this.undo(); + } + }, + redo: () => { + if (!this.isEmptyRedoStack()) { + exitCropOnAction(); + this.redo(); + } + }, + reset: () => { + exitCropOnAction(); + this.loadImageFromURL(this.ui.initializeImgUrl, 'resetImage').then(sizeValue => { + exitCropOnAction(); + this.ui.resizeEditor({imageSize: sizeValue}); + this.clearUndoStack(); + }); + }, + delete: () => { + this.ui.changeDeleteButtonEnabled(false); + exitCropOnAction(); + this.removeActiveObject(); + this.activeObjectId = null; + }, + deleteAll: () => { + exitCropOnAction(); + this.clearObjects(); + this.ui.changeDeleteButtonEnabled(false); + this.ui.changeDeleteAllButtonEnabled(false); + }, + load: file => { + if (!util.isSupportFileApi()) { + alert('This browser does not support file-api'); + } + + this.ui.initializeImgUrl = URL.createObjectURL(file); + this.loadImageFromFile(file).then(() => { + exitCropOnAction(); + this.clearUndoStack(); + this.ui.resizeEditor(); + })['catch'](message => ( + Promise.reject(message) + )); + }, + download: () => { + const dataURL = this.toDataURL(); + let imageName = this.getImageName(); + let blob, type, w; + + if (util.isSupportFileApi() && window.saveAs) { + blob = util.base64ToBlob(dataURL); + type = blob.type.split('/')[1]; + if (imageName.split('.').pop() !== type) { + imageName += `.${type}`; + } + saveAs(blob, imageName); // eslint-disable-line + } else { + w = window.open(); + w.document.body.innerHTML = ``; + } + } + }, this._commonAction()); + }, + + /** + * Icon Action + * @returns {Object} actions for ui icon + * @private + */ + _iconAction() { + let cacheIconType; + let cacheIconColor; + let startX; + let startY; + let iconWidth; + let iconHeight; + let objId; + + this.on({ + 'iconCreateResize': ({moveOriginPointer}) => { + const scaleX = (moveOriginPointer.x - startX) / iconWidth; + const scaleY = (moveOriginPointer.y - startY) / iconHeight; + + this.setObjectPropertiesQuietly(objId, { + scaleX: Math.abs(scaleX * 2), + scaleY: Math.abs(scaleY * 2) + }); + }, + 'iconCreateEnd': () => { + this.ui.icon.clearIconType(); + this.changeSelectableAll(true); + } + }); + + const mouseDown = (e, originPointer) => { + startX = originPointer.x; + startY = originPointer.y; + + this.addIcon(cacheIconType, { + left: originPointer.x, + top: originPointer.y, + fill: cacheIconColor + }).then(obj => { + objId = obj.id; + iconWidth = obj.width; + iconHeight = obj.height; + }); + }; + + return extend({ + changeColor: color => { + if (this.activeObjectId) { + this.changeIconColor(this.activeObjectId, color); + } + }, + addIcon: (iconType, iconColor) => { + cacheIconType = iconType; + cacheIconColor = iconColor; + // this.readyAddIcon(); + this.changeCursor('crosshair'); + this.off('mousedown'); + this.once('mousedown', mouseDown.bind(this)); + }, + cancelAddIcon: () => { + this.off('mousedown'); + this.ui.icon.clearIconType(); + this.changeSelectableAll(true); + this.changeCursor('default'); + }, + registDefalutIcons: (type, path) => { + const iconObj = {}; + iconObj[type] = path; + this.registerIcons(iconObj); + }, + registCustomIcon: (imgUrl, file) => { + const imagetracer = new Imagetracer(); + imagetracer.imageToSVG( + imgUrl, + svgstr => { + const [, svgPath] = svgstr.match(/path[^>]*d="([^"]*)"/); + const iconObj = {}; + iconObj[file.name] = svgPath; + this.registerIcons(iconObj); + this.addIcon(file.name, { + left: 100, + top: 100 + }); + }, Imagetracer.tracerDefaultOption() + ); + } + }, this._commonAction()); + }, + + /** + * Draw Action + * @returns {Object} actions for ui draw + * @private + */ + _drawAction() { + return extend({ + setDrawMode: (type, settings) => { + this.stopDrawingMode(); + if (type === 'free') { + this.startDrawingMode('FREE_DRAWING', settings); + } else { + this.startDrawingMode('LINE_DRAWING', settings); + } + }, + setColor: color => { + this.setBrush({ + color + }); + } + }, this._commonAction()); + }, + + /** + * Mask Action + * @returns {Object} actions for ui mask + * @private + */ + _maskAction() { + return extend({ + loadImageFromURL: (imgUrl, file) => ( + this.loadImageFromURL(this.toDataURL(), 'FilterImage').then(() => { + this.addImageObject(imgUrl).then(() => { + URL.revokeObjectURL(file); + }); + }) + ), + applyFilter: () => { + this.applyFilter('mask', { + maskObjId: this.activeObjectId + }); + } + }, this._commonAction()); + }, + + /** + * Text Action + * @returns {Object} actions for ui text + * @private + */ + _textAction() { + return extend({ + changeTextStyle: styleObj => { + if (this.activeObjectId) { + this.changeTextStyle(this.activeObjectId, styleObj); + } + } + }, this._commonAction()); + }, + + /** + * Rotate Action + * @returns {Object} actions for ui rotate + * @private + */ + _rotateAction() { + return extend({ + rotate: angle => { + this.rotate(angle); + this.ui.resizeEditor(); + }, + setAngle: angle => { + this.setAngle(angle); + this.ui.resizeEditor(); + } + }, this._commonAction()); + }, + + /** + * Shape Action + * @returns {Object} actions for ui shape + * @private + */ + _shapeAction() { + return extend({ + changeShape: changeShapeObject => { + if (this.activeObjectId) { + this.changeShape(this.activeObjectId, changeShapeObject); + } + }, + setDrawingShape: shapeType => { + this.setDrawingShape(shapeType); + } + }, this._commonAction()); + }, + + /** + * Crop Action + * @returns {Object} actions for ui crop + * @private + */ + _cropAction() { + return extend({ + crop: () => { + const cropRect = this.getCropzoneRect(); + if (cropRect) { + this.crop(cropRect).then(() => { + this.stopDrawingMode(); + this.ui.resizeEditor(); + this.ui.changeMenu('crop'); + })['catch'](message => ( + Promise.reject(message) + )); + } + }, + cancel: () => { + this.stopDrawingMode(); + this.ui.changeMenu('crop'); + } + }, this._commonAction()); + }, + + /** + * Flip Action + * @returns {Object} actions for ui flip + * @private + */ + _flipAction() { + return extend({ + flip: flipType => this[flipType]() + }, this._commonAction()); + }, + + /** + * Filter Action + * @returns {Object} actions for ui filter + * @private + */ + _filterAction() { + return extend({ + applyFilter: (applying, type, options) => { + if (applying) { + this.applyFilter(type, options); + } else if (this.hasFilter(type)) { + this.removeFilter(type); + } + } + }, this._commonAction()); + }, + + /** + * Image Editor Event Observer + */ + setReAction() { + this.on({ + undoStackChanged: length => { + if (length) { + this.ui.changeUndoButtonStatus(true); + this.ui.changeResetButtonStatus(true); + } else { + this.ui.changeUndoButtonStatus(false); + this.ui.changeResetButtonStatus(false); + } + this.ui.resizeEditor(); + }, + redoStackChanged: length => { + if (length) { + this.ui.changeRedoButtonStatus(true); + } else { + this.ui.changeRedoButtonStatus(false); + } + this.ui.resizeEditor(); + }, + /* eslint-disable complexity */ + objectActivated: obj => { + this.activeObjectId = obj.id; + + this.ui.changeDeleteButtonEnabled(true); + this.ui.changeDeleteAllButtonEnabled(true); + + if (obj.type === 'cropzone') { + this.ui.crop.changeApplyButtonStatus(true); + } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) > -1) { + this.stopDrawingMode(); + if (this.ui.submenu !== 'shape') { + this.ui.changeMenu('shape', false, false); + } + this.ui.shape.setShapeStatus({ + strokeColor: obj.stroke, + strokeWidth: obj.strokeWidth, + fillColor: obj.fill + }); + + this.ui.shape.setMaxStrokeValue(Math.min(obj.width, obj.height)); + } else if (obj.type === 'path' || obj.type === 'line') { + if (this.ui.submenu !== 'draw') { + this.ui.changeMenu('draw', false, false); + this.ui.draw.changeStandbyMode(); + } + } else if (['i-text', 'text'].indexOf(obj.type) > -1) { + if (this.ui.submenu !== 'text') { + this.ui.changeMenu('text', false, false); + } + } else if (obj.type === 'icon') { + this.stopDrawingMode(); + if (this.ui.submenu !== 'icon') { + this.ui.changeMenu('icon', false, false); + } + this.ui.icon.setIconPickerColor(obj.fill); + } + }, + /* eslint-enable complexity */ + addText: pos => { + this.addText('Double Click', { + position: pos.originPosition, + styles: { + fill: this.ui.text.textColor, + fontSize: util.toInteger(this.ui.text.fontSize) + } + }).then(() => { + this.changeCursor('default'); + }); + }, + addObjectAfter: obj => { + if (['rect', 'circle', 'triangle'].indexOf(obj.type) > -1) { + this.ui.shape.setMaxStrokeValue(Math.min(obj.width, obj.height)); + this.ui.shape.changeStandbyMode(); + } + }, + objectScaled: obj => { + if (['i-text', 'text'].indexOf(obj.type) > -1) { + this.ui.text.fontSize = util.toInteger(obj.fontSize); + } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) >= 0) { + const {width, height} = obj; + const strokeValue = this.ui.shape.getStrokeValue(); + + if (width < strokeValue) { + this.ui.shape.setStrokeValue(width); + } + if (height < strokeValue) { + this.ui.shape.setStrokeValue(height); + } + } + }, + selectionCleared: () => { + this.activeObjectId = null; + if (this.ui.submenu === 'text') { + this.changeCursor('text'); + } else if (this.ui.submenu !== 'draw' && this.ui.submenu !== 'crop') { + this.stopDrawingMode(); + } + } + }); + }, + + /** + * Common Action + * @returns {Object} common actions for ui + * @private + */ + _commonAction() { + return { + modeChange: menu => { + switch (menu) { + case 'text': + this._changeActivateMode('TEXT'); + break; + case 'crop': + this.startDrawingMode('CROPPER'); + break; + case 'shape': + this._changeActivateMode('SHAPE'); + this.setDrawingShape(this.ui.shape.type, this.ui.shape.options); + break; + default: + break; + } + }, + deactivateAll: this.deactivateAll.bind(this), + changeSelectableAll: this.changeSelectableAll.bind(this), + discardSelection: this.discardSelection.bind(this), + stopDrawingMode: this.stopDrawingMode.bind(this) + }; + }, + + /** + * Mixin + * @param {ImageEditor} ImageEditor instance + */ + mixin(ImageEditor) { + extend(ImageEditor.prototype, this); + } +}; diff --git a/src/js/component/cropper.js b/src/js/component/cropper.js index 8efd11d6d..e2333e04a 100644 --- a/src/js/component/cropper.js +++ b/src/js/component/cropper.js @@ -71,9 +71,11 @@ class Cropper extends Component { return; } const canvas = this.getCanvas(); + canvas.forEachObject(obj => { // {@link http://fabricjs.com/docs/fabric.Object.html#evented} obj.evented = false; }); + this._cropzone = new Cropzone({ left: -10, top: -10, @@ -87,7 +89,8 @@ class Cropper extends Component { hasBorders: false, lockScalingFlip: true, lockRotation: true - }); + }, this.graphics.cropSelectionStyle); + canvas.deactivateAll(); canvas.add(this._cropzone); canvas.on('mouse:down', this._listeners.mousedown); diff --git a/src/js/component/icon.js b/src/js/component/icon.js index 1e2aded3d..8435d9d56 100644 --- a/src/js/component/icon.js +++ b/src/js/component/icon.js @@ -8,6 +8,7 @@ import Promise from 'core-js/library/es6/promise'; import Component from '../interface/component'; import consts from '../consts'; +const events = consts.eventNames; const {rejectMessages} = consts; const pathMap = { @@ -38,6 +39,12 @@ class Icon extends Component { * @type {Object} */ this._pathMap = pathMap; + + /** + * Option to add icon to drag. + * @type {boolean} + */ + this.useDragAddIcon = graphics.useDragAddIcon; } /** @@ -64,9 +71,33 @@ class Icon extends Component { icon.set(snippet.extend({ type: 'icon', fill: this._oColor - }, selectionStyle, options)); + }, selectionStyle, options, this.graphics.controlStyle)); + + 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); + } - canvas.add(icon).setActiveObject(icon); resolve(this.graphics.createObjectProperties(icon)); }); } diff --git a/src/js/component/shape.js b/src/js/component/shape.js index 95ba7b6db..9777949de 100644 --- a/src/js/component/shape.js +++ b/src/js/component/shape.js @@ -11,6 +11,7 @@ import {extend, inArray} from 'tui-code-snippet'; const {rejectMessages, eventNames} = consts; const KEY_CODES = consts.keyCodes; + const DEFAULT_TYPE = 'rect'; const DEFAULT_OPTIONS = { strokeWidth: 1, @@ -126,6 +127,7 @@ class Shape extends Component { this._isSelected = false; canvas.defaultCursor = 'default'; + canvas.selection = true; canvas.uniScaleTransform = false; canvas.off({ @@ -308,6 +310,11 @@ class Shape extends Component { * @private */ _onFabricMouseDown(fEvent) { + if (!fEvent.target) { + this._isSelected = false; + this._shapeObj = false; + } + if (!this._isSelected && !this._shapeObj) { const canvas = this.getCanvas(); this._startPoint = canvas.getPointer(fEvent.e); @@ -363,6 +370,8 @@ class Shape extends Component { resizeHelper.adjustOriginToCenter(shape); } + this.fire(eventNames.ADD_OBJECT_AFTER, this.graphics.createObjectProperties(shape)); + canvas.off({ 'mouse:move': this._handlers.mousemove, 'mouse:up': this._handlers.mouseup diff --git a/src/js/component/text.js b/src/js/component/text.js index 53309f017..26420d446 100644 --- a/src/js/component/text.js +++ b/src/js/component/text.js @@ -118,6 +118,12 @@ class Text extends Component { * @type {boolean} */ this.isPrevEditing = false; + + /** + * use itext + * @type {boolean} + */ + this.useItext = graphics.useItext; } /** @@ -132,10 +138,24 @@ class Text extends Component { 'mouse:down': this._listeners.mousedown, 'object:selected': this._listeners.select, '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(); + } this.setCanvasRatio(); } @@ -148,15 +168,34 @@ class Text extends Component { canvas.selection = true; 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({ 'mouse:down': this._listeners.mousedown, 'object:selected': this._listeners.select, 'before:selection:cleared': this._listeners.selectClear, - 'object:scaling': this._listeners.scaling + 'object:scaling': this._listeners.scaling, + 'text:editing': this._listeners.modify }); - - this._removeTextarea(); } /** @@ -177,16 +216,27 @@ class Text extends Component { add(text, options) { return new Promise(resolve => { const canvas = this.getCanvas(); + let newText = null; + let selectionStyle = consts.fObjectOptions.SELECTION_STYLE; let styles = this._defaultStyles; this._setInitPos(options.position); 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({ mouseup: this._onFabricMouseUp.bind(this) }); @@ -493,6 +543,7 @@ class Text extends Component { */ _onFabricMouseDown(fEvent) { const obj = fEvent.target; + if (obj && !obj.isType('text')) { return; } @@ -539,7 +590,9 @@ class Text extends Component { const newClickTime = (new Date()).getTime(); if (this._isDoubleClick(newClickTime)) { - this._changeToEditingMode(fEvent.target); + if (!this.useItext) { + this._changeToEditingMode(fEvent.target); + } this.fire(events.TEXT_EDITING); // fire editing text event } diff --git a/src/js/consts.js b/src/js/consts.js index 4c771f683..e700cd742 100644 --- a/src/js/consts.js +++ b/src/js/consts.js @@ -56,14 +56,22 @@ module.exports = { OBJECT_ACTIVATED: 'objectActivated', OBJECT_MOVED: 'objectMoved', 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' }, /** @@ -123,5 +131,91 @@ module.exports = { unsupportedType: 'Unsupported object type', noObject: 'The object is not in canvas.', addedObject: 'The object is already added.' + }, + + /** + * Default icon menu svg path + * @type {Object.} + */ + 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 + } } }; diff --git a/src/js/extension/colorFilter.js b/src/js/extension/colorFilter.js index 441f3216a..9849901fb 100644 --- a/src/js/extension/colorFilter.js +++ b/src/js/extension/colorFilter.js @@ -40,7 +40,7 @@ const ColorFilter = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** * Applies filter to canvas element * @param {Object} canvasEl Canvas element to apply filter to */ - applyTo(canvasEl) { + applyTo(canvasEl) { // eslint-disable-line const context = canvasEl.getContext('2d'); const imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height); const {data} = imageData; diff --git a/src/js/extension/cropzone.js b/src/js/extension/cropzone.js index b76774a99..04190bfc9 100644 --- a/src/js/extension/cropzone.js +++ b/src/js/extension/cropzone.js @@ -29,9 +29,14 @@ const Cropzone = fabric.util.createClass(fabric.Rect, /** @lends Cropzone.protot * @param {Object} options Options object * @override */ - initialize(options) { + initialize(options, extendsOptions) { + options = snippet.extend(options, extendsOptions); options.type = 'cropzone'; + this.callSuper('initialize', options); + + this.options = options; + this.on({ 'moving': this._onMoving, 'scaling': this._onScaling @@ -61,11 +66,20 @@ const Cropzone = fabric.util.createClass(fabric.Rect, /** @lends Cropzone.protot // Render outer rect this._fillOuterRect(ctx, 'rgba(0, 0, 0, 0.55)'); - // Black dash line - this._strokeBorder(ctx, 'rgb(0, 0, 0)', cropzoneDashLineWidth); - - // White dash line - this._strokeBorder(ctx, 'rgb(255, 255, 255)', cropzoneDashLineWidth, cropzoneDashLineOffset); + 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)', { + lineDashWidth: cropzoneDashLineWidth, + lineDashOffset: cropzoneDashLineOffset + }); + } // Reset scale ctx.scale(1 / originalScaleX, 1 / originalScaleY); @@ -108,7 +122,7 @@ const Cropzone = fabric.util.createClass(fabric.Rect, /** @lends Cropzone.protot ctx.moveTo(x[0] - 1, y[0] - 1); ctx.lineTo(x[3] + 1, y[0] - 1); 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); ctx.closePath(); @@ -124,6 +138,55 @@ const Cropzone = fabric.util.createClass(fabric.Rect, /** @lends Cropzone.protot ctx.restore(); }, + /** + * 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 * @param {CanvasRenderingContext2D} ctx - Context @@ -163,18 +226,22 @@ const Cropzone = fabric.util.createClass(fabric.Rect, /** @lends Cropzone.protot * @param {number} [lineDashOffset] - Dash offset * @private */ - _strokeBorder(ctx, strokeStyle, lineDashWidth, lineDashOffset) { + _strokeBorder(ctx, strokeStyle, {lineDashWidth, lineDashOffset, lineWidth}) { const halfWidth = this.getWidth() / 2, halfHeight = this.getHeight() / 2; ctx.save(); ctx.strokeStyle = strokeStyle; + if (ctx.setLineDash) { ctx.setLineDash([lineDashWidth, lineDashWidth]); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } + if (lineWidth) { + ctx.lineWidth = lineWidth; + } ctx.beginPath(); ctx.moveTo(-halfWidth, -halfHeight); diff --git a/src/js/graphics.js b/src/js/graphics.js index e21bca2c0..af32f71ae 100644 --- a/src/js/graphics.js +++ b/src/js/graphics.js @@ -25,6 +25,7 @@ import util from './util'; const components = consts.componentNames; const events = consts.eventNames; + const {drawingModes, fObjectOptions} = consts; const {extend, stamp, isArray, isString, forEachArray, forEachOwnProperties, CustomEvents} = snippet; @@ -45,10 +46,17 @@ const backstoreOnly = { * @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.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 + } = {}) { /** * Fabric image instance * @type {fabric.Image} @@ -67,6 +75,24 @@ class Graphics { */ this.cssMaxHeight = cssMaxHeight || DEFAULT_CSS_MAX_HEIGHT; + /** + * 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 * @type {string} @@ -120,7 +146,9 @@ class Graphics { onObjectMoved: this._onObjectMoved.bind(this), onObjectScaled: this._onObjectScaled.bind(this), 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) }; this._setCanvasElement(element); @@ -174,6 +202,7 @@ class Graphics { this._canvas.add(...theArgs); } + /** * Removes the object or group * @param {Object} target - graphics object or group @@ -276,6 +305,14 @@ class Graphics { return this._canvas.getActiveObject(); } + /** + * Gets an active group object + * @returns {Object} active group object instance + */ + getActiveGroupObject() { + return this._canvas.getActiveGroup(); + } + /** * Activates an object or group * @param {Object} target - target object or group @@ -284,6 +321,14 @@ class Graphics { this._canvas.setActiveObject(target); } + /** + * Set Crop selection style + * @param {Object} style - Selection styles + */ + setCropSelectionStyle(style) { + this.cropSelectionStyle = style; + } + /** * Get component * @param {string} name - Component name @@ -326,6 +371,7 @@ class Graphics { return !!drawingModeInstance; } + /** * Stop the current drawing mode and back to the 'NORMAL' mode */ @@ -556,6 +602,16 @@ class Graphics { this.getComponent(components.ICON).registerPaths(pathInfos); } + /** + * 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 * @param {string} type - Filter type @@ -831,7 +887,9 @@ class Graphics { 'object:moving': handler.onObjectMoved, 'object:scaling': handler.onObjectScaled, 'object:selected': handler.onObjectSelected, - 'path:created': handler.onPathCreated + 'path:created': handler.onPathCreated, + 'selection:cleared': handler.onSelectionCleared, + 'selection:created': handler.onSelectionCreated }); } @@ -919,6 +977,43 @@ class Graphics { this.fire(events.ADD_OBJECT, params); } + /** + * "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 * @param {fabric.Object} obj - fabric object @@ -942,7 +1037,7 @@ class Graphics { extend(props, util.getProperties(obj, predefinedKeys)); - if (obj.type === 'text') { + if (['i-text', 'text'].indexOf(obj.type) > -1) { extend(props, this._createTextProperties(obj, props)); } diff --git a/src/js/helper/imagetracer.js b/src/js/helper/imagetracer.js new file mode 100644 index 000000000..5030a576a --- /dev/null +++ b/src/js/helper/imagetracer.js @@ -0,0 +1,1151 @@ +/* + imagetracer.js version 1.2.4 + Simple raster image tracer and vectorizer written in JavaScript. + andras@jankovics.net +*/ + +/* + The Unlicense / PUBLIC DOMAIN + This is free and unencumbered software released into the public domain. + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + For more information, please refer to http://unlicense.org/ +*/ +export default class ImageTracer { + static tracerDefaultOption() { + return { + pathomit: 100, + ltres: 0.1, + qtres: 1, + + scale: 1, + strokewidth: 5, + viewbox: false, + linefilter: true, + desc: false, + rightangleenhance: false, + pal: [{ + r: 0, + g: 0, + b: 0, + a: 255 + }, { + r: 255, + g: 255, + b: 255, + a: 255 + }] + }; + } + /* eslint-disable */ + constructor() { + this.versionnumber = '1.2.4'; + this.optionpresets = { + default: { + corsenabled: false, + ltres: 1, + qtres: 1, + pathomit: 8, + rightangleenhance: true, + colorsampling: 2, + numberofcolors: 16, + mincolorratio: 0, + colorquantcycles: 3, + layering: 0, + strokewidth: 1, + linefilter: false, + scale: 1, + roundcoords: 1, + viewbox: false, + desc: false, + lcpr: 0, + qcpr: 0, + blurradius: 0, + blurdelta: 20 + }, + 'posterized1': { + colorsampling: 0, + numberofcolors: 2 + }, + 'posterized2': { + numberofcolors: 4, + blurradius: 5 + }, + 'curvy': { + ltres: 0.01, + linefilter: true, + rightangleenhance: false}, + 'sharp': {qtres: 0.01, + linefilter: false}, + 'detailed': {pathomit: 0, + roundcoords: 2, + ltres: 0.5, + qtres: 0.5, + numberofcolors: 64}, + 'smoothed': {blurradius: 5, + blurdelta: 64}, + 'grayscale': {colorsampling: 0, + colorquantcycles: 1, + numberofcolors: 7}, + 'fixedpalette': {colorsampling: 0, + colorquantcycles: 1, + numberofcolors: 27}, + 'randomsampling1': {colorsampling: 1, + numberofcolors: 8}, + 'randomsampling2': {colorsampling: 1, + numberofcolors: 64}, + 'artistic1': {colorsampling: 0, + colorquantcycles: 1, + pathomit: 0, + blurradius: 5, + blurdelta: 64, + ltres: 0.01, + linefilter: true, + numberofcolors: 16, + strokewidth: 2}, + 'artistic2': {qtres: 0.01, + colorsampling: 0, + colorquantcycles: 1, + numberofcolors: 4, + strokewidth: 0}, + 'artistic3': {qtres: 10, + ltres: 10, + numberofcolors: 8}, + 'artistic4': {qtres: 10, + ltres: 10, + numberofcolors: 64, + blurradius: 5, + blurdelta: 256, + strokewidth: 2}, + 'posterized3': {ltres: 1, + qtres: 1, + pathomit: 20, + rightangleenhance: true, + colorsampling: 0, + numberofcolors: 3, + mincolorratio: 0, + colorquantcycles: 3, + blurradius: 3, + blurdelta: 20, + strokewidth: 0, + linefilter: false, + roundcoords: 1, + pal: [{r: 0, + g: 0, + b: 100, + a: 255}, {r: 255, + g: 255, + b: 255, + a: 255}]} + }; + + this.pathscan_combined_lookup = [ + [[-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]], + [[ 0, 1, 0,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [ 0, 2,-1, 0]], + [[-1,-1,-1,-1], [-1,-1,-1,-1], [ 0, 1, 0,-1], [ 0, 0, 1, 0]], + [[ 0, 0, 1, 0], [-1,-1,-1,-1], [ 0, 2,-1, 0], [-1,-1,-1,-1]], + [[-1,-1,-1,-1], [ 0, 0, 1, 0], [ 0, 3, 0, 1], [-1,-1,-1,-1]], + [[13, 3, 0, 1], [13, 2,-1, 0], [ 7, 1, 0,-1], [ 7, 0, 1, 0]], + [[-1,-1,-1,-1], [ 0, 1, 0,-1], [-1,-1,-1,-1], [ 0, 3, 0, 1]], + [[ 0, 3, 0, 1], [ 0, 2,-1, 0], [-1,-1,-1,-1], [-1,-1,-1,-1]], + [[ 0, 3, 0, 1], [ 0, 2,-1, 0], [-1,-1,-1,-1], [-1,-1,-1,-1]], + [[-1,-1,-1,-1], [ 0, 1, 0,-1], [-1,-1,-1,-1], [ 0, 3, 0, 1]], + [[11, 1, 0,-1], [14, 0, 1, 0], [14, 3, 0, 1], [11, 2,-1, 0]], + [[-1,-1,-1,-1], [ 0, 0, 1, 0], [ 0, 3, 0, 1], [-1,-1,-1,-1]], + [[ 0, 0, 1, 0], [-1,-1,-1,-1], [ 0, 2,-1, 0], [-1,-1,-1,-1]], + [[-1,-1,-1,-1], [-1,-1,-1,-1], [ 0, 1, 0,-1], [ 0, 0, 1, 0]], + [[ 0, 1, 0,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [ 0, 2,-1, 0]], + [[-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1], [-1,-1,-1,-1]] + ]; + + this.gks = [ [0.27901,0.44198,0.27901], [0.135336,0.228569,0.272192,0.228569,0.135336], [0.086776,0.136394,0.178908,0.195843,0.178908,0.136394,0.086776], + [0.063327,0.093095,0.122589,0.144599,0.152781,0.144599,0.122589,0.093095,0.063327], [0.049692,0.069304,0.089767,0.107988,0.120651,0.125194,0.120651,0.107988,0.089767,0.069304,0.049692] ]; + + this.specpalette = [ + {r:0,g:0,b:0,a:255}, {r:128,g:128,b:128,a:255}, {r:0,g:0,b:128,a:255}, {r:64,g:64,b:128,a:255}, + {r:192,g:192,b:192,a:255}, {r:255,g:255,b:255,a:255}, {r:128,g:128,b:192,a:255}, {r:0,g:0,b:192,a:255}, + {r:128,g:0,b:0,a:255}, {r:128,g:64,b:64,a:255}, {r:128,g:0,b:128,a:255}, {r:168,g:168,b:168,a:255}, + {r:192,g:128,b:128,a:255}, {r:192,g:0,b:0,a:255}, {r:255,g:255,b:255,a:255}, {r:0,g:128,b:0,a:255} + ]; + } + + imageToSVG(url, callback, options) { + options = this.checkoptions(options); + this.loadImage( + url, + canvas => { + callback( + this.imagedataToSVG(this.getImgdata(canvas), options) + ); + }, + options + ); + } + + imagedataToSVG(imgd, options) { + options = this.checkoptions(options); + const td = this.imagedataToTracedata(imgd, options); + + return this.getsvgstring(td, options); + } + + imageToTracedata(url, callback, options) { + options = this.checkoptions(options); + this.loadImage( + url, + canvas => { + callback( + this.imagedataToTracedata(this.getImgdata(canvas), options) + ); + }, + options + ); + } + + imagedataToTracedata(imgd, options) { + options = this.checkoptions(options); + const ii = this.colorquantization(imgd, options); + let tracedata; + if (options.layering === 0) { + tracedata = { + layers: [], + palette: ii.palette, + width: ii.array[0].length - 2, + height: ii.array.length - 2 + }; + + for (let colornum = 0; colornum < ii.palette.length; colornum += 1) { + const tracedlayer = this.batchtracepaths( + this.internodes( + this.pathscan( + this.layeringstep(ii, colornum), + options.pathomit + ), + options + ), + options.ltres, + options.qtres + ); + tracedata.layers.push(tracedlayer); + } + } else { + const ls = this.layering(ii); + if (options.layercontainerid) { + this.drawLayers(ls, this.specpalette, options.scale, options.layercontainerid); + } + const bps = this.batchpathscan(ls, options.pathomit); + const bis = this.batchinternodes(bps, options); + tracedata = { + layers: this.batchtracelayers(bis, options.ltres, options.qtres), + palette: ii.palette, + width: imgd.width, + height: imgd.height + }; + } + + return tracedata; + } + + checkoptions(options) { + options = options || {}; + if (typeof options === 'string') { + options = options.toLowerCase(); + if (this.optionpresets[options]) { + options = this.optionpresets[options]; + } else { + options = {}; + } + } + const ok = Object.keys(this.optionpresets['default']); + for (let k = 0; k < ok.length; k += 1) { + if (!options.hasOwnProperty(ok[k])) { + options[ok[k]] = this.optionpresets['default'][ok[k]]; + } + } + + return options; + } + + colorquantization(imgd, options) { + const arr = []; + let idx = 0; + let cd; + let cdl; + let ci; + const paletteacc = []; + const pixelnum = imgd.width * imgd.height; + let i; + let j; + let k; + let cnt; + let palette; + + for (j = 0; j < imgd.height + 2; j += 1) { + arr[j] = []; + for (i = 0; i < imgd.width + 2; i += 1) { + arr[j][i] = -1; + } + } + if (options.pal) { + palette = options.pal; + } else if (options.colorsampling === 0) { + palette = this.generatepalette(options.numberofcolors); + } else if (options.colorsampling === 1) { + palette = this.samplepalette(options.numberofcolors, imgd); + } else { + palette = this.samplepalette2(options.numberofcolors, imgd); + } + if (options.blurradius > 0) { + imgd = this.blur(imgd, options.blurradius, options.blurdelta); + } + for (cnt = 0; cnt < options.colorquantcycles; cnt += 1) { + if (cnt > 0) { + for (k = 0; k < palette.length; k += 1) { + if (paletteacc[k].n > 0) { + palette[k] = {r: Math.floor(paletteacc[k].r / paletteacc[k].n), + g: Math.floor(paletteacc[k].g / paletteacc[k].n), + b: Math.floor(paletteacc[k].b / paletteacc[k].n), + a: Math.floor(paletteacc[k].a / paletteacc[k].n)}; + } + + if ((paletteacc[k].n / pixelnum < options.mincolorratio) && (cnt < options.colorquantcycles - 1)) { + palette[k] = {r: Math.floor(Math.random() * 255), + g: Math.floor(Math.random() * 255), + b: Math.floor(Math.random() * 255), + a: Math.floor(Math.random() * 255)}; + } + } + } + + for (i = 0; i < palette.length; i += 1) { + paletteacc[i] = {r: 0, + g: 0, + b: 0, + a: 0, + n: 0}; + } + + for (j = 0; j < imgd.height; j += 1) { + for (i = 0; i < imgd.width; i += 1) { + idx = ((j * imgd.width) + i) * 4; + + ci = 0; + cdl = 1024; + for (k = 0; k < palette.length; k += 1) { + cd = Math.abs(palette[k].r - imgd.data[idx]) + + Math.abs(palette[k].g - imgd.data[idx + 1]) + + Math.abs(palette[k].b - imgd.data[idx + 2]) + + Math.abs(palette[k].a - imgd.data[idx + 3]); + + if (cd < cdl) { + cdl = cd; + ci = k; + } + } + + paletteacc[ci].r += imgd.data[idx]; + paletteacc[ci].g += imgd.data[idx + 1]; + paletteacc[ci].b += imgd.data[idx + 2]; + paletteacc[ci].a += imgd.data[idx + 3]; + paletteacc[ci].n += 1; + + arr[j + 1][i + 1] = ci; + } + } + } + + return {array: arr, + palette}; + } + + samplepalette(numberofcolors, imgd) { + let idx; + const palette = []; + for (let i = 0; i < numberofcolors; i += 1) { + idx = Math.floor(Math.random() * imgd.data.length / 4) * 4; + palette.push({r: imgd.data[idx], + g: imgd.data[idx + 1], + b: imgd.data[idx + 2], + a: imgd.data[idx + 3]}); + } + + return palette; + } + + samplepalette2(numberofcolors, imgd) { + let idx; + const palette = []; + const ni = Math.ceil(Math.sqrt(numberofcolors)); + const nj = Math.ceil(numberofcolors / ni); + const vx = imgd.width / (ni + 1); + const vy = imgd.height / (nj + 1); + for (let j = 0; j < nj; j += 1) { + for (let i = 0; i < ni; i += 1) { + if (palette.length === numberofcolors) { + break; + } else { + idx = Math.floor((((j + 1) * vy) * imgd.width) + ((i + 1) * vx)) * 4; + palette.push({r: imgd.data[idx], + g: imgd.data[idx + 1], + b: imgd.data[idx + 2], + a: imgd.data[idx + 3]}); + } + } + } + + return palette; + } + + generatepalette(numberofcolors) { + const palette = []; + let rcnt; + let gcnt; + let bcnt; + if (numberofcolors < 8) { + const graystep = Math.floor(255 / (numberofcolors - 1)); + for (let i = 0; i < numberofcolors; i += 1) { + palette.push({r: i * graystep, + g: i * graystep, + b: i * graystep, + a: 255}); + } + } else { + const colorqnum = Math.floor(Math.pow(numberofcolors, 1 / 3)); + const colorstep = Math.floor(255 / (colorqnum - 1)); + const rndnum = numberofcolors - (colorqnum * colorqnum * colorqnum); + for (rcnt = 0; rcnt < colorqnum; rcnt += 1) { + for (gcnt = 0; gcnt < colorqnum; gcnt += 1) { + for (bcnt = 0; bcnt < colorqnum; bcnt += 1) { + palette.push({r: rcnt * colorstep, + g: gcnt * colorstep, + b: bcnt * colorstep, + a: 255}); + } + } + } + for (rcnt = 0; rcnt < rndnum; rcnt += 1) { + palette.push({r: Math.floor(Math.random() * 255), + g: Math.floor(Math.random() * 255), + b: Math.floor(Math.random() * 255), + a: Math.floor(Math.random() * 255)}); + } + } + + return palette; + } + + layering(ii) { + const layers = []; + let val = 0; + const ah = ii.array.length; + const aw = ii.array[0].length; + let n1; + let n2; + let n3; + let n4; + let n5; + let n6; + let n7; + let n8; + let i; + let j; + let k; + for (k = 0; k < ii.palette.length; k += 1) { + layers[k] = []; + for (j = 0; j < ah; j += 1) { + layers[k][j] = []; + for (i = 0; i < aw; i += 1) { + layers[k][j][i] = 0; + } + } + } + for (j = 1; j < ah - 1; j += 1) { + for (i = 1; i < aw - 1; i += 1) { + val = ii.array[j][i]; + + n1 = ii.array[j - 1][i - 1] === val ? 1 : 0; + n2 = ii.array[j - 1][i] === val ? 1 : 0; + n3 = ii.array[j - 1][i + 1] === val ? 1 : 0; + n4 = ii.array[j][i - 1] === val ? 1 : 0; + n5 = ii.array[j][i + 1] === val ? 1 : 0; + n6 = ii.array[j + 1][i - 1] === val ? 1 : 0; + n7 = ii.array[j + 1][i] === val ? 1 : 0; + n8 = ii.array[j + 1][i + 1] === val ? 1 : 0; + + layers[val][j + 1][i + 1] = 1 + (n5 * 2) + (n8 * 4) + (n7 * 8); + if (!n4) { + layers[val][j + 1][i] = 0 + 2 + (n7 * 4) + (n6 * 8); + } + if (!n2) { + layers[val][j][i + 1] = 0 + (n3 * 2) + (n5 * 4) + 8; + } + if (!n1) { + layers[val][j][i] = 0 + (n2 * 2) + 4 + (n4 * 8); + } + } + } + + return layers; + } + + layeringstep(ii, cnum) { + const layer = []; + const ah = ii.array.length; + const aw = ii.array[0].length; + let i; + let j; + for (j = 0; j < ah; j += 1) { + layer[j] = []; + for (i = 0; i < aw; i += 1) { + layer[j][i] = 0; + } + } + for (j = 1; j < ah; j += 1) { + for (i = 1; i < aw; i += 1) { + layer[j][i] = + (ii.array[j - 1][i - 1] === cnum ? 1 : 0) + + (ii.array[j - 1][i] === cnum ? 2 : 0) + + (ii.array[j][i - 1] === cnum ? 8 : 0) + + (ii.array[j][i] === cnum ? 4 : 0); + } + } + + return layer; + } + + pathscan(arr, pathomit) { + const paths = []; + let pacnt = 0; + let pcnt = 0; + let px = 0; + let py = 0; + const w = arr[0].length; + const h = arr.length; + let dir = 0; + let pathfinished = true; + let holepath = false; + let lookuprow; + for (let j = 0; j < h; j += 1) { + for (let i = 0; i < w; i += 1) { + if ((arr[j][i] === 4) || (arr[j][i] === 11)) { + px = i; + py = j; + paths[pacnt] = {}; + paths[pacnt].points = []; + paths[pacnt].boundingbox = [px, py, px, py]; + paths[pacnt].holechildren = []; + pathfinished = false; + pcnt = 0; + holepath = (arr[j][i] === 11); + dir = 1; + + while (!pathfinished) { + paths[pacnt].points[pcnt] = {}; + paths[pacnt].points[pcnt].x = px - 1; + paths[pacnt].points[pcnt].y = py - 1; + paths[pacnt].points[pcnt].t = arr[py][px]; + + if ((px - 1) < paths[pacnt].boundingbox[0]) { + paths[pacnt].boundingbox[0] = px - 1; + } + if ((px - 1) > paths[pacnt].boundingbox[2]) { + paths[pacnt].boundingbox[2] = px - 1; + } + if ((py - 1) < paths[pacnt].boundingbox[1]) { + paths[pacnt].boundingbox[1] = py - 1; + } + if ((py - 1) > paths[pacnt].boundingbox[3]) { + paths[pacnt].boundingbox[3] = py - 1; + } + + lookuprow = this.pathscan_combined_lookup[arr[py][px]][dir]; + arr[py][px] = lookuprow[0]; dir = lookuprow[1]; px += lookuprow[2]; py += lookuprow[3]; + + if ((px - 1 === paths[pacnt].points[0].x) && (py - 1 === paths[pacnt].points[0].y)) { + pathfinished = true; + + if (paths[pacnt].points.length < pathomit) { + paths.pop(); + } else { + paths[pacnt].isholepath = !!holepath; + + if (holepath) { + let parentidx = 0, parentbbox = [-1, -1, w + 1, h + 1]; + for (let parentcnt = 0; parentcnt < pacnt; parentcnt++) { + if ((!paths[parentcnt].isholepath) && + this.boundingboxincludes(paths[parentcnt].boundingbox, paths[pacnt].boundingbox) && + this.boundingboxincludes(parentbbox, paths[parentcnt].boundingbox) + ) { + parentidx = parentcnt; + parentbbox = paths[parentcnt].boundingbox; + } + } + paths[parentidx].holechildren.push(pacnt); + } + pacnt += 1; + } + } + pcnt += 1; + } + } + } + } + + return paths; + } + + boundingboxincludes(parentbbox, childbbox) { + return ((parentbbox[0] < childbbox[0]) && (parentbbox[1] < childbbox[1]) && + (parentbbox[2] > childbbox[2]) && (parentbbox[3] > childbbox[3])); + } + + batchpathscan(layers, pathomit) { + const bpaths = []; + for (const k in layers) { + if (!layers.hasOwnProperty(k)) { + continue; + } + bpaths[k] = this.pathscan(layers[k], pathomit); + } + + return bpaths; + } + + internodes(paths, options) { + const ins = []; + let palen = 0; + let nextidx = 0; + let nextidx2 = 0; + let previdx = 0; + let previdx2 = 0; + let pacnt; + let pcnt; + for (pacnt = 0; pacnt < paths.length; pacnt += 1) { + ins[pacnt] = {}; + ins[pacnt].points = []; + ins[pacnt].boundingbox = paths[pacnt].boundingbox; + ins[pacnt].holechildren = paths[pacnt].holechildren; + ins[pacnt].isholepath = paths[pacnt].isholepath; + palen = paths[pacnt].points.length; + + for (pcnt = 0; pcnt < palen; pcnt += 1) { + nextidx = (pcnt + 1) % palen; nextidx2 = (pcnt + 2) % palen; previdx = (pcnt - 1 + palen) % palen; previdx2 = (pcnt - 2 + palen) % palen; + + if (options.rightangleenhance && this.testrightangle(paths[pacnt], previdx2, previdx, pcnt, nextidx, nextidx2)) { + if (ins[pacnt].points.length > 0) { + ins[pacnt].points[ins[pacnt].points.length - 1].linesegment = this.getdirection( + ins[pacnt].points[ins[pacnt].points.length - 1].x, + ins[pacnt].points[ins[pacnt].points.length - 1].y, + paths[pacnt].points[pcnt].x, + paths[pacnt].points[pcnt].y + ); + } + + ins[pacnt].points.push({ + x: paths[pacnt].points[pcnt].x, + y: paths[pacnt].points[pcnt].y, + linesegment: this.getdirection( + paths[pacnt].points[pcnt].x, + paths[pacnt].points[pcnt].y, + ((paths[pacnt].points[pcnt].x + paths[pacnt].points[nextidx].x) / 2), + ((paths[pacnt].points[pcnt].y + paths[pacnt].points[nextidx].y) / 2) + ) + }); + } + + ins[pacnt].points.push({ + x: ((paths[pacnt].points[pcnt].x + paths[pacnt].points[nextidx].x) / 2), + y: ((paths[pacnt].points[pcnt].y + paths[pacnt].points[nextidx].y) / 2), + linesegment: this.getdirection( + ((paths[pacnt].points[pcnt].x + paths[pacnt].points[nextidx].x) / 2), + ((paths[pacnt].points[pcnt].y + paths[pacnt].points[nextidx].y) / 2), + ((paths[pacnt].points[nextidx].x + paths[pacnt].points[nextidx2].x) / 2), + ((paths[pacnt].points[nextidx].y + paths[pacnt].points[nextidx2].y) / 2) + ) + }); + } + } + + return ins; + } + + testrightangle(path, idx1, idx2, idx3, idx4, idx5) { + return (((path.points[idx3].x === path.points[idx1].x) && + (path.points[idx3].x === path.points[idx2].x) && + (path.points[idx3].y === path.points[idx4].y) && + (path.points[idx3].y === path.points[idx5].y) + ) || + ((path.points[idx3].y === path.points[idx1].y) && + (path.points[idx3].y === path.points[idx2].y) && + (path.points[idx3].x === path.points[idx4].x) && + (path.points[idx3].x === path.points[idx5].x) + ) + ); + } + + getdirection(x1, y1, x2, y2) { + let val = 8; + if (x1 < x2) { + if (y1 < y2) { + val = 1; + } else if (y1 > y2) { + val = 7; + } else { + val = 0; + } + } else if (x1 > x2) { + if (y1 < y2) { + val = 3; + } else if (y1 > y2) { + val = 5; + } else { + val = 4; + } + } else if (y1 < y2) { + val = 2; + } else if (y1 > y2) { + val = 6; + } else { + val = 8; + } + + return val; + } + + batchinternodes(bpaths, options) { + const binternodes = []; + for (const k in bpaths) { + if (!bpaths.hasOwnProperty(k)) { + continue; + } + binternodes[k] = this.internodes(bpaths[k], options); + } + + return binternodes; + } + + tracepath(path, ltres, qtres) { + let pcnt = 0; + let segtype1; + let segtype2; + let seqend; + const smp = {}; + smp.segments = []; + smp.boundingbox = path.boundingbox; + smp.holechildren = path.holechildren; + smp.isholepath = path.isholepath; + + while (pcnt < path.points.length) { + segtype1 = path.points[pcnt].linesegment; + segtype2 = -1; + seqend = pcnt + 1; + while ( + ((path.points[seqend].linesegment === segtype1) || (path.points[seqend].linesegment === segtype2) || (segtype2 === -1)) && (seqend < path.points.length - 1)) { + if ((path.points[seqend].linesegment !== segtype1) && (segtype2 === -1)) { + segtype2 = path.points[seqend].linesegment; + } + seqend += 1; + } + if (seqend === path.points.length - 1) { + seqend = 0; + } + + smp.segments = smp.segments.concat(this.fitseq(path, ltres, qtres, pcnt, seqend)); + + if (seqend > 0) { + pcnt = seqend; + } else { + pcnt = path.points.length; + } + } + + return smp; + } + + fitseq(path, ltres, qtres, seqstart, seqend) { + if ((seqend > path.points.length) || (seqend < 0)) { + return []; + } + let errorpoint = seqstart, errorval = 0, curvepass = true, px, py, dist2; + let tl = (seqend - seqstart); if (tl < 0) { + tl += path.points.length; + } + let vx = (path.points[seqend].x - path.points[seqstart].x) / tl, + vy = (path.points[seqend].y - path.points[seqstart].y) / tl; + let pcnt = (seqstart + 1) % path.points.length, pl; + while (pcnt != seqend) { + pl = pcnt - seqstart; if (pl < 0) { + pl += path.points.length; + } + px = path.points[seqstart].x + vx * pl; py = path.points[seqstart].y + vy * pl; + dist2 = (path.points[pcnt].x - px) * (path.points[pcnt].x - px) + (path.points[pcnt].y - py) * (path.points[pcnt].y - py); + if (dist2 > ltres) { + curvepass = false; + } + if (dist2 > errorval) { + errorpoint = pcnt; errorval = dist2; + } + pcnt = (pcnt + 1) % path.points.length; + } + if (curvepass) { + return [{type: 'L', + x1: path.points[seqstart].x, + y1: path.points[seqstart].y, + x2: path.points[seqend].x, + y2: path.points[seqend].y}]; + } + const fitpoint = errorpoint; curvepass = true; errorval = 0; + let t = (fitpoint - seqstart) / tl, t1 = (1 - t) * (1 - t), t2 = 2 * (1 - t) * t, t3 = t * t; + let cpx = (t1 * path.points[seqstart].x + t3 * path.points[seqend].x - path.points[fitpoint].x) / -t2, + cpy = (t1 * path.points[seqstart].y + t3 * path.points[seqend].y - path.points[fitpoint].y) / -t2; + pcnt = seqstart + 1; + while (pcnt != seqend) { + t = (pcnt - seqstart) / tl; t1 = (1 - t) * (1 - t); t2 = 2 * (1 - t) * t; t3 = t * t; + px = t1 * path.points[seqstart].x + t2 * cpx + t3 * path.points[seqend].x; + py = t1 * path.points[seqstart].y + t2 * cpy + t3 * path.points[seqend].y; + dist2 = (path.points[pcnt].x - px) * (path.points[pcnt].x - px) + (path.points[pcnt].y - py) * (path.points[pcnt].y - py); + if (dist2 > qtres) { + curvepass = false; + } + if (dist2 > errorval) { + errorpoint = pcnt; errorval = dist2; + } + pcnt = (pcnt + 1) % path.points.length; + } + if (curvepass) { + return [{type: 'Q', + x1: path.points[seqstart].x, + y1: path.points[seqstart].y, + x2: cpx, + y2: cpy, + x3: path.points[seqend].x, + y3: path.points[seqend].y}]; + } + const splitpoint = fitpoint; + + return this.fitseq(path, ltres, qtres, seqstart, splitpoint).concat( + this.fitseq(path, ltres, qtres, splitpoint, seqend)); + } + + batchtracepaths(internodepaths, ltres, qtres) { + const btracedpaths = []; + for (const k in internodepaths) { + if (!internodepaths.hasOwnProperty(k)) { + continue; + } + btracedpaths.push(this.tracepath(internodepaths[k], ltres, qtres)); + } + + return btracedpaths; + } + + batchtracelayers(binternodes, ltres, qtres) { + const btbis = []; + for (const k in binternodes) { + if (!binternodes.hasOwnProperty(k)) { + continue; + } + btbis[k] = this.batchtracepaths(binternodes[k], ltres, qtres); + } + + return btbis; + } + + roundtodec(val, places) { + return Number(val.toFixed(places)); + } + + svgpathstring(tracedata, lnum, pathnum, options) { + let layer = tracedata.layers[lnum], smp = layer[pathnum], str = '', pcnt; + if (options.linefilter && (smp.segments.length < 3)) { + return str; + } + str = `'; + if (options.lcpr || options.qcpr) { + for (pcnt = 0; pcnt < smp.segments.length; pcnt++) { + if (smp.segments[pcnt].hasOwnProperty('x3') && options.qcpr) { + str += ``; + str += ``; + str += ``; + str += ``; + } + if ((!smp.segments[pcnt].hasOwnProperty('x3')) && options.lcpr) { + str += ``; + } + } + + for (var hcnt = 0; hcnt < smp.holechildren.length; hcnt++) { + var hsmp = layer[smp.holechildren[hcnt]]; + for (pcnt = 0; pcnt < hsmp.segments.length; pcnt++) { + if (hsmp.segments[pcnt].hasOwnProperty('x3') && options.qcpr) { + str += ``; + str += ``; + str += ``; + str += ``; + } + if ((!hsmp.segments[pcnt].hasOwnProperty('x3')) && options.lcpr) { + str += ``; + } + } + } + } + + return str; + } + + getsvgstring(tracedata, options) { + options = this.checkoptions(options); + const w = tracedata.width * options.scale; + const h = tracedata.height * options.scale; + + let svgstr = ``; + for (let lcnt = 0; lcnt < tracedata.layers.length; lcnt += 1) { + for (let pcnt = 0; pcnt < tracedata.layers[lcnt].length; pcnt += 1) { + if (!tracedata.layers[lcnt][pcnt].isholepath) { + svgstr += this.svgpathstring(tracedata, lcnt, pcnt, options); + } + } + } + svgstr += ''; + + return svgstr; + } + + compareNumbers(a, b) { + return a - b; + } + + torgbastr(c) { + return `rgba(${c.r},${c.g},${c.b},${c.a})`; + } + + tosvgcolorstr(c, options) { + return `fill="rgb(${c.r},${c.g},${c.b})" stroke="rgb(${c.r},${c.g},${c.b})" stroke-width="${options.strokewidth}" opacity="${c.a / 255.0}" `; + } + + appendSVGString(svgstr, parentid) { + let div; + if (parentid) { + div = document.getElementById(parentid); + if (!div) { + div = document.createElement('div'); + div.id = parentid; + document.body.appendChild(div); + } + } else { + div = document.createElement('div'); + document.body.appendChild(div); + } + div.innerHTML += svgstr; + } + + blur(imgd, radius, delta) { + let i, j, k, d, idx, racc, gacc, bacc, aacc, wacc; + const imgd2 = {width: imgd.width, + height: imgd.height, + data: []}; + radius = Math.floor(radius); if (radius < 1) { + return imgd; + } if (radius > 5) { + radius = 5; + } delta = Math.abs(delta); if (delta > 1024) { + delta = 1024; + } + const thisgk = this.gks[radius - 1]; + for (j = 0; j < imgd.height; j++) { + for (i = 0; i < imgd.width; i++) { + racc = 0; gacc = 0; bacc = 0; aacc = 0; wacc = 0; + + for (k = -radius; k < radius + 1; k++) { + if ((i + k > 0) && (i + k < imgd.width)) { + idx = (j * imgd.width + i + k) * 4; + racc += imgd.data[idx] * thisgk[k + radius]; + gacc += imgd.data[idx + 1] * thisgk[k + radius]; + bacc += imgd.data[idx + 2] * thisgk[k + radius]; + aacc += imgd.data[idx + 3] * thisgk[k + radius]; + wacc += thisgk[k + radius]; + } + } + + idx = (j * imgd.width + i) * 4; + imgd2.data[idx] = Math.floor(racc / wacc); + imgd2.data[idx + 1] = Math.floor(gacc / wacc); + imgd2.data[idx + 2] = Math.floor(bacc / wacc); + imgd2.data[idx + 3] = Math.floor(aacc / wacc); + } + } + const himgd = new Uint8ClampedArray(imgd2.data); + for (j = 0; j < imgd.height; j++) { + for (i = 0; i < imgd.width; i++) { + racc = 0; gacc = 0; bacc = 0; aacc = 0; wacc = 0; + + for (k = -radius; k < radius + 1; k++) { + if ((j + k > 0) && (j + k < imgd.height)) { + idx = ((j + k) * imgd.width + i) * 4; + racc += himgd[idx] * thisgk[k + radius]; + gacc += himgd[idx + 1] * thisgk[k + radius]; + bacc += himgd[idx + 2] * thisgk[k + radius]; + aacc += himgd[idx + 3] * thisgk[k + radius]; + wacc += thisgk[k + radius]; + } + } + + idx = (j * imgd.width + i) * 4; + imgd2.data[idx] = Math.floor(racc / wacc); + imgd2.data[idx + 1] = Math.floor(gacc / wacc); + imgd2.data[idx + 2] = Math.floor(bacc / wacc); + imgd2.data[idx + 3] = Math.floor(aacc / wacc); + } + } + for (j = 0; j < imgd.height; j++) { + for (i = 0; i < imgd.width; i++) { + idx = (j * imgd.width + i) * 4; + + d = Math.abs(imgd2.data[idx] - imgd.data[idx]) + Math.abs(imgd2.data[idx + 1] - imgd.data[idx + 1]) + + Math.abs(imgd2.data[idx + 2] - imgd.data[idx + 2]) + Math.abs(imgd2.data[idx + 3] - imgd.data[idx + 3]); + + if (d > delta) { + imgd2.data[idx] = imgd.data[idx]; + imgd2.data[idx + 1] = imgd.data[idx + 1]; + imgd2.data[idx + 2] = imgd.data[idx + 2]; + imgd2.data[idx + 3] = imgd.data[idx + 3]; + } + } + } + + return imgd2; + } + + loadImage(url, callback, options) { + const img = new Image(); + if (options && options.corsenabled) { + img.crossOrigin = 'Anonymous'; + } + img.src = url; + img.onload = function() { + const canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + const context = canvas.getContext('2d'); + context.drawImage(img, 0, 0); + callback(canvas); + }; + } + + getImgdata(canvas) { + const context = canvas.getContext('2d'); + + return context.getImageData(0, 0, canvas.width, canvas.height); + } + + drawLayers(layers, palette, scale, parentid) { + scale = scale || 1; + let w, h, i, j, k; + let div; + if (parentid) { + div = document.getElementById(parentid); + if (!div) { + div = document.createElement('div'); + div.id = parentid; + document.body.appendChild(div); + } + } else { + div = document.createElement('div'); + document.body.appendChild(div); + } + for (k in layers) { + if (!layers.hasOwnProperty(k)) { + continue; + } + + w = layers[k][0].length; + h = layers[k].length; + + const canvas = document.createElement('canvas'); + canvas.width = w * scale; + canvas.height = h * scale; + const context = canvas.getContext('2d'); + + for (j = 0; j < h; j += 1) { + for (i = 0; i < w; i += 1) { + context.fillStyle = this.torgbastr(palette[layers[k][j][i] % palette.length]); + context.fillRect(i * scale, j * scale, scale, scale); + } + } + + div.appendChild(canvas); + } + } +} diff --git a/src/js/imageEditor.js b/src/js/imageEditor.js index 483b87da0..dfa3004db 100644 --- a/src/js/imageEditor.js +++ b/src/js/imageEditor.js @@ -3,7 +3,10 @@ * @fileoverview Image-editor application class */ 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'; import Graphics from './graphics'; import consts from './consts'; @@ -18,16 +21,59 @@ const {isUndefined, forEach, CustomEvents} = snippet; * Image editor * @class * @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 @@ -41,7 +87,14 @@ class ImageEditor { * @type {Graphics} * @private */ - 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 + } + ); /** * Event handler list @@ -57,20 +110,30 @@ class ImageEditor { createdPath: this._onCreatedPath, addText: this._onAddText.bind(this), 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) }; this._attachInvokerEvents(); this._attachGraphicsEvents(); 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(); } } @@ -88,7 +151,6 @@ class ImageEditor { * @property {boolean} flipY - y axis * @property {Number} angle - angle */ - /** * Rotation status * @typedef {Number} RotateStatus @@ -130,11 +192,28 @@ class ImageEditor { /** * 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); + } + }); + } } /** @@ -182,7 +261,12 @@ class ImageEditor { 'addText': this._handlers.addText, 'addObject': this._handlers.addObject, '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 }); } @@ -211,9 +295,6 @@ class ImageEditor { */ /* eslint-disable complexity */ _onKeyDown(e) { - const activeObject = this._graphics.getActiveObject(); - const activeObjectId = this._graphics.getObjectId(activeObject); - if ((e.ctrlKey || e.metaKey) && e.keyCode === keyCodes.Z) { // There is no error message on shortcut when it's empty this.undo()['catch'](() => {}); @@ -224,14 +305,48 @@ class ImageEditor { this.redo()['catch'](() => {}); } - 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(); } } /* eslint-enable complexity */ + /** + * 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.} 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 * @param {Event} event mouse down event @@ -371,6 +486,25 @@ class ImageEditor { this._graphics.renderAll(); } + /** + * 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 * @param {String} commandName - Command name @@ -848,6 +982,17 @@ class ImageEditor { return this.execute(commands.CHANGE_TEXT_STYLE, id, styleObj); } + /** + * change text mode + * @param {string} type - change type + * @private + */ + _changeActivateMode(type) { + if (type !== 'ICON' && this.getDrawingMode() !== type) { + this.startDrawingMode(type); + } + } + /** * 'textChanged' event handler * @param {Object} objectProps changed object properties @@ -857,6 +1002,28 @@ class ImageEditor { this.changeText(objectProps.id, objectProps.text); } + /** + * '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 * @private @@ -914,6 +1081,32 @@ class ImageEditor { this._pushAddObjectCommand(obj); } + /** + * '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 * @param {{iconType: string, pathValue: string}} infos - Infos to register icons @@ -927,6 +1120,16 @@ class ImageEditor { this._graphics.registerPaths(infos); } + /** + * Change canvas cursor type + * @param {string} cursorType - cursor type + * @example + * imageEditor.changeCursor('crosshair'); + */ + changeCursor(cursorType) { + this._graphics.changeCursor(cursorType); + } + /** * Add icon on canvas * @param {string} type - Icon type ('arrow', 'cancel', custom icon name) @@ -1144,6 +1347,23 @@ class ImageEditor { return this.execute(commands.SET_OBJECT_PROPERTIES, id, keyValue); } + /** + * 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 * @param {number} id - object id @@ -1250,5 +1470,7 @@ class ImageEditor { } } +action.mixin(ImageEditor); CustomEvents.mixin(ImageEditor); + module.exports = ImageEditor; diff --git a/src/js/polyfill.js b/src/js/polyfill.js new file mode 100644 index 000000000..6cd9d2efd --- /dev/null +++ b/src/js/polyfill.js @@ -0,0 +1,492 @@ +// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest +// Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/if (!Element.prototype.matches) + Element.prototype.matches = Element.prototype.msMatchesSelector || + Element.prototype.webkitMatchesSelector; + +if (!Element.prototype.closest) + Element.prototype.closest = function(s) { + var el = this; + if (!document.documentElement.contains(el)) return null; + do { + if (el.matches(s)) return el; + el = el.parentElement || el.parentNode; + } while (el !== null && el.nodeType === 1); + return null; + }; + + + +/* + * classList.js: Cross-browser full element.classList implementation. + * 1.1.20170427 + * + * By Eli Grey, http://eligrey.com + * License: Dedicated to the public domain. + * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md + */ + +/*global self, document, DOMException */ + +/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ + +if ("document" in window.self) { + +// Full polyfill for browsers with no classList support +// Including IE < Edge missing SVGElement.classList +if (!("classList" in document.createElement("_")) + || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))) { + +(function (view) { + +"use strict"; + +if (!('Element' in view)) return; + +var + classListProp = "classList" + , protoProp = "prototype" + , elemCtrProto = view.Element[protoProp] + , objCtr = Object + , strTrim = String[protoProp].trim || function () { + return this.replace(/^\s+|\s+$/g, ""); + } + , arrIndexOf = Array[protoProp].indexOf || function (item) { + var + i = 0 + , len = this.length + ; + for (; i < len; i++) { + if (i in this && this[i] === item) { + return i; + } + } + return -1; + } + // Vendors: please allow content code to instantiate DOMExceptions + , DOMEx = function (type, message) { + this.name = type; + this.code = DOMException[type]; + this.message = message; + } + , checkTokenAndGetIndex = function (classList, token) { + if (token === "") { + throw new DOMEx( + "SYNTAX_ERR" + , "An invalid or illegal string was specified" + ); + } + if (/\s/.test(token)) { + throw new DOMEx( + "INVALID_CHARACTER_ERR" + , "String contains an invalid character" + ); + } + return arrIndexOf.call(classList, token); + } + , ClassList = function (elem) { + var + trimmedClasses = strTrim.call(elem.getAttribute("class") || "") + , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] + , i = 0 + , len = classes.length + ; + for (; i < len; i++) { + this.push(classes[i]); + } + this._updateClassName = function () { + elem.setAttribute("class", this.toString()); + }; + } + , classListProto = ClassList[protoProp] = [] + , classListGetter = function () { + return new ClassList(this); + } +; +// Most DOMException implementations don't allow calling DOMException's toString() +// on non-DOMExceptions. Error's toString() is sufficient here. +DOMEx[protoProp] = Error[protoProp]; +classListProto.item = function (i) { + return this[i] || null; +}; +classListProto.contains = function (token) { + token += ""; + return checkTokenAndGetIndex(this, token) !== -1; +}; +classListProto.add = function () { + var + tokens = arguments + , i = 0 + , l = tokens.length + , token + , updated = false + ; + do { + token = tokens[i] + ""; + if (checkTokenAndGetIndex(this, token) === -1) { + this.push(token); + updated = true; + } + } + while (++i < l); + + if (updated) { + this._updateClassName(); + } +}; +classListProto.remove = function () { + var + tokens = arguments + , i = 0 + , l = tokens.length + , token + , updated = false + , index + ; + do { + token = tokens[i] + ""; + index = checkTokenAndGetIndex(this, token); + while (index !== -1) { + this.splice(index, 1); + updated = true; + index = checkTokenAndGetIndex(this, token); + } + } + while (++i < l); + + if (updated) { + this._updateClassName(); + } +}; +classListProto.toggle = function (token, force) { + token += ""; + + var + result = this.contains(token) + , method = result ? + force !== true && "remove" + : + force !== false && "add" + ; + + if (method) { + this[method](token); + } + + if (force === true || force === false) { + return force; + } else { + return !result; + } +}; +classListProto.toString = function () { + return this.join(" "); +}; + +if (objCtr.defineProperty) { + var classListPropDesc = { + get: classListGetter + , enumerable: true + , configurable: true + }; + try { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } catch (ex) { // IE 8 doesn't support enumerable:true + // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 + // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected + if (ex.number === undefined || ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } +} else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); +} + +}(window.self)); + +} + +// There is full or partial native classList support, so just check if we need +// to normalize the add/remove and toggle APIs. + +(function () { + "use strict"; + + var testElement = document.createElement("_"); + + testElement.classList.add("c1", "c2"); + + // Polyfill for IE 10/11 and Firefox <26, where classList.add and + // classList.remove exist but support only one argument at a time. + if (!testElement.classList.contains("c2")) { + var createMethod = function(method) { + var original = DOMTokenList.prototype[method]; + + DOMTokenList.prototype[method] = function(token) { + var i, len = arguments.length; + + for (i = 0; i < len; i++) { + token = arguments[i]; + original.call(this, token); + } + }; + }; + createMethod('add'); + createMethod('remove'); + } + + testElement.classList.toggle("c3", false); + + // Polyfill for IE 10 and Firefox <24, where classList.toggle does not + // support the second argument. + if (testElement.classList.contains("c3")) { + var _toggle = DOMTokenList.prototype.toggle; + + DOMTokenList.prototype.toggle = function(token, force) { + if (1 in arguments && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; + + } + + testElement = null; +}()); + +} + + +/*! + * @copyright Copyright (c) 2017 IcoMoon.io + * @license Licensed under MIT license + * See https://github.com/Keyamoon/svgxuse + * @version 1.2.6 + */ +/*jslint browser: true */ +/*global XDomainRequest, MutationObserver, window */ +(function () { + "use strict"; + if (typeof window !== "undefined" && window.addEventListener) { + var cache = Object.create(null); // holds xhr objects to prevent multiple requests + var checkUseElems; + var tid; // timeout id + var debouncedCheck = function () { + clearTimeout(tid); + tid = setTimeout(checkUseElems, 100); + }; + var unobserveChanges = function () { + return; + }; + var observeChanges = function () { + var observer; + window.addEventListener("resize", debouncedCheck, false); + window.addEventListener("orientationchange", debouncedCheck, false); + if (window.MutationObserver) { + observer = new MutationObserver(debouncedCheck); + observer.observe(document.documentElement, { + childList: true, + subtree: true, + attributes: true + }); + unobserveChanges = function () { + try { + observer.disconnect(); + window.removeEventListener("resize", debouncedCheck, false); + window.removeEventListener("orientationchange", debouncedCheck, false); + } catch (ignore) {} + }; + } else { + document.documentElement.addEventListener("DOMSubtreeModified", debouncedCheck, false); + unobserveChanges = function () { + document.documentElement.removeEventListener("DOMSubtreeModified", debouncedCheck, false); + window.removeEventListener("resize", debouncedCheck, false); + window.removeEventListener("orientationchange", debouncedCheck, false); + }; + } + }; + var createRequest = function (url) { + // In IE 9, cross origin requests can only be sent using XDomainRequest. + // XDomainRequest would fail if CORS headers are not set. + // Therefore, XDomainRequest should only be used with cross origin requests. + function getOrigin(loc) { + var a; + if (loc.protocol !== undefined) { + a = loc; + } else { + a = document.createElement("a"); + a.href = loc; + } + return a.protocol.replace(/:/g, "") + a.host; + } + var Request; + var origin; + var origin2; + if (window.XMLHttpRequest) { + Request = new XMLHttpRequest(); + origin = getOrigin(location); + origin2 = getOrigin(url); + if (Request.withCredentials === undefined && origin2 !== "" && origin2 !== origin) { + Request = XDomainRequest || undefined; + } else { + Request = XMLHttpRequest; + } + } + return Request; + }; + var xlinkNS = "http://www.w3.org/1999/xlink"; + checkUseElems = function () { + var base; + var bcr; + var fallback = ""; // optional fallback URL in case no base path to SVG file was given and no symbol definition was found. + var hash; + var href; + var i; + var inProgressCount = 0; + var isHidden; + var Request; + var url; + var uses; + var xhr; + function observeIfDone() { + // If done with making changes, start watching for chagnes in DOM again + inProgressCount -= 1; + if (inProgressCount === 0) { // if all xhrs were resolved + unobserveChanges(); // make sure to remove old handlers + observeChanges(); // watch for changes to DOM + } + } + function attrUpdateFunc(spec) { + return function () { + if (cache[spec.base] !== true) { + spec.useEl.setAttributeNS(xlinkNS, "xlink:href", "#" + spec.hash); + if (spec.useEl.hasAttribute("href")) { + spec.useEl.setAttribute("href", "#" + spec.hash); + } + } + }; + } + function onloadFunc(xhr) { + return function () { + var body = document.body; + var x = document.createElement("x"); + var svg; + xhr.onload = null; + x.innerHTML = xhr.responseText; + svg = x.getElementsByTagName("svg")[0]; + if (svg) { + svg.setAttribute("aria-hidden", "true"); + svg.style.position = "absolute"; + svg.style.width = 0; + svg.style.height = 0; + svg.style.overflow = "hidden"; + body.insertBefore(svg, body.firstChild); + } + observeIfDone(); + }; + } + function onErrorTimeout(xhr) { + return function () { + xhr.onerror = null; + xhr.ontimeout = null; + observeIfDone(); + }; + } + unobserveChanges(); // stop watching for changes to DOM + // find all use elements + uses = document.getElementsByTagName("use"); + for (i = 0; i < uses.length; i += 1) { + try { + bcr = uses[i].getBoundingClientRect(); + } catch (ignore) { + // failed to get bounding rectangle of the use element + bcr = false; + } + href = uses[i].getAttribute("href") + || uses[i].getAttributeNS(xlinkNS, "href") + || uses[i].getAttribute("xlink:href"); + if (href && href.split) { + url = href.split("#"); + } else { + url = ["", ""]; + } + base = url[0]; + hash = url[1]; + isHidden = bcr && bcr.left === 0 && bcr.right === 0 && bcr.top === 0 && bcr.bottom === 0; + if (bcr && bcr.width === 0 && bcr.height === 0 && !isHidden) { + // the use element is empty + // if there is a reference to an external SVG, try to fetch it + // use the optional fallback URL if there is no reference to an external SVG + if (fallback && !base.length && hash && !document.getElementById(hash)) { + base = fallback; + } + if (uses[i].hasAttribute("href")) { + uses[i].setAttributeNS(xlinkNS, "xlink:href", href); + } + if (base.length) { + // schedule updating xlink:href + xhr = cache[base]; + if (xhr !== true) { + // true signifies that prepending the SVG was not required + setTimeout(attrUpdateFunc({ + useEl: uses[i], + base: base, + hash: hash + }), 0); + } + if (xhr === undefined) { + Request = createRequest(base); + if (Request !== undefined) { + xhr = new Request(); + cache[base] = xhr; + xhr.onload = onloadFunc(xhr); + xhr.onerror = onErrorTimeout(xhr); + xhr.ontimeout = onErrorTimeout(xhr); + xhr.open("GET", base); + xhr.send(); + inProgressCount += 1; + } + } + } + } else { + if (!isHidden) { + if (cache[base] === undefined) { + // remember this URL if the use element was not empty and no request was sent + cache[base] = true; + } else if (cache[base].onload) { + // if it turns out that prepending the SVG is not necessary, + // abort the in-progress xhr. + cache[base].abort(); + delete cache[base].onload; + cache[base] = true; + } + } else if (base.length && cache[base]) { + setTimeout(attrUpdateFunc({ + useEl: uses[i], + base: base, + hash: hash + }), 0); + } + } + } + uses = ""; + inProgressCount += 1; + observeIfDone(); + }; + var winLoad; + winLoad = function () { + window.removeEventListener("load", winLoad, false); // to prevent memory leaks + tid = setTimeout(checkUseElems, 0); + }; + if (document.readyState !== "complete") { + // The load event fires when all resources have finished loading, which allows detecting whether SVG use elements are empty. + window.addEventListener("load", winLoad, false); + } else { + // No need to add a listener if the document is already loaded, initialize immediately. + winLoad(); + } + } +}()); + + diff --git a/src/js/ui.js b/src/js/ui.js new file mode 100644 index 000000000..4d5392ae1 --- /dev/null +++ b/src/js/ui.js @@ -0,0 +1,554 @@ +import snippet from 'tui-code-snippet'; +import util from './util'; +import mainContainer from './ui/template/mainContainer'; +import controls from './ui/template/controls'; + +import Theme from './ui/theme/theme'; +import Shape from './ui/shape'; +import Crop from './ui/crop'; +import Flip from './ui/flip'; +import Rotate from './ui/rotate'; +import Text from './ui/text'; +import Mask from './ui/mask'; +import Icon from './ui/icon'; +import Draw from './ui/draw'; +import Filter from './ui/filter'; + +const SUB_UI_COMPONENT = { + Shape, + Crop, + Flip, + Rotate, + Text, + Mask, + Icon, + Draw, + Filter +}; + +const BI_EXPRESSION_MINSIZE_WHEN_TOP_POSITION = '1300'; + +/** + * Ui class + * @class + * @param {string|jQuery|HTMLElement} element - Wrapper's element or selector + * @param {Object} [options] - Ui setting options + * @param {number} option.loadImage - Init default load image + * @param {number} option.initMenu - Init start menu + * @param {Boolean} [option.menuBarPosition=bottom] - Let + * @param {Boolean} [option.applyCropSelectionStyle=false] - Let + * @param {Objecdt} actions - ui action instance + * @ignore + */ +class Ui { + constructor(element, options, actions) { + this.options = this._initializeOption(options); + + this._actions = actions; + this.submenu = false; + this.imageSize = {}; + this.uiSize = {}; + this.theme = new Theme(this.options.theme); + + this._submenuChangeTransection = false; + this._selectedElement = null; + this._mainElement = null; + this._editorElementWrap = null; + this._editorElement = null; + this._menuElement = null; + this._subMenuElement = null; + this._makeUiElement(element); + this._setUiSize(); + + this._els = { + 'undo': this._menuElement.querySelector('#tie-btn-undo'), + 'redo': this._menuElement.querySelector('#tie-btn-redo'), + 'reset': this._menuElement.querySelector('#tie-btn-reset'), + 'delete': this._menuElement.querySelector('#tie-btn-delete'), + 'deleteAll': this._menuElement.querySelector('#tie-btn-delete-all'), + 'download': this._selectedElement.querySelectorAll('.tui-image-editor-download-btn'), + 'load': this._selectedElement.querySelectorAll('.tui-image-editor-load-btn') + }; + + this._makeSubMenu(); + } + + /** + * Set Default Selection for includeUI + * @param {Object} option - imageEditor options + * @returns {Object} - extends selectionStyle option + */ + setUiDefaultSelectionStyle(option) { + return snippet.extend({ + applyCropSelectionStyle: true, + applyGroupSelectionStyle: true, + selectionStyle: { + cornerStyle: 'circle', + cornerSize: 20, + cornerColor: '#fff', + cornerStrokeColor: '#000', + transparentCorners: false, + lineWidth: 2, + borderColor: '#fff' + } + }, option); + } + + /** + * Change editor size + * @param {Object} resizeInfo - ui & image size info + * @param {Object} resizeInfo.uiSize - image size dimension + * @param {Number} resizeInfo.uiSize.width - ui width + * @param {Number} resizeInfo.uiSize.height - ui height + * @param {Object} resizeInfo.imageSize - image size dimension + * @param {Number} resizeInfo.imageSize.oldWidth - old width + * @param {Number} resizeInfo.imageSize.oldHeight - old height + * @param {Number} resizeInfo.imageSize.newWidth - new width + * @param {Number} resizeInfo.imageSize.newHeight - new height + */ + resizeEditor({uiSize, imageSize = this.imageSize} = {}) { + if (imageSize !== this.imageSize) { + this.imageSize = imageSize; + } + if (uiSize) { + this._setUiSize(uiSize); + } + const {width, height} = this._getEditorDimension(); + const editorElementStyle = this._editorElement.style; + const {menuBarPosition} = this.options; + + editorElementStyle.height = `${height}px`; + editorElementStyle.width = `${width}px`; + + const {top, bottom, left, right} = this._getEditorPosition(menuBarPosition); + + this._editorElementWrap.style.bottom = `${bottom}px`; + this._editorElementWrap.style.top = `${top}px`; + this._editorElementWrap.style.left = `${left}px`; + this._editorElementWrap.style.width = `calc(100% - ${right}px)`; + const selectElementClassList = this._selectedElement.classList; + + if (menuBarPosition === 'top' && this._selectedElement.offsetWidth < BI_EXPRESSION_MINSIZE_WHEN_TOP_POSITION) { + selectElementClassList.add('tui-image-editor-top-optimization'); + } else { + selectElementClassList.remove('tui-image-editor-top-optimization'); + } + } + + /** + * Change undo button status + * @param {Boolean} enableStatus - enabled status + */ + changeUndoButtonStatus(enableStatus) { + if (enableStatus) { + this._els.undo.classList.add('enabled'); + } else { + this._els.undo.classList.remove('enabled'); + } + } + + /** + * Change redo button status + * @param {Boolean} enableStatus - enabled status + */ + changeRedoButtonStatus(enableStatus) { + if (enableStatus) { + this._els.redo.classList.add('enabled'); + } else { + this._els.redo.classList.remove('enabled'); + } + } + + /** + * Change reset button status + * @param {Boolean} enableStatus - enabled status + */ + changeResetButtonStatus(enableStatus) { + if (enableStatus) { + this._els.reset.classList.add('enabled'); + } else { + this._els.reset.classList.remove('enabled'); + } + } + + /** + * Change delete-all button status + * @param {Boolean} enableStatus - enabled status + */ + changeDeleteAllButtonEnabled(enableStatus) { + if (enableStatus) { + this._els.deleteAll.classList.add('enabled'); + } else { + this._els.deleteAll.classList.remove('enabled'); + } + } + + /** + * Change delete button status + * @param {Boolean} enableStatus - enabled status + */ + changeDeleteButtonEnabled(enableStatus) { + if (enableStatus) { + this._els['delete'].classList.add('enabled'); + } else { + this._els['delete'].classList.remove('enabled'); + } + } + + /** + * Change delete button status + * @param {Object} [options] - Ui setting options + * @param {number} option.loadImage - Init default load image + * @param {number} option.initMenu - Init start menu + * @param {Boolean} [option.menuBarPosition=bottom] - Let + * @param {Boolean} [option.applyCropSelectionStyle=false] - Let + * @returns {Object} initialize option + * @private + */ + _initializeOption(options) { + return snippet.extend({ + loadImage: { + path: '', + name: '' + }, + menuIconPath: '', + menu: ['crop', 'flip', 'rotate', 'draw', 'shape', 'icon', 'text', 'mask', 'filter'], + initMenu: false, + uiSize: { + width: '100%', + height: '100%' + }, + menuBarPosition: 'bottom' + }, options); + } + + /** + * Set ui container size + * @param {Object} uiSize - ui dimension + * @param {number} width - width + * @param {number} height - height + * @private + */ + _setUiSize(uiSize = this.options.uiSize) { + const elementDimension = this._selectedElement.style; + elementDimension.width = uiSize.width; + elementDimension.height = uiSize.height; + } + + /** + * Make submenu dom element + * @private + */ + _makeSubMenu() { + snippet.forEach(this.options.menu, menuName => { + const SubComponentClass = SUB_UI_COMPONENT[menuName.replace(/^[a-z]/, $0 => $0.toUpperCase())]; + + // make menu element + this._makeMenuElement(menuName); + + // menu btn element + this._els[menuName] = this._menuElement.querySelector(`#tie-btn-${menuName}`); + + // submenu ui instance + this[menuName] = new SubComponentClass(this._subMenuElement, { + iconStyle: this.theme.getStyle('submenu.icon'), + menuBarPosition: this.options.menuBarPosition + }); + }); + } + + /** + * Make primary ui dom element + * @param {string|jQuery|HTMLElement} element - Wrapper's element or selector + * @private + */ + _makeUiElement(element) { + let selectedElement; + + window.snippet = snippet; + + if (element.jquery) { + [selectedElement] = element; + } else if (element.nodeType) { + selectedElement = element; + } else { + selectedElement = document.querySelector(element); + } + const selector = util.getSelector(selectedElement); + + selectedElement.classList.add('tui-image-editor-container'); + selectedElement.innerHTML = controls({ + biImage: this.theme.getStyle('common.bi'), + iconStyle: this.theme.getStyle('menu.icon'), + loadButtonStyle: this.theme.getStyle('loadButton'), + downloadButtonStyle: this.theme.getStyle('downloadButton') + }) + + mainContainer({ + biImage: this.theme.getStyle('common.bi'), + commonStyle: this.theme.getStyle('common'), + headerStyle: this.theme.getStyle('header'), + loadButtonStyle: this.theme.getStyle('loadButton'), + downloadButtonStyle: this.theme.getStyle('downloadButton'), + submenuStyle: this.theme.getStyle('submenu') + }); + + this._selectedElement = selectedElement; + this._selectedElement.classList.add(this.options.menuBarPosition); + + this._mainElement = selector('.tui-image-editor-main'); + this._editorElementWrap = selector('.tui-image-editor-wrap'); + this._editorElement = selector('.tui-image-editor'); + this._menuElement = selector('.tui-image-editor-menu'); + this._subMenuElement = selector('.tui-image-editor-submenu'); + } + + /** + * Make menu ui dom element + * @param {string} menuName - menu name + * @private + */ + _makeMenuElement(menuName) { + const btnElement = document.createElement('li'); + const {normal, active} = this.theme.getStyle('menu.icon'); + const menuItemHtml = ` + + + + + `; + + btnElement.id = `tie-btn-${menuName}`; + btnElement.className = 'tui-image-editor-item'; + btnElement.title = menuName; + btnElement.innerHTML = menuItemHtml; + + this._menuElement.appendChild(btnElement); + } + + /** + * Add help action event + * @param {string} helpName - help menu name + * @private + */ + _addHelpActionEvent(helpName) { + this._els[helpName].addEventListener('click', () => { + this._actions.main[helpName](); + }); + } + + /** + * Add download event + * @private + */ + _addDownloadEvent() { + snippet.forEach(this._els.download, element => { + element.addEventListener('click', () => { + this._actions.main.download(); + }); + }); + } + + /** + * Add load event + * @private + */ + _addLoadEvent() { + snippet.forEach(this._els.load, element => { + element.addEventListener('change', event => { + this._actions.main.load(event.target.files[0]); + }); + }); + } + + /** + * Add menu event + * @param {string} menuName - menu name + * @private + */ + _addMenuEvent(menuName) { + this._els[menuName].addEventListener('click', () => { + this.changeMenu(menuName); + }); + } + + /** + * Add menu event + * @param {string} menuName - menu name + * @private + */ + _addSubMenuEvent(menuName) { + this[menuName].addEvent(this._actions[menuName]); + } + + /** + * get editor area element + * @returns {HTMLElement} editor area html element + */ + getEditorArea() { + return this._editorElement; + } + + /** + * Init canvas + */ + initCanvas() { + const loadImageInfo = this._getLoadImage(); + if (loadImageInfo) { + this._actions.main.initLoadImage(loadImageInfo.path, loadImageInfo.name).then(() => { + this._addHelpActionEvent('undo'); + this._addHelpActionEvent('redo'); + this._addHelpActionEvent('reset'); + this._addHelpActionEvent('delete'); + this._addHelpActionEvent('deleteAll'); + + this._addDownloadEvent(); + this._addLoadEvent(); + + snippet.forEach(this.options.menu, menuName => { + this._addMenuEvent(menuName); + this._addSubMenuEvent(menuName); + }); + this._initMenu(); + }); + } + + const gridVisual = document.createElement('div'); + gridVisual.className = 'tui-image-editor-grid-visual'; + const grid = ` + + + +
`; + gridVisual.innerHTML = grid; + this._editorContainerElement = this._editorElement.querySelector('.tui-image-editor-canvas-container'); + this._editorContainerElement.appendChild(gridVisual); + } + + /** + * get editor area element + * @returns {Object} loadimage optionk + * @private + */ + _getLoadImage() { + return this.options.loadImage; + } + + /** + * change menu + * @param {string} menuName - menu name + * @param {boolean} toggle - whether toogle or not + * @param {boolean} discardSelection - discard selection + */ + changeMenu(menuName, toggle = true, discardSelection = true) { + if (!this._submenuChangeTransection) { + this._submenuChangeTransection = true; + this._changeMenu(menuName, toggle, discardSelection); + this._submenuChangeTransection = false; + } + } + + /** + * change menu + * @param {string} menuName - menu name + * @param {boolean} toggle - whether toogle or not + * @param {boolean} discardSelection - discard selection + * @private + */ + _changeMenu(menuName, toggle, discardSelection) { + if (this.submenu) { + this._els[this.submenu].classList.remove('active'); + this._mainElement.classList.remove(`tui-image-editor-menu-${this.submenu}`); + if (discardSelection) { + this._actions.main.discardSelection(); + } + this._actions.main.changeSelectableAll(true); + this[this.submenu].changeStandbyMode(); + } + + if (this.submenu === menuName && toggle) { + this.submenu = null; + } else { + this._els[menuName].classList.add('active'); + this._mainElement.classList.add(`tui-image-editor-menu-${menuName}`); + this.submenu = menuName; + this[this.submenu].changeStartMode(); + } + + this.resizeEditor(); + } + + /** + * Init menu + * @private + */ + _initMenu() { + if (this.options.initMenu) { + const evt = document.createEvent('MouseEvents'); + evt.initEvent('click', true, false); + this._els[this.options.initMenu].dispatchEvent(evt); + if (this.icon) { + this.icon.registDefaultIcon(); + } + } + } + + /** + * Get editor dimension + * @returns {Object} - width & height of editor + * @private + */ + _getEditorDimension() { + const maxHeight = parseFloat(this._editorContainerElement.style.maxHeight); + const height = (this.imageSize.newHeight > maxHeight) ? maxHeight : this.imageSize.newHeight; + + const maxWidth = parseFloat(this._editorContainerElement.style.maxWidth); + const width = (this.imageSize.newWidth > maxWidth) ? maxWidth : this.imageSize.newWidth; + + return { + width, + height + }; + } + + /** + * Get editor position + * @param {string} menuBarPosition - top or right or bottom or left + * @returns {Object} - positions (top, right, bottom, left) + * @private + */ + _getEditorPosition(menuBarPosition) { + let bottom = 0; + let top = 0; + let left = 0; + let right = 0; + + if (this.submenu) { + switch (menuBarPosition) { + case 'bottom': + bottom += 150; + break; + case 'top': + top += 150; + break; + case 'left': + left += 248; + right += 248; + break; + case 'right': + right += 248; + break; + default: + break; + } + } + + return { + top, + bottom, + left, + right + }; + } +} + +export default Ui; diff --git a/src/js/ui/crop.js b/src/js/ui/crop.js new file mode 100644 index 000000000..d8a47848a --- /dev/null +++ b/src/js/ui/crop.js @@ -0,0 +1,71 @@ +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/crop'; + +/** + * Crop ui class + * @class + * @ignore + */ +class Crop extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'crop', + iconStyle, + menuBarPosition, + templateHtml + }); + + this.status = 'active'; + this._els = { + apply: this.selector('#tie-crop-button .apply'), + cancel: this.selector('#tie-crop-button .cancel') + }; + } + + /** + * Add event for crop + * @param {Object} actions - actions for crop + * @param {Function} actions.crop - crop action + * @param {Function} actions.cancel - cancel action + */ + addEvent(actions) { + this.actions = actions; + this._els.apply.addEventListener('click', () => { + this.actions.crop(); + this._els.apply.classList.remove('active'); + }); + + this._els.cancel.addEventListener('click', () => { + this.actions.cancel(); + this._els.apply.classList.remove('active'); + }); + } + + /** + * Executed when the menu starts. + */ + changeStartMode() { + this.actions.modeChange('crop'); + } + + /** + * Returns the menu to its default state. + */ + changeStandbyMode() { + this.actions.stopDrawingMode(); + } + + /** + * Change apply button status + * @param {Boolean} enableStatus - apply button status + */ + changeApplyButtonStatus(enableStatus) { + if (enableStatus) { + this._els.apply.classList.add('active'); + } else { + this._els.apply.classList.remove('active'); + } + } +} + +export default Crop; diff --git a/src/js/ui/draw.js b/src/js/ui/draw.js new file mode 100644 index 000000000..f0ea56787 --- /dev/null +++ b/src/js/ui/draw.js @@ -0,0 +1,135 @@ +import util from '../util'; +import Colorpicker from './tools/colorpicker'; +import Range from './tools/range'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/draw'; +import {defaultDrawRangeValus} from '../consts'; +const DRAW_OPACITY = 0.7; + +/** + * Draw ui class + * @class + * @ignore + */ +class Draw extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'draw', + iconStyle, + menuBarPosition, + templateHtml + }); + + this._els = { + lineSelectButton: this.selector('#tie-draw-line-select-button'), + drawColorpicker: new Colorpicker(this.selector('#tie-draw-color'), '#00a9ff', this.toggleDirection), + drawRange: new Range(this.selector('#tie-draw-range'), defaultDrawRangeValus), + drawRangeValue: this.selector('#tie-draw-range-value') + }; + + this.type = null; + this.color = this._els.drawColorpicker.color; + this.width = this._els.drawRange.value; + } + + /** + * Add event for draw + * @param {Object} actions - actions for crop + * @param {Function} actions.setDrawMode - set draw mode + */ + addEvent(actions) { + this.actions = actions; + + this._els.lineSelectButton.addEventListener('click', this._changeDrawType.bind(this)); + this._els.drawColorpicker.on('change', this._changeDrawColor.bind(this)); + this._els.drawRange.on('change', this._changeDrawRange.bind(this)); + this._els.drawRangeValue.value = this._els.drawRange.value; + this._els.drawRangeValue.setAttribute('readonly', true); + } + + /** + * set draw mode - action runner + */ + setDrawMode() { + this.actions.setDrawMode(this.type, { + width: this.width, + color: util.getRgb(this.color, DRAW_OPACITY) + }); + } + + /** + * Returns the menu to its default state. + */ + changeStandbyMode() { + this.type = null; + this.actions.stopDrawingMode(); + this.actions.changeSelectableAll(true); + this._els.lineSelectButton.classList.remove('free'); + this._els.lineSelectButton.classList.remove('line'); + } + + /** + * Executed when the menu starts. + */ + changeStartMode() { + this.type = 'free'; + this._els.lineSelectButton.classList.add('free'); + this.setDrawMode(); + } + + /** + * Change draw type event + * @param {object} event - line select event + * @private + */ + _changeDrawType(event) { + const button = event.target.closest('.tui-image-editor-button'); + if (button) { + const lineType = this.getButtonType(button, ['free', 'line']); + this.actions.discardSelection(); + + if (this.type === lineType) { + this.changeStandbyMode(); + + return; + } + + this.changeStandbyMode(); + this.type = lineType; + this._els.lineSelectButton.classList.add(lineType); + this.setDrawMode(); + } + } + + /** + * Change drawing color + * @param {string} color - select drawing color + * @private + */ + _changeDrawColor(color) { + this.color = color || 'transparent'; + if (!this.type) { + this.changeStartMode(); + } else { + this.setDrawMode(); + } + } + + /** + * Change drawing Range + * @param {number} value - select drawing range + * @private + */ + _changeDrawRange(value) { + value = util.toInteger(value); + this._els.drawRangeValue.value = value; + this.width = value; + if (!this.type) { + this.changeStartMode(); + } else { + this.setDrawMode(); + } + } +} + +export default Draw; diff --git a/src/js/ui/filter.js b/src/js/ui/filter.js new file mode 100644 index 000000000..2576f3c11 --- /dev/null +++ b/src/js/ui/filter.js @@ -0,0 +1,231 @@ +import snippet from 'tui-code-snippet'; +import Colorpicker from './tools/colorpicker'; +import Range from './tools/range'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/filter'; +import {toInteger, toCamelCase} from '../util'; +import {defaultFilterRangeValus as FILTER_RANGE} from '../consts'; + +const PICKER_CONTROL_HEIGHT = '130px'; +const BLEND_OPTIONS = ['add', 'diff', 'subtract', 'multiply', 'screen', 'lighten', 'darken']; +const FILTER_OPTIONS = [ + 'grayscale', + 'invert', + 'sepia', + 'sepia2', + 'blur', + 'sharpen', + 'emboss', + 'remove-white', + 'gradient-transparency', + 'brightness', + 'noise', + 'pixelate', + 'color-filter', + 'tint', + 'multiply', + 'blend' +]; + +/** + * Filter ui class + * @class + * @ignore + */ +class Filter extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'filter', + iconStyle, + menuBarPosition, + templateHtml + }); + + this.checkedMap = {}; + this._makeControlElement(); + } + + /** + * Add event for filter + * @param {Object} actions - actions for crop + * @param {Function} actions.applyFilter - apply filter option + */ + addEvent({applyFilter}) { + const changeRangeValue = filterName => { + const apply = this.checkedMap[filterName].checked; + const type = filterName; + + applyFilter(apply, type, this._getFilterOption(type)); + }; + + snippet.forEach(FILTER_OPTIONS, filterName => { + const filterCheckElement = this.selector(`#tie-${filterName}`); + const filterNameCamelCase = toCamelCase(filterName); + this.checkedMap[filterNameCamelCase] = filterCheckElement; + + filterCheckElement.addEventListener('change', () => changeRangeValue(filterNameCamelCase)); + }); + + this._els.removewhiteThresholdRange.on('change', () => changeRangeValue('removeWhite')); + this._els.removewhiteDistanceRange.on('change', () => changeRangeValue('removeWhite')); + this._els.gradientTransparencyRange.on('change', () => changeRangeValue('gradientTransparency')); + this._els.colorfilterThresholeRange.on('change', () => changeRangeValue('colorFilter')); + this._els.pixelateRange.on('change', () => changeRangeValue('pixelate')); + this._els.noiseRange.on('change', () => changeRangeValue('noise')); + this._els.brightnessRange.on('change', () => changeRangeValue('brightness')); + this._els.blendType.addEventListener('change', () => changeRangeValue('blend')); + this._els.filterBlendColor.on('change', () => changeRangeValue('blend')); + this._els.filterMultiplyColor.on('change', () => changeRangeValue('multiply')); + this._els.tintOpacity.on('change', () => changeRangeValue('tint')); + this._els.filterTintColor.on('change', () => changeRangeValue('tint')); + this._els.blendType.addEventListener('click', event => event.stopPropagation()); + } + + /** + * Get filter option + * @param {String} type - filter type + * @returns {Object} filter option object + * @private + */ + _getFilterOption(type) { // eslint-disable-line + const option = {}; + switch (type) { + case 'removeWhite': + option.threshold = toInteger(this._els.removewhiteThresholdRange.value); + option.distance = toInteger(this._els.removewhiteDistanceRange.value); + break; + case 'gradientTransparency': + option.threshold = toInteger(this._els.gradientTransparencyRange.value); + break; + case 'colorFilter': + option.color = '#FFFFFF'; + option.threshold = this._els.colorfilterThresholeRange.value; + break; + case 'pixelate': + option.blocksize = toInteger(this._els.pixelateRange.value); + break; + case 'noise': + option.noise = toInteger(this._els.noiseRange.value); + break; + case 'brightness': + option.brightness = toInteger(this._els.brightnessRange.value); + break; + case 'blend': + option.color = this._els.filterBlendColor.color; + option.mode = this._els.blendType.value; + break; + case 'multiply': + option.color = this._els.filterMultiplyColor.color; + break; + case 'tint': + option.color = this._els.filterTintColor.color; + option.opacity = this._els.tintOpacity.value; + break; + default: + break; + } + + return option; + } + + /** + * Make submenu range and colorpicker control + * @private + */ + _makeControlElement() { + const {selector} = this; + this._els = { + removewhiteThresholdRange: new Range( + selector('#tie-removewhite-threshold-range'), + FILTER_RANGE.removewhiteThresholdRange + ), + removewhiteDistanceRange: new Range( + selector('#tie-removewhite-distance-range'), + FILTER_RANGE.removewhiteDistanceRange + ), + gradientTransparencyRange: new Range( + selector('#tie-gradient-transparency-range'), + FILTER_RANGE.gradientTransparencyRange + ), + brightnessRange: new Range( + selector('#tie-brightness-range'), + FILTER_RANGE.brightnessRange + ), + noiseRange: new Range( + selector('#tie-noise-range'), + FILTER_RANGE.noiseRange + ), + pixelateRange: new Range( + selector('#tie-pixelate-range'), + FILTER_RANGE.pixelateRange + ), + colorfilterThresholeRange: new Range( + selector('#tie-colorfilter-threshole-range'), + FILTER_RANGE.colorfilterThresholeRange + ), + filterTintColor: new Colorpicker(selector('#tie-filter-tint-color'), '#03bd9e', this.toggleDirection), + filterMultiplyColor: new Colorpicker(selector('#tie-filter-multiply-color'), '#515ce6', this.toggleDirection), + filterBlendColor: new Colorpicker(selector('#tie-filter-blend-color'), '#ffbb3b', this.toggleDirection) + }; + this._els.tintOpacity = this._pickerWithRange(this._els.filterTintColor.pickerControl); + this._els.blendType = this._pickerWithSelectbox(this._els.filterBlendColor.pickerControl); + } + + /** + * Make submenu control for picker & range mixin + * @param {HTMLElement} pickerControl - pickerControl dom element + * @returns {Range} + * @private + */ + _pickerWithRange(pickerControl) { + const rangeWrap = document.createElement('div'); + const rangelabel = document.createElement('label'); + const range = document.createElement('div'); + + range.id = 'tie-filter-tint-opacity'; + rangelabel.innerHTML = 'Opacity'; + rangeWrap.appendChild(rangelabel); + rangeWrap.appendChild(range); + pickerControl.appendChild(rangeWrap); + pickerControl.style.height = PICKER_CONTROL_HEIGHT; + + return new Range(range, FILTER_RANGE.tintOpacityRange); + } + + /** + * Make submenu control for picker & selectbox + * @param {HTMLElement} pickerControl - pickerControl dom element + * @returns {HTMLElement} + * @private + */ + _pickerWithSelectbox(pickerControl) { + const selectlistWrap = document.createElement('div'); + const selectlist = document.createElement('select'); + + selectlistWrap.className = 'tui-image-editor-selectlist-wrap'; + selectlistWrap.appendChild(selectlist); + + this._makeSelectOptionList(selectlist); + + pickerControl.appendChild(selectlistWrap); + pickerControl.style.height = PICKER_CONTROL_HEIGHT; + + return selectlist; + } + + /** + * Make blend select option + * @param {HTMLElement} selectlist - blend option select list element + * @private + */ + _makeSelectOptionList(selectlist) { + snippet.forEach(BLEND_OPTIONS, option => { + const selectOption = document.createElement('option'); + selectOption.setAttribute('value', option); + selectOption.innerHTML = option.replace(/^[a-z]/, $0 => $0.toUpperCase()); + selectlist.appendChild(selectOption); + }); + } +} + +export default Filter; diff --git a/src/js/ui/flip.js b/src/js/ui/flip.js new file mode 100644 index 000000000..5657af8de --- /dev/null +++ b/src/js/ui/flip.js @@ -0,0 +1,66 @@ +import snippet from 'tui-code-snippet'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/flip'; + +/** + * Flip ui class + * @class + * @ignore + */ +class Flip extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'flip', + iconStyle, + menuBarPosition, + templateHtml + }); + this.flipStatus = false; + + this._els = { + flipButton: this.selector('#tie-flip-button') + }; + } + + /** + * Add event for flip + * @param {Object} actions - actions for flip + * @param {Function} actions.flip - flip action + */ + addEvent(actions) { + this._actions = actions; + this._els.flipButton.addEventListener('click', this._changeFlip.bind(this)); + } + + /** + * change Flip status + * @param {object} event - change event + * @private + */ + _changeFlip(event) { + const button = event.target.closest('.tui-image-editor-button'); + if (button) { + const flipType = this.getButtonType(button, ['flipX', 'flipY', 'resetFlip']); + if (!this.flipStatus && flipType === 'resetFlip') { + return; + } + + this._actions.flip(flipType).then(flipStatus => { + const flipClassList = this._els.flipButton.classList; + this.flipStatus = false; + + flipClassList.remove('resetFlip'); + snippet.forEach(['flipX', 'flipY'], type => { + flipClassList.remove(type); + if (flipStatus[type]) { + flipClassList.add(type); + flipClassList.add('resetFlip'); + this.flipStatus = true; + } + }); + }); + } + } +} + +export default Flip; diff --git a/src/js/ui/icon.js b/src/js/ui/icon.js new file mode 100644 index 000000000..f49cb1559 --- /dev/null +++ b/src/js/ui/icon.js @@ -0,0 +1,136 @@ +import snippet from 'tui-code-snippet'; +import Colorpicker from './tools/colorpicker'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/icon'; +import {isSupportFileApi} from '../util'; +import {defaultIconPath} from '../consts'; + +/** + * Icon ui class + * @class + * @ignore + */ +class Icon extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'icon', + iconStyle, + menuBarPosition, + templateHtml + }); + + this.iconType = null; + this._iconMap = {}; + + this._els = { + registIconButton: this.selector('#tie-icon-image-file'), + addIconButton: this.selector('#tie-icon-add-button'), + iconColorpicker: new Colorpicker(this.selector('#tie-icon-color'), '#ffbb3b', this.toggleDirection) + }; + } + + /** + * Add event for icon + * @param {Object} actions - actions for icon + * @param {Function} actions.registCustomIcon - register icon + * @param {Function} actions.addIcon - add icon + * @param {Function} actions.changeColor - change icon color + */ + addEvent(actions) { + this.actions = actions; + + this._els.iconColorpicker.on('change', this._changeColorHandler.bind(this)); + this._els.registIconButton.addEventListener('change', this._registeIconHandler.bind(this)); + this._els.addIconButton.addEventListener('click', this._addIconHandler.bind(this)); + } + + /** + * Clear icon type + */ + clearIconType() { + this._els.addIconButton.classList.remove(this.iconType); + this.iconType = null; + } + + /** + * Register default icon + */ + registDefaultIcon() { + snippet.forEach(defaultIconPath, (path, type) => { + this.actions.registDefalutIcons(type, path); + }); + } + + /** + * Set icon picker color + * @param {string} iconColor - rgb color string + */ + setIconPickerColor(iconColor) { + this._els.iconColorpicker.color = iconColor; + } + + /** + * Returns the menu to its default state. + */ + changeStandbyMode() { + this.clearIconType(); + this.actions.cancelAddIcon(); + } + + /** + * Change icon color + * @param {string} color - color for change + * @private + */ + _changeColorHandler(color) { + color = color || 'transparent'; + this.actions.changeColor(color); + } + + /** + * Change icon color + * @param {object} event - add button event object + * @private + */ + _addIconHandler(event) { + const button = event.target.closest('.tui-image-editor-button'); + + if (button) { + const iconType = button.getAttribute('data-icontype'); + const iconColor = this._els.iconColorpicker.color; + this.actions.discardSelection(); + this.actions.changeSelectableAll(false); + this._els.addIconButton.classList.remove(this.iconType); + this._els.addIconButton.classList.add(iconType); + + if (this.iconType === iconType) { + this.changeStandbyMode(); + } else { + this.actions.addIcon(iconType, iconColor); + this.iconType = iconType; + } + } + } + + /** + * register icon + * @param {object} event - file change event object + * @private + */ + _registeIconHandler(event) { + let imgUrl; + + if (!isSupportFileApi) { + alert('This browser does not support file-api'); + } + + const [file] = event.target.files; + + if (file) { + imgUrl = URL.createObjectURL(file); + this.actions.registCustomIcon(imgUrl, file); + } + } +} + +export default Icon; diff --git a/src/js/ui/mask.js b/src/js/ui/mask.js new file mode 100644 index 000000000..1d9e62cea --- /dev/null +++ b/src/js/ui/mask.js @@ -0,0 +1,68 @@ +import Submenu from './submenuBase'; +import util from '../util'; +import templateHtml from './template/submenu/mask'; + +/** + * Mask ui class + * @class + * @ignore + */ +class Mask extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'mask', + iconStyle, + menuBarPosition, + templateHtml + }); + + this._els = { + applyButton: this.selector('#tie-mask-apply'), + maskImageButton: this.selector('#tie-mask-image-file') + }; + } + + /** + * Add event for mask + * @param {Object} actions - actions for crop + * @param {Function} actions.loadImageFromURL - load image action + * @param {Function} actions.applyFilter - apply filter action + */ + addEvent(actions) { + this.actions = actions; + this._els.maskImageButton.addEventListener('change', this._loadMaskFile.bind(this)); + this._els.applyButton.addEventListener('click', this._applyMask.bind(this)); + } + + /** + * Apply mask + * @private + */ + _applyMask() { + this.actions.applyFilter(); + this._els.applyButton.classList.remove('active'); + } + + /** + * Load mask file + * @param {object} event - File change event object + * @private + */ + _loadMaskFile(event) { + let imgUrl; + + if (!util.isSupportFileApi()) { + alert('This browser does not support file-api'); + } + + const [file] = event.target.files; + + if (file) { + imgUrl = URL.createObjectURL(file); + this.actions.loadImageFromURL(imgUrl, file); + this._els.applyButton.classList.add('active'); + } + } +} + +export default Mask; diff --git a/src/js/ui/rotate.js b/src/js/ui/rotate.js new file mode 100644 index 000000000..a50a1df31 --- /dev/null +++ b/src/js/ui/rotate.js @@ -0,0 +1,74 @@ +import Range from './tools/range'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/rotate'; +import {toInteger} from '../util'; +import {defaultRotateRangeValus} from '../consts'; + +const CLOCKWISE = 30; +const COUNTERCLOCKWISE = -30; + +/** + * Rotate ui class + * @class + * @ignore + */ +class Rotate extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'rotate', + iconStyle, + menuBarPosition, + templateHtml + }); + + this._els = { + rotateButton: this.selector('#tie-retate-button'), + rotateRange: new Range(this.selector('#tie-rotate-range'), defaultRotateRangeValus), + rotateRangeValue: this.selector('#tie-ratate-range-value') + }; + } + + /** + * Add event for rotate + * @param {Object} actions - actions for crop + * @param {Function} actions.rotate - rotate action + * @param {Function} actions.setAngle - set angle action + */ + addEvent(actions) { + // {rotate, setAngle} + this.actions = actions; + this._els.rotateButton.addEventListener('click', this._changeRotateForButton.bind(this)); + this._els.rotateRange.on('change', this._changeRotateForRange.bind(this)); + this._els.rotateRangeValue.setAttribute('readonly', true); + } + + /** + * Change rotate for range + * @param {number} value - angle value + * @private + */ + _changeRotateForRange(value) { + const angle = toInteger(value); + this._els.rotateRangeValue.value = angle; + this.actions.setAngle(angle); + } + + /** + * Change rotate for button + * @param {object} event - add button event object + * @private + */ + _changeRotateForButton(event) { + const button = event.target.closest('.tui-image-editor-button'); + if (button) { + const rotateType = this.getButtonType(button, ['counterclockwise', 'clockwise']); + const rotateAngle = { + clockwise: CLOCKWISE, + counterclockwise: COUNTERCLOCKWISE + }[rotateType]; + this.actions.rotate(rotateAngle); + } + } +} + +export default Rotate; diff --git a/src/js/ui/shape.js b/src/js/ui/shape.js new file mode 100644 index 000000000..1405b555e --- /dev/null +++ b/src/js/ui/shape.js @@ -0,0 +1,190 @@ +import Colorpicker from './tools/colorpicker'; +import Range from './tools/range'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/shape'; +import {toInteger} from '../util'; +import {defaultShapeStrokeValus} from '../consts'; + +const SHAPE_DEFAULT_OPTION = { + stroke: '#ffbb3b', + fill: '', + strokeWidth: 3 +}; + +/** + * Shape ui class + * @class + * @ignore + */ +class Shape extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'shape', + iconStyle, + menuBarPosition, + templateHtml + }); + this.type = null; + this.options = SHAPE_DEFAULT_OPTION; + + this._els = { + shapeSelectButton: this.selector('#tie-shape-button'), + shapeColorButton: this.selector('#tie-shape-color-button'), + strokeRange: new Range(this.selector('#tie-stroke-range'), defaultShapeStrokeValus), + strokeRangeValue: this.selector('#tie-stroke-range-value'), + fillColorpicker: new Colorpicker(this.selector('#tie-color-fill'), '', this.toggleDirection), + strokeColorpicker: new Colorpicker(this.selector('#tie-color-stroke'), '#ffbb3b', this.toggleDirection) + }; + } + + /** + * Add event for shape + * @param {Object} actions - actions for shape + * @param {Function} actions.changeShape - change shape mode + * @param {Function} actions.setDrawingShape - set dreawing shape + */ + addEvent(actions) { + this.actions = actions; + + this._els.shapeSelectButton.addEventListener('click', this._changeShapeHandler.bind(this)); + this._els.strokeRange.on('change', this._changeStrokeRangeHandler.bind(this)); + this._els.fillColorpicker.on('change', this._changeFillColorHandler.bind(this)); + this._els.strokeColorpicker.on('change', this._changeStrokeColorHandler.bind(this)); + this._els.strokeRangeValue.value = this._els.strokeRange.value; + this._els.strokeRangeValue.setAttribute('readonly', true); + } + + /** + * Set Shape status + * @param {Object} options - options of shape status + * @param {string} strokeWidth - stroke width + * @param {string} strokeColor - stroke color + * @param {string} fillColor - fill color + */ + setShapeStatus({strokeWidth, strokeColor, fillColor}) { + this._els.strokeRange.value = strokeWidth; + this._els.strokeRange.trigger('change'); + + this._els.strokeColorpicker.color = strokeColor; + this._els.fillColorpicker.color = fillColor; + this.options.stroke = strokeColor; + this.options.fill = fillColor; + this.options.strokeWidth = strokeWidth; + } + + /** + * Executed when the menu starts. + */ + changeStartMode() { + this.actions.stopDrawingMode(); + } + + /** + * Returns the menu to its default state. + */ + changeStandbyMode() { + this.type = null; + this.actions.changeSelectableAll(true); + this._els.shapeSelectButton.classList.remove('circle'); + this._els.shapeSelectButton.classList.remove('triangle'); + this._els.shapeSelectButton.classList.remove('rect'); + } + + /** + * set range stroke max value + * @param {number} maxValue - expect max value for change + */ + setMaxStrokeValue(maxValue) { + let strokeMaxValue = maxValue; + if (strokeMaxValue <= 0) { + strokeMaxValue = defaultShapeStrokeValus.max; + } + this._els.strokeRange.max = strokeMaxValue; + } + + /** + * Set stroke value + * @param {number} value - expect value for strokeRange change + */ + setStrokeValue(value) { + this._els.strokeRange.value = value; + this._els.strokeRange.trigger('change'); + } + + /** + * Get stroke value + * @returns {number} - stroke range value + */ + getStrokeValue() { + return this._els.strokeRange.value; + } + + /** + * Change icon color + * @param {object} event - add button event object + * @private + */ + _changeShapeHandler(event) { + const button = event.target.closest('.tui-image-editor-button'); + if (button) { + this.actions.stopDrawingMode(); + this.actions.discardSelection(); + const shapeType = this.getButtonType(button, ['circle', 'triangle', 'rect']); + + if (this.type === shapeType) { + this.changeStandbyMode(); + + return; + } + this.changeStandbyMode(); + this.type = shapeType; + event.currentTarget.classList.add(shapeType); + this.actions.changeSelectableAll(false); + this.actions.modeChange('shape'); + } + } + + /** + * Change stroke range + * @param {number} value - stroke range value + * @private + */ + _changeStrokeRangeHandler(value) { + this.options.strokeWidth = toInteger(value); + this._els.strokeRangeValue.value = toInteger(value); + + this.actions.changeShape({ + strokeWidth: value + }); + + this.actions.setDrawingShape(this.type, this.options); + } + + /** + * Change shape color + * @param {string} color - fill color + * @private + */ + _changeFillColorHandler(color) { + color = color || 'transparent'; + this.options.fill = color; + this.actions.changeShape({ + fill: color + }); + } + + /** + * Change shape stroke color + * @param {string} color - fill color + * @private + */ + _changeStrokeColorHandler(color) { + color = color || 'transparent'; + this.options.stroke = color; + this.actions.changeShape({ + stroke: color + }); + } +} + +export default Shape; diff --git a/src/js/ui/submenuBase.js b/src/js/ui/submenuBase.js new file mode 100644 index 000000000..399bbaa9c --- /dev/null +++ b/src/js/ui/submenuBase.js @@ -0,0 +1,66 @@ +/** + * Submenu Base Class + * @class + * @ignore + */ +class Submenu { + constructor(subMenuElement, {name, iconStyle, menuBarPosition, templateHtml}) { + this.selector = str => subMenuElement.querySelector(str); + this.menuBarPosition = menuBarPosition; + this.toggleDirection = menuBarPosition === 'top' ? 'down' : 'up'; + this._makeSubMenuElement(subMenuElement, { + name, + iconStyle, + templateHtml + }); + } + + /** + * Get butten type + * @param {HTMLElement} button - event target element + * @param {array} buttonNames - Array of button names + * @returns {string} - button type + */ + getButtonType(button, buttonNames) { + return button.className.match(RegExp(`(${buttonNames.join('|')})`))[0]; + } + + /** + * Get butten type + * @param {HTMLElement} target - event target element + * @param {string} removeClass - remove class name + * @param {string} addClass - add class name + */ + changeClass(target, removeClass, addClass) { + target.classList.remove(removeClass); + target.classList.add(addClass); + } + + /** + * Interface method whose implementation is optional. + * Returns the menu to its default state. + */ + changeStandbyMode() {} + + /** + * Interface method whose implementation is optional. + * Executed when the menu starts. + */ + changeStartMode() {} + + /** + * Make submenu dom element + * @param {HTMLElement} subMenuElement - subment dom element + * @param {Object} iconStyle - icon style + * @private + */ + _makeSubMenuElement(subMenuElement, {name, iconStyle, templateHtml}) { + const iconSubMenu = document.createElement('div'); + iconSubMenu.className = `tui-image-editor-menu-${name}`; + iconSubMenu.innerHTML = templateHtml({iconStyle}); + + subMenuElement.appendChild(iconSubMenu); + } +} + +export default Submenu; diff --git a/src/js/ui/template/controls.js b/src/js/ui/template/controls.js new file mode 100644 index 000000000..149475f76 --- /dev/null +++ b/src/js/ui/template/controls.js @@ -0,0 +1,55 @@ +export default ({biImage, iconStyle: {normal, active}, loadButtonStyle, downloadButtonStyle}) => (` +
+ +
    +
  • + + + + +
  • +
  • + + + + +
  • +
  • + + + + +
  • +
  • +
    +
  • +
  • + + + + +
  • +
  • + + + + +
  • +
  • +
    +
  • +
+ +
+ + +
+
+`); diff --git a/src/js/ui/template/mainContainer.js b/src/js/ui/template/mainContainer.js new file mode 100644 index 000000000..f07461263 --- /dev/null +++ b/src/js/ui/template/mainContainer.js @@ -0,0 +1,29 @@ +export default ({biImage, commonStyle, headerStyle, loadButtonStyle, downloadButtonStyle, submenuStyle}) => (` +
+
+ +
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+`); diff --git a/src/js/ui/template/style.js b/src/js/ui/template/style.js new file mode 100644 index 000000000..80f80a526 --- /dev/null +++ b/src/js/ui/template/style.js @@ -0,0 +1,91 @@ +export default ({ + subMenuLabelActive, + subMenuLabelNormal, + subMenuRangeTitle, + submenuPartitionVertical, + submenuPartitionHorizontal, + submenuCheckbox, + submenuRangePointer, + submenuRangeValue, + submenuColorpickerTitle, + submenuColorpickerButton, + submenuRangeBar, + submenuRangeSubbar, + submenuIconSize, + menuIconSize, + biSize +}) => (` + #tie-icon-add-button.icon-bubble .tui-image-editor-button[data-icontype="icon-bubble"] label, + #tie-icon-add-button.icon-heart .tui-image-editor-button[data-icontype="icon-heart"] label, + #tie-icon-add-button.icon-location .tui-image-editor-button[data-icontype="icon-location"] label, + #tie-icon-add-button.icon-polygon .tui-image-editor-button[data-icontype="icon-polygon"] label, + #tie-icon-add-button.icon-star .tui-image-editor-button[data-icontype="icon-star"] label, + #tie-icon-add-button.icon-arrow-3 .tui-image-editor-button[data-icontype="icon-arrow-3"] label, + #tie-icon-add-button.icon-arrow-2 .tui-image-editor-button[data-icontype="icon-arrow-2"] label, + #tie-icon-add-button.icon-arrow .tui-image-editor-button[data-icontype="icon-arrow"] label, + #tie-icon-add-button.icon-bubble .tui-image-editor-button[data-icontype="icon-bubble"] label, + #tie-draw-line-select-button.line .tui-image-editor-button.line label, + #tie-draw-line-select-button.free .tui-image-editor-button.free label, + #tie-flip-button.flipX .tui-image-editor-button.flipX label, + #tie-flip-button.flipY .tui-image-editor-button.flipY label, + #tie-flip-button.resetFlip .tui-image-editor-button.resetFlip label, + #tie-crop-button .tui-image-editor-button.apply.active label, + #tie-shape-button.rect .tui-image-editor-button.rect label, + #tie-shape-button.circle .tui-image-editor-button.circle label, + #tie-shape-button.triangle .tui-image-editor-button.triangle label, + #tie-text-effect-button .tui-image-editor-button.active label, + #tie-text-align-button.left .tui-image-editor-button.left label, + #tie-text-align-button.center .tui-image-editor-button.center label, + #tie-text-align-button.right .tui-image-editor-button.right label, + #tie-mask-apply.apply.active .tui-image-editor-button.apply label, + .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button:hover > label, + .tui-image-editor-container .tui-image-editor-checkbox input + label { + ${subMenuLabelActive} + } + .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button > label, + .tui-image-editor-container .tui-image-editor-range-wrap.tui-image-editor-newline.short label { + ${subMenuLabelNormal} + } + .tui-image-editor-container .tui-image-editor-range-wrap label { + ${subMenuRangeTitle} + } + .tui-image-editor-container .tui-image-editor-partition > div { + ${submenuPartitionVertical} + } + .tui-image-editor-container.left .tui-image-editor-submenu .tui-image-editor-partition > div, + .tui-image-editor-container.right .tui-image-editor-submenu .tui-image-editor-partition > div { + ${submenuPartitionHorizontal} + } + .tui-image-editor-container .tui-image-editor-checkbox input + label:before { + ${submenuCheckbox} + } + .tui-image-editor-container .tui-image-editor-virtual-range-pointer { + ${submenuRangePointer} + } + .tui-image-editor-container .tui-image-editor-virtual-range-bar { + ${submenuRangeBar} + } + .tui-image-editor-container .tui-image-editor-virtual-range-subbar { + ${submenuRangeSubbar} + } + .tui-image-editor-container .tui-image-editor-range-value { + ${submenuRangeValue} + } + .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button .color-picker-value + label { + ${submenuColorpickerTitle} + } + .tui-image-editor-container .tui-image-editor-submenu .tui-image-editor-button .color-picker-value { + ${submenuColorpickerButton} + } + .tui-image-editor-container .svg_ic-menu { + ${menuIconSize} + } + .tui-image-editor-container .svg_ic-submenu { + ${submenuIconSize} + } + .tui-image-editor-container .tui-image-editor-controls-logo > img, + .tui-image-editor-container .tui-image-editor-header-logo > img { + ${biSize} + } + +`); diff --git a/src/js/ui/template/submenu/crop.js b/src/js/ui/template/submenu/crop.js new file mode 100644 index 000000000..109169846 --- /dev/null +++ b/src/js/ui/template/submenu/crop.js @@ -0,0 +1,24 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    + + + + + +
    +
    + + + + + +
    +
  • +
+`); diff --git a/src/js/ui/template/submenu/draw.js b/src/js/ui/template/submenu/draw.js new file mode 100644 index 000000000..c073f7c80 --- /dev/null +++ b/src/js/ui/template/submenu/draw.js @@ -0,0 +1,42 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • + +
    + +
  • +
+`); diff --git a/src/js/ui/template/submenu/filter.js b/src/js/ui/template/submenu/filter.js new file mode 100644 index 000000000..461b066fc --- /dev/null +++ b/src/js/ui/template/submenu/filter.js @@ -0,0 +1,140 @@ +export default () => (` +
    +
  • +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
  • +
  • +
    +
  • +
  • +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    +
  • +
  • +
    +
  • +
  • +
    +
    + + +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    +
  • +
  • +
    +
  • +
  • +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
  • +
+`); diff --git a/src/js/ui/template/submenu/flip.js b/src/js/ui/template/submenu/flip.js new file mode 100644 index 000000000..fce01af04 --- /dev/null +++ b/src/js/ui/template/submenu/flip.js @@ -0,0 +1,46 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
    + + + + +
    + +
    +
  • +
+`); diff --git a/src/js/ui/template/submenu/icon.js b/src/js/ui/template/submenu/icon.js new file mode 100644 index 000000000..70899a250 --- /dev/null +++ b/src/js/ui/template/submenu/icon.js @@ -0,0 +1,148 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    + +
    +
    + + + + +
    + +
    + +
    +
    + + + + +
    + +
    + +
    +
    + + + + +
    + +
    + +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
    + + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
+`); diff --git a/src/js/ui/template/submenu/mask.js b/src/js/ui/template/submenu/mask.js new file mode 100644 index 000000000..809c7e14d --- /dev/null +++ b/src/js/ui/template/submenu/mask.js @@ -0,0 +1,30 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    + + + + + +
    +
  • +
+`); diff --git a/src/js/ui/template/submenu/rotate.js b/src/js/ui/template/submenu/rotate.js new file mode 100644 index 000000000..13b0dadca --- /dev/null +++ b/src/js/ui/template/submenu/rotate.js @@ -0,0 +1,36 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • + +
    + +
  • +
+`); diff --git a/src/js/ui/template/submenu/shape.js b/src/js/ui/template/submenu/shape.js new file mode 100644 index 000000000..945ab6eed --- /dev/null +++ b/src/js/ui/template/submenu/shape.js @@ -0,0 +1,54 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
    +
  • +
  • +
    +
  • +
  • + +
    + +
  • +
+`); diff --git a/src/js/ui/template/submenu/text.js b/src/js/ui/template/submenu/text.js new file mode 100644 index 000000000..f0a960490 --- /dev/null +++ b/src/js/ui/template/submenu/text.js @@ -0,0 +1,87 @@ +export default ({iconStyle: {normal, active}}) => (` +
    +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +
    + + + + +
    + +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • + +
    + +
  • +
+`); diff --git a/src/js/ui/text.js b/src/js/ui/text.js new file mode 100644 index 000000000..6ad6906b3 --- /dev/null +++ b/src/js/ui/text.js @@ -0,0 +1,157 @@ +import Range from './tools/range'; +import Colorpicker from './tools/colorpicker'; +import Submenu from './submenuBase'; +import templateHtml from './template/submenu/text'; +import {toInteger} from '../util'; +import {defaultTextRangeValus} from '../consts'; + +/** + * Crop ui class + * @class + * @ignore + */ +class Text extends Submenu { + constructor(subMenuElement, {iconStyle, menuBarPosition}) { + super(subMenuElement, { + name: 'text', + iconStyle, + menuBarPosition, + templateHtml + }); + this.effect = { + bold: false, + italic: false, + underline: false + }; + this.align = 'left'; + this._els = { + textEffectButton: this.selector('#tie-text-effect-button'), + textAlignButton: this.selector('#tie-text-align-button'), + textColorpicker: new Colorpicker(this.selector('#tie-text-color'), '#ffbb3b', this.toggleDirection), + textRange: new Range(this.selector('#tie-text-range'), defaultTextRangeValus), + textRangeValue: this.selector('#tie-text-range-value') + }; + } + + /** + * Add event for text + * @param {Object} actions - actions for text + * @param {Function} actions.changeTextStyle - change text style + */ + addEvent(actions) { + this.actions = actions; + this._els.textEffectButton.addEventListener('click', this._setTextEffectHandler.bind(this)); + this._els.textAlignButton.addEventListener('click', this._setTextAlignHandler.bind(this)); + this._els.textRange.on('change', this._changeTextRnageHandler.bind(this)); + this._els.textRangeValue.value = this._els.textRange.value; + this._els.textRangeValue.setAttribute('readonly', true); + this._els.textColorpicker.on('change', this._changeColorHandler.bind(this)); + } + + /** + * Returns the menu to its default state. + */ + changeStandbyMode() { + this.actions.stopDrawingMode(); + } + + /** + * Executed when the menu starts. + */ + changeStartMode() { + this.actions.modeChange('text'); + } + + /** + * Get text color + * @returns {string} - text color + */ + get textColor() { + return this._els.textColorpicker.color; + } + + /** + * Get text size + * @returns {string} - text size + */ + get fontSize() { + return this._els.textRange.value; + } + + /** + * Set text size + * @param {Number} value - text size + */ + set fontSize(value) { + this._els.textRange.value = value; + this._els.textRangeValue.value = value; + } + + /** + * text effect set handler + * @param {object} event - add button event object + * @private + */ + _setTextEffectHandler(event) { + const button = event.target.closest('.tui-image-editor-button'); + const [styleType] = button.className.match(/(bold|italic|underline)/); + const styleObj = { + 'bold': {fontWeight: 'bold'}, + 'italic': {fontStyle: 'italic'}, + 'underline': {textDecoration: 'underline'} + }[styleType]; + + this.effect[styleType] = !this.effect[styleType]; + button.classList.toggle('active'); + this.actions.changeTextStyle(styleObj); + } + + /** + * text effect set handler + * @param {object} event - add button event object + * @private + */ + _setTextAlignHandler(event) { + const button = event.target.closest('.tui-image-editor-button'); + if (button) { + const styleType = this.getButtonType(button, ['left', 'center', 'right']); + + event.currentTarget.classList.remove(this.align); + if (this.align !== styleType) { + event.currentTarget.classList.add(styleType); + } + this.actions.changeTextStyle({textAlign: styleType}); + + this.align = styleType; + } + } + + /** + * text align set handler + * @param {number} value - range value + * @private + */ + _changeTextRnageHandler(value) { + value = toInteger(value); + if (toInteger(this._els.textRangeValue.value) !== value) { + this.actions.changeTextStyle({ + fontSize: value + }); + this._els.textRangeValue.value = value; + } + } + + /** + * change color handler + * @param {string} color - change color string + * @private + */ + _changeColorHandler(color) { + color = color || 'transparent'; + this.actions.changeTextStyle({ + 'fill': color + }); + } +} + +export default Text; diff --git a/src/js/ui/theme/standard.js b/src/js/ui/theme/standard.js new file mode 100644 index 000000000..71cdc21ba --- /dev/null +++ b/src/js/ui/theme/standard.js @@ -0,0 +1,208 @@ +/** + * @fileoverview The standard theme + * @author NHN Ent. FE Development Team + */ + +/** + * Full configuration for theme.
+ * @typedef {object} themeConfig + * @property {string} common.bi.image - Brand icon image + * @property {string} common.bisize.width - Icon image width + * @property {string} common.bisize.height - Icon Image Height + * @property {string} common.backgroundImage - Background image + * @property {string} common.backgroundColor - Background color + * @property {string} common.border - Full area border style + * @property {string} header.backgroundImage - header area background + * @property {string} header.backgroundColor - header area background color + * @property {string} header.border - header area border style + * @property {string} loadButton.backgroundColor - load button background color + * @property {string} loadButton.border - load button border style + * @property {string} loadButton.color - load button foreground color + * @property {string} loadButton.fontFamily - load button font type + * @property {string} loadButton.fontSize - load button font size + * @property {string} downloadButton.backgroundColor - download button background color + * @property {string} downloadButton.border - download button border style + * @property {string} downloadButton.color - download button foreground color + * @property {string} downloadButton.fontFamily - download button font type + * @property {string} downloadButton.fontSize - download button font size + * @property {string} menu.normalIcon.path - Menu default icon svg bundle file path + * @property {string} menu.normalIcon.name - Menu default icon svg bundle name + * @property {string} menu.activeIcon.path - Menu active icon svg bundle file path + * @property {string} menu.activeIcon.name - Menu active icon svg bundle name + * @property {string} menu.iconSize.width - Menu icon Size Width + * @property {string} menu.iconSize.height - Menu Icon Size Height + * @property {string} submenu.backgroundColor - Sub-menu area background color + * @property {string} submenu.partition.color - Submenu partition line color + * @property {string} submenu.normalIcon.path - Submenu default icon svg bundle file path + * @property {string} submenu.normalIcon.name - Submenu default icon svg bundle name + * @property {string} submenu.activeIcon.path - Submenu active icon svg bundle file path + * @property {string} submenu.activeIcon.name - Submenu active icon svg bundle name + * @property {string} submenu.iconSize.width - Submenu icon Size Width + * @property {string} submenu.iconSize.height - Submenu Icon Size Height + * @property {string} submenu.normalLabel.color - Submenu default label color + * @property {string} submenu.normalLabel.fontWeight - Sub Menu Default Label Font Thickness + * @property {string} submenu.activeLabel.color - Submenu active label color + * @property {string} submenu.activeLabel.fontWeight - Submenu active label Font thickness + * @property {string} checkbox.border - Checkbox border style + * @property {string} checkbox.backgroundColor - Checkbox background color + * @property {string} range.pointer.color - range control pointer color + * @property {string} range.bar.color - range control bar color + * @property {string} range.subbar.color - range control subbar color + * @property {string} range.value.color - range number box font color + * @property {string} range.value.fontWeight - range number box font thickness + * @property {string} range.value.fontSize - range number box font size + * @property {string} range.value.border - range number box border style + * @property {string} range.value.backgroundColor - range number box background color + * @property {string} range.title.color - range title font color + * @property {string} range.title.fontWeight - range title font weight + * @property {string} colorpicker.button.border - colorpicker button border style + * @property {string} colorpicker.title.color - colorpicker button title font color + * @example + // default keys and styles + var customTheme = { + 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png', + 'common.bisize.width': '251px', + 'common.bisize.height': '21px', + 'common.backgroundImage': 'none', + 'common.backgroundColor': '#1e1e1e', + 'common.border': '0px', + + // header + 'header.backgroundImage': 'none', + 'header.backgroundColor': 'transparent', + 'header.border': '0px', + + // load button + 'loadButton.backgroundColor': '#fff', + 'loadButton.border': '1px solid #ddd', + 'loadButton.color': '#222', + 'loadButton.fontFamily': 'NotoSans, sans-serif', + 'loadButton.fontSize': '12px', + + // download button + 'downloadButton.backgroundColor': '#fdba3b', + 'downloadButton.border': '1px solid #fdba3b', + 'downloadButton.color': '#fff', + 'downloadButton.fontFamily': 'NotoSans, sans-serif', + 'downloadButton.fontSize': '12px', + + // main icons + 'menu.normalIcon.path': '../dist/svg/icon-b.svg', + 'menu.normalIcon.name': 'icon-b', + 'menu.activeIcon.path': '../dist/svg/icon-a.svg', + 'menu.activeIcon.name': 'icon-a', + 'menu.iconSize.width': '24px', + 'menu.iconSize.height': '24px', + + // submenu primary color + 'submenu.backgroundColor': '#1e1e1e', + 'submenu.partition.color': '#858585', + + // submenu icons + 'submenu.normalIcon.path': '../dist/svg/icon-a.svg', + 'submenu.normalIcon.name': 'icon-a', + 'submenu.activeIcon.path': '../dist/svg/icon-c.svg', + 'submenu.activeIcon.name': 'icon-c', + 'submenu.iconSize.width': '32px', + 'submenu.iconSize.height': '32px', + + // submenu labels + 'submenu.normalLabel.color': '#858585', + 'submenu.normalLabel.fontWeight': 'lighter', + 'submenu.activeLabel.color': '#fff', + 'submenu.activeLabel.fontWeight': 'lighter', + + // checkbox style + 'checkbox.border': '1px solid #ccc', + 'checkbox.backgroundColor': '#fff', + + // rango style + 'range.pointer.color': '#fff', + 'range.bar.color': '#666', + 'range.subbar.color': '#d1d1d1', + 'range.value.color': '#fff', + 'range.value.fontWeight': 'lighter', + 'range.value.fontSize': '11px', + 'range.value.border': '1px solid #353535', + 'range.value.backgroundColor': '#151515', + 'range.title.color': '#fff', + 'range.title.fontWeight': 'lighter', + + // colorpicker style + 'colorpicker.button.border': '1px solid #1e1e1e', + 'colorpicker.title.color': '#fff' +}; + */ +export default { + 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png', + 'common.bisize.width': '251px', + 'common.bisize.height': '21px', + 'common.backgroundImage': 'none', + 'common.backgroundColor': '#1e1e1e', + 'common.border': '0px', + + // header + 'header.backgroundImage': 'none', + 'header.backgroundColor': 'transparent', + 'header.border': '0px', + + // load button + 'loadButton.backgroundColor': '#fff', + 'loadButton.border': '1px solid #ddd', + 'loadButton.color': '#222', + 'loadButton.fontFamily': 'NotoSans, sans-serif', + 'loadButton.fontSize': '12px', + + // download button + 'downloadButton.backgroundColor': '#fdba3b', + 'downloadButton.border': '1px solid #fdba3b', + 'downloadButton.color': '#fff', + 'downloadButton.fontFamily': 'NotoSans, sans-serif', + 'downloadButton.fontSize': '12px', + + // main icons + 'menu.normalIcon.path': 'icon-b.svg', + 'menu.normalIcon.name': 'icon-b', + 'menu.activeIcon.path': 'icon-a.svg', + 'menu.activeIcon.name': 'icon-a', + 'menu.iconSize.width': '24px', + 'menu.iconSize.height': '24px', + + // submenu primary color + 'submenu.backgroundColor': 'transparent', + 'submenu.partition.color': '#858585', + + // submenu icons + 'submenu.normalIcon.path': 'icon-a.svg', + 'submenu.normalIcon.name': 'icon-a', + 'submenu.activeIcon.path': 'icon-c.svg', + 'submenu.activeIcon.name': 'icon-c', + 'submenu.iconSize.width': '32px', + 'submenu.iconSize.height': '32px', + + // submenu labels + 'submenu.normalLabel.color': '#858585', + 'submenu.normalLabel.fontWeight': 'lighter', + 'submenu.activeLabel.color': '#fff', + 'submenu.activeLabel.fontWeight': 'lighter', + + // checkbox style + 'checkbox.border': '1px solid #ccc', + 'checkbox.backgroundColor': '#fff', + + // rango style + 'range.pointer.color': '#fff', + 'range.bar.color': '#666', + 'range.subbar.color': '#d1d1d1', + 'range.value.color': '#fff', + 'range.value.fontWeight': 'lighter', + 'range.value.fontSize': '11px', + 'range.value.border': '1px solid #353535', + 'range.value.backgroundColor': '#151515', + 'range.title.color': '#fff', + 'range.title.fontWeight': 'lighter', + + // colorpicker style + 'colorpicker.button.border': '1px solid #1e1e1e', + 'colorpicker.title.color': '#fff' +}; diff --git a/src/js/ui/theme/theme.js b/src/js/ui/theme/theme.js new file mode 100644 index 000000000..d1d574bbd --- /dev/null +++ b/src/js/ui/theme/theme.js @@ -0,0 +1,143 @@ +import {extend, forEach} from 'tui-code-snippet'; +import {styleLoad} from '../../util'; +import style from '../template/style'; +import standardTheme from './standard'; + +/** + * Theme manager + * @class + * @param {Object} customTheme - custom theme + * @ignore + */ +class Theme { + constructor(customTheme) { + this.styles = this._changeToObject(extend(standardTheme, customTheme)); + styleLoad(this._styleMaker()); + } + + /** + * Get a Style cssText or StyleObject + * @param {string} type - style type + * @returns {string|object} - cssText or StyleObject + */ + getStyle(type) { // eslint-disable-line + let result = null; + const firstProperty = type.replace(/\..+$/, ''); + const option = this.styles[type]; + switch (type) { + case 'common.bi': + result = this.styles[type].image; + break; + case 'menu.icon': + case 'submenu.icon': + result = { + active: this.styles[`${firstProperty}.activeIcon`], + normal: this.styles[`${firstProperty}.normalIcon`] + }; + break; + case 'submenu.label': + result = { + active: this._makeCssText(this.styles[`${firstProperty}.activeLabel`]), + normal: this._makeCssText(this.styles[`${firstProperty}.normalLabel`]) + }; + break; + case 'submenu.partition': + result = { + vertical: this._makeCssText(extend({}, option, {borderLeft: `1px solid ${option.color}`})), + horizontal: this._makeCssText(extend({}, option, {borderBottom: `1px solid ${option.color}`})) + }; + break; + case 'range.pointer': + case 'range.bar': + case 'range.subbar': + option.backgroundColor = option.color; + result = this._makeCssText(option); + break; + default: + result = this._makeCssText(option); + break; + } + + return result; + } + + /** + * Make css resource + * @returns {string} - serialized css text + * @private + */ + _styleMaker() { + const submenuLabelStyle = this.getStyle('submenu.label'); + const submenuPartitionStyle = this.getStyle('submenu.partition'); + + return style({ + subMenuLabelActive: submenuLabelStyle.active, + subMenuLabelNormal: submenuLabelStyle.normal, + submenuPartitionVertical: submenuPartitionStyle.vertical, + submenuPartitionHorizontal: submenuPartitionStyle.horizontal, + biSize: this.getStyle('common.bisize'), + subMenuRangeTitle: this.getStyle('range.title'), + submenuRangePointer: this.getStyle('range.pointer'), + submenuRangeBar: this.getStyle('range.bar'), + submenuRangeSubbar: this.getStyle('range.subbar'), + submenuRangeValue: this.getStyle('range.value'), + submenuColorpickerTitle: this.getStyle('colorpicker.title'), + submenuColorpickerButton: this.getStyle('colorpicker.button'), + submenuCheckbox: this.getStyle('checkbox'), + menuIconSize: this.getStyle('menu.iconSize'), + submenuIconSize: this.getStyle('submenu.iconSize') + }); + } + + /** + * Change to low dimensional object. + * @param {object} styleOptions - style object of user interface + * @returns {object} low level object for style apply + * @private + */ + _changeToObject(styleOptions) { + const styleObject = {}; + forEach(styleOptions, (value, key) => { + const keyExplode = key.match(/^(.+)\.([a-z]+)$/i); + const [, property, subProperty] = keyExplode; + + if (!styleObject[property]) { + styleObject[property] = {}; + } + styleObject[property][subProperty] = value; + }); + + return styleObject; + } + + /** + * Style object to Csstext serialize + * @param {object} styleObject - style object + * @returns {string} - css text string + * @private + */ + _makeCssText(styleObject) { + const converterStack = []; + + forEach(styleObject, (value, key) => { + if (['backgroundImage'].indexOf(key) > -1 && value !== 'none') { + value = `url(${value})`; + } + converterStack.push(`${this._toUnderScore(key)}: ${value}`); + }); + + return converterStack.join(';'); + } + + /** + * Camel key string to Underscore string + * @param {string} targetString - change target + * @returns {string} + * @private + */ + _toUnderScore(targetString) { + return targetString.replace(/([A-Z])/g, ($0, $1) => `-${$1.toLowerCase()}`); + } +} + +export default Theme; diff --git a/src/js/ui/tools/colorpicker.js b/src/js/ui/tools/colorpicker.js new file mode 100644 index 000000000..2631a59b5 --- /dev/null +++ b/src/js/ui/tools/colorpicker.js @@ -0,0 +1,167 @@ +import snippet from 'tui-code-snippet'; +import {toInteger} from '../../util'; +import tuiColorPicker from 'tui-color-picker'; +const PICKER_COLOR = [ + '#000000', + '#2a2a2a', + '#545454', + '#7e7e7e', + '#a8a8a8', + '#d2d2d2', + '#ffffff', + '', + '#ff4040', + '#ff6518', + '#ffbb3b', + '#03bd9e', + '#00a9ff', + '#515ce6', + '#9e5fff', + '#ff5583' +]; + +/** + * Colorpicker control class + * @class + * @ignore + */ +class Colorpicker { + constructor(colorpickerElement, defaultColor = '#7e7e7e', toggleDirection = 'up') { + const title = colorpickerElement.getAttribute('title'); + + this._show = false; + + this._toggleDirection = toggleDirection; + this._makePickerButtonElement(colorpickerElement, defaultColor); + this._makePickerLayerElement(colorpickerElement, title); + this._color = defaultColor; + this.picker = tuiColorPicker.create({ + container: this.pickerElement, + preset: PICKER_COLOR, + color: defaultColor + }); + + this._addEvent(colorpickerElement); + } + + /** + * Get color + * @returns {Number} color value + */ + get color() { + return this._color; + } + + /** + * Set color + * @param {string} color color value + */ + set color(color) { + this._color = color; + this._changeColorElement(color); + } + + /** + * Change color element + * @param {string} color color value + * #private + */ + _changeColorElement(color) { + if (color) { + this.colorElement.classList.remove('transparent'); + this.colorElement.style.backgroundColor = color; + } else { + this.colorElement.style.backgroundColor = '#fff'; + this.colorElement.classList.add('transparent'); + } + } + + /** + * Make picker button element + * @param {HTMLElement} colorpickerElement color picker element + * @param {string} defaultColor color value + * @private + */ + _makePickerButtonElement(colorpickerElement, defaultColor) { + colorpickerElement.classList.add('tui-image-editor-button'); + + this.colorElement = document.createElement('div'); + this.colorElement.className = 'color-picker-value'; + if (defaultColor) { + this.colorElement.style.backgroundColor = defaultColor; + } else { + this.colorElement.classList.add('transparent'); + } + } + + /** + * Make picker layer element + * @param {HTMLElement} colorpickerElement color picker element + * @param {string} title picker title + * @private + */ + _makePickerLayerElement(colorpickerElement, title) { + const label = document.createElement('label'); + const triangle = document.createElement('div'); + + this.pickerControl = document.createElement('div'); + this.pickerControl.className = 'color-picker-control'; + + this.pickerElement = document.createElement('div'); + this.pickerElement.className = 'color-picker'; + + label.innerHTML = title; + triangle.className = 'triangle'; + + this.pickerControl.appendChild(this.pickerElement); + this.pickerControl.appendChild(triangle); + + colorpickerElement.appendChild(this.pickerControl); + colorpickerElement.appendChild(this.colorElement); + colorpickerElement.appendChild(label); + + this._setPickerControlPosition(); + } + + /** + * Add event + * @param {HTMLElement} colorpickerElement color picker element + * @private + */ + _addEvent(colorpickerElement) { + this.picker.on('selectColor', value => { + this._changeColorElement(value.color); + this._color = value.color; + this.fire('change', value.color); + }); + colorpickerElement.addEventListener('click', event => { + this._show = !this._show; + this.pickerControl.style.display = this._show ? 'block' : 'none'; + event.stopPropagation(); + }); + document.body.addEventListener('click', () => { + this._show = false; + this.pickerControl.style.display = 'none'; + }); + } + + /** + * Set picker control position + * @private + */ + _setPickerControlPosition() { + const controlStyle = this.pickerControl.style; + const left = (toInteger(window.getComputedStyle(this.pickerControl, null).width) / 2) - 20; + let top = (toInteger(window.getComputedStyle(this.pickerControl, null).height) + 12) * -1; + + if (this._toggleDirection === 'down') { + top = 30; + } + + controlStyle.top = `${top}px`; + controlStyle.left = `-${left}px`; + } +} + +snippet.CustomEvents.mixin(Colorpicker); +export default Colorpicker; diff --git a/src/js/ui/tools/range.js b/src/js/ui/tools/range.js new file mode 100644 index 000000000..740bea46b --- /dev/null +++ b/src/js/ui/tools/range.js @@ -0,0 +1,171 @@ +import snippet from 'tui-code-snippet'; +import {toInteger} from '../../util'; + +/** + * Range control class + * @class + * @ignore + */ +class Range { + constructor(rangeElement, options = {}) { + this._value = options.value || 0; + this.rangeElement = rangeElement; + this._drawRangeElement(); + + this.rangeWidth = toInteger(window.getComputedStyle(rangeElement, null).width) - 12; + this._min = options.min || 0; + this._max = options.max || 100; + this._absMax = (this._min * -1) + this._max; + this.realTimeEvent = options.realTimeEvent || false; + + this._addClickEvent(); + this._addDragEvent(); + this.value = options.value; + this.trigger('change'); + } + + /** + * Set range max value and re position cursor + * @param {number} maxValue - max value + */ + set max(maxValue) { + this._max = maxValue; + this._absMax = (this._min * -1) + this._max; + this.value = this._value; + } + + get max() { + return this._max; + } + + /** + * Get range value + * @returns {Number} range value + */ + get value() { + return this._value; + } + + /** + * Set range value + * @param {Number} value range value + * @param {Boolean} fire whether fire custom event or not + */ + set value(value) { + const absValue = value - this._min; + let leftPosition = (absValue * this.rangeWidth) / this._absMax; + + if (this.rangeWidth < leftPosition) { + leftPosition = this.rangeWidth; + } + + this.pointer.style.left = `${leftPosition}px`; + this.subbar.style.right = `${this.rangeWidth - leftPosition}px`; + this._value = value; + } + + /** + * event tirigger + * @param {string} type - type + */ + trigger(type) { + this.fire(type, this._value); + } + + /** + * Make range element + * @private + */ + _drawRangeElement() { + this.rangeElement.classList.add('tui-image-editor-range'); + + this.bar = document.createElement('div'); + this.bar.className = 'tui-image-editor-virtual-range-bar'; + + this.subbar = document.createElement('div'); + this.subbar.className = 'tui-image-editor-virtual-range-subbar'; + + this.pointer = document.createElement('div'); + this.pointer.className = 'tui-image-editor-virtual-range-pointer'; + + this.bar.appendChild(this.subbar); + this.bar.appendChild(this.pointer); + this.rangeElement.appendChild(this.bar); + } + + /** + * Add Range click event + * @private + */ + _addClickEvent() { + this.rangeElement.addEventListener('click', event => { + event.stopPropagation(); + if (event.target.className !== 'tui-image-editor-range') { + return; + } + const touchPx = event.offsetX; + const ratio = touchPx / this.rangeWidth; + const value = (this._absMax * ratio) + this._min; + this.pointer.style.left = `${ratio * this.rangeWidth}px`; + this.subbar.style.right = `${(1 - ratio) * this.rangeWidth}px`; + this._value = value; + + this.fire('change', value); + }); + } + + /** + * Add Range drag event + * @private + */ + _addDragEvent() { + this.pointer.addEventListener('mousedown', event => { + this.firstPosition = event.screenX; + this.firstLeft = toInteger(this.pointer.style.left) || 0; + this.dragEventHandler = { + changeAngle: this._changeAngle.bind(this), + stopChangingAngle: this._stopChangingAngle.bind(this) + }; + + document.addEventListener('mousemove', this.dragEventHandler.changeAngle); + document.addEventListener('mouseup', this.dragEventHandler.stopChangingAngle); + }); + } + + /** + * change angle event + * @param {object} event - change event + * @private + */ + _changeAngle(event) { + const changePosition = event.screenX; + const diffPosition = changePosition - this.firstPosition; + let touchPx = this.firstLeft + diffPosition; + touchPx = touchPx > this.rangeWidth ? this.rangeWidth : touchPx; + touchPx = touchPx < 0 ? 0 : touchPx; + + this.pointer.style.left = `${touchPx}px`; + this.subbar.style.right = `${this.rangeWidth - touchPx}px`; + const ratio = touchPx / this.rangeWidth; + const value = (this._absMax * ratio) + this._min; + + this._value = value; + + if (this.realTimeEvent) { + this.fire('change', value); + } + } + + /** + * stop change angle event + * @private + */ + _stopChangingAngle() { + this.fire('change', this._value); + document.removeEventListener('mousemove', this.dragEventHandler.changeAngle); + document.removeEventListener('mouseup', this.dragEventHandler.stopChangingAngle); + } +} + +snippet.CustomEvents.mixin(Range); +export default Range; diff --git a/src/js/util.js b/src/js/util.js index 88e634c72..b79e1b945 100644 --- a/src/js/util.js +++ b/src/js/util.js @@ -74,6 +74,52 @@ module.exports = { return props; }, + /** + * 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 */ @@ -92,5 +138,60 @@ module.exports = { dp: hostname, dh: 'image-editor' }); + }, + + /** + * 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}); } }; diff --git a/src/svg/icon-a/ic-apply.svg b/src/svg/icon-a/ic-apply.svg new file mode 100644 index 000000000..ee04da028 --- /dev/null +++ b/src/svg/icon-a/ic-apply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-cancel.svg b/src/svg/icon-a/ic-cancel.svg new file mode 100644 index 000000000..16cf91940 --- /dev/null +++ b/src/svg/icon-a/ic-cancel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-color-transparent-w.svg b/src/svg/icon-a/ic-color-transparent-w.svg new file mode 100644 index 000000000..8238a0988 --- /dev/null +++ b/src/svg/icon-a/ic-color-transparent-w.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/svg/icon-a/ic-crop.svg b/src/svg/icon-a/ic-crop.svg new file mode 100644 index 000000000..bc6566cbc --- /dev/null +++ b/src/svg/icon-a/ic-crop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-delete-all.svg b/src/svg/icon-a/ic-delete-all.svg new file mode 100644 index 000000000..d5d7b62aa --- /dev/null +++ b/src/svg/icon-a/ic-delete-all.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-delete.svg b/src/svg/icon-a/ic-delete.svg new file mode 100644 index 000000000..24743f466 --- /dev/null +++ b/src/svg/icon-a/ic-delete.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-draw-free.svg b/src/svg/icon-a/ic-draw-free.svg new file mode 100644 index 000000000..14b58d329 --- /dev/null +++ b/src/svg/icon-a/ic-draw-free.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-draw-line.svg b/src/svg/icon-a/ic-draw-line.svg new file mode 100644 index 000000000..3ccb98099 --- /dev/null +++ b/src/svg/icon-a/ic-draw-line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-draw.svg b/src/svg/icon-a/ic-draw.svg new file mode 100644 index 000000000..ef76d7f4e --- /dev/null +++ b/src/svg/icon-a/ic-draw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-filter.svg b/src/svg/icon-a/ic-filter.svg new file mode 100644 index 000000000..c93300ada --- /dev/null +++ b/src/svg/icon-a/ic-filter.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-flip-reset.svg b/src/svg/icon-a/ic-flip-reset.svg new file mode 100644 index 000000000..eb00f251f --- /dev/null +++ b/src/svg/icon-a/ic-flip-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-flip-x.svg b/src/svg/icon-a/ic-flip-x.svg new file mode 100644 index 000000000..b30d90660 --- /dev/null +++ b/src/svg/icon-a/ic-flip-x.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-flip-y.svg b/src/svg/icon-a/ic-flip-y.svg new file mode 100644 index 000000000..309411b75 --- /dev/null +++ b/src/svg/icon-a/ic-flip-y.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-flip.svg b/src/svg/icon-a/ic-flip.svg new file mode 100644 index 000000000..626618181 --- /dev/null +++ b/src/svg/icon-a/ic-flip.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-icon-arrow-2.svg b/src/svg/icon-a/ic-icon-arrow-2.svg new file mode 100644 index 000000000..68bca5e47 --- /dev/null +++ b/src/svg/icon-a/ic-icon-arrow-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-arrow-3.svg b/src/svg/icon-a/ic-icon-arrow-3.svg new file mode 100644 index 000000000..a9208a941 --- /dev/null +++ b/src/svg/icon-a/ic-icon-arrow-3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-arrow.svg b/src/svg/icon-a/ic-icon-arrow.svg new file mode 100644 index 000000000..615b22369 --- /dev/null +++ b/src/svg/icon-a/ic-icon-arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-bubble.svg b/src/svg/icon-a/ic-icon-bubble.svg new file mode 100644 index 000000000..28cb1e4a6 --- /dev/null +++ b/src/svg/icon-a/ic-icon-bubble.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-heart.svg b/src/svg/icon-a/ic-icon-heart.svg new file mode 100644 index 000000000..51c69bafc --- /dev/null +++ b/src/svg/icon-a/ic-icon-heart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-load.svg b/src/svg/icon-a/ic-icon-load.svg new file mode 100644 index 000000000..3d6e3128b --- /dev/null +++ b/src/svg/icon-a/ic-icon-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-a/ic-icon-location.svg b/src/svg/icon-a/ic-icon-location.svg new file mode 100644 index 000000000..0fc24b5f4 --- /dev/null +++ b/src/svg/icon-a/ic-icon-location.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-a/ic-icon-polygon.svg b/src/svg/icon-a/ic-icon-polygon.svg new file mode 100644 index 000000000..a82a977b1 --- /dev/null +++ b/src/svg/icon-a/ic-icon-polygon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-star-2.svg b/src/svg/icon-a/ic-icon-star-2.svg new file mode 100644 index 000000000..1d4ba7ad8 --- /dev/null +++ b/src/svg/icon-a/ic-icon-star-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon-star.svg b/src/svg/icon-a/ic-icon-star.svg new file mode 100644 index 000000000..10110ce3e --- /dev/null +++ b/src/svg/icon-a/ic-icon-star.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-icon.svg b/src/svg/icon-a/ic-icon.svg new file mode 100644 index 000000000..16ebe5cee --- /dev/null +++ b/src/svg/icon-a/ic-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-mask-load.svg b/src/svg/icon-a/ic-mask-load.svg new file mode 100644 index 000000000..f4a1c1fde --- /dev/null +++ b/src/svg/icon-a/ic-mask-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-a/ic-mask.svg b/src/svg/icon-a/ic-mask.svg new file mode 100644 index 000000000..aaabf2173 --- /dev/null +++ b/src/svg/icon-a/ic-mask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-redo.svg b/src/svg/icon-a/ic-redo.svg new file mode 100644 index 000000000..9610a9f8d --- /dev/null +++ b/src/svg/icon-a/ic-redo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-reset.svg b/src/svg/icon-a/ic-reset.svg new file mode 100644 index 000000000..4b91e28d7 --- /dev/null +++ b/src/svg/icon-a/ic-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-rotate-clockwise.svg b/src/svg/icon-a/ic-rotate-clockwise.svg new file mode 100644 index 000000000..b35212c0d --- /dev/null +++ b/src/svg/icon-a/ic-rotate-clockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-rotate-counterclockwise.svg b/src/svg/icon-a/ic-rotate-counterclockwise.svg new file mode 100644 index 000000000..284d7af2f --- /dev/null +++ b/src/svg/icon-a/ic-rotate-counterclockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-rotate.svg b/src/svg/icon-a/ic-rotate.svg new file mode 100644 index 000000000..399654945 --- /dev/null +++ b/src/svg/icon-a/ic-rotate.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/svg/icon-a/ic-shape-circle.svg b/src/svg/icon-a/ic-shape-circle.svg new file mode 100644 index 000000000..97cfc569f --- /dev/null +++ b/src/svg/icon-a/ic-shape-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-shape-rectangle.svg b/src/svg/icon-a/ic-shape-rectangle.svg new file mode 100644 index 000000000..ddb83cbe2 --- /dev/null +++ b/src/svg/icon-a/ic-shape-rectangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-shape-triangle.svg b/src/svg/icon-a/ic-shape-triangle.svg new file mode 100644 index 000000000..965074838 --- /dev/null +++ b/src/svg/icon-a/ic-shape-triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-a/ic-shape.svg b/src/svg/icon-a/ic-shape.svg new file mode 100644 index 000000000..3e35ccdc3 --- /dev/null +++ b/src/svg/icon-a/ic-shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-text-align-center.svg b/src/svg/icon-a/ic-text-align-center.svg new file mode 100644 index 000000000..8b6a693fb --- /dev/null +++ b/src/svg/icon-a/ic-text-align-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-text-align-left.svg b/src/svg/icon-a/ic-text-align-left.svg new file mode 100644 index 000000000..1da665e36 --- /dev/null +++ b/src/svg/icon-a/ic-text-align-left.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-text-align-right.svg b/src/svg/icon-a/ic-text-align-right.svg new file mode 100644 index 000000000..1d109c703 --- /dev/null +++ b/src/svg/icon-a/ic-text-align-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-text-bold.svg b/src/svg/icon-a/ic-text-bold.svg new file mode 100644 index 000000000..d1a7754b6 --- /dev/null +++ b/src/svg/icon-a/ic-text-bold.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-text-italic.svg b/src/svg/icon-a/ic-text-italic.svg new file mode 100644 index 000000000..d1f3e436e --- /dev/null +++ b/src/svg/icon-a/ic-text-italic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-a/ic-text-underline.svg b/src/svg/icon-a/ic-text-underline.svg new file mode 100644 index 000000000..416426d87 --- /dev/null +++ b/src/svg/icon-a/ic-text-underline.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-text.svg b/src/svg/icon-a/ic-text.svg new file mode 100644 index 000000000..5c9552f77 --- /dev/null +++ b/src/svg/icon-a/ic-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/ic-undo.svg b/src/svg/icon-a/ic-undo.svg new file mode 100644 index 000000000..f56e4ff4e --- /dev/null +++ b/src/svg/icon-a/ic-undo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-a/img-bi.svg b/src/svg/icon-a/img-bi.svg new file mode 100644 index 000000000..b08a9d547 --- /dev/null +++ b/src/svg/icon-a/img-bi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-apply.svg b/src/svg/icon-b/ic-apply.svg new file mode 100644 index 000000000..25e20fa72 --- /dev/null +++ b/src/svg/icon-b/ic-apply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-cancel.svg b/src/svg/icon-b/ic-cancel.svg new file mode 100644 index 000000000..d6a081730 --- /dev/null +++ b/src/svg/icon-b/ic-cancel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-crop.svg b/src/svg/icon-b/ic-crop.svg new file mode 100644 index 000000000..e70863701 --- /dev/null +++ b/src/svg/icon-b/ic-crop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-delete-all.svg b/src/svg/icon-b/ic-delete-all.svg new file mode 100644 index 000000000..0606b2159 --- /dev/null +++ b/src/svg/icon-b/ic-delete-all.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-delete.svg b/src/svg/icon-b/ic-delete.svg new file mode 100644 index 000000000..0b668f91c --- /dev/null +++ b/src/svg/icon-b/ic-delete.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-draw-free.svg b/src/svg/icon-b/ic-draw-free.svg new file mode 100644 index 000000000..abf3d608d --- /dev/null +++ b/src/svg/icon-b/ic-draw-free.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-draw-line.svg b/src/svg/icon-b/ic-draw-line.svg new file mode 100644 index 000000000..4287054d8 --- /dev/null +++ b/src/svg/icon-b/ic-draw-line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-draw.svg b/src/svg/icon-b/ic-draw.svg new file mode 100644 index 000000000..b5ebbd167 --- /dev/null +++ b/src/svg/icon-b/ic-draw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-filter.svg b/src/svg/icon-b/ic-filter.svg new file mode 100644 index 000000000..42318d876 --- /dev/null +++ b/src/svg/icon-b/ic-filter.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-flip-reset.svg b/src/svg/icon-b/ic-flip-reset.svg new file mode 100644 index 000000000..4dc2e5fbd --- /dev/null +++ b/src/svg/icon-b/ic-flip-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-flip-x.svg b/src/svg/icon-b/ic-flip-x.svg new file mode 100644 index 000000000..9d7a35f8a --- /dev/null +++ b/src/svg/icon-b/ic-flip-x.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-flip-y.svg b/src/svg/icon-b/ic-flip-y.svg new file mode 100644 index 000000000..ef53d30f2 --- /dev/null +++ b/src/svg/icon-b/ic-flip-y.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-flip.svg b/src/svg/icon-b/ic-flip.svg new file mode 100644 index 000000000..2e0f821ed --- /dev/null +++ b/src/svg/icon-b/ic-flip.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-icon-arrow-2.svg b/src/svg/icon-b/ic-icon-arrow-2.svg new file mode 100644 index 000000000..0605d0404 --- /dev/null +++ b/src/svg/icon-b/ic-icon-arrow-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-arrow-3.svg b/src/svg/icon-b/ic-icon-arrow-3.svg new file mode 100644 index 000000000..5562d63e6 --- /dev/null +++ b/src/svg/icon-b/ic-icon-arrow-3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-arrow.svg b/src/svg/icon-b/ic-icon-arrow.svg new file mode 100644 index 000000000..88fede82f --- /dev/null +++ b/src/svg/icon-b/ic-icon-arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-bubble.svg b/src/svg/icon-b/ic-icon-bubble.svg new file mode 100644 index 000000000..bcb3cfc9c --- /dev/null +++ b/src/svg/icon-b/ic-icon-bubble.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-heart.svg b/src/svg/icon-b/ic-icon-heart.svg new file mode 100644 index 000000000..a2af6754b --- /dev/null +++ b/src/svg/icon-b/ic-icon-heart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-load.svg b/src/svg/icon-b/ic-icon-load.svg new file mode 100644 index 000000000..fa6338bfe --- /dev/null +++ b/src/svg/icon-b/ic-icon-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-b/ic-icon-location.svg b/src/svg/icon-b/ic-icon-location.svg new file mode 100644 index 000000000..9352f8ad5 --- /dev/null +++ b/src/svg/icon-b/ic-icon-location.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-b/ic-icon-polygon.svg b/src/svg/icon-b/ic-icon-polygon.svg new file mode 100644 index 000000000..6c64f78f7 --- /dev/null +++ b/src/svg/icon-b/ic-icon-polygon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-star-2.svg b/src/svg/icon-b/ic-icon-star-2.svg new file mode 100644 index 000000000..c3b3528b4 --- /dev/null +++ b/src/svg/icon-b/ic-icon-star-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon-star.svg b/src/svg/icon-b/ic-icon-star.svg new file mode 100644 index 000000000..f8309625c --- /dev/null +++ b/src/svg/icon-b/ic-icon-star.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-icon.svg b/src/svg/icon-b/ic-icon.svg new file mode 100644 index 000000000..ac9709752 --- /dev/null +++ b/src/svg/icon-b/ic-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-mask-load.svg b/src/svg/icon-b/ic-mask-load.svg new file mode 100644 index 000000000..1e904ec96 --- /dev/null +++ b/src/svg/icon-b/ic-mask-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-b/ic-mask.svg b/src/svg/icon-b/ic-mask.svg new file mode 100644 index 000000000..420e29291 --- /dev/null +++ b/src/svg/icon-b/ic-mask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-redo.svg b/src/svg/icon-b/ic-redo.svg new file mode 100644 index 000000000..34344dce1 --- /dev/null +++ b/src/svg/icon-b/ic-redo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-reset.svg b/src/svg/icon-b/ic-reset.svg new file mode 100644 index 000000000..2f1ab1756 --- /dev/null +++ b/src/svg/icon-b/ic-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-rotate-clockwise.svg b/src/svg/icon-b/ic-rotate-clockwise.svg new file mode 100644 index 000000000..27babf0aa --- /dev/null +++ b/src/svg/icon-b/ic-rotate-clockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-rotate-counterclockwise.svg b/src/svg/icon-b/ic-rotate-counterclockwise.svg new file mode 100644 index 000000000..bd8e1d66b --- /dev/null +++ b/src/svg/icon-b/ic-rotate-counterclockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-rotate.svg b/src/svg/icon-b/ic-rotate.svg new file mode 100644 index 000000000..83b521665 --- /dev/null +++ b/src/svg/icon-b/ic-rotate.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/svg/icon-b/ic-shape-circle.svg b/src/svg/icon-b/ic-shape-circle.svg new file mode 100644 index 000000000..9c7259a96 --- /dev/null +++ b/src/svg/icon-b/ic-shape-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-shape-rectangle.svg b/src/svg/icon-b/ic-shape-rectangle.svg new file mode 100644 index 000000000..0b4f5007f --- /dev/null +++ b/src/svg/icon-b/ic-shape-rectangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-shape-triangle.svg b/src/svg/icon-b/ic-shape-triangle.svg new file mode 100644 index 000000000..afe24b9e0 --- /dev/null +++ b/src/svg/icon-b/ic-shape-triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-b/ic-shape.svg b/src/svg/icon-b/ic-shape.svg new file mode 100644 index 000000000..f613aac09 --- /dev/null +++ b/src/svg/icon-b/ic-shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-text-align-center.svg b/src/svg/icon-b/ic-text-align-center.svg new file mode 100644 index 000000000..a9f191896 --- /dev/null +++ b/src/svg/icon-b/ic-text-align-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-text-align-left.svg b/src/svg/icon-b/ic-text-align-left.svg new file mode 100644 index 000000000..df974de56 --- /dev/null +++ b/src/svg/icon-b/ic-text-align-left.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-text-align-right.svg b/src/svg/icon-b/ic-text-align-right.svg new file mode 100644 index 000000000..3bd294494 --- /dev/null +++ b/src/svg/icon-b/ic-text-align-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-text-bold.svg b/src/svg/icon-b/ic-text-bold.svg new file mode 100644 index 000000000..3a8db1a7e --- /dev/null +++ b/src/svg/icon-b/ic-text-bold.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-text-italic.svg b/src/svg/icon-b/ic-text-italic.svg new file mode 100644 index 000000000..a1f1b6c7a --- /dev/null +++ b/src/svg/icon-b/ic-text-italic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-b/ic-text-underline.svg b/src/svg/icon-b/ic-text-underline.svg new file mode 100644 index 000000000..6a136bfe9 --- /dev/null +++ b/src/svg/icon-b/ic-text-underline.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-text.svg b/src/svg/icon-b/ic-text.svg new file mode 100644 index 000000000..a6aff70aa --- /dev/null +++ b/src/svg/icon-b/ic-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/ic-undo.svg b/src/svg/icon-b/ic-undo.svg new file mode 100644 index 000000000..b2e853d15 --- /dev/null +++ b/src/svg/icon-b/ic-undo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-b/img-bi.svg b/src/svg/icon-b/img-bi.svg new file mode 100644 index 000000000..b08a9d547 --- /dev/null +++ b/src/svg/icon-b/img-bi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-apply.svg b/src/svg/icon-c/ic-apply.svg new file mode 100644 index 000000000..92aee6e2b --- /dev/null +++ b/src/svg/icon-c/ic-apply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-cancel.svg b/src/svg/icon-c/ic-cancel.svg new file mode 100644 index 000000000..933c777fd --- /dev/null +++ b/src/svg/icon-c/ic-cancel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-crop.svg b/src/svg/icon-c/ic-crop.svg new file mode 100644 index 000000000..8ce0ac473 --- /dev/null +++ b/src/svg/icon-c/ic-crop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-delete-all.svg b/src/svg/icon-c/ic-delete-all.svg new file mode 100644 index 000000000..758f62e09 --- /dev/null +++ b/src/svg/icon-c/ic-delete-all.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-delete.svg b/src/svg/icon-c/ic-delete.svg new file mode 100644 index 000000000..05d1b54d8 --- /dev/null +++ b/src/svg/icon-c/ic-delete.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-draw-free.svg b/src/svg/icon-c/ic-draw-free.svg new file mode 100644 index 000000000..3ae0f5316 --- /dev/null +++ b/src/svg/icon-c/ic-draw-free.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-draw-line.svg b/src/svg/icon-c/ic-draw-line.svg new file mode 100644 index 000000000..389329b89 --- /dev/null +++ b/src/svg/icon-c/ic-draw-line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-draw.svg b/src/svg/icon-c/ic-draw.svg new file mode 100644 index 000000000..e2f711d68 --- /dev/null +++ b/src/svg/icon-c/ic-draw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-filter.svg b/src/svg/icon-c/ic-filter.svg new file mode 100644 index 000000000..7da9de698 --- /dev/null +++ b/src/svg/icon-c/ic-filter.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-flip-reset.svg b/src/svg/icon-c/ic-flip-reset.svg new file mode 100644 index 000000000..2fe3fe76e --- /dev/null +++ b/src/svg/icon-c/ic-flip-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-flip-x.svg b/src/svg/icon-c/ic-flip-x.svg new file mode 100644 index 000000000..a1c9620ab --- /dev/null +++ b/src/svg/icon-c/ic-flip-x.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-flip-y.svg b/src/svg/icon-c/ic-flip-y.svg new file mode 100644 index 000000000..e0463190b --- /dev/null +++ b/src/svg/icon-c/ic-flip-y.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-flip.svg b/src/svg/icon-c/ic-flip.svg new file mode 100644 index 000000000..6132191d6 --- /dev/null +++ b/src/svg/icon-c/ic-flip.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-icon-arrow-2.svg b/src/svg/icon-c/ic-icon-arrow-2.svg new file mode 100644 index 000000000..c5319afb8 --- /dev/null +++ b/src/svg/icon-c/ic-icon-arrow-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-arrow-3.svg b/src/svg/icon-c/ic-icon-arrow-3.svg new file mode 100644 index 000000000..fefe259fb --- /dev/null +++ b/src/svg/icon-c/ic-icon-arrow-3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-arrow.svg b/src/svg/icon-c/ic-icon-arrow.svg new file mode 100644 index 000000000..748c6b1ee --- /dev/null +++ b/src/svg/icon-c/ic-icon-arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-bubble.svg b/src/svg/icon-c/ic-icon-bubble.svg new file mode 100644 index 000000000..dc0ef7e46 --- /dev/null +++ b/src/svg/icon-c/ic-icon-bubble.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-heart.svg b/src/svg/icon-c/ic-icon-heart.svg new file mode 100644 index 000000000..d32148347 --- /dev/null +++ b/src/svg/icon-c/ic-icon-heart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-load.svg b/src/svg/icon-c/ic-icon-load.svg new file mode 100644 index 000000000..2cd39380f --- /dev/null +++ b/src/svg/icon-c/ic-icon-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-c/ic-icon-location.svg b/src/svg/icon-c/ic-icon-location.svg new file mode 100644 index 000000000..b182aeebe --- /dev/null +++ b/src/svg/icon-c/ic-icon-location.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-c/ic-icon-polygon.svg b/src/svg/icon-c/ic-icon-polygon.svg new file mode 100644 index 000000000..b633f86ba --- /dev/null +++ b/src/svg/icon-c/ic-icon-polygon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-star-2.svg b/src/svg/icon-c/ic-icon-star-2.svg new file mode 100644 index 000000000..8c4b7b587 --- /dev/null +++ b/src/svg/icon-c/ic-icon-star-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon-star.svg b/src/svg/icon-c/ic-icon-star.svg new file mode 100644 index 000000000..221229146 --- /dev/null +++ b/src/svg/icon-c/ic-icon-star.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-icon.svg b/src/svg/icon-c/ic-icon.svg new file mode 100644 index 000000000..ba25d2137 --- /dev/null +++ b/src/svg/icon-c/ic-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-mask-load.svg b/src/svg/icon-c/ic-mask-load.svg new file mode 100644 index 000000000..e0b16a66f --- /dev/null +++ b/src/svg/icon-c/ic-mask-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-c/ic-mask.svg b/src/svg/icon-c/ic-mask.svg new file mode 100644 index 000000000..ccd2d3544 --- /dev/null +++ b/src/svg/icon-c/ic-mask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-redo.svg b/src/svg/icon-c/ic-redo.svg new file mode 100644 index 000000000..e5a95c469 --- /dev/null +++ b/src/svg/icon-c/ic-redo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-reset.svg b/src/svg/icon-c/ic-reset.svg new file mode 100644 index 000000000..2940a491f --- /dev/null +++ b/src/svg/icon-c/ic-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-rotate-clockwise.svg b/src/svg/icon-c/ic-rotate-clockwise.svg new file mode 100644 index 000000000..65fd4db37 --- /dev/null +++ b/src/svg/icon-c/ic-rotate-clockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-rotate-counterclockwise.svg b/src/svg/icon-c/ic-rotate-counterclockwise.svg new file mode 100644 index 000000000..56bb228b7 --- /dev/null +++ b/src/svg/icon-c/ic-rotate-counterclockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-rotate.svg b/src/svg/icon-c/ic-rotate.svg new file mode 100644 index 000000000..ec40df8b3 --- /dev/null +++ b/src/svg/icon-c/ic-rotate.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/svg/icon-c/ic-shape-circle.svg b/src/svg/icon-c/ic-shape-circle.svg new file mode 100644 index 000000000..f24488ca7 --- /dev/null +++ b/src/svg/icon-c/ic-shape-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-shape-rectangle.svg b/src/svg/icon-c/ic-shape-rectangle.svg new file mode 100644 index 000000000..5ae54dd28 --- /dev/null +++ b/src/svg/icon-c/ic-shape-rectangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-shape-triangle.svg b/src/svg/icon-c/ic-shape-triangle.svg new file mode 100644 index 000000000..9c1bd6abc --- /dev/null +++ b/src/svg/icon-c/ic-shape-triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-c/ic-shape.svg b/src/svg/icon-c/ic-shape.svg new file mode 100644 index 000000000..3753f54d4 --- /dev/null +++ b/src/svg/icon-c/ic-shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-text-align-center.svg b/src/svg/icon-c/ic-text-align-center.svg new file mode 100644 index 000000000..7e2e5398c --- /dev/null +++ b/src/svg/icon-c/ic-text-align-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-text-align-left.svg b/src/svg/icon-c/ic-text-align-left.svg new file mode 100644 index 000000000..776214871 --- /dev/null +++ b/src/svg/icon-c/ic-text-align-left.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-text-align-right.svg b/src/svg/icon-c/ic-text-align-right.svg new file mode 100644 index 000000000..095675eae --- /dev/null +++ b/src/svg/icon-c/ic-text-align-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-text-bold.svg b/src/svg/icon-c/ic-text-bold.svg new file mode 100644 index 000000000..79ed1f384 --- /dev/null +++ b/src/svg/icon-c/ic-text-bold.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-text-italic.svg b/src/svg/icon-c/ic-text-italic.svg new file mode 100644 index 000000000..b30ee6e22 --- /dev/null +++ b/src/svg/icon-c/ic-text-italic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-c/ic-text-underline.svg b/src/svg/icon-c/ic-text-underline.svg new file mode 100644 index 000000000..bb6f2472d --- /dev/null +++ b/src/svg/icon-c/ic-text-underline.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-text.svg b/src/svg/icon-c/ic-text.svg new file mode 100644 index 000000000..90374fafe --- /dev/null +++ b/src/svg/icon-c/ic-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/ic-undo.svg b/src/svg/icon-c/ic-undo.svg new file mode 100644 index 000000000..a0c5256dc --- /dev/null +++ b/src/svg/icon-c/ic-undo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-c/img-bi.svg b/src/svg/icon-c/img-bi.svg new file mode 100644 index 000000000..b08a9d547 --- /dev/null +++ b/src/svg/icon-c/img-bi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-apply.svg b/src/svg/icon-d/ic-apply.svg new file mode 100644 index 000000000..1916c4e46 --- /dev/null +++ b/src/svg/icon-d/ic-apply.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-cancel.svg b/src/svg/icon-d/ic-cancel.svg new file mode 100644 index 000000000..6e64a48eb --- /dev/null +++ b/src/svg/icon-d/ic-cancel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-crop.svg b/src/svg/icon-d/ic-crop.svg new file mode 100644 index 000000000..f8761a291 --- /dev/null +++ b/src/svg/icon-d/ic-crop.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-delete-all.svg b/src/svg/icon-d/ic-delete-all.svg new file mode 100644 index 000000000..56a8afab3 --- /dev/null +++ b/src/svg/icon-d/ic-delete-all.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-delete.svg b/src/svg/icon-d/ic-delete.svg new file mode 100644 index 000000000..eb86aed1a --- /dev/null +++ b/src/svg/icon-d/ic-delete.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-draw-free.svg b/src/svg/icon-d/ic-draw-free.svg new file mode 100644 index 000000000..b8b45a893 --- /dev/null +++ b/src/svg/icon-d/ic-draw-free.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-draw-line.svg b/src/svg/icon-d/ic-draw-line.svg new file mode 100644 index 000000000..5c7eb755a --- /dev/null +++ b/src/svg/icon-d/ic-draw-line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-draw.svg b/src/svg/icon-d/ic-draw.svg new file mode 100644 index 000000000..f83faf86a --- /dev/null +++ b/src/svg/icon-d/ic-draw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-filter.svg b/src/svg/icon-d/ic-filter.svg new file mode 100644 index 000000000..fbb510951 --- /dev/null +++ b/src/svg/icon-d/ic-filter.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-flip-reset.svg b/src/svg/icon-d/ic-flip-reset.svg new file mode 100644 index 000000000..c70c7eb2f --- /dev/null +++ b/src/svg/icon-d/ic-flip-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-flip-x.svg b/src/svg/icon-d/ic-flip-x.svg new file mode 100644 index 000000000..50e6f7107 --- /dev/null +++ b/src/svg/icon-d/ic-flip-x.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-flip-y.svg b/src/svg/icon-d/ic-flip-y.svg new file mode 100644 index 000000000..f9c85dd73 --- /dev/null +++ b/src/svg/icon-d/ic-flip-y.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-flip.svg b/src/svg/icon-d/ic-flip.svg new file mode 100644 index 000000000..65686b30d --- /dev/null +++ b/src/svg/icon-d/ic-flip.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-icon-arrow-2.svg b/src/svg/icon-d/ic-icon-arrow-2.svg new file mode 100644 index 000000000..108c63628 --- /dev/null +++ b/src/svg/icon-d/ic-icon-arrow-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-arrow-3.svg b/src/svg/icon-d/ic-icon-arrow-3.svg new file mode 100644 index 000000000..17175cb0f --- /dev/null +++ b/src/svg/icon-d/ic-icon-arrow-3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-arrow.svg b/src/svg/icon-d/ic-icon-arrow.svg new file mode 100644 index 000000000..85ee7b29f --- /dev/null +++ b/src/svg/icon-d/ic-icon-arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-bubble.svg b/src/svg/icon-d/ic-icon-bubble.svg new file mode 100644 index 000000000..2632c59d4 --- /dev/null +++ b/src/svg/icon-d/ic-icon-bubble.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-heart.svg b/src/svg/icon-d/ic-icon-heart.svg new file mode 100644 index 000000000..ea18e58ca --- /dev/null +++ b/src/svg/icon-d/ic-icon-heart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-load.svg b/src/svg/icon-d/ic-icon-load.svg new file mode 100644 index 000000000..7efdc3d24 --- /dev/null +++ b/src/svg/icon-d/ic-icon-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-d/ic-icon-location.svg b/src/svg/icon-d/ic-icon-location.svg new file mode 100644 index 000000000..e916a8956 --- /dev/null +++ b/src/svg/icon-d/ic-icon-location.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-d/ic-icon-polygon.svg b/src/svg/icon-d/ic-icon-polygon.svg new file mode 100644 index 000000000..e5f5ec961 --- /dev/null +++ b/src/svg/icon-d/ic-icon-polygon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-star-2.svg b/src/svg/icon-d/ic-icon-star-2.svg new file mode 100644 index 000000000..32bac09a2 --- /dev/null +++ b/src/svg/icon-d/ic-icon-star-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon-star.svg b/src/svg/icon-d/ic-icon-star.svg new file mode 100644 index 000000000..b58fb4cdc --- /dev/null +++ b/src/svg/icon-d/ic-icon-star.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-icon.svg b/src/svg/icon-d/ic-icon.svg new file mode 100644 index 000000000..cacdb4b2f --- /dev/null +++ b/src/svg/icon-d/ic-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-mask-load.svg b/src/svg/icon-d/ic-mask-load.svg new file mode 100644 index 000000000..329555a5b --- /dev/null +++ b/src/svg/icon-d/ic-mask-load.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/svg/icon-d/ic-mask.svg b/src/svg/icon-d/ic-mask.svg new file mode 100644 index 000000000..6baa07d97 --- /dev/null +++ b/src/svg/icon-d/ic-mask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-redo.svg b/src/svg/icon-d/ic-redo.svg new file mode 100644 index 000000000..82758d1c1 --- /dev/null +++ b/src/svg/icon-d/ic-redo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-reset.svg b/src/svg/icon-d/ic-reset.svg new file mode 100644 index 000000000..802308647 --- /dev/null +++ b/src/svg/icon-d/ic-reset.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-rotate-clockwise.svg b/src/svg/icon-d/ic-rotate-clockwise.svg new file mode 100644 index 000000000..5bfafd5a5 --- /dev/null +++ b/src/svg/icon-d/ic-rotate-clockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-rotate-counterclockwise.svg b/src/svg/icon-d/ic-rotate-counterclockwise.svg new file mode 100644 index 000000000..60a0ac915 --- /dev/null +++ b/src/svg/icon-d/ic-rotate-counterclockwise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-rotate.svg b/src/svg/icon-d/ic-rotate.svg new file mode 100644 index 000000000..54fc39576 --- /dev/null +++ b/src/svg/icon-d/ic-rotate.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/svg/icon-d/ic-shape-circle.svg b/src/svg/icon-d/ic-shape-circle.svg new file mode 100644 index 000000000..60deaef76 --- /dev/null +++ b/src/svg/icon-d/ic-shape-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-shape-rectangle.svg b/src/svg/icon-d/ic-shape-rectangle.svg new file mode 100644 index 000000000..5e6c90e3b --- /dev/null +++ b/src/svg/icon-d/ic-shape-rectangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-shape-triangle.svg b/src/svg/icon-d/ic-shape-triangle.svg new file mode 100644 index 000000000..cc22ae899 --- /dev/null +++ b/src/svg/icon-d/ic-shape-triangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/svg/icon-d/ic-shape.svg b/src/svg/icon-d/ic-shape.svg new file mode 100644 index 000000000..37a5aae91 --- /dev/null +++ b/src/svg/icon-d/ic-shape.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-text-align-center.svg b/src/svg/icon-d/ic-text-align-center.svg new file mode 100644 index 000000000..d14cfb819 --- /dev/null +++ b/src/svg/icon-d/ic-text-align-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-text-align-left.svg b/src/svg/icon-d/ic-text-align-left.svg new file mode 100644 index 000000000..e943d0dc3 --- /dev/null +++ b/src/svg/icon-d/ic-text-align-left.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-text-align-right.svg b/src/svg/icon-d/ic-text-align-right.svg new file mode 100644 index 000000000..39465ac0c --- /dev/null +++ b/src/svg/icon-d/ic-text-align-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-text-bold.svg b/src/svg/icon-d/ic-text-bold.svg new file mode 100644 index 000000000..728074ede --- /dev/null +++ b/src/svg/icon-d/ic-text-bold.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-text-italic.svg b/src/svg/icon-d/ic-text-italic.svg new file mode 100644 index 000000000..8c895b0c3 --- /dev/null +++ b/src/svg/icon-d/ic-text-italic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/svg/icon-d/ic-text-underline.svg b/src/svg/icon-d/ic-text-underline.svg new file mode 100644 index 000000000..194d50f43 --- /dev/null +++ b/src/svg/icon-d/ic-text-underline.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-text.svg b/src/svg/icon-d/ic-text.svg new file mode 100644 index 000000000..a14602206 --- /dev/null +++ b/src/svg/icon-d/ic-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/ic-undo.svg b/src/svg/icon-d/ic-undo.svg new file mode 100644 index 000000000..00bf06ea1 --- /dev/null +++ b/src/svg/icon-d/ic-undo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/svg/icon-d/img-bi.svg b/src/svg/icon-d/img-bi.svg new file mode 100644 index 000000000..b08a9d547 --- /dev/null +++ b/src/svg/icon-d/img-bi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/test/action.spec.js b/test/action.spec.js new file mode 100644 index 000000000..cd4785279 --- /dev/null +++ b/test/action.spec.js @@ -0,0 +1,511 @@ +/** + * @author NHN Ent. FE Development Team + * @fileoverview Test cases of "src/js/action.js" + */ +import snippet from 'tui-code-snippet'; +import Promise from 'core-js/library/es6/promise'; +import ImageEditor from '../src/js/imageEditor'; +import action from '../src/js/action'; + +describe('Ui', () => { + let actions; + let imageEditorMock; + + beforeEach(() => { + action.mixin(ImageEditor); + imageEditorMock = new ImageEditor(document.createElement('div'), { + includeUI: { + loadImage: false, + initMenu: 'flip', + menuBarPosition: 'bottom', + applyCropSelectionStyle: true + } + }); + actions = imageEditorMock.getActions(); + + spyOn(snippet, 'imagePing'); + }); + + afterEach(() => { + imageEditorMock.destroy(); + }); + + describe('mainAction', () => { + let mainAction; + beforeEach(() => { + mainAction = actions.main; + }); + + it('LoadImageFromURL() API should be executed When the initLoadImage action occurs', done => { + const promise = new Promise(resolve => { + resolve(300); + }); + spyOn(imageEditorMock, 'loadImageFromURL').and.returnValue(promise); + spyOn(imageEditorMock, 'clearUndoStack'); + spyOn(imageEditorMock.ui, 'resizeEditor'); + + mainAction.initLoadImage('path', 'imageName').then(() => { + expect(imageEditorMock.clearUndoStack).toHaveBeenCalled(); + expect(imageEditorMock.ui.resizeEditor).toHaveBeenCalled(); + expect(imageEditorMock.loadImageFromURL).toHaveBeenCalled(); + done(); + }); + }); + + it('Undo() API should be executed When the undo action occurs', () => { + spyOn(imageEditorMock, 'isEmptyUndoStack').and.returnValue(false); + spyOn(imageEditorMock, 'undo'); + + mainAction.undo(); + + expect(imageEditorMock.undo).toHaveBeenCalled(); + }); + + it('Redo() API should be executed When the redo action occurs', () => { + spyOn(imageEditorMock, 'isEmptyRedoStack').and.returnValue(false); + spyOn(imageEditorMock, 'redo'); + + mainAction.redo(); + + expect(imageEditorMock.redo).toHaveBeenCalled(); + }); + + it('removeObject() API should be executed When the delete action occurs', () => { + imageEditorMock.activeObjectId = 10; + spyOn(imageEditorMock, 'removeActiveObject'); + + mainAction['delete'](); + + expect(imageEditorMock.removeActiveObject).toHaveBeenCalled(); + expect(imageEditorMock.activeObjectId).toBe(null); + }); + + it('clearObjects() API should be run and the enabled state should be changed When the deleteAll action occurs', () => { + spyOn(imageEditorMock, 'clearObjects'); + spyOn(imageEditorMock.ui, 'changeDeleteButtonEnabled'); + spyOn(imageEditorMock.ui, 'changeDeleteAllButtonEnabled'); + + mainAction.deleteAll(); + expect(imageEditorMock.clearObjects).toHaveBeenCalled(); + expect(imageEditorMock.ui.changeDeleteButtonEnabled).toHaveBeenCalled(); + expect(imageEditorMock.ui.changeDeleteAllButtonEnabled).toHaveBeenCalled(); + }); + + it('loadImageFromFile() API should be executed When the load action occurs', done => { + const promise = new Promise(resolve => { + resolve(); + }); + + spyOn(imageEditorMock, 'loadImageFromFile').and.returnValue(promise); + spyOn(imageEditorMock, 'clearUndoStack'); + spyOn(imageEditorMock.ui, 'resizeEditor'); + + window.URL = { + createObjectURL: jasmine.createSpy('URL') + }; + + mainAction.load(); + + promise.then(() => { + expect(imageEditorMock.loadImageFromFile).toHaveBeenCalled(); + expect(imageEditorMock.clearUndoStack).toHaveBeenCalled(); + expect(imageEditorMock.ui.resizeEditor).toHaveBeenCalled(); + done(); + }); + }); + }); + + describe('shapeAction', () => { + let shapeAction; + + beforeEach(() => { + shapeAction = actions.shape; + }); + + it('changeShape() API should be executed When the changeShape action occurs', () => { + imageEditorMock.activeObjectId = 10; + spyOn(imageEditorMock, 'changeShape'); + + shapeAction.changeShape({ + strokeWidth: '#000000' + }); + expect(imageEditorMock.changeShape).toHaveBeenCalled(); + }); + + it('setDrawingShape() API should be executed When the setDrawingShape action occurs', () => { + spyOn(imageEditorMock, 'setDrawingShape'); + + shapeAction.setDrawingShape(); + expect(imageEditorMock.setDrawingShape).toHaveBeenCalled(); + }); + }); + + describe('cropAction', () => { + let cropAction; + beforeEach(() => { + cropAction = actions.crop; + }); + it('getCropzoneRect(), stopDrawingMode(), ui.resizeEditor(), ui.changeMenu() API should be executed When the crop action occurs', done => { + const promise = new Promise(resolve => { + resolve(); + }); + spyOn(imageEditorMock, 'crop').and.returnValue(promise); + spyOn(imageEditorMock, 'getCropzoneRect').and.returnValue(true); + spyOn(imageEditorMock, 'stopDrawingMode'); + spyOn(imageEditorMock.ui, 'resizeEditor'); + spyOn(imageEditorMock.ui, 'changeMenu'); + + cropAction.crop(); + + expect(imageEditorMock.getCropzoneRect).toHaveBeenCalled(); + expect(imageEditorMock.crop).toHaveBeenCalled(); + promise.then(() => { + expect(imageEditorMock.stopDrawingMode).toHaveBeenCalled(); + expect(imageEditorMock.ui.resizeEditor).toHaveBeenCalled(); + expect(imageEditorMock.ui.changeMenu).toHaveBeenCalled(); + done(); + }); + }); + + it('stopDrawingMode() API should be executed When the cancel action occurs', () => { + spyOn(imageEditorMock, 'stopDrawingMode'); + spyOn(imageEditorMock.ui, 'changeMenu'); + + cropAction.cancel(); + expect(imageEditorMock.stopDrawingMode).toHaveBeenCalled(); + expect(imageEditorMock.ui.changeMenu).toHaveBeenCalled(); + }); + }); + + describe('flipAction', () => { + let flipAction; + beforeEach(() => { + flipAction = actions.flip; + }); + it('{flipType}() API should be executed When the flip(fliptype) action occurs', () => { + spyOn(imageEditorMock, 'flipX'); + spyOn(imageEditorMock, 'flipY'); + + flipAction.flip('flipX'); + expect(imageEditorMock.flipX).toHaveBeenCalled(); + + flipAction.flip('flipY'); + expect(imageEditorMock.flipY).toHaveBeenCalled(); + }); + }); + + describe('rotateAction', () => { + let rotateAction; + beforeEach(() => { + rotateAction = actions.rotate; + }); + + it('rotate() API should be executed When the rotate action occurs', () => { + spyOn(imageEditorMock, 'rotate'); + spyOn(imageEditorMock.ui, 'resizeEditor'); + + rotateAction.rotate(30); + expect(imageEditorMock.rotate).toHaveBeenCalled(); + expect(imageEditorMock.ui.resizeEditor).toHaveBeenCalled(); + }); + + it('setAngle() API should be executed When the setAngle action occurs', () => { + spyOn(imageEditorMock, 'setAngle'); + spyOn(imageEditorMock.ui, 'resizeEditor'); + + rotateAction.setAngle(30); + expect(imageEditorMock.setAngle).toHaveBeenCalled(); + expect(imageEditorMock.ui.resizeEditor).toHaveBeenCalled(); + }); + }); + + describe('textAction', () => { + let textAction; + beforeEach(() => { + textAction = actions.text; + }); + + it('changeTextStyle() API should be executed When the changeTextStyle action occurs', () => { + imageEditorMock.activeObjectId = 10; + spyOn(imageEditorMock, 'changeTextStyle'); + + textAction.changeTextStyle({fontSize: 10}); + expect(imageEditorMock.changeTextStyle.calls.mostRecent().args[0]).toBe(10); + expect(imageEditorMock.changeTextStyle.calls.mostRecent().args[1]).toEqual({fontSize: 10}); + }); + }); + + describe('maskAction', () => { + let maskAction; + beforeEach(() => { + maskAction = actions.mask; + }); + + it('applyFilter() API should be executed When the applyFilter action occurs', () => { + imageEditorMock.activeObjectId = 10; + spyOn(imageEditorMock, 'applyFilter'); + + maskAction.applyFilter(); + expect(imageEditorMock.applyFilter.calls.mostRecent().args[1]).toEqual({maskObjId: 10}); + }); + }); + + describe('drawAction', () => { + let drawAction, expected; + beforeEach(() => { + drawAction = actions.draw; + }); + + it('startDrawingMode("FREE_DRAWING") API should be executed When the setDrawMode("free") action occurs', () => { + spyOn(imageEditorMock, 'startDrawingMode'); + drawAction.setDrawMode('free'); + + expected = imageEditorMock.startDrawingMode.calls.mostRecent().args[0]; + expect(expected).toBe('FREE_DRAWING'); + }); + + it('setBrush() API should be executed When the setColor() action occurs', () => { + spyOn(imageEditorMock, 'setBrush'); + drawAction.setColor('#000000'); + + expected = imageEditorMock.setBrush.calls.mostRecent().args[0].color; + expect(expected).toBe('#000000'); + }); + }); + + describe('iconAction', () => { + let iconAction, expected; + beforeEach(() => { + iconAction = actions.icon; + }); + + it('add once event mousedown should be executed When the addIcon action occurs', () => { + const promise = new Promise(resolve => { + resolve(300); + }); + + spyOn(imageEditorMock, 'changeCursor'); + spyOn(imageEditorMock, 'addIcon').and.returnValue(promise); + + iconAction.addIcon('iconTypeA'); + expect(imageEditorMock.changeCursor).toHaveBeenCalled(); + + imageEditorMock.fire('mousedown', null, { + x: 10, + y: 10 + }); + expected = imageEditorMock.addIcon.calls.mostRecent().args[0]; + expect(expected).toBe('iconTypeA'); + }); + }); + + describe('filterAction', () => { + let filterAction; + beforeEach(() => { + filterAction = actions.filter; + }); + + it('removeFilter() API should be executed When the type of applyFilter is false', () => { + spyOn(imageEditorMock, 'removeFilter'); + spyOn(imageEditorMock, 'hasFilter').and.returnValue(true); + filterAction.applyFilter(false, {}); + + expect(imageEditorMock.removeFilter).toHaveBeenCalled(); + }); + + it('applyFilter() API should be executed When the type of applyFilter is true', () => { + spyOn(imageEditorMock, 'applyFilter'); + filterAction.applyFilter(true, {}); + + expect(imageEditorMock.applyFilter).toHaveBeenCalled(); + }); + }); + + describe('commonAction', () => { + it('Each action returned to the getActions method must contain commonAction.', () => { + const submenus = ['shape', 'crop', 'flip', 'rotate', 'text', 'mask', 'draw', 'icon', 'filter']; + snippet.forEach(submenus, submenu => { + expect(actions[submenu].modeChange).toBeDefined(); + expect(actions[submenu].deactivateAll).toBeDefined(); + expect(actions[submenu].changeSelectableAll).toBeDefined(); + expect(actions[submenu].discardSelection).toBeDefined(); + expect(actions[submenu].stopDrawingMode).toBeDefined(); + }); + }); + + describe('modeChange()', () => { + let commonAction; + beforeEach(() => { + commonAction = actions.main; + }); + + it('_changeActivateMode("TEXT") API should be executed When the modeChange("text") action occurs', () => { + spyOn(imageEditorMock, '_changeActivateMode'); + + commonAction.modeChange('text'); + expect(imageEditorMock._changeActivateMode).toHaveBeenCalled(); + }); + + it('startDrawingMode() API should be executed When the modeChange("crop") action occurs', () => { + spyOn(imageEditorMock, 'startDrawingMode'); + + commonAction.modeChange('crop'); + expect(imageEditorMock.startDrawingMode).toHaveBeenCalled(); + }); + + it('stopDrawingMode(), setDrawingShape(), _changeActivateMode() API should be executed When the modeChange("shape") action occurs', () => { + spyOn(imageEditorMock, 'setDrawingShape'); + spyOn(imageEditorMock, '_changeActivateMode'); + + commonAction.modeChange('shape'); + expect(imageEditorMock.setDrawingShape).toHaveBeenCalled(); + expect(imageEditorMock._changeActivateMode).toHaveBeenCalled(); + }); + }); + }); + + describe('reAction', () => { + beforeEach(() => { + imageEditorMock.setReAction(); + }); + + describe('undoStackChanged', () => { + it('If the undo stack has a length greater than zero, the state of changeUndoButtonStatus, changeResetButtonStatus should be true.', () => { + spyOn(imageEditorMock.ui, 'changeUndoButtonStatus'); + spyOn(imageEditorMock.ui, 'changeResetButtonStatus'); + imageEditorMock.fire('undoStackChanged', 1); + + expect(imageEditorMock.ui.changeUndoButtonStatus.calls.mostRecent().args[0]).toBe(true); + expect(imageEditorMock.ui.changeResetButtonStatus.calls.mostRecent().args[0]).toBe(true); + }); + + it('If the undo stack has a length of 0, the state of changeUndoButtonStatus, changeResetButtonStatus should be false.', () => { + spyOn(imageEditorMock.ui, 'changeUndoButtonStatus'); + spyOn(imageEditorMock.ui, 'changeResetButtonStatus'); + imageEditorMock.fire('undoStackChanged', 0); + + expect(imageEditorMock.ui.changeUndoButtonStatus.calls.mostRecent().args[0]).toBe(false); + expect(imageEditorMock.ui.changeResetButtonStatus.calls.mostRecent().args[0]).toBe(false); + }); + }); + + describe('redoStackChanged', () => { + it('If the redo stack is greater than zero length, the state of changeRedoButtonStatus should be true.', () => { + spyOn(imageEditorMock.ui, 'changeRedoButtonStatus'); + imageEditorMock.fire('redoStackChanged', 1); + expect(imageEditorMock.ui.changeRedoButtonStatus.calls.mostRecent().args[0]).toBe(true); + }); + + it('If the redo stack has a length of zero, the state of changeRedoButtonStatus should be false.', () => { + spyOn(imageEditorMock.ui, 'changeRedoButtonStatus'); + imageEditorMock.fire('redoStackChanged', 0); + expect(imageEditorMock.ui.changeRedoButtonStatus.calls.mostRecent().args[0]).toBe(false); + }); + }); + + describe('objectActivated', () => { + it('When objectActivated occurs, the state of the delete button should be enabled.', () => { + spyOn(imageEditorMock.ui, 'changeDeleteButtonEnabled'); + spyOn(imageEditorMock.ui, 'changeDeleteAllButtonEnabled'); + imageEditorMock.fire('objectActivated', {id: 1}); + expect(imageEditorMock.ui.changeDeleteButtonEnabled.calls.mostRecent().args[0]).toBe(true); + expect(imageEditorMock.ui.changeDeleteAllButtonEnabled.calls.mostRecent().args[0]).toBe(true); + }); + + it('When objectActivated\'s target is cropzone, changeApplyButtonStatus should be enabled.', () => { + spyOn(imageEditorMock.ui.crop, 'changeApplyButtonStatus'); + imageEditorMock.fire('objectActivated', { + id: 1, + type: 'cropzone' + }); + expect(imageEditorMock.ui.crop.changeApplyButtonStatus.calls.mostRecent().args[0]).toBe(true); + }); + + it('If the target of objectActivated is shape and the existing menu is not shpe, the menu should be changed to shape.', () => { + imageEditorMock.ui.submenu = 'crop'; + spyOn(imageEditorMock.ui, 'changeMenu'); + spyOn(imageEditorMock.ui.shape, 'setShapeStatus'); + spyOn(imageEditorMock.ui.shape, 'setMaxStrokeValue'); + imageEditorMock.fire('objectActivated', { + id: 1, + type: 'circle' + }); + + expect(imageEditorMock.ui.changeMenu.calls.mostRecent().args[0]).toBe('shape'); + expect(imageEditorMock.ui.shape.setMaxStrokeValue).toHaveBeenCalled(); + }); + + it('If the target of objectActivated is text and the existing menu is not text, the menu should be changed to text.', () => { + imageEditorMock.ui.submenu = 'crop'; + spyOn(imageEditorMock.ui, 'changeMenu'); + imageEditorMock.fire('objectActivated', { + id: 1, + type: 'i-text' + }); + + expect(imageEditorMock.ui.changeMenu.calls.mostRecent().args[0]).toBe('text'); + }); + + it('If the target of objectActivated is icon and the existing menu is not icon, the menu should be changed to icon.', () => { + imageEditorMock.ui.submenu = 'crop'; + spyOn(imageEditorMock.ui, 'changeMenu'); + spyOn(imageEditorMock.ui.icon, 'setIconPickerColor'); + imageEditorMock.fire('objectActivated', { + id: 1, + type: 'icon' + }); + + expect(imageEditorMock.ui.changeMenu.calls.mostRecent().args[0]).toBe('icon'); + expect(imageEditorMock.ui.icon.setIconPickerColor).toHaveBeenCalled(); + }); + }); + + describe('addObjectAfter', () => { + it('When addObjectAfter occurs, the shape\'s maxStrokeValue should be changed to match the size of the added object.', () => { + spyOn(imageEditorMock.ui.shape, 'setMaxStrokeValue'); + spyOn(imageEditorMock.ui.shape, 'changeStandbyMode'); + imageEditorMock.fire('addObjectAfter', { + type: 'circle', + width: 100, + height: 200 + }); + + expect(imageEditorMock.ui.shape.setMaxStrokeValue.calls.mostRecent().args[0]).toBe(100); + expect(imageEditorMock.ui.shape.changeStandbyMode).toHaveBeenCalled(); + }); + }); + + describe('objectScaled', () => { + it('If objectScaled occurs on an object of type text, fontSize must be changed.', () => { + imageEditorMock.ui.text.fontSize = 0; + imageEditorMock.fire('objectScaled', { + type: 'i-text', + fontSize: 20 + }); + + expect(imageEditorMock.ui.text.fontSize).toBe(20); + }); + + it('If objectScaled is for a shape type object and strokeValue is greater than the size of the object, the value should change.', () => { + spyOn(imageEditorMock.ui.shape, 'getStrokeValue').and.returnValue(20); + spyOn(imageEditorMock.ui.shape, 'setStrokeValue'); + imageEditorMock.fire('objectScaled', { + type: 'rect', + width: 10, + height: 10 + }); + expect(imageEditorMock.ui.shape.setStrokeValue.calls.mostRecent().args[0]).toBe(10); + }); + }); + + describe('selectionCleared', () => { + it('If selectionCleared occurs in the text menu state, the menu should be closed.', () => { + imageEditorMock.ui.submenu = 'text'; + spyOn(imageEditorMock, 'changeCursor'); + + imageEditorMock.fire('selectionCleared'); + expect(imageEditorMock.changeCursor.calls.mostRecent().args[0]).toBe('text'); + }); + }); + }); +}); diff --git a/test/graphics.spec.js b/test/graphics.spec.js index d8ebc2da6..10006b20d 100644 --- a/test/graphics.spec.js +++ b/test/graphics.spec.js @@ -17,7 +17,10 @@ describe('Graphics', () => { let graphics, canvas; beforeEach(() => { - graphics = new Graphics($(''), cssMaxWidth, cssMaxHeight); + graphics = new Graphics($(''), { + cssMaxWidth, + cssMaxHeight + }); canvas = graphics.getCanvas(); }); diff --git a/test/imageEditor.spec.js b/test/imageEditor.spec.js index 8fe882a4c..83a02c457 100644 --- a/test/imageEditor.spec.js +++ b/test/imageEditor.spec.js @@ -4,6 +4,7 @@ */ import snippet from 'tui-code-snippet'; +import Promise from 'core-js/library/es6/promise'; import ImageEditor from '../src/js/imageEditor'; describe('ImageEditor', () => { @@ -15,6 +16,10 @@ describe('ImageEditor', () => { beforeEach(() => { el = document.createElement('div'); spyOn(snippet, 'imagePing'); + + imageEditor = new ImageEditor(el, { + usageStatistics: false + }); }); afterEach(() => { @@ -34,5 +39,49 @@ describe('ImageEditor', () => { expect(snippet.imagePing).not.toHaveBeenCalled(); }); + + 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(); + }); + }); }); }); diff --git a/test/rotation.spec.js b/test/rotation.spec.js index 97119d97c..1fd869054 100644 --- a/test/rotation.spec.js +++ b/test/rotation.spec.js @@ -44,9 +44,10 @@ describe('Rotation', () => { expect(rotationModule.getCurrentAngle()).toBe(current + 20); }); - 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(); }); }); diff --git a/test/theme.spec.js b/test/theme.spec.js new file mode 100644 index 000000000..e986afd2b --- /dev/null +++ b/test/theme.spec.js @@ -0,0 +1,75 @@ +/** + * @author NHN Ent. FE Development Team + * @fileoverview Test cases of "src/js/component/cropper.js" + */ +import Theme from '../src/js/ui/theme/theme'; +import defaultTheme from '../src/js/ui/theme/standard'; + +describe('Theme', () => { + let theme; + beforeEach(() => { + theme = new Theme(defaultTheme); + }); + + describe('getStyle()', () => { + it('In case of icon type, the object should be returned as it is.', () => { + const expected = { + active: { + path: 'icon-a.svg', + name: 'icon-a' + }, + normal: { + path: 'icon-b.svg', + name: 'icon-b' + } + }; + + expect(theme.getStyle('menu.icon')).toEqual(expected); + }); + + it('In normal types, cssText should be returned.', () => { + theme.styles.normal = { + backgroundColor: '#fdba3b', + border: '1px solid #fdba3b', + color: '#fff', + fontFamily: 'NotoSans, sans-serif', + fontSize: '12px' + }; + + const expected = 'background-color: #fdba3b;border: 1px solid #fdba3b;color: #fff;font-family: NotoSans, sans-serif;font-size: 12px'; + expect(theme.getStyle('normal')).toBe(expected); + }); + + it('If all members are objects, you must leave the structure intact and return cssText.', () => { + theme.styles['submenu.normalLabel'] = { + color: '#858585', + fontWeight: 'normal' + }; + theme.styles['submenu.activeLabel'] = { + color: '#000', + fontWeight: 'normal' + }; + + const expected = { + normal: 'color: #858585;font-weight: normal', + active: 'color: #000;font-weight: normal' + }; + expect(theme.getStyle('submenu.label')).toEqual(expected); + }); + }); + + describe('_makeCssText()', () => { + it('Should return the cssText of the expected value for the object.', () => { + const styleObject = { + backgroundColor: '#fff', + backgroundImage: './img/bg.png', + border: '1px solid #ddd', + color: '#222', + fontFamily: 'NotoSans, sans-serif', + fontSize: '12px' + }; + const expected = 'background-color: #fff;background-image: url(./img/bg.png);border: 1px solid #ddd;color: #222;font-family: NotoSans, sans-serif;font-size: 12px'; + expect(theme._makeCssText(styleObject)).toBe(expected); + }); + }); +}); diff --git a/test/ui.spec.js b/test/ui.spec.js new file mode 100644 index 000000000..b3add291c --- /dev/null +++ b/test/ui.spec.js @@ -0,0 +1,137 @@ +/** + * @author NHN Ent. FE Development Team + * @fileoverview Test cases of "src/js/ui.js" + */ +import snippet from 'tui-code-snippet'; +import Promise from 'core-js/library/es6/promise'; +import UI from '../src/js/ui'; + +describe('UI', () => { + let ui; + let uiOptions; + beforeEach(() => { + uiOptions = { + loadImage: { + path: '', + name: '' + }, + menu: ['crop', 'flip', 'rotate', 'draw', 'shape', 'icon', 'text', 'mask', 'filter'], + initMenu: 'shape', + menuBarPosition: 'bottom' + }; + ui = new UI(document.createElement('div'), uiOptions, {}); + }); + describe('_changeMenu()', () => { + beforeEach(() => { + ui.submenu = 'shape'; + spyOn(ui, 'resizeEditor'); + spyOn(ui.shape, 'changeStandbyMode'); + spyOn(ui.filter, 'changeStartMode'); + ui._actions.main = { + changeSelectableAll: jasmine.createSpy('changeSelectableAll') + }; + ui._changeMenu('filter', false, false); + }); + it('When the menu changes, the changeStartMode () of the menu instance to be changed must be executed.', () => { + expect(ui.shape.changeStandbyMode).toHaveBeenCalled(); + }); + + it('When the menu changes, the changeStandbyMode () of the existing menu instance must be executed.', () => { + expect(ui.filter.changeStartMode).toHaveBeenCalled(); + }); + }); + + describe('_makeSubMenu()', () => { + it('MakeMenuElement should be executed for the number of menus specified in the option.', () => { + spyOn(ui, '_makeMenuElement'); + + ui._makeSubMenu(); + expect(ui._makeMenuElement.calls.count()).toBe(uiOptions.menu.length); + }); + + it('Instance of the menu specified in the option must be created.', () => { + spyOn(ui, '_makeMenuElement'); + const getConstructorName = constructor => ( + constructor.toString().match(/^function\s(.+)\(/)[1] + ); + + ui._makeSubMenu(); + snippet.forEach(uiOptions.menu, menuName => { + const constructorNameOfInstance = getConstructorName(ui[menuName].constructor); + const expected = menuName.replace(/^[a-z]/, $0 => $0.toUpperCase()); + expect(constructorNameOfInstance).toBe(expected); + }); + }); + }); + + describe('initCanvas()', () => { + it('When initCanvas is executed, some internal methods must be run as required.', done => { + const promise = new Promise(resolve => { + resolve(); + }); + ui._editorElement = { + querySelector: jasmine.createSpy('querySelector').and.returnValue(document.createElement('div')) + }; + ui._actions.main = { + initLoadImage: jasmine.createSpy('initLoadImage').and.returnValue(promise) + }; + + spyOn(ui, '_addDownloadEvent'); + spyOn(ui, '_addLoadEvent'); + spyOn(ui, '_addMenuEvent'); + spyOn(ui, '_addSubMenuEvent'); + spyOn(ui, '_addHelpActionEvent'); + spyOn(ui, '_initMenu'); + + ui.initCanvas(); + promise.then(() => { + expect(ui._addDownloadEvent).toHaveBeenCalled(); + expect(ui._addLoadEvent).toHaveBeenCalled(); + expect(ui._addMenuEvent).toHaveBeenCalled(); + expect(ui._addSubMenuEvent).toHaveBeenCalled(); + expect(ui._addHelpActionEvent).toHaveBeenCalled(); + done(); + }); + }); + }); + + describe('_getEditorPosition()', () => { + it('Position is bottom, it should be reflected in the bottom of the editor position.', () => { + ui.submenu = true; + expect(ui._getEditorPosition('bottom')).toEqual({ + top: 0, + bottom: 150, + left: 0, + right: 0 + }); + }); + + it('Position is top, it should be reflected in the top of the editor position.', () => { + ui.submenu = true; + expect(ui._getEditorPosition('top')).toEqual({ + top: 150, + bottom: 0, + left: 0, + right: 0 + }); + }); + it('Position is left, it should be reflected in the left, right of the editor position.', () => { + ui.submenu = true; + expect(ui._getEditorPosition('left')).toEqual({ + top: 0, + bottom: 0, + left: 248, + right: 248 + }); + }); + it('Position is right, it should be reflected in the right of the editor position.', () => { + ui.submenu = true; + expect(ui._getEditorPosition('right')).toEqual({ + top: 0, + bottom: 0, + left: 0, + right: 248 + }); + }); + }); +}); diff --git a/webpack.config.js b/webpack.config.js index 2e62d434d..b660bd16a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,12 +6,13 @@ const pkg = require('./package.json'); const webpack = require('webpack'); 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}`, `@author ${pkg.author}`, `@license ${pkg.license}` @@ -27,7 +28,7 @@ module.exports = { libraryTarget: 'umd', path: 'dist', publicPath: 'dist', - filename: FILENAME + filename: `${FILENAME}.js` }, externals: { 'tui-code-snippet': { @@ -56,11 +57,17 @@ module.exports = { test: /\.js$/, exclude: /(node_modules|bower_components)/, loader: 'babel' + }, + { + test: /\.styl$/, + loader: ExtractTextPlugin.extract('css-loader?sourceMap!stylus-loader?paths=src/css/') } + ] }, plugins: [ new webpack.BannerPlugin(BANNER), + new ExtractTextPlugin(`${FILENAME}.css`), new SafeUmdPlugin() ], devServer: {