initial commit

This commit is contained in:
equippedcoding-master
2025-09-17 09:37:06 -05:00
parent 86108ca47e
commit e2c98790b2
55389 changed files with 6206730 additions and 0 deletions
@@ -0,0 +1,5 @@
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
@@ -0,0 +1,41 @@
module.exports = {
root: true,
extends: ['tui/es6', 'plugin:jest/recommended', 'plugin:prettier/recommended'],
plugins: ['jest', 'prettier'],
env: {
browser: true,
amd: true,
node: true,
es6: true,
jest: true,
'jest/globals': true,
},
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module',
babelOptions: {
rootMode: 'upward',
},
},
ignorePatterns: ['node_modules/*', 'dist', 'examples'],
rules: {
'prefer-destructuring': [
'error',
{
VariableDeclarator: { array: true, object: true },
AssignmentExpression: { array: false, object: false },
},
],
},
overrides: [
{
files: ['*.spec.js'],
rules: {
'max-nested-callbacks': ['error', { max: 5 }],
'dot-notation': ['error', { allowKeywords: true }],
'no-undefined': 'off',
'jest/expect-expect': ['error', { assertFunctionNames: ['expect', 'assert*'] }],
},
},
],
};
@@ -0,0 +1,67 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
# Bower Components
bower_components
lib
#JSDOC
doc
# IDEA
.idea
*.iml
# Window
Thumbs.db
Desktop.ini
# MAC
.DS_Store
# SVN
.svn
# eclipse
.project
.metadata
# build
build
# Atom
tags
.ctags
.tern-project
# etc
.agignore
*.swp
etc
temp
api
.tern-port
*.vim
.\#*
.vscode
@@ -0,0 +1,18 @@
{
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"trailingComma": "es5",
"arrowParens": "always",
"endOfLine": "lf",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"vueIndentScriptAndStyle": false
}
@@ -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@nhn.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
@@ -0,0 +1,107 @@
# 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/enhancement) 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.
```
<Type>: Short Description (fix #111)
<Type>: Short Description (fix #123, #111, #122)
<Type>: 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@nhn.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)
@@ -0,0 +1,30 @@
<!--
Thank you for your contribution.
When it comes to write an issue, please, use the template below.
To use the template is mandatory for submit new issue and we won't reply the issue that without the template.
And you can write template's contents in Korean also.
-->
<!-- TEMPLATE -->
## Version
<!-- Write the version of the grid you are currently using. -->
## Development Environment
<!-- Write the browser type, OS and so on -->
## Current Behavior
<!-- Write a description of the current operation. You can add example code, 'CodePen' or 'jsfiddle' links. -->
```js
// Write example code
```
## Expected Behavior
<!-- Write a description of the future action. -->
@@ -0,0 +1,22 @@
The MIT License
Copyright (c) 2019 NHN Cloud Corp.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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 OR COPYRIGHT HOLDERS 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.
@@ -0,0 +1,206 @@
# ![Toast UI ImageEditor](https://user-images.githubusercontent.com/35218826/40895380-0b9f4cd6-67ea-11e8-982f-18121daa3a04.png)
> Full featured image editor using HTML5 Canvas. It's easy to use and provides powerful filters.
[![github version](https://img.shields.io/github/release/nhn/tui.image-editor.svg)](https://github.com/nhn/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)
[![license](https://img.shields.io/github/license/nhn/tui.image-editor.svg)](https://github.com/nhn/tui.image-editor/blob/master/LICENSE)
[![PRs welcome](https://img.shields.io/badge/PRs-welcome-ff69b4.svg)](https://github.com/nhn/tui.image-editor/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
[![code with hearth by NHN Cloud](https://img.shields.io/badge/%3C%2F%3E%20with%20%E2%99%A5%20by-NHN_CLOUD-ff1414.svg)](https://github.com/nhn)
[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/)
## 📦 Packages
- [toast-ui.image-editor](https://github.com/nhn/tui.image-editor/tree/master/apps/image-editor
) - Plain JavaScript component implemented by [NHN Cloud](https://github.com/nhn).
- [toast-ui.vue-image-editor](https://github.com/nhn/tui.image-editor/tree/master/apps/vue-image-editor
) - **Vue** wrapper component is powered by [NHN Cloud](https://github.com/nhn).
- [toast-ui.react-image-editor](https://github.com/nhn/tui.image-editor/tree/master/apps/react-image-editor
) - **React** wrapper component is powered by [NHN Cloud](https://github.com/nhn).
![6 -20-2018 17-45-54](https://user-images.githubusercontent.com/35218826/41647896-7b218ae0-74b2-11e8-90db-d7805cc23e8c.gif)
## 🚩 Table of Contents
- [!Toast UI ImageEditor](#)
- [📦 Packages](#packages)
- [🚩 Table of Contents](#-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)
- [🙆 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)
- [🔧 Pull Request Steps](#-pull-request-steps)
- [Setup](#setup)
- [Pull Request](#pull-request)
- [📙 Documents](#-documents)
- [💬 Contributing](#-contributing)
- [🔩 Dependency](#-dependency)
- [🍞 TOAST UI Family](#-toast-ui-family)
- [🚀 Used By](#-used-by)
- [📜 License](#-license)
## 🌏 Browser Support
| <img src="https://user-images.githubusercontent.com/1215767/34348387-a2e64588-ea4d-11e7-8267-a43365103afe.png" alt="Chrome" width="16px" height="16px" /> Chrome | <img src="https://user-images.githubusercontent.com/1215767/34348590-250b3ca2-ea4f-11e7-9efb-da953359321f.png" alt="IE" width="16px" height="16px" /> Internet Explorer | <img src="https://user-images.githubusercontent.com/1215767/34348380-93e77ae8-ea4d-11e7-8696-9a989ddbbbf5.png" alt="Edge" width="16px" height="16px" /> Edge | <img src="https://user-images.githubusercontent.com/1215767/34348394-a981f892-ea4d-11e7-9156-d128d58386b9.png" alt="Safari" width="16px" height="16px" /> Safari | <img src="https://user-images.githubusercontent.com/1215767/34348383-9e7ed492-ea4d-11e7-910c-03b39d52f496.png" alt="Firefox" width="16px" height="16px" /> Firefox |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| Yes | 10+ | Yes | Yes | Yes |
## 💪 Has full features that stick to the basic.
### Photo manipulation
- Crop, Flip, Rotation, Drawing, Shape, Icon, Text, Mask Filter, Image Filter
### Integration function
- Download, Image Load, Undo, Redo, Reset, Delete Object(Shape, Line, Mask Image...)
<table>
<tbody>
<tr>
<th width="20%">Crop</th>
<th width="20%">Flip</th>
<th width="20%">Rotation</th>
<th width="20%">Drawing</th>
<th width="20%">Shape</th>
</tr>
<tr>
<td><img src="https://user-images.githubusercontent.com/35218826/40904241-0c28ec68-6815-11e8-8296-89a1716b22d8.png" alt="2018-06-04 4 33 16" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40904521-f7c6e184-6815-11e8-8ba3-c94664da69a2.png" alt="2018-06-04 4 40 06" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40904664-656aa748-6816-11e8-9943-6607c209deac.png" alt="2018-06-04 4 43 02" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40904850-0f26ebde-6817-11e8-97d0-d3a7e4bc02da.png" alt="2018-06-04 4 47 40" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40905037-a026296a-6817-11e8-9d28-9e1ca7bc58c4.png" alt="2018-06-04 4 51 45" style="max-width:100%;"></td>
</tr>
<tr>
<th>Icon</th>
<th>Text</th>
<th>Mask</th>
<th>Filter</th>
<th></th>
</tr>
<tr>
<td><img src="https://user-images.githubusercontent.com/35218826/40931205-2d255db6-6865-11e8-98af-ad34c5a01da1.png" alt="2018-06-05 2 06 29" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40931484-46253948-6866-11e8-8a04-fa042920e457.png" alt="2018-06-05 2 14 36" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40931743-21eeb346-6867-11e8-8e31-a59f7a43482b.png" alt="2018-06-05 2 20 46" style="max-width:100%;"></td>
<td><img src="https://user-images.githubusercontent.com/35218826/40932016-093ed1f4-6868-11e8-8224-a048c3ee8a09.png" alt="2018-06-05 2 27 10" style="max-width:100%;"></td>
<td></td>
</tr>
</tbody>
</table>
### Powerful filter function
- Grayscale, Invert, Sepia, Blur Sharpen, Emboss, RemoveWhite, Brightness, Noise, Pixelate, ColorFilter, Tint, Multiply, Blend
| Grayscale | Noise | 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) | ![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) |
## 🙆 Easy to apply the size and design you want
### Can be used everywhere.
- Widely supported in browsers including IE10.
- 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
- Resize
- Free drawing
- Line drawing
- Shape
- Icon
- Text
- Mask Filter
- Image Filter
## 🔧 Pull Request Steps
TOAST UI products are open source, so you can create a pull request(PR) after you fix issues.
Run npm scripts and develop yourself with the following process.
### Setup
Fork `develop` branch into your personal repository.
Clone it to local computer. Install node modules.
Before starting development, you should check if there are any errors.
```sh
$ git clone https://github.com/{your-personal-repo}/[[repo name]].git
$ cd [[repo name]]
$ npm install
```
### Pull Request
Before uploading your PR, run test one last time to check if there are any errors.
If it has no errors, commit and then push it!
For more information on PR's steps, please see links in the Contributing section.
## 📙 Documents
- [Getting Started](https://github.com/nhn/tui.image-editor/tree/master/docs/Basic-Tutorial.md)
- [Tutorial](https://github.com/nhn/tui.image-editor/tree/master/docs)
- [Example](http://nhn.github.io/tui.image-editor/latest/tutorial-example01-includeUi)
- [API](http://nhn.github.io/tui.image-editor/latest)
## 💬 Contributing
- [Code of Conduct](https://github.com/nhn/tui.image-editor/blob/master/CODE_OF_CONDUCT.md)
- [Contributing guideline](https://github.com/nhn/tui.image-editor/blob/master/CONTRIBUTING.md)
- [Issue guideline](https://github.com/nhn/tui.image-editor/blob/master/ISSUE_TEMPLATE.md)
- [Commit convention](https://github.com/nhn/tui.image-editor/blob/production/docs/COMMIT_MESSAGE_CONVENTION.md)
## 🔩 Dependency
- [fabric.js](https://github.com/fabricjs/fabric.js/releases) = 4.2.0
- [tui.code-snippet](https://github.com/nhn/tui.code-snippet/releases/tag/v1.5.0) >= 1.5.0
- [tui.color-picker](https://github.com/nhn/tui.color-picker/releases/tag/v2.2.6) >= 2.2.6
## 🍞 TOAST UI Family
- [TOAST UI Editor](https://github.com/nhn/tui.editor)
- [TOAST UI Grid](https://github.com/nhn/tui.grid)
- [TOAST UI Chart](https://github.com/nhn/tui.chart)
- [TOAST UI Calendar](https://github.com/nhn/tui.calendar)
- [TOAST UI Components](https://github.com/nhn)
## 🚀 Used By
- [NHN Dooray! - Collaboration Service (Project, Messenger, Mail, Calendar, Drive, Wiki, Contacts)](https://dooray.com/home/)
- [Catalyst](https://catalystapp.co/)
## 📜 License
[MIT LICENSE](https://github.com/nhn/tui.image-editor/blob/master/LICENSE)
@@ -0,0 +1,193 @@
# ![Toast UI ImageEditor](https://user-images.githubusercontent.com/35218826/40895380-0b9f4cd6-67ea-11e8-982f-18121daa3a04.png)
> Full featured image editor using HTML5 Canvas. It's easy to use and provides powerful filters.
[![npm version](https://img.shields.io/npm/v/tui-image-editor.svg)](https://www.npmjs.com/package/tui-image-editor)
## 🚩 Table of Contents
- [Collect statistics on the use of open source](#Collect-statistics-on-the-use-of-open-source)
- [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)
- [Menu svg icon setting](#menu-svg-icon-setting)
- [TypeScript](#typescript)
## Collect statistics on the use of open source
TOAST UI ImageEditor applies Google Analytics (GA) to collect statistics on the use of open source, in order to identify how widely TOAST UI ImageEditor is used throughout the world. It also serves as important index to determine the future course of projects. location.hostname (e.g. > “ui.toast.com") is to be collected and the sole purpose is nothing but to measure statistics on the usage. To disable GA, use the following `usageStatistics` option when creating the instance.
```js
const options = {
//...
usageStatistics: false,
};
const imageEditor = new tui.ImageEditor('#tui-image-editor-container', options);
```
Or, include [`tui-code-snippet`](https://github.com/nhn/tui.code-snippet)(**v1.4.0** or **later**) and then immediately write the options as follows:
```js
tui.usageStatistics = false;
```
## 💾 Install
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
You can find TOAST UI products 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.
#### npm
##### 1. ImageEditor installation
```sh
$ npm install --save tui-image-editor # Latest version
$ npm install --save tui-image-editor@<version> # Specific version
```
##### 2. If the installation of the `fabric.js` dependency module does not go smoothly
To solve the problem, you need to refer to [Some Steps](https://github.com/fabricjs/fabric.js#install-with-npm) to solve the problem.
#### bower
```sh
$ bower install tui-image-editor # Latest version
$ bower install tui-image-editor#<tag> # Specific version
```
### Via Contents Delivery Network (CDN)
TOAST UI products are available over the CDN powered by [NHN Cloud](https://www.toast.com).
You can use the CDN as below.
```html
<link
rel="stylesheet"
href="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.css"
/>
<script src="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.js"></script>
```
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/
│ ├─ ...
```
### Download Source Files
- [Download bundle files from `dist` folder](https://github.com/nhn/tui.image-editor/tree/production/dist)
- [Download all sources for each version](https://github.com/nhn/tui.image-editor/releases)
## 🔨 Usage
### HTML
Add the container element where TOAST UI ImageEditor will be created.
```html
<body>
...
<div id="tui-image-editor"></div>
...
</body>
```
### javascript
Add dependencies & initialize ImageEditor class with given element to make an image editor.
```javascript
const ImageEditor = require('tui-image-editor');
const FileSaver = require('file-saver'); //to download edited image to local. Use after npm install file-saver
const blackTheme = require('./js/theme/black-theme.js');
const locale_ru_RU = {
// override default English locale to your custom
Crop: 'Обзрезать',
'Delete-all': 'Удалить всё',
// etc...
};
const instance = new ImageEditor(document.querySelector('#tui-image-editor'), {
includeUI: {
loadImage: {
path: 'img/sampleImage.jpg',
name: 'SampleImage',
},
locale: locale_ru_RU,
theme: blackTheme, // or whiteTheme
initMenu: 'filter',
menuBarPosition: 'bottom',
},
cssMaxWidth: 700,
cssMaxHeight: 500,
selectionStyle: {
cornerSize: 20,
rotatingPointOffset: 70,
},
});
```
Or
```javascript
const ImageEditor = require('tui-image-editor');
const instance = new ImageEditor(document.querySelector('#tui-image-editor'), {
cssMaxWidth: 700,
cssMaxHeight: 500,
selectionStyle: {
cornerSize: 20,
rotatingPointOffset: 70,
},
});
```
### Menu svg icon setting
#### There are two ways to set icons.
1. **Use default svg built** into imageEditor without setting svg file path (Features added since version v3.9.0).
2. There is a way to use the **actual physical svg file** and **set the file location manually**.
Can find more details in [this document](https://github.com/nhn/tui.image-editor/blob/master/docs/Basic-Tutorial.md#4-menu-submenu-svg-icon-setting).
### TypeScript
If you use TypeScript, You must `import module = require('module')` on importing.
[`export =` and `import = require()`](https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require)
```typescript
import ImageEditor = require('tui-image-editor');
const FileSaver = require('file-saver'); //to download edited image to local. Use after npm install file-saver
const instance = new ImageEditor(document.querySelector('#tui-image-editor'), {
cssMaxWidth: 700,
cssMaxHeight: 500,
selectionStyle: {
cornerSize: 20,
rotatingPointOffset: 70,
},
});
```
See [details](https://nhn.github.io/tui.image-editor/latest) for additional information.
@@ -0,0 +1,7 @@
const path = require('path');
module.exports = {
process(src, filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
},
};
@@ -0,0 +1,5 @@
module.exports = {
process() {
return `module.exports = 'PGRlZnMgaWQ9InR1aS1pbWFnZS1lZGl0b3Itc3ZnLWRlZmF1bHQtaWNvbnMiPg=='`;
},
};
@@ -0,0 +1,37 @@
const fs = require('fs');
const path = require('path');
const config = require(path.resolve(__dirname, 'tuidoc.config.json'));
const examples = config.examples || {};
const { filePath, globalErrorLogVariable } = examples;
/**
* Get Examples Url
*/
function getTestUrls() {
if (!filePath) {
throw Error('not exist examples path at tuidoc.config.json');
}
const urlPrefix = 'http://nhn.github.io/tui.image-editor/latest';
const testUrls = fs.readdirSync(filePath).reduce((urls, fileName) => {
if (/html$/.test(fileName)) {
urls.push(`${urlPrefix}/${filePath}/${fileName}`);
}
return urls;
}, []);
fs.writeFileSync('url.txt', testUrls.join(', '));
}
function getGlobalVariable() {
if (!globalErrorLogVariable) {
throw Error('not exist examples path at tuidoc.config.json');
}
fs.writeFileSync('errorVariable.txt', globalErrorLogVariable);
}
getTestUrls();
getGlobalVariable();
@@ -0,0 +1,376 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs>
<circle id="a" cx="16" cy="16" r="16"/>
<path id="rfn4rylffa" d="M7 12c-.335 0-.663-.025-.983-.074C3.171 11.492 1 9.205 1 6.444c0-1.363.534-2.613 1.415-3.58"/>
<mask id="6f9gn2dysb" width="6" height="9.136" x="0" y="0" fill="#fff" maskUnits="objectBoundingBox">
<use xlink:href="#rfn4rylffa"/>
</mask>
</defs><symbol id="icon-a-ic-apply" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#434343" d="M4 12.011l5 5L20.011 6"/>
</g>
</symbol><symbol id="icon-a-ic-cancel" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#434343" d="M6 6l12 12M18 6L6 18"/>
</g>
</symbol><symbol id="icon-a-ic-color-transparent-w" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<g>
<use fill="#FFF" xlink:href="#a"/>
<circle cx="16" cy="16" r="15.5" stroke="#D5D5D5"/>
</g>
<path stroke="#FF4040" stroke-width="1.5" d="M27 5L5 27"/>
</g>
</symbol><symbol id="icon-a-ic-crop" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#434343" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
<path fill="#434343" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
</g>
</symbol><symbol id="icon-a-ic-delete-all" viewBox="0 0 24 24">
<g fill="#434343" fill-rule="evenodd">
<path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
</g>
</symbol><symbol id="icon-a-ic-delete" viewBox="0 0 24 24">
<g fill="#434343" fill-rule="evenodd">
<path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
</g>
</symbol><symbol id="icon-a-ic-draw-free" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
</g>
</symbol><symbol id="icon-a-ic-draw-line" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M2 15.5h28"/>
</g>
</symbol><symbol id="icon-a-ic-draw" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#434343" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
<path fill="#434343" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
</g>
</symbol><symbol id="icon-a-ic-filter" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#434343" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
<path fill="#434343" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
</g>
</symbol><symbol id="icon-a-ic-flip-reset" viewBox="0 0 31 32">
<g fill="none" fill-rule="evenodd">
<path d="M31 0H0v32h31z"/>
<path fill="#434343" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
<path stroke="#434343" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
</g>
</symbol><symbol id="icon-a-ic-flip-x" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M32 32H0V0h32z"/>
<path fill="#434343" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
</g>
</symbol><symbol id="icon-a-ic-flip-y" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0v32h32V0z"/>
<path fill="#434343" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
</g>
</symbol><symbol id="icon-a-ic-flip" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#434343" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
</g>
</symbol><symbol id="icon-a-ic-history-check" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M4.5 -1L1.5 2 6.5 7" transform="translate(-60 -804) translate(60 804) translate(2 3) rotate(-90 4 3)"/>
</g>
</symbol><symbol id="icon-a-ic-history-crop" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#434343" d="M2 0h1v10c-.552 0-1-.448-1-1V0zM10 9v3H9V9h1zM9 2h1v6H9V2z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#434343" d="M2 9H12V10H2zM9 2c.513 0 .936.386.993.883L10 3H3V2h6zM2 3H0V2h2v1z" transform="translate(-84 -804) translate(84 804)"/>
</g>
</symbol><symbol id="icon-a-ic-history-delete" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g fill="#434343">
<path d="M2 9h8V1h1v8.5c0 .276-.224.5-.5.5h-9c-.276 0-.5-.224-.5-.5V1h1v8zM0 0H12V1H0z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 3H5V7H4zM7 3H8V7H7z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 1h4V0h1v1.5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V0h1v1z" transform="translate(-300 -804) translate(300 804) matrix(1 0 0 -1 0 2)"/>
</g>
</g>
</symbol><symbol id="icon-a-ic-history-draw" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 1H12V13H0z" transform="translate(-156 -804) translate(156 803)"/>
<path stroke="#434343" d="M9.622 1.584l1.835 1.658-8.31 8.407L.5 12.5V11l9.122-9.416z" transform="translate(-156 -804) translate(156 803)"/>
<path fill="#FFF" d="M7.628 3.753L10.378 3.753 10.378 4.253 7.628 4.253z" transform="translate(-156 -804) translate(156 803) rotate(45 9.003 4.003)"/>
</g>
</symbol><symbol id="icon-a-ic-history-filter" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#434343" d="M12 3v1H9V3h3zM7 4H0V3h7v1z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#434343" d="M12 8v1H9V8h3zM7 9H0V8h7v1z" transform="translate(-276 -804) translate(276 804) matrix(-1 0 0 1 12 0)"/>
<path fill="#434343" d="M8 1c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1zM4 7c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1z" transform="translate(-276 -804) translate(276 804)"/>
</g>
</symbol><symbol id="icon-a-ic-history-flip" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-108 -804) translate(108 804)"/>
<path fill="#434343" d="M6 0L7 0 7 12 6 12zM11 10V9h1v1.5c0 .276-.224.5-.5.5H10v-1h1zM5 1v1H1v8h4v1H.5c-.276 0-.5-.224-.5-.5v-9c0-.276.224-.5.5-.5H5zm7 5v2h-1V6h1zm0-3v2h-1V3h1zM9 1v1H7V1h2zm2.5 0c.276 0 .5.224.5.5V2h-2V1h1.5zM9 11H7v-1h2v1z" transform="translate(-108 -804) translate(108 804)"/>
</g>
</symbol><symbol id="icon-a-ic-history-group" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-348 -804) translate(348 804)">
<path d="M0 0H12V12H0z"/>
<path fill="#434343" d="M1 9v2h1v1H.5c-.276 0-.5-.224-.5-.5V9h1zm11 1v1.5c0 .276-.224.5-.5.5H9v-1h2v-1h1zm-4 1v1H6v-1h2zm-3 0v1H3v-1h2zm7-4v2h-1V7h1zM1 6v2H0V6h1zm11-2v2h-1V4h1zM1 3v2H0V3h1zm10.5-3c.276 0 .5.224.5.5V3h-1V1h-1V0h1.5zM6 0v1H4V0h2zm3 0v1H7V0h2zM0 .5C0 .224.224 0 .5 0H3v1H1v1H0V.5zM9.5 4c.276 0 .5.224.5.5v5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V8.355c.317.094.652.145 1 .145V9h4V5h-.5c0-.348-.05-.683-.145-1H9.5z"/>
<circle cx="5" cy="5" r="2.5" stroke="#434343"/>
</g>
</g>
</symbol><symbol id="icon-a-ic-history-icon" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-204 -804) translate(204 804)"/>
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="M6 9.568L2.601 11 2.975 7.467 0.5 4.82 4.13 4.068 6 1 7.87 4.068 11.5 4.82 9.025 7.467 9.399 11z" transform="translate(-204 -804) translate(204 804)"/>
</g>
</symbol><symbol id="icon-a-ic-history-load" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#434343" d="M5 0c.552 0 1 .448 1 1v1h5.5c.276 0 .5.224.5.5v8c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V1c0-.552.448-1 1-1h4zm0 1H1v9h10V3H5V1z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#434343" d="M1 2L5 2 5 3 1 3z" transform="translate(-324 -805) translate(324 805)"/>
</g>
</symbol><symbol id="icon-a-ic-history-mask" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-252 -804) translate(252 804)">
<path d="M0 0H12V12H0z"/>
<circle cx="6" cy="6" r="2.5" stroke="#444"/>
<path fill="#434343" d="M11.5 0c.276 0 .5.224.5.5v11c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V.5C0 .224.224 0 .5 0h11zM11 1H1v10h10V1z"/>
</g>
</g>
</symbol><symbol id="icon-a-ic-history-resize" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path fill="#434343" d="M 9.492188 1.507812 C 9.398438 1.414062 9.25 1.414062 9.15625 1.507812 L 5.8125 4.851562 C 5.714844 4.945312 5.714844 5.097656 5.8125 5.1875 C 5.855469 5.234375 5.917969 5.257812 5.976562 5.257812 C 6.039062 5.257812 6.101562 5.234375 6.148438 5.1875 L 9.492188 1.84375 C 9.585938 1.75 9.585938 1.601562 9.492188 1.507812 Z M 9.492188 1.507812 "/>
<path fill="#434343" d="M 9.328125 1.433594 C 9.195312 1.433594 9.085938 1.539062 9.085938 1.671875 L 9.085938 4.542969 C 9.085938 4.671875 9.195312 4.78125 9.328125 4.78125 C 9.460938 4.78125 9.566406 4.671875 9.566406 4.542969 L 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#434343" d="M 9.328125 1.433594 L 6.457031 1.433594 C 6.328125 1.433594 6.21875 1.539062 6.21875 1.671875 C 6.21875 1.804688 6.328125 1.914062 6.457031 1.914062 L 9.328125 1.914062 C 9.460938 1.914062 9.566406 1.804688 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#434343" d="M 5.1875 5.8125 C 5.097656 5.71875 4.945312 5.71875 4.851562 5.8125 L 1.503906 9.15625 C 1.410156 9.25 1.410156 9.402344 1.503906 9.496094 C 1.554688 9.542969 1.613281 9.566406 1.671875 9.566406 C 1.734375 9.566406 1.796875 9.542969 1.84375 9.496094 L 5.1875 6.148438 C 5.28125 6.054688 5.28125 5.902344 5.1875 5.8125 Z M 5.1875 5.8125 "/>
<path fill="#434343" d="M 1.671875 6.21875 C 1.539062 6.21875 1.433594 6.328125 1.433594 6.457031 L 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 C 1.804688 9.566406 1.914062 9.460938 1.914062 9.328125 L 1.914062 6.457031 C 1.914062 6.328125 1.804688 6.21875 1.671875 6.21875 Z M 1.671875 6.21875 "/>
<path fill="#434343" d="M 4.542969 9.085938 L 1.671875 9.085938 C 1.539062 9.085938 1.433594 9.195312 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 L 4.542969 9.566406 C 4.671875 9.566406 4.78125 9.460938 4.78125 9.328125 C 4.78125 9.195312 4.671875 9.085938 4.542969 9.085938 Z M 4.542969 9.085938 "/>
<path fill="#434343" d="M 10.28125 0 L 0.71875 0 C 0.320312 0 0 0.320312 0 0.71875 L 0 10.28125 C 0 10.679688 0.320312 11 0.71875 11 L 10.28125 11 C 10.679688 11 11 10.679688 11 10.28125 L 11 0.71875 C 11 0.320312 10.679688 0 10.28125 0 Z M 10.523438 10.28125 C 10.523438 10.414062 10.414062 10.523438 10.28125 10.523438 L 0.71875 10.523438 C 0.585938 10.523438 0.476562 10.414062 0.476562 10.28125 L 0.476562 0.71875 C 0.476562 0.585938 0.585938 0.476562 0.71875 0.476562 L 10.28125 0.476562 C 10.414062 0.476562 10.523438 0.585938 10.523438 0.71875 Z M 10.523438 10.28125 "/>
</g>
</symbol><symbol id="icon-a-ic-history-rotate" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-132 -804) translate(132 804)">
<path d="M0 0.5H12V12.5H0z"/>
<path fill="#434343" d="M6.5 1C9.538 1 12 3.462 12 6.5c0 2.37-1.5 4.39-3.6 5.163l-.407-.916C9.744 10.13 11 8.462 11 6.5 11 4.015 8.985 2 6.5 2c-.777 0-1.509.197-2.147.544L4 1.75l-.205-.04C4.594 1.258 5.517 1 6.5 1z"/>
<use stroke="#434343" stroke-dasharray="2 1.25" stroke-width="4" mask="url(#6f9gn2dysb)" xlink:href="#rfn4rylffa"/>
<path fill="#434343" d="M4.279 0L6 1.75 4.25 3.571 3.543 2.864 4.586 1.75 3.572 0.707z" transform="matrix(-1 0 0 1 9.543 0)"/>
</g>
</g>
</symbol><symbol id="icon-a-ic-history-shape" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-180 -804) translate(180 804)"/>
<path fill="#434343" d="M11.5 4c.276 0 .5.224.5.5v7c0 .276-.224.5-.5.5h-7c-.276 0-.5-.224-.5-.5V8.8h1V11h6V5H8.341l-.568-1H11.5z" transform="translate(-180 -804) translate(180 804)"/>
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M4.5 0.5L8.5 7.611 0.5 7.611z" transform="translate(-180 -804) translate(180 804)"/>
</g>
</symbol><symbol id="icon-a-ic-history-text" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#434343" d="M2 1h8c.552 0 1 .448 1 1H1c0-.552.448-1 1-1z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#434343" d="M1 1H2V3H1zM10 1H11V3H10zM5.5 1L6.5 1 6.5 11 5.5 11z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#434343" d="M4 10H8V11H4z" transform="translate(-228 -804) translate(228 804)"/>
</g>
</symbol><symbol id="icon-a-ic-history" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H24V24H0z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
<path fill="#434343" d="M12.5 1C18.299 1 23 5.701 23 11.5S18.299 22 12.5 22c-5.29 0-9.665-3.911-10.394-8.999h1.012C3.838 17.534 7.764 21 12.5 21c5.247 0 9.5-4.253 9.5-9.5S17.747 2 12.5 2C8.49 2 5.06 4.485 3.666 8H3h4v1H2V4h1v3.022C4.68 3.462 8.303 1 12.5 1zm.5 5l-.001 5.291 2.537 2.537-.708.708L12.292 12H12V6h1z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
</g>
</symbol><symbol id="icon-a-ic-icon-arrow-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-arrow-3" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-arrow" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-bubble" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-heart" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill-rule="nonzero" stroke="#434343" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
<path fill="#434343" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
<path fill="#434343" d="M25 3h1v9h-1z"/>
<path stroke="#434343" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-a-ic-icon-location" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<g stroke="#434343">
<path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
<circle cx="16" cy="13" r="4.5"/>
</g>
</g>
</symbol><symbol id="icon-a-ic-icon-polygon" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-star-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
</g>
</symbol><symbol id="icon-a-ic-icon-star" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
</g>
</symbol><symbol id="icon-a-ic-icon" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
</g>
</symbol><symbol id="icon-a-ic-mask-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
<path fill="#434343" d="M25 3h1v9h-1z"/>
<path stroke="#434343" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-a-ic-mask" viewBox="0 0 24 24">
<g fill="none">
<circle cx="12" cy="12" r="4.5" stroke="#434343"/>
<path fill="#434343" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
</g>
</symbol><symbol id="icon-a-ic-redo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#434343" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
<path stroke="#434343" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
</g>
</symbol><symbol id="icon-a-ic-reset" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#434343" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
<path stroke="#434343" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
</g>
</symbol><symbol id="icon-a-ic-resize" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#434343" d="M 18.988281 3.011719 C 18.800781 2.824219 18.5 2.824219 18.3125 3.011719 L 11.621094 9.707031 C 11.429688 9.894531 11.429688 10.195312 11.621094 10.378906 C 11.710938 10.472656 11.835938 10.519531 11.957031 10.519531 C 12.078125 10.519531 12.203125 10.472656 12.292969 10.378906 L 18.988281 3.6875 C 19.175781 3.5 19.175781 3.199219 18.988281 3.011719 Z M 18.988281 3.011719 "/>
<path fill="#434343" d="M 18.652344 2.867188 C 18.386719 2.867188 18.171875 3.082031 18.171875 3.347656 L 18.171875 9.085938 C 18.171875 9.347656 18.386719 9.5625 18.652344 9.5625 C 18.917969 9.5625 19.132812 9.347656 19.132812 9.085938 L 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#434343" d="M 18.652344 2.867188 L 12.914062 2.867188 C 12.652344 2.867188 12.4375 3.082031 12.4375 3.347656 C 12.4375 3.613281 12.652344 3.828125 12.914062 3.828125 L 18.652344 3.828125 C 18.917969 3.828125 19.132812 3.613281 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#434343" d="M 10.378906 11.621094 C 10.195312 11.433594 9.890625 11.433594 9.703125 11.621094 L 3.007812 18.316406 C 2.820312 18.5 2.820312 18.804688 3.007812 18.992188 C 3.105469 19.085938 3.226562 19.132812 3.347656 19.132812 C 3.46875 19.132812 3.589844 19.085938 3.683594 18.992188 L 10.378906 12.296875 C 10.566406 12.109375 10.566406 11.804688 10.378906 11.621094 Z M 10.378906 11.621094 "/>
<path fill="#434343" d="M 3.347656 12.4375 C 3.082031 12.4375 2.867188 12.652344 2.867188 12.914062 L 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 C 3.613281 19.132812 3.828125 18.917969 3.828125 18.652344 L 3.828125 12.914062 C 3.828125 12.652344 3.613281 12.4375 3.347656 12.4375 Z M 3.347656 12.4375 "/>
<path fill="#434343" d="M 9.085938 18.171875 L 3.347656 18.171875 C 3.082031 18.171875 2.867188 18.386719 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 L 9.085938 19.132812 C 9.347656 19.132812 9.5625 18.917969 9.5625 18.652344 C 9.5625 18.386719 9.347656 18.171875 9.085938 18.171875 Z M 9.085938 18.171875 "/>
<path fill="#434343" d="M 20.5625 0 L 1.4375 0 C 0.644531 0 0 0.644531 0 1.4375 L 0 20.5625 C 0 21.355469 0.644531 22 1.4375 22 L 20.5625 22 C 21.355469 22 22 21.355469 22 20.5625 L 22 1.4375 C 22 0.644531 21.355469 0 20.5625 0 Z M 21.042969 20.5625 C 21.042969 20.828125 20.828125 21.042969 20.5625 21.042969 L 1.4375 21.042969 C 1.171875 21.042969 0.957031 20.828125 0.957031 20.5625 L 0.957031 1.4375 C 0.957031 1.171875 1.171875 0.957031 1.4375 0.957031 L 20.5625 0.957031 C 20.828125 0.957031 21.042969 1.171875 21.042969 1.4375 Z M 21.042969 20.5625 "/>
</g>
</symbol><symbol id="icon-a-ic-rotate-clockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#434343" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
<path stroke="#434343" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
<path fill="#434343" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
</g>
</symbol><symbol id="icon-a-ic-rotate-counterclockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#434343" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
<path fill="#434343" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
<path stroke="#434343" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
</g>
</symbol><symbol id="icon-a-ic-rotate" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#434343" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
<path stroke="#434343" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
</g>
</symbol><symbol id="icon-a-ic-shape-circle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14.5" stroke="#434343"/>
</g>
</symbol><symbol id="icon-a-ic-shape-rectangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<rect width="27" height="27" x="2.5" y="2.5" stroke="#434343" rx="1"/>
</g>
</symbol><symbol id="icon-a-ic-shape-triangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
</g>
</symbol><symbol id="icon-a-ic-shape" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path fill="#434343" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
<path stroke="#434343" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
</g>
</symbol><symbol id="icon-a-ic-text-align-center" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
</g>
</symbol><symbol id="icon-a-ic-text-align-left" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
</g>
</symbol><symbol id="icon-a-ic-text-align-right" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
</g>
</symbol><symbol id="icon-a-ic-text-bold" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M7 2h2v2H7zM7 28h2v2H7z"/>
<path stroke="#434343" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
</g>
</symbol><symbol id="icon-a-ic-text-italic" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
</g>
</symbol><symbol id="icon-a-ic-text-underline" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#434343" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
<path fill="#434343" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
</g>
</symbol><symbol id="icon-a-ic-text" viewBox="0 0 24 24">
<g fill="#434343" fill-rule="evenodd">
<path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
<path d="M11 3h1v18h-1z"/>
<path d="M10 20h3v1h-3z"/>
</g>
</symbol><symbol id="icon-a-ic-undo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M24 0H0v24h24z" opacity=".5"/>
<path fill="#434343" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
<path stroke="#434343" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
</g>
</symbol><symbol id="icon-a-ic-zoom-hand" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<path d="M0 0H24V24H0z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#434343" fill-rule="nonzero" d="M8.672 3.36c1.328 0 2.114.78 2.29 1.869l.014.101.023.006v1.042l-.638-.185c-.187-.055-.323-.211-.354-.399L10 5.713c0-.825-.42-1.353-1.328-1.353C7.695 4.36 7 5.041 7 5.713v7.941c0 .439-.524.665-.843.364l-1.868-1.761c-.595-.528-1.316-.617-1.918-.216-.522.348-.562 1.203-.18 1.8L7.738 22h11.013l.285-.518c1.247-2.326 1.897-4.259 1.96-5.785l.004-.239V8.035c0-.656-.5-1.17-1-1.17-.503 0-1 .456-1 1.17 0 .333-.32.573-.64.48L18 8.41V7.368l.086.026.042-.136c.279-.805.978-1.332 1.738-1.388L20 5.865c1.057 0 2 .967 2 2.17v7.423c0 1.929-.845 4.352-2.521 7.29-.09.156-.255.252-.435.252H7.474c-.166 0-.321-.082-.414-.219l-5.704-8.39c-.653-1.019-.584-2.486.46-3.182 1-.666 2.216-.516 3.148.31L6 12.495V5.713c0-1.18 1.058-2.263 2.49-2.348z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#434343" fill-rule="nonzero" d="M12.5 1.5c1.325 0 2.41 1.032 2.495 2.336L15 4v7.22h-1V4c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L11 4v7.22h-1V4c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#434343" fill-rule="nonzero" d="M16.5 3.5c1.325 0 2.41 1.032 2.495 2.336L19 6v6.3h-1V6c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L15 6v2.44h-1V6c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
</g>
</symbol><symbol id="icon-a-ic-zoom-zoom-in" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-229 -290) translate(229 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#434343"/>
<path fill="#434343" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#434343" d="M7 10H14V11H7z"/>
<path fill="#434343" d="M10 7H11V14H10z"/>
</g>
</g>
</symbol><symbol id="icon-a-ic-zoom-zoom-out" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-263 -290) translate(263 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#434343"/>
<path fill="#434343" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#434343" d="M7 10H14V11H7z"/>
</g>
</g>
</symbol><symbol id="icon-a-img-bi" viewBox="0 0 257 26">
<g fill="#FDBA3B">
<path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
</g>
</symbol></svg>

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,369 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs>
<path id="rfn4rylffa" d="M7 12c-.335 0-.663-.025-.983-.074C3.171 11.492 1 9.205 1 6.444c0-1.363.534-2.613 1.415-3.58"/>
<mask id="6f9gn2dysb" width="6" height="9.136" x="0" y="0" fill="#fff" maskUnits="objectBoundingBox">
<use xlink:href="#rfn4rylffa"/>
</mask>
</defs><symbol id="icon-b-ic-apply" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#555555" d="M4 12.011l5 5L20.011 6"/>
</g>
</symbol><symbol id="icon-b-ic-cancel" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#555555" d="M6 6l12 12M18 6L6 18"/>
</g>
</symbol><symbol id="icon-b-ic-crop" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#555555" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
<path fill="#555555" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
</g>
</symbol><symbol id="icon-b-ic-delete-all" viewBox="0 0 24 24">
<g fill="#555555" fill-rule="evenodd">
<path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
</g>
</symbol><symbol id="icon-b-ic-delete" viewBox="0 0 24 24">
<g fill="#555555" fill-rule="evenodd">
<path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
</g>
</symbol><symbol id="icon-b-ic-draw-free" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
</g>
</symbol><symbol id="icon-b-ic-draw-line" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M2 15.5h28"/>
</g>
</symbol><symbol id="icon-b-ic-draw" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#555555" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
<path fill="#555555" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
</g>
</symbol><symbol id="icon-b-ic-filter" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#555555" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
<path fill="#555555" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
</g>
</symbol><symbol id="icon-b-ic-flip-reset" viewBox="0 0 31 32">
<g fill="none" fill-rule="evenodd">
<path d="M31 0H0v32h31z"/>
<path fill="#555555" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
<path stroke="#555555" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
</g>
</symbol><symbol id="icon-b-ic-flip-x" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M32 32H0V0h32z"/>
<path fill="#555555" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
</g>
</symbol><symbol id="icon-b-ic-flip-y" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0v32h32V0z"/>
<path fill="#555555" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
</g>
</symbol><symbol id="icon-b-ic-flip" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#555555" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
</g>
</symbol><symbol id="icon-b-ic-history-check" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M4.5 -1L1.5 2 6.5 7" transform="translate(-60 -804) translate(60 804) translate(2 3) rotate(-90 4 3)"/>
</g>
</symbol><symbol id="icon-b-ic-history-crop" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#555555" d="M2 0h1v10c-.552 0-1-.448-1-1V0zM10 9v3H9V9h1zM9 2h1v6H9V2z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#555555" d="M2 9H12V10H2zM9 2c.513 0 .936.386.993.883L10 3H3V2h6zM2 3H0V2h2v1z" transform="translate(-84 -804) translate(84 804)"/>
</g>
</symbol><symbol id="icon-b-ic-history-delete" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g fill="#555555">
<path d="M2 9h8V1h1v8.5c0 .276-.224.5-.5.5h-9c-.276 0-.5-.224-.5-.5V1h1v8zM0 0H12V1H0z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 3H5V7H4zM7 3H8V7H7z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 1h4V0h1v1.5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V0h1v1z" transform="translate(-300 -804) translate(300 804) matrix(1 0 0 -1 0 2)"/>
</g>
</g>
</symbol><symbol id="icon-b-ic-history-draw" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 1H12V13H0z" transform="translate(-156 -804) translate(156 803)"/>
<path stroke="#555555" d="M9.622 1.584l1.835 1.658-8.31 8.407L.5 12.5V11l9.122-9.416z" transform="translate(-156 -804) translate(156 803)"/>
<path fill="#FFF" d="M7.628 3.753L10.378 3.753 10.378 4.253 7.628 4.253z" transform="translate(-156 -804) translate(156 803) rotate(45 9.003 4.003)"/>
</g>
</symbol><symbol id="icon-b-ic-history-filter" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#555555" d="M12 3v1H9V3h3zM7 4H0V3h7v1z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#555555" d="M12 8v1H9V8h3zM7 9H0V8h7v1z" transform="translate(-276 -804) translate(276 804) matrix(-1 0 0 1 12 0)"/>
<path fill="#555555" d="M8 1c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1zM4 7c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1z" transform="translate(-276 -804) translate(276 804)"/>
</g>
</symbol><symbol id="icon-b-ic-history-flip" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-108 -804) translate(108 804)"/>
<path fill="#555555" d="M6 0L7 0 7 12 6 12zM11 10V9h1v1.5c0 .276-.224.5-.5.5H10v-1h1zM5 1v1H1v8h4v1H.5c-.276 0-.5-.224-.5-.5v-9c0-.276.224-.5.5-.5H5zm7 5v2h-1V6h1zm0-3v2h-1V3h1zM9 1v1H7V1h2zm2.5 0c.276 0 .5.224.5.5V2h-2V1h1.5zM9 11H7v-1h2v1z" transform="translate(-108 -804) translate(108 804)"/>
</g>
</symbol><symbol id="icon-b-ic-history-group" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-348 -804) translate(348 804)">
<path d="M0 0H12V12H0z"/>
<path fill="#555555" d="M1 9v2h1v1H.5c-.276 0-.5-.224-.5-.5V9h1zm11 1v1.5c0 .276-.224.5-.5.5H9v-1h2v-1h1zm-4 1v1H6v-1h2zm-3 0v1H3v-1h2zm7-4v2h-1V7h1zM1 6v2H0V6h1zm11-2v2h-1V4h1zM1 3v2H0V3h1zm10.5-3c.276 0 .5.224.5.5V3h-1V1h-1V0h1.5zM6 0v1H4V0h2zm3 0v1H7V0h2zM0 .5C0 .224.224 0 .5 0H3v1H1v1H0V.5zM9.5 4c.276 0 .5.224.5.5v5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V8.355c.317.094.652.145 1 .145V9h4V5h-.5c0-.348-.05-.683-.145-1H9.5z"/>
<circle cx="5" cy="5" r="2.5" stroke="#555555"/>
</g>
</g>
</symbol><symbol id="icon-b-ic-history-icon" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-204 -804) translate(204 804)"/>
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="M6 9.568L2.601 11 2.975 7.467 0.5 4.82 4.13 4.068 6 1 7.87 4.068 11.5 4.82 9.025 7.467 9.399 11z" transform="translate(-204 -804) translate(204 804)"/>
</g>
</symbol><symbol id="icon-b-ic-history-load" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#555555" d="M5 0c.552 0 1 .448 1 1v1h5.5c.276 0 .5.224.5.5v8c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V1c0-.552.448-1 1-1h4zm0 1H1v9h10V3H5V1z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#555555" d="M1 2L5 2 5 3 1 3z" transform="translate(-324 -805) translate(324 805)"/>
</g>
</symbol><symbol id="icon-b-ic-history-mask" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-252 -804) translate(252 804)">
<path d="M0 0H12V12H0z"/>
<circle cx="6" cy="6" r="2.5" stroke="#444"/>
<path fill="#555555" d="M11.5 0c.276 0 .5.224.5.5v11c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V.5C0 .224.224 0 .5 0h11zM11 1H1v10h10V1z"/>
</g>
</g>
</symbol><symbol id="icon-b-ic-history-resize" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path fill="#555555" d="M 9.492188 1.507812 C 9.398438 1.414062 9.25 1.414062 9.15625 1.507812 L 5.8125 4.851562 C 5.714844 4.945312 5.714844 5.097656 5.8125 5.1875 C 5.855469 5.234375 5.917969 5.257812 5.976562 5.257812 C 6.039062 5.257812 6.101562 5.234375 6.148438 5.1875 L 9.492188 1.84375 C 9.585938 1.75 9.585938 1.601562 9.492188 1.507812 Z M 9.492188 1.507812 "/>
<path fill="#555555" d="M 9.328125 1.433594 C 9.195312 1.433594 9.085938 1.539062 9.085938 1.671875 L 9.085938 4.542969 C 9.085938 4.671875 9.195312 4.78125 9.328125 4.78125 C 9.460938 4.78125 9.566406 4.671875 9.566406 4.542969 L 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#555555" d="M 9.328125 1.433594 L 6.457031 1.433594 C 6.328125 1.433594 6.21875 1.539062 6.21875 1.671875 C 6.21875 1.804688 6.328125 1.914062 6.457031 1.914062 L 9.328125 1.914062 C 9.460938 1.914062 9.566406 1.804688 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#555555" d="M 5.1875 5.8125 C 5.097656 5.71875 4.945312 5.71875 4.851562 5.8125 L 1.503906 9.15625 C 1.410156 9.25 1.410156 9.402344 1.503906 9.496094 C 1.554688 9.542969 1.613281 9.566406 1.671875 9.566406 C 1.734375 9.566406 1.796875 9.542969 1.84375 9.496094 L 5.1875 6.148438 C 5.28125 6.054688 5.28125 5.902344 5.1875 5.8125 Z M 5.1875 5.8125 "/>
<path fill="#555555" d="M 1.671875 6.21875 C 1.539062 6.21875 1.433594 6.328125 1.433594 6.457031 L 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 C 1.804688 9.566406 1.914062 9.460938 1.914062 9.328125 L 1.914062 6.457031 C 1.914062 6.328125 1.804688 6.21875 1.671875 6.21875 Z M 1.671875 6.21875 "/>
<path fill="#555555" d="M 4.542969 9.085938 L 1.671875 9.085938 C 1.539062 9.085938 1.433594 9.195312 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 L 4.542969 9.566406 C 4.671875 9.566406 4.78125 9.460938 4.78125 9.328125 C 4.78125 9.195312 4.671875 9.085938 4.542969 9.085938 Z M 4.542969 9.085938 "/>
<path fill="#555555" d="M 10.28125 0 L 0.71875 0 C 0.320312 0 0 0.320312 0 0.71875 L 0 10.28125 C 0 10.679688 0.320312 11 0.71875 11 L 10.28125 11 C 10.679688 11 11 10.679688 11 10.28125 L 11 0.71875 C 11 0.320312 10.679688 0 10.28125 0 Z M 10.523438 10.28125 C 10.523438 10.414062 10.414062 10.523438 10.28125 10.523438 L 0.71875 10.523438 C 0.585938 10.523438 0.476562 10.414062 0.476562 10.28125 L 0.476562 0.71875 C 0.476562 0.585938 0.585938 0.476562 0.71875 0.476562 L 10.28125 0.476562 C 10.414062 0.476562 10.523438 0.585938 10.523438 0.71875 Z M 10.523438 10.28125 "/>
</g>
</symbol><symbol id="icon-b-ic-history-rotate" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-132 -804) translate(132 804)">
<path d="M0 0.5H12V12.5H0z"/>
<path fill="#555555" d="M6.5 1C9.538 1 12 3.462 12 6.5c0 2.37-1.5 4.39-3.6 5.163l-.407-.916C9.744 10.13 11 8.462 11 6.5 11 4.015 8.985 2 6.5 2c-.777 0-1.509.197-2.147.544L4 1.75l-.205-.04C4.594 1.258 5.517 1 6.5 1z"/>
<use stroke="#555555" stroke-dasharray="2 1.25" stroke-width="4" mask="url(#6f9gn2dysb)" xlink:href="#rfn4rylffa"/>
<path fill="#555555" d="M4.279 0L6 1.75 4.25 3.571 3.543 2.864 4.586 1.75 3.572 0.707z" transform="matrix(-1 0 0 1 9.543 0)"/>
</g>
</g>
</symbol><symbol id="icon-b-ic-history-shape" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-180 -804) translate(180 804)"/>
<path fill="#555555" d="M11.5 4c.276 0 .5.224.5.5v7c0 .276-.224.5-.5.5h-7c-.276 0-.5-.224-.5-.5V8.8h1V11h6V5H8.341l-.568-1H11.5z" transform="translate(-180 -804) translate(180 804)"/>
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M4.5 0.5L8.5 7.611 0.5 7.611z" transform="translate(-180 -804) translate(180 804)"/>
</g>
</symbol><symbol id="icon-b-ic-history-text" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#555555" d="M2 1h8c.552 0 1 .448 1 1H1c0-.552.448-1 1-1z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#555555" d="M1 1H2V3H1zM10 1H11V3H10zM5.5 1L6.5 1 6.5 11 5.5 11z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#555555" d="M4 10H8V11H4z" transform="translate(-228 -804) translate(228 804)"/>
</g>
</symbol><symbol id="icon-b-ic-history" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H24V24H0z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
<path fill="#555555" d="M12.5 1C18.299 1 23 5.701 23 11.5S18.299 22 12.5 22c-5.29 0-9.665-3.911-10.394-8.999h1.012C3.838 17.534 7.764 21 12.5 21c5.247 0 9.5-4.253 9.5-9.5S17.747 2 12.5 2C8.49 2 5.06 4.485 3.666 8H3h4v1H2V4h1v3.022C4.68 3.462 8.303 1 12.5 1zm.5 5l-.001 5.291 2.537 2.537-.708.708L12.292 12H12V6h1z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
</g>
</symbol><symbol id="icon-b-ic-icon-arrow-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-arrow-3" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-arrow" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-bubble" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-heart" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill-rule="nonzero" stroke="#555555" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
<path fill="#555555" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
<path fill="#555555" d="M25 3h1v9h-1z"/>
<path stroke="#555555" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-b-ic-icon-location" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<g stroke="#555555">
<path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
<circle cx="16" cy="13" r="4.5"/>
</g>
</g>
</symbol><symbol id="icon-b-ic-icon-polygon" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-star-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
</g>
</symbol><symbol id="icon-b-ic-icon-star" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
</g>
</symbol><symbol id="icon-b-ic-icon" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
</g>
</symbol><symbol id="icon-b-ic-mask-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
<path fill="#555555" d="M25 3h1v9h-1z"/>
<path stroke="#555555" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-b-ic-mask" viewBox="0 0 24 24">
<g fill="none">
<circle cx="12" cy="12" r="4.5" stroke="#555555"/>
<path fill="#555555" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
</g>
</symbol><symbol id="icon-b-ic-redo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#555555" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
<path stroke="#555555" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
</g>
</symbol><symbol id="icon-b-ic-reset" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#555555" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
<path stroke="#555555" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
</g>
</symbol><symbol id="icon-b-ic-resize" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#555555" d="M 18.988281 3.011719 C 18.800781 2.824219 18.5 2.824219 18.3125 3.011719 L 11.621094 9.707031 C 11.429688 9.894531 11.429688 10.195312 11.621094 10.378906 C 11.710938 10.472656 11.835938 10.519531 11.957031 10.519531 C 12.078125 10.519531 12.203125 10.472656 12.292969 10.378906 L 18.988281 3.6875 C 19.175781 3.5 19.175781 3.199219 18.988281 3.011719 Z M 18.988281 3.011719 "/>
<path fill="#555555" d="M 18.652344 2.867188 C 18.386719 2.867188 18.171875 3.082031 18.171875 3.347656 L 18.171875 9.085938 C 18.171875 9.347656 18.386719 9.5625 18.652344 9.5625 C 18.917969 9.5625 19.132812 9.347656 19.132812 9.085938 L 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#555555" d="M 18.652344 2.867188 L 12.914062 2.867188 C 12.652344 2.867188 12.4375 3.082031 12.4375 3.347656 C 12.4375 3.613281 12.652344 3.828125 12.914062 3.828125 L 18.652344 3.828125 C 18.917969 3.828125 19.132812 3.613281 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#555555" d="M 10.378906 11.621094 C 10.195312 11.433594 9.890625 11.433594 9.703125 11.621094 L 3.007812 18.316406 C 2.820312 18.5 2.820312 18.804688 3.007812 18.992188 C 3.105469 19.085938 3.226562 19.132812 3.347656 19.132812 C 3.46875 19.132812 3.589844 19.085938 3.683594 18.992188 L 10.378906 12.296875 C 10.566406 12.109375 10.566406 11.804688 10.378906 11.621094 Z M 10.378906 11.621094 "/>
<path fill="#555555" d="M 3.347656 12.4375 C 3.082031 12.4375 2.867188 12.652344 2.867188 12.914062 L 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 C 3.613281 19.132812 3.828125 18.917969 3.828125 18.652344 L 3.828125 12.914062 C 3.828125 12.652344 3.613281 12.4375 3.347656 12.4375 Z M 3.347656 12.4375 "/>
<path fill="#555555" d="M 9.085938 18.171875 L 3.347656 18.171875 C 3.082031 18.171875 2.867188 18.386719 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 L 9.085938 19.132812 C 9.347656 19.132812 9.5625 18.917969 9.5625 18.652344 C 9.5625 18.386719 9.347656 18.171875 9.085938 18.171875 Z M 9.085938 18.171875 "/>
<path fill="#555555" d="M 20.5625 0 L 1.4375 0 C 0.644531 0 0 0.644531 0 1.4375 L 0 20.5625 C 0 21.355469 0.644531 22 1.4375 22 L 20.5625 22 C 21.355469 22 22 21.355469 22 20.5625 L 22 1.4375 C 22 0.644531 21.355469 0 20.5625 0 Z M 21.042969 20.5625 C 21.042969 20.828125 20.828125 21.042969 20.5625 21.042969 L 1.4375 21.042969 C 1.171875 21.042969 0.957031 20.828125 0.957031 20.5625 L 0.957031 1.4375 C 0.957031 1.171875 1.171875 0.957031 1.4375 0.957031 L 20.5625 0.957031 C 20.828125 0.957031 21.042969 1.171875 21.042969 1.4375 Z M 21.042969 20.5625 "/>
</g>
</symbol><symbol id="icon-b-ic-rotate-clockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#555555" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
<path stroke="#555555" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
<path fill="#555555" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
</g>
</symbol><symbol id="icon-b-ic-rotate-counterclockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#555555" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
<path fill="#555555" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
<path stroke="#555555" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
</g>
</symbol><symbol id="icon-b-ic-rotate" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#555555" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
<path stroke="#555555" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
</g>
</symbol><symbol id="icon-b-ic-shape-circle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14.5" stroke="#555555"/>
</g>
</symbol><symbol id="icon-b-ic-shape-rectangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<rect width="27" height="27" x="2.5" y="2.5" stroke="#555555" rx="1"/>
</g>
</symbol><symbol id="icon-b-ic-shape-triangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
</g>
</symbol><symbol id="icon-b-ic-shape" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path fill="#555555" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
<path stroke="#555555" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
</g>
</symbol><symbol id="icon-b-ic-text-align-center" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
</g>
</symbol><symbol id="icon-b-ic-text-align-left" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
</g>
</symbol><symbol id="icon-b-ic-text-align-right" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
</g>
</symbol><symbol id="icon-b-ic-text-bold" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M7 2h2v2H7zM7 28h2v2H7z"/>
<path stroke="#555555" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
</g>
</symbol><symbol id="icon-b-ic-text-italic" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
</g>
</symbol><symbol id="icon-b-ic-text-underline" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#555555" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
<path fill="#555555" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
</g>
</symbol><symbol id="icon-b-ic-text" viewBox="0 0 24 24">
<g fill="#555555" fill-rule="evenodd">
<path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
<path d="M11 3h1v18h-1z"/>
<path d="M10 20h3v1h-3z"/>
</g>
</symbol><symbol id="icon-b-ic-undo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M24 0H0v24h24z" opacity=".5"/>
<path fill="#555555" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
<path stroke="#555555" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
</g>
</symbol><symbol id="icon-b-ic-zoom-hand" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<path d="M0 0H24V24H0z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#555555" fill-rule="nonzero" d="M8.672 3.36c1.328 0 2.114.78 2.29 1.869l.014.101.023.006v1.042l-.638-.185c-.187-.055-.323-.211-.354-.399L10 5.713c0-.825-.42-1.353-1.328-1.353C7.695 4.36 7 5.041 7 5.713v7.941c0 .439-.524.665-.843.364l-1.868-1.761c-.595-.528-1.316-.617-1.918-.216-.522.348-.562 1.203-.18 1.8L7.738 22h11.013l.285-.518c1.247-2.326 1.897-4.259 1.96-5.785l.004-.239V8.035c0-.656-.5-1.17-1-1.17-.503 0-1 .456-1 1.17 0 .333-.32.573-.64.48L18 8.41V7.368l.086.026.042-.136c.279-.805.978-1.332 1.738-1.388L20 5.865c1.057 0 2 .967 2 2.17v7.423c0 1.929-.845 4.352-2.521 7.29-.09.156-.255.252-.435.252H7.474c-.166 0-.321-.082-.414-.219l-5.704-8.39c-.653-1.019-.584-2.486.46-3.182 1-.666 2.216-.516 3.148.31L6 12.495V5.713c0-1.18 1.058-2.263 2.49-2.348z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#555555" fill-rule="nonzero" d="M12.5 1.5c1.325 0 2.41 1.032 2.495 2.336L15 4v7.22h-1V4c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L11 4v7.22h-1V4c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#555555" fill-rule="nonzero" d="M16.5 3.5c1.325 0 2.41 1.032 2.495 2.336L19 6v6.3h-1V6c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L15 6v2.44h-1V6c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
</g>
</symbol><symbol id="icon-b-ic-zoom-zoom-in" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g>
<g transform="translate(-229 -290) translate(229 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#555555"/>
<path fill="#555555" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#555555" d="M7 10H14V11H7z"/>
<path fill="#555555" d="M10 7H11V14H10z"/>
</g>
</g>
</g>
</symbol><symbol id="icon-b-ic-zoom-zoom-out" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g>
<g transform="translate(-263 -290) translate(263 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#555555"/>
<path fill="#555555" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#555555" d="M7 10H14V11H7z"/>
</g>
</g>
</g>
</symbol><symbol id="icon-b-img-bi" viewBox="0 0 257 26">
<g fill="#FDBA3B">
<path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
</g>
</symbol></svg>

After

Width:  |  Height:  |  Size: 35 KiB

@@ -0,0 +1,369 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs>
<path id="rfn4rylffa" d="M7 12c-.335 0-.663-.025-.983-.074C3.171 11.492 1 9.205 1 6.444c0-1.363.534-2.613 1.415-3.58"/>
<mask id="6f9gn2dysb" width="6" height="9.136" x="0" y="0" fill="#fff" maskUnits="objectBoundingBox">
<use xlink:href="#rfn4rylffa"/>
</mask>
</defs><symbol id="icon-c-ic-apply" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#e9e9e9" d="M4 12.011l5 5L20.011 6"/>
</g>
</symbol><symbol id="icon-c-ic-cancel" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#e9e9e9" d="M6 6l12 12M18 6L6 18"/>
</g>
</symbol><symbol id="icon-c-ic-crop" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#e9e9e9" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
<path fill="#e9e9e9" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
</g>
</symbol><symbol id="icon-c-ic-delete-all" viewBox="0 0 24 24">
<g fill="#e9e9e9" fill-rule="evenodd">
<path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
</g>
</symbol><symbol id="icon-c-ic-delete" viewBox="0 0 24 24">
<g fill="#e9e9e9" fill-rule="evenodd">
<path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
</g>
</symbol><symbol id="icon-c-ic-draw-free" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
</g>
</symbol><symbol id="icon-c-ic-draw-line" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M2 15.5h28"/>
</g>
</symbol><symbol id="icon-c-ic-draw" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#e9e9e9" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
<path fill="#e9e9e9" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
</g>
</symbol><symbol id="icon-c-ic-filter" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#e9e9e9" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
<path fill="#e9e9e9" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
</g>
</symbol><symbol id="icon-c-ic-flip-reset" viewBox="0 0 31 32">
<g fill="none" fill-rule="evenodd">
<path d="M31 0H0v32h31z"/>
<path fill="#e9e9e9" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
</g>
</symbol><symbol id="icon-c-ic-flip-x" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M32 32H0V0h32z"/>
<path fill="#e9e9e9" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
</g>
</symbol><symbol id="icon-c-ic-flip-y" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0v32h32V0z"/>
<path fill="#e9e9e9" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
</g>
</symbol><symbol id="icon-c-ic-flip" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#e9e9e9" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
</g>
</symbol><symbol id="icon-c-ic-history-check" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M4.5 -1L1.5 2 6.5 7" transform="translate(-60 -804) translate(60 804) translate(2 3) rotate(-90 4 3)"/>
</g>
</symbol><symbol id="icon-c-ic-history-crop" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#e9e9e9" d="M2 0h1v10c-.552 0-1-.448-1-1V0zM10 9v3H9V9h1zM9 2h1v6H9V2z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#e9e9e9" d="M2 9H12V10H2zM9 2c.513 0 .936.386.993.883L10 3H3V2h6zM2 3H0V2h2v1z" transform="translate(-84 -804) translate(84 804)"/>
</g>
</symbol><symbol id="icon-c-ic-history-delete" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g fill="#e9e9e9">
<path d="M2 9h8V1h1v8.5c0 .276-.224.5-.5.5h-9c-.276 0-.5-.224-.5-.5V1h1v8zM0 0H12V1H0z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 3H5V7H4zM7 3H8V7H7z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 1h4V0h1v1.5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V0h1v1z" transform="translate(-300 -804) translate(300 804) matrix(1 0 0 -1 0 2)"/>
</g>
</g>
</symbol><symbol id="icon-c-ic-history-draw" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 1H12V13H0z" transform="translate(-156 -804) translate(156 803)"/>
<path stroke="#e9e9e9" d="M9.622 1.584l1.835 1.658-8.31 8.407L.5 12.5V11l9.122-9.416z" transform="translate(-156 -804) translate(156 803)"/>
<path fill="#FFF" d="M7.628 3.753L10.378 3.753 10.378 4.253 7.628 4.253z" transform="translate(-156 -804) translate(156 803) rotate(45 9.003 4.003)"/>
</g>
</symbol><symbol id="icon-c-ic-history-filter" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#e9e9e9" d="M12 3v1H9V3h3zM7 4H0V3h7v1z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#e9e9e9" d="M12 8v1H9V8h3zM7 9H0V8h7v1z" transform="translate(-276 -804) translate(276 804) matrix(-1 0 0 1 12 0)"/>
<path fill="#e9e9e9" d="M8 1c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1zM4 7c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1z" transform="translate(-276 -804) translate(276 804)"/>
</g>
</symbol><symbol id="icon-c-ic-history-flip" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-108 -804) translate(108 804)"/>
<path fill="#e9e9e9" d="M6 0L7 0 7 12 6 12zM11 10V9h1v1.5c0 .276-.224.5-.5.5H10v-1h1zM5 1v1H1v8h4v1H.5c-.276 0-.5-.224-.5-.5v-9c0-.276.224-.5.5-.5H5zm7 5v2h-1V6h1zm0-3v2h-1V3h1zM9 1v1H7V1h2zm2.5 0c.276 0 .5.224.5.5V2h-2V1h1.5zM9 11H7v-1h2v1z" transform="translate(-108 -804) translate(108 804)"/>
</g>
</symbol><symbol id="icon-c-ic-history-group" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-348 -804) translate(348 804)">
<path d="M0 0H12V12H0z"/>
<path fill="#e9e9e9" d="M1 9v2h1v1H.5c-.276 0-.5-.224-.5-.5V9h1zm11 1v1.5c0 .276-.224.5-.5.5H9v-1h2v-1h1zm-4 1v1H6v-1h2zm-3 0v1H3v-1h2zm7-4v2h-1V7h1zM1 6v2H0V6h1zm11-2v2h-1V4h1zM1 3v2H0V3h1zm10.5-3c.276 0 .5.224.5.5V3h-1V1h-1V0h1.5zM6 0v1H4V0h2zm3 0v1H7V0h2zM0 .5C0 .224.224 0 .5 0H3v1H1v1H0V.5zM9.5 4c.276 0 .5.224.5.5v5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V8.355c.317.094.652.145 1 .145V9h4V5h-.5c0-.348-.05-.683-.145-1H9.5z"/>
<circle cx="5" cy="5" r="2.5" stroke="#e9e9e9"/>
</g>
</g>
</symbol><symbol id="icon-c-ic-history-icon" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-204 -804) translate(204 804)"/>
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="M6 9.568L2.601 11 2.975 7.467 0.5 4.82 4.13 4.068 6 1 7.87 4.068 11.5 4.82 9.025 7.467 9.399 11z" transform="translate(-204 -804) translate(204 804)"/>
</g>
</symbol><symbol id="icon-c-ic-history-load" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#e9e9e9" d="M5 0c.552 0 1 .448 1 1v1h5.5c.276 0 .5.224.5.5v8c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V1c0-.552.448-1 1-1h4zm0 1H1v9h10V3H5V1z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#e9e9e9" d="M1 2L5 2 5 3 1 3z" transform="translate(-324 -805) translate(324 805)"/>
</g>
</symbol><symbol id="icon-c-ic-history-mask" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-252 -804) translate(252 804)">
<path d="M0 0H12V12H0z"/>
<circle cx="6" cy="6" r="2.5" stroke="#444"/>
<path fill="#e9e9e9" d="M11.5 0c.276 0 .5.224.5.5v11c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V.5C0 .224.224 0 .5 0h11zM11 1H1v10h10V1z"/>
</g>
</g>
</symbol><symbol id="icon-c-ic-history-resize" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path fill="#e9e9e9" d="M 9.492188 1.507812 C 9.398438 1.414062 9.25 1.414062 9.15625 1.507812 L 5.8125 4.851562 C 5.714844 4.945312 5.714844 5.097656 5.8125 5.1875 C 5.855469 5.234375 5.917969 5.257812 5.976562 5.257812 C 6.039062 5.257812 6.101562 5.234375 6.148438 5.1875 L 9.492188 1.84375 C 9.585938 1.75 9.585938 1.601562 9.492188 1.507812 Z M 9.492188 1.507812 "/>
<path fill="#e9e9e9" d="M 9.328125 1.433594 C 9.195312 1.433594 9.085938 1.539062 9.085938 1.671875 L 9.085938 4.542969 C 9.085938 4.671875 9.195312 4.78125 9.328125 4.78125 C 9.460938 4.78125 9.566406 4.671875 9.566406 4.542969 L 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#e9e9e9" d="M 9.328125 1.433594 L 6.457031 1.433594 C 6.328125 1.433594 6.21875 1.539062 6.21875 1.671875 C 6.21875 1.804688 6.328125 1.914062 6.457031 1.914062 L 9.328125 1.914062 C 9.460938 1.914062 9.566406 1.804688 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#e9e9e9" d="M 5.1875 5.8125 C 5.097656 5.71875 4.945312 5.71875 4.851562 5.8125 L 1.503906 9.15625 C 1.410156 9.25 1.410156 9.402344 1.503906 9.496094 C 1.554688 9.542969 1.613281 9.566406 1.671875 9.566406 C 1.734375 9.566406 1.796875 9.542969 1.84375 9.496094 L 5.1875 6.148438 C 5.28125 6.054688 5.28125 5.902344 5.1875 5.8125 Z M 5.1875 5.8125 "/>
<path fill="#e9e9e9" d="M 1.671875 6.21875 C 1.539062 6.21875 1.433594 6.328125 1.433594 6.457031 L 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 C 1.804688 9.566406 1.914062 9.460938 1.914062 9.328125 L 1.914062 6.457031 C 1.914062 6.328125 1.804688 6.21875 1.671875 6.21875 Z M 1.671875 6.21875 "/>
<path fill="#e9e9e9" d="M 4.542969 9.085938 L 1.671875 9.085938 C 1.539062 9.085938 1.433594 9.195312 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 L 4.542969 9.566406 C 4.671875 9.566406 4.78125 9.460938 4.78125 9.328125 C 4.78125 9.195312 4.671875 9.085938 4.542969 9.085938 Z M 4.542969 9.085938 "/>
<path fill="#e9e9e9" d="M 10.28125 0 L 0.71875 0 C 0.320312 0 0 0.320312 0 0.71875 L 0 10.28125 C 0 10.679688 0.320312 11 0.71875 11 L 10.28125 11 C 10.679688 11 11 10.679688 11 10.28125 L 11 0.71875 C 11 0.320312 10.679688 0 10.28125 0 Z M 10.523438 10.28125 C 10.523438 10.414062 10.414062 10.523438 10.28125 10.523438 L 0.71875 10.523438 C 0.585938 10.523438 0.476562 10.414062 0.476562 10.28125 L 0.476562 0.71875 C 0.476562 0.585938 0.585938 0.476562 0.71875 0.476562 L 10.28125 0.476562 C 10.414062 0.476562 10.523438 0.585938 10.523438 0.71875 Z M 10.523438 10.28125 "/>
</g>
</symbol><symbol id="icon-c-ic-history-rotate" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-132 -804) translate(132 804)">
<path d="M0 0.5H12V12.5H0z"/>
<path fill="#e9e9e9" d="M6.5 1C9.538 1 12 3.462 12 6.5c0 2.37-1.5 4.39-3.6 5.163l-.407-.916C9.744 10.13 11 8.462 11 6.5 11 4.015 8.985 2 6.5 2c-.777 0-1.509.197-2.147.544L4 1.75l-.205-.04C4.594 1.258 5.517 1 6.5 1z"/>
<use stroke="#e9e9e9" stroke-dasharray="2 1.25" stroke-width="4" mask="url(#6f9gn2dysb)" xlink:href="#rfn4rylffa"/>
<path fill="#e9e9e9" d="M4.279 0L6 1.75 4.25 3.571 3.543 2.864 4.586 1.75 3.572 0.707z" transform="matrix(-1 0 0 1 9.543 0)"/>
</g>
</g>
</symbol><symbol id="icon-c-ic-history-shape" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-180 -804) translate(180 804)"/>
<path fill="#e9e9e9" d="M11.5 4c.276 0 .5.224.5.5v7c0 .276-.224.5-.5.5h-7c-.276 0-.5-.224-.5-.5V8.8h1V11h6V5H8.341l-.568-1H11.5z" transform="translate(-180 -804) translate(180 804)"/>
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M4.5 0.5L8.5 7.611 0.5 7.611z" transform="translate(-180 -804) translate(180 804)"/>
</g>
</symbol><symbol id="icon-c-ic-history-text" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#e9e9e9" d="M2 1h8c.552 0 1 .448 1 1H1c0-.552.448-1 1-1z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#e9e9e9" d="M1 1H2V3H1zM10 1H11V3H10zM5.5 1L6.5 1 6.5 11 5.5 11z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#e9e9e9" d="M4 10H8V11H4z" transform="translate(-228 -804) translate(228 804)"/>
</g>
</symbol><symbol id="icon-c-ic-history" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H24V24H0z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
<path fill="#e9e9e9" d="M12.5 1C18.299 1 23 5.701 23 11.5S18.299 22 12.5 22c-5.29 0-9.665-3.911-10.394-8.999h1.012C3.838 17.534 7.764 21 12.5 21c5.247 0 9.5-4.253 9.5-9.5S17.747 2 12.5 2C8.49 2 5.06 4.485 3.666 8H3h4v1H2V4h1v3.022C4.68 3.462 8.303 1 12.5 1zm.5 5l-.001 5.291 2.537 2.537-.708.708L12.292 12H12V6h1z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
</g>
</symbol><symbol id="icon-c-ic-icon-arrow-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-arrow-3" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-arrow" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-bubble" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-heart" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill-rule="nonzero" stroke="#e9e9e9" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
<path fill="#e9e9e9" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
<path fill="#e9e9e9" d="M25 3h1v9h-1z"/>
<path stroke="#e9e9e9" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-c-ic-icon-location" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<g stroke="#e9e9e9">
<path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
<circle cx="16" cy="13" r="4.5"/>
</g>
</g>
</symbol><symbol id="icon-c-ic-icon-polygon" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-star-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
</g>
</symbol><symbol id="icon-c-ic-icon-star" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
</g>
</symbol><symbol id="icon-c-ic-icon" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
</g>
</symbol><symbol id="icon-c-ic-mask-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
<path fill="#e9e9e9" d="M25 3h1v9h-1z"/>
<path stroke="#e9e9e9" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-c-ic-mask" viewBox="0 0 24 24">
<g fill="none">
<circle cx="12" cy="12" r="4.5" stroke="#e9e9e9"/>
<path fill="#e9e9e9" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
</g>
</symbol><symbol id="icon-c-ic-redo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#e9e9e9" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
</g>
</symbol><symbol id="icon-c-ic-reset" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#e9e9e9" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
</g>
</symbol><symbol id="icon-c-ic-resize" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#e9e9e9" d="M 18.988281 3.011719 C 18.800781 2.824219 18.5 2.824219 18.3125 3.011719 L 11.621094 9.707031 C 11.429688 9.894531 11.429688 10.195312 11.621094 10.378906 C 11.710938 10.472656 11.835938 10.519531 11.957031 10.519531 C 12.078125 10.519531 12.203125 10.472656 12.292969 10.378906 L 18.988281 3.6875 C 19.175781 3.5 19.175781 3.199219 18.988281 3.011719 Z M 18.988281 3.011719 "/>
<path fill="#e9e9e9" d="M 18.652344 2.867188 C 18.386719 2.867188 18.171875 3.082031 18.171875 3.347656 L 18.171875 9.085938 C 18.171875 9.347656 18.386719 9.5625 18.652344 9.5625 C 18.917969 9.5625 19.132812 9.347656 19.132812 9.085938 L 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#e9e9e9" d="M 18.652344 2.867188 L 12.914062 2.867188 C 12.652344 2.867188 12.4375 3.082031 12.4375 3.347656 C 12.4375 3.613281 12.652344 3.828125 12.914062 3.828125 L 18.652344 3.828125 C 18.917969 3.828125 19.132812 3.613281 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#e9e9e9" d="M 10.378906 11.621094 C 10.195312 11.433594 9.890625 11.433594 9.703125 11.621094 L 3.007812 18.316406 C 2.820312 18.5 2.820312 18.804688 3.007812 18.992188 C 3.105469 19.085938 3.226562 19.132812 3.347656 19.132812 C 3.46875 19.132812 3.589844 19.085938 3.683594 18.992188 L 10.378906 12.296875 C 10.566406 12.109375 10.566406 11.804688 10.378906 11.621094 Z M 10.378906 11.621094 "/>
<path fill="#e9e9e9" d="M 3.347656 12.4375 C 3.082031 12.4375 2.867188 12.652344 2.867188 12.914062 L 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 C 3.613281 19.132812 3.828125 18.917969 3.828125 18.652344 L 3.828125 12.914062 C 3.828125 12.652344 3.613281 12.4375 3.347656 12.4375 Z M 3.347656 12.4375 "/>
<path fill="#e9e9e9" d="M 9.085938 18.171875 L 3.347656 18.171875 C 3.082031 18.171875 2.867188 18.386719 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 L 9.085938 19.132812 C 9.347656 19.132812 9.5625 18.917969 9.5625 18.652344 C 9.5625 18.386719 9.347656 18.171875 9.085938 18.171875 Z M 9.085938 18.171875 "/>
<path fill="#e9e9e9" d="M 20.5625 0 L 1.4375 0 C 0.644531 0 0 0.644531 0 1.4375 L 0 20.5625 C 0 21.355469 0.644531 22 1.4375 22 L 20.5625 22 C 21.355469 22 22 21.355469 22 20.5625 L 22 1.4375 C 22 0.644531 21.355469 0 20.5625 0 Z M 21.042969 20.5625 C 21.042969 20.828125 20.828125 21.042969 20.5625 21.042969 L 1.4375 21.042969 C 1.171875 21.042969 0.957031 20.828125 0.957031 20.5625 L 0.957031 1.4375 C 0.957031 1.171875 1.171875 0.957031 1.4375 0.957031 L 20.5625 0.957031 C 20.828125 0.957031 21.042969 1.171875 21.042969 1.4375 Z M 21.042969 20.5625 "/>
</g>
</symbol><symbol id="icon-c-ic-rotate-clockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#e9e9e9" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
<path fill="#e9e9e9" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
</g>
</symbol><symbol id="icon-c-ic-rotate-counterclockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#e9e9e9" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
<path fill="#e9e9e9" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
</g>
</symbol><symbol id="icon-c-ic-rotate" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#e9e9e9" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
</g>
</symbol><symbol id="icon-c-ic-shape-circle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14.5" stroke="#e9e9e9"/>
</g>
</symbol><symbol id="icon-c-ic-shape-rectangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<rect width="27" height="27" x="2.5" y="2.5" stroke="#e9e9e9" rx="1"/>
</g>
</symbol><symbol id="icon-c-ic-shape-triangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
</g>
</symbol><symbol id="icon-c-ic-shape" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path fill="#e9e9e9" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
<path stroke="#e9e9e9" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
</g>
</symbol><symbol id="icon-c-ic-text-align-center" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
</g>
</symbol><symbol id="icon-c-ic-text-align-left" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
</g>
</symbol><symbol id="icon-c-ic-text-align-right" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
</g>
</symbol><symbol id="icon-c-ic-text-bold" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M7 2h2v2H7zM7 28h2v2H7z"/>
<path stroke="#e9e9e9" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
</g>
</symbol><symbol id="icon-c-ic-text-italic" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
</g>
</symbol><symbol id="icon-c-ic-text-underline" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#e9e9e9" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
<path fill="#e9e9e9" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
</g>
</symbol><symbol id="icon-c-ic-text" viewBox="0 0 24 24">
<g fill="#e9e9e9" fill-rule="evenodd">
<path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
<path d="M11 3h1v18h-1z"/>
<path d="M10 20h3v1h-3z"/>
</g>
</symbol><symbol id="icon-c-ic-undo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M24 0H0v24h24z" opacity=".5"/>
<path fill="#e9e9e9" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
<path stroke="#e9e9e9" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
</g>
</symbol><symbol id="icon-c-ic-zoom-hand" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<path d="M0 0H24V24H0z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#e9e9e9" fill-rule="nonzero" d="M8.672 3.36c1.328 0 2.114.78 2.29 1.869l.014.101.023.006v1.042l-.638-.185c-.187-.055-.323-.211-.354-.399L10 5.713c0-.825-.42-1.353-1.328-1.353C7.695 4.36 7 5.041 7 5.713v7.941c0 .439-.524.665-.843.364l-1.868-1.761c-.595-.528-1.316-.617-1.918-.216-.522.348-.562 1.203-.18 1.8L7.738 22h11.013l.285-.518c1.247-2.326 1.897-4.259 1.96-5.785l.004-.239V8.035c0-.656-.5-1.17-1-1.17-.503 0-1 .456-1 1.17 0 .333-.32.573-.64.48L18 8.41V7.368l.086.026.042-.136c.279-.805.978-1.332 1.738-1.388L20 5.865c1.057 0 2 .967 2 2.17v7.423c0 1.929-.845 4.352-2.521 7.29-.09.156-.255.252-.435.252H7.474c-.166 0-.321-.082-.414-.219l-5.704-8.39c-.653-1.019-.584-2.486.46-3.182 1-.666 2.216-.516 3.148.31L6 12.495V5.713c0-1.18 1.058-2.263 2.49-2.348z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#e9e9e9" fill-rule="nonzero" d="M12.5 1.5c1.325 0 2.41 1.032 2.495 2.336L15 4v7.22h-1V4c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L11 4v7.22h-1V4c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#e9e9e9" fill-rule="nonzero" d="M16.5 3.5c1.325 0 2.41 1.032 2.495 2.336L19 6v6.3h-1V6c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L15 6v2.44h-1V6c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
</g>
</symbol><symbol id="icon-c-ic-zoom-zoom-in" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g>
<g transform="translate(-229 -290) translate(229 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#e9e9e9"/>
<path fill="#e9e9e9" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#e9e9e9" d="M7 10H14V11H7z"/>
<path fill="#e9e9e9" d="M10 7H11V14H10z"/>
</g>
</g>
</g>
</symbol><symbol id="icon-c-ic-zoom-zoom-out" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g>
<g transform="translate(-263 -290) translate(263 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#e9e9e9"/>
<path fill="#e9e9e9" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#e9e9e9" d="M7 10H14V11H7z"/>
</g>
</g>
</g>
</symbol><symbol id="icon-c-img-bi" viewBox="0 0 257 26">
<g fill="#FDBA3B">
<path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
</g>
</symbol></svg>

After

Width:  |  Height:  |  Size: 35 KiB

@@ -0,0 +1,369 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs>
<path id="rfn4rylffa" d="M7 12c-.335 0-.663-.025-.983-.074C3.171 11.492 1 9.205 1 6.444c0-1.363.534-2.613 1.415-3.58"/>
<mask id="6f9gn2dysb" width="6" height="9.136" x="0" y="0" fill="#fff" maskUnits="objectBoundingBox">
<use xlink:href="#rfn4rylffa"/>
</mask>
</defs><symbol id="icon-d-ic-apply" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#8a8a8a" d="M4 12.011l5 5L20.011 6"/>
</g>
</symbol><symbol id="icon-d-ic-cancel" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#8a8a8a" d="M6 6l12 12M18 6L6 18"/>
</g>
</symbol><symbol id="icon-d-ic-crop" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#8a8a8a" d="M4 0h1v20a1 1 0 0 1-1-1V0zM20 17h-1V5h1v12zm0 2v5h-1v-5h1z"/>
<path fill="#8a8a8a" d="M5 19h19v1H5zM4.762 4v1H0V4h4.762zM7 4h12a1 1 0 0 1 1 1H7V4z"/>
</g>
</symbol><symbol id="icon-d-ic-delete-all" viewBox="0 0 24 24">
<g fill="#8a8a8a" fill-rule="evenodd">
<path d="M5 23H3a1 1 0 0 1-1-1V6h1v16h2v1zm16-10h-1V6h1v7zM9 13H8v-3h1v3zm3 0h-1v-3h1v3zm3 0h-1v-3h1v3zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM11.286 21H8.714L8 23H7l1-2.8V20h.071L9.5 16h1l1.429 4H12v.2l1 2.8h-1l-.714-2zm-.357-1L10 17.4 9.071 20h1.858zM20 22h3v1h-4v-7h1v6zm-5 0h3v1h-4v-7h1v6z"/>
</g>
</symbol><symbol id="icon-d-ic-delete" viewBox="0 0 24 24">
<g fill="#8a8a8a" fill-rule="evenodd">
<path d="M3 6v16h17V6h1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h1zM14.794 3.794L13 2h-3L8.206 3.794A.963.963 0 0 1 8 2.5l.703-1.055A1 1 0 0 1 9.535 1h3.93a1 1 0 0 1 .832.445L15 2.5a.965.965 0 0 1-.206 1.294zM14.197 4H8.803h5.394z"/>
<path d="M0 3h23v1H0zM8 10h1v6H8v-6zm3 0h1v6h-1v-6zm3 0h1v6h-1v-6z"/>
</g>
</symbol><symbol id="icon-d-ic-draw-free" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M2.5 20.929C2.594 10.976 4.323 6 7.686 6c5.872 0 2.524 19 7.697 19s1.89-14.929 6.414-14.929 1.357 10.858 5.13 10.858c1.802 0 2.657-2.262 2.566-6.786"/>
</g>
</symbol><symbol id="icon-d-ic-draw-line" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M2 15.5h28"/>
</g>
</symbol><symbol id="icon-d-ic-draw" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#8a8a8a" d="M2.5 21.5H5c.245 0 .48-.058.691-.168l.124-.065.14.01c.429.028.85-.127 1.16-.437L22.55 5.405a.5.5 0 0 0 0-.707l-3.246-3.245a.5.5 0 0 0-.707 0L3.162 16.888a1.495 1.495 0 0 0-.437 1.155l.01.14-.065.123c-.111.212-.17.448-.17.694v2.5z"/>
<path fill="#8a8a8a" d="M16.414 3.707l3.89 3.89-.708.706-3.889-3.889z"/>
</g>
</symbol><symbol id="icon-d-ic-filter" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#8a8a8a" d="M12 7v1H2V7h10zm6 0h4v1h-4V7zM12 16v1h10v-1H12zm-6 0H2v1h4v-1z"/>
<path fill="#8a8a8a" d="M8.5 20a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5zM15.5 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm0-1a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z"/>
</g>
</symbol><symbol id="icon-d-ic-flip-reset" viewBox="0 0 31 32">
<g fill="none" fill-rule="evenodd">
<path d="M31 0H0v32h31z"/>
<path fill="#8a8a8a" d="M28 16a8 8 0 0 1-8 8H3v-1h1v-7H3a8 8 0 0 1 8-8h17v1h-1v7h1zM11 9a7 7 0 0 0-7 7v7h16a7 7 0 0 0 7-7V9H11z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M24 5l3.5 3.5L24 12M7 20l-3.5 3.5L7 27"/>
</g>
</symbol><symbol id="icon-d-ic-flip-x" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M32 32H0V0h32z"/>
<path fill="#8a8a8a" d="M17 32h-1V0h1zM27.167 11l.5 3h-1.03l-.546-3h1.076zm-.5-3h-1.122L25 5h-5V4h5.153a1 1 0 0 1 .986.836L26.667 8zm1.5 9l.5 3h-.94l-.545-3h.985zm1 6l.639 3.836A1 1 0 0 1 28.819 28H26v-1h3l-.726-4h.894zM23 28h-3v-1h3v1zM13 4v1H7L3 27h10v1H3.18a1 1 0 0 1-.986-1.164l3.666-22A1 1 0 0 1 6.847 4H13z"/>
</g>
</symbol><symbol id="icon-d-ic-flip-y" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0v32h32V0z"/>
<path fill="#8a8a8a" d="M0 16v1h32v-1zM11 27.167l3 .5v-1.03l-3-.546v1.076zm-3-.5v-1.122L5 25v-5H4v5.153a1 1 0 0 0 .836.986L8 26.667zm9 1.5l3 .5v-.94l-3-.545v.985zm6 1l3.836.639A1 1 0 0 0 28 28.82V26h-1v3l-4-.727v.894zM28 23v-3h-1v3h1zM4 13h1V7l22-4v10h1V3.18a1 1 0 0 0-1.164-.986l-22 3.667A1 1 0 0 0 4 6.847V13z"/>
</g>
</symbol><symbol id="icon-d-ic-flip" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#8a8a8a" d="M11 0h1v24h-1zM19 21v-1h2v-2h1v2a1 1 0 0 1-1 1h-2zm-2 0h-3v-1h3v1zm5-5h-1v-3h1v3zm0-5h-1V8h1v3zm0-5h-1V4h-2V3h2a1 1 0 0 1 1 1v2zm-5-3v1h-3V3h3zM9 3v1H2v16h7v1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h7z"/>
</g>
</symbol><symbol id="icon-d-ic-history-check" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M4.5 -1L1.5 2 6.5 7" transform="translate(-60 -804) translate(60 804) translate(2 3) rotate(-90 4 3)"/>
</g>
</symbol><symbol id="icon-d-ic-history-crop" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#8a8a8a" d="M2 0h1v10c-.552 0-1-.448-1-1V0zM10 9v3H9V9h1zM9 2h1v6H9V2z" transform="translate(-84 -804) translate(84 804)"/>
<path fill="#8a8a8a" d="M2 9H12V10H2zM9 2c.513 0 .936.386.993.883L10 3H3V2h6zM2 3H0V2h2v1z" transform="translate(-84 -804) translate(84 804)"/>
</g>
</symbol><symbol id="icon-d-ic-history-delete" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g fill="#8a8a8a">
<path d="M2 9h8V1h1v8.5c0 .276-.224.5-.5.5h-9c-.276 0-.5-.224-.5-.5V1h1v8zM0 0H12V1H0z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 3H5V7H4zM7 3H8V7H7z" transform="translate(-300 -804) translate(300 804) translate(0 2)"/>
<path d="M4 1h4V0h1v1.5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V0h1v1z" transform="translate(-300 -804) translate(300 804) matrix(1 0 0 -1 0 2)"/>
</g>
</g>
</symbol><symbol id="icon-d-ic-history-draw" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 1H12V13H0z" transform="translate(-156 -804) translate(156 803)"/>
<path stroke="#8a8a8a" d="M9.622 1.584l1.835 1.658-8.31 8.407L.5 12.5V11l9.122-9.416z" transform="translate(-156 -804) translate(156 803)"/>
<path fill="#FFF" d="M7.628 3.753L10.378 3.753 10.378 4.253 7.628 4.253z" transform="translate(-156 -804) translate(156 803) rotate(45 9.003 4.003)"/>
</g>
</symbol><symbol id="icon-d-ic-history-filter" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#8a8a8a" d="M12 3v1H9V3h3zM7 4H0V3h7v1z" transform="translate(-276 -804) translate(276 804)"/>
<path fill="#8a8a8a" d="M12 8v1H9V8h3zM7 9H0V8h7v1z" transform="translate(-276 -804) translate(276 804) matrix(-1 0 0 1 12 0)"/>
<path fill="#8a8a8a" d="M8 1c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1zM4 7c1.105 0 2 .895 2 2s-.895 2-2 2-2-.895-2-2 .895-2 2-2zm0 1c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1z" transform="translate(-276 -804) translate(276 804)"/>
</g>
</symbol><symbol id="icon-d-ic-history-flip" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-108 -804) translate(108 804)"/>
<path fill="#8a8a8a" d="M6 0L7 0 7 12 6 12zM11 10V9h1v1.5c0 .276-.224.5-.5.5H10v-1h1zM5 1v1H1v8h4v1H.5c-.276 0-.5-.224-.5-.5v-9c0-.276.224-.5.5-.5H5zm7 5v2h-1V6h1zm0-3v2h-1V3h1zM9 1v1H7V1h2zm2.5 0c.276 0 .5.224.5.5V2h-2V1h1.5zM9 11H7v-1h2v1z" transform="translate(-108 -804) translate(108 804)"/>
</g>
</symbol><symbol id="icon-d-ic-history-group" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-348 -804) translate(348 804)">
<path d="M0 0H12V12H0z"/>
<path fill="#8a8a8a" d="M1 9v2h1v1H.5c-.276 0-.5-.224-.5-.5V9h1zm11 1v1.5c0 .276-.224.5-.5.5H9v-1h2v-1h1zm-4 1v1H6v-1h2zm-3 0v1H3v-1h2zm7-4v2h-1V7h1zM1 6v2H0V6h1zm11-2v2h-1V4h1zM1 3v2H0V3h1zm10.5-3c.276 0 .5.224.5.5V3h-1V1h-1V0h1.5zM6 0v1H4V0h2zm3 0v1H7V0h2zM0 .5C0 .224.224 0 .5 0H3v1H1v1H0V.5zM9.5 4c.276 0 .5.224.5.5v5c0 .276-.224.5-.5.5h-5c-.276 0-.5-.224-.5-.5V8.355c.317.094.652.145 1 .145V9h4V5h-.5c0-.348-.05-.683-.145-1H9.5z"/>
<circle cx="5" cy="5" r="2.5" stroke="#8a8a8a"/>
</g>
</g>
</symbol><symbol id="icon-d-ic-history-icon" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-204 -804) translate(204 804)"/>
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.1" d="M6 9.568L2.601 11 2.975 7.467 0.5 4.82 4.13 4.068 6 1 7.87 4.068 11.5 4.82 9.025 7.467 9.399 11z" transform="translate(-204 -804) translate(204 804)"/>
</g>
</symbol><symbol id="icon-d-ic-history-load" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#8a8a8a" d="M5 0c.552 0 1 .448 1 1v1h5.5c.276 0 .5.224.5.5v8c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V1c0-.552.448-1 1-1h4zm0 1H1v9h10V3H5V1z" transform="translate(-324 -805) translate(324 805)"/>
<path fill="#8a8a8a" d="M1 2L5 2 5 3 1 3z" transform="translate(-324 -805) translate(324 805)"/>
</g>
</symbol><symbol id="icon-d-ic-history-mask" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-252 -804) translate(252 804)">
<path d="M0 0H12V12H0z"/>
<circle cx="6" cy="6" r="2.5" stroke="#444"/>
<path fill="#8a8a8a" d="M11.5 0c.276 0 .5.224.5.5v11c0 .276-.224.5-.5.5H.5c-.276 0-.5-.224-.5-.5V.5C0 .224.224 0 .5 0h11zM11 1H1v10h10V1z"/>
</g>
</g>
</symbol><symbol id="icon-d-ic-history-resize" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path fill="#8a8a8a" d="M 9.492188 1.507812 C 9.398438 1.414062 9.25 1.414062 9.15625 1.507812 L 5.8125 4.851562 C 5.714844 4.945312 5.714844 5.097656 5.8125 5.1875 C 5.855469 5.234375 5.917969 5.257812 5.976562 5.257812 C 6.039062 5.257812 6.101562 5.234375 6.148438 5.1875 L 9.492188 1.84375 C 9.585938 1.75 9.585938 1.601562 9.492188 1.507812 Z M 9.492188 1.507812 "/>
<path fill="#8a8a8a" d="M 9.328125 1.433594 C 9.195312 1.433594 9.085938 1.539062 9.085938 1.671875 L 9.085938 4.542969 C 9.085938 4.671875 9.195312 4.78125 9.328125 4.78125 C 9.460938 4.78125 9.566406 4.671875 9.566406 4.542969 L 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#8a8a8a" d="M 9.328125 1.433594 L 6.457031 1.433594 C 6.328125 1.433594 6.21875 1.539062 6.21875 1.671875 C 6.21875 1.804688 6.328125 1.914062 6.457031 1.914062 L 9.328125 1.914062 C 9.460938 1.914062 9.566406 1.804688 9.566406 1.671875 C 9.566406 1.539062 9.460938 1.433594 9.328125 1.433594 Z M 9.328125 1.433594 "/>
<path fill="#8a8a8a" d="M 5.1875 5.8125 C 5.097656 5.71875 4.945312 5.71875 4.851562 5.8125 L 1.503906 9.15625 C 1.410156 9.25 1.410156 9.402344 1.503906 9.496094 C 1.554688 9.542969 1.613281 9.566406 1.671875 9.566406 C 1.734375 9.566406 1.796875 9.542969 1.84375 9.496094 L 5.1875 6.148438 C 5.28125 6.054688 5.28125 5.902344 5.1875 5.8125 Z M 5.1875 5.8125 "/>
<path fill="#8a8a8a" d="M 1.671875 6.21875 C 1.539062 6.21875 1.433594 6.328125 1.433594 6.457031 L 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 C 1.804688 9.566406 1.914062 9.460938 1.914062 9.328125 L 1.914062 6.457031 C 1.914062 6.328125 1.804688 6.21875 1.671875 6.21875 Z M 1.671875 6.21875 "/>
<path fill="#8a8a8a" d="M 4.542969 9.085938 L 1.671875 9.085938 C 1.539062 9.085938 1.433594 9.195312 1.433594 9.328125 C 1.433594 9.460938 1.539062 9.566406 1.671875 9.566406 L 4.542969 9.566406 C 4.671875 9.566406 4.78125 9.460938 4.78125 9.328125 C 4.78125 9.195312 4.671875 9.085938 4.542969 9.085938 Z M 4.542969 9.085938 "/>
<path fill="#8a8a8a" d="M 10.28125 0 L 0.71875 0 C 0.320312 0 0 0.320312 0 0.71875 L 0 10.28125 C 0 10.679688 0.320312 11 0.71875 11 L 10.28125 11 C 10.679688 11 11 10.679688 11 10.28125 L 11 0.71875 C 11 0.320312 10.679688 0 10.28125 0 Z M 10.523438 10.28125 C 10.523438 10.414062 10.414062 10.523438 10.28125 10.523438 L 0.71875 10.523438 C 0.585938 10.523438 0.476562 10.414062 0.476562 10.28125 L 0.476562 0.71875 C 0.476562 0.585938 0.585938 0.476562 0.71875 0.476562 L 10.28125 0.476562 C 10.414062 0.476562 10.523438 0.585938 10.523438 0.71875 Z M 10.523438 10.28125 "/>
</g>
</symbol><symbol id="icon-d-ic-history-rotate" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<g transform="translate(-132 -804) translate(132 804)">
<path d="M0 0.5H12V12.5H0z"/>
<path fill="#8a8a8a" d="M6.5 1C9.538 1 12 3.462 12 6.5c0 2.37-1.5 4.39-3.6 5.163l-.407-.916C9.744 10.13 11 8.462 11 6.5 11 4.015 8.985 2 6.5 2c-.777 0-1.509.197-2.147.544L4 1.75l-.205-.04C4.594 1.258 5.517 1 6.5 1z"/>
<use stroke="#8a8a8a" stroke-dasharray="2 1.25" stroke-width="4" mask="url(#6f9gn2dysb)" xlink:href="#rfn4rylffa"/>
<path fill="#8a8a8a" d="M4.279 0L6 1.75 4.25 3.571 3.543 2.864 4.586 1.75 3.572 0.707z" transform="matrix(-1 0 0 1 9.543 0)"/>
</g>
</g>
</symbol><symbol id="icon-d-ic-history-shape" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-180 -804) translate(180 804)"/>
<path fill="#8a8a8a" d="M11.5 4c.276 0 .5.224.5.5v7c0 .276-.224.5-.5.5h-7c-.276 0-.5-.224-.5-.5V8.8h1V11h6V5H8.341l-.568-1H11.5z" transform="translate(-180 -804) translate(180 804)"/>
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M4.5 0.5L8.5 7.611 0.5 7.611z" transform="translate(-180 -804) translate(180 804)"/>
</g>
</symbol><symbol id="icon-d-ic-history-text" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H12V12H0z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#8a8a8a" d="M2 1h8c.552 0 1 .448 1 1H1c0-.552.448-1 1-1z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#8a8a8a" d="M1 1H2V3H1zM10 1H11V3H10zM5.5 1L6.5 1 6.5 11 5.5 11z" transform="translate(-228 -804) translate(228 804)"/>
<path fill="#8a8a8a" d="M4 10H8V11H4z" transform="translate(-228 -804) translate(228 804)"/>
</g>
</symbol><symbol id="icon-d-ic-history" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0H24V24H0z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
<path fill="#8a8a8a" d="M12.5 1C18.299 1 23 5.701 23 11.5S18.299 22 12.5 22c-5.29 0-9.665-3.911-10.394-8.999h1.012C3.838 17.534 7.764 21 12.5 21c5.247 0 9.5-4.253 9.5-9.5S17.747 2 12.5 2C8.49 2 5.06 4.485 3.666 8H3h4v1H2V4h1v3.022C4.68 3.462 8.303 1 12.5 1zm.5 5l-.001 5.291 2.537 2.537-.708.708L12.292 12H12V6h1z" transform="translate(-740 -16) translate(547 8) translate(193 8)"/>
</g>
</symbol><symbol id="icon-d-ic-icon-arrow-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M21.793 18.5H2.5v-5h18.935l-7.6-8h5.872l10.5 10.5-10.5 10.5h-5.914l8-8z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-arrow-3" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M25.288 16.42L14.208 27.5H6.792l11.291-11.291L6.826 4.5h7.381l11.661 11.661-.58.258z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-arrow" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M2.5 11.5v9h18v5.293L30.293 16 20.5 6.207V11.5h-18z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-bubble" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M22.207 24.5L16.5 30.207V24.5H8A6.5 6.5 0 0 1 1.5 18V9A6.5 6.5 0 0 1 8 2.5h16A6.5 6.5 0 0 1 30.5 9v9a6.5 6.5 0 0 1-6.5 6.5h-1.793z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-heart" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill-rule="nonzero" stroke="#8a8a8a" d="M15.996 30.675l1.981-1.79c7.898-7.177 10.365-9.718 12.135-13.012.922-1.716 1.377-3.37 1.377-5.076 0-4.65-3.647-8.297-8.297-8.297-2.33 0-4.86 1.527-6.817 3.824l-.38.447-.381-.447C13.658 4.027 11.126 2.5 8.797 2.5 4.147 2.5.5 6.147.5 10.797c0 1.714.46 3.375 1.389 5.098 1.775 3.288 4.26 5.843 12.123 12.974l1.984 1.806z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M17.314 18.867l1.951-2.53 4 5.184h-17l6.5-8.84 4.549 6.186z"/>
<path fill="#8a8a8a" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01z"/>
<path fill="#8a8a8a" d="M25 3h1v9h-1z"/>
<path stroke="#8a8a8a" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-d-ic-icon-location" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<g stroke="#8a8a8a">
<path d="M16 31.28C23.675 23.302 27.5 17.181 27.5 13c0-6.351-5.149-11.5-11.5-11.5S4.5 6.649 4.5 13c0 4.181 3.825 10.302 11.5 18.28z"/>
<circle cx="16" cy="13" r="4.5"/>
</g>
</g>
</symbol><symbol id="icon-d-ic-icon-polygon" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M.576 16L8.29 29.5h15.42L31.424 16 23.71 2.5H8.29L.576 16z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-star-2" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M19.446 31.592l2.265-3.272 3.946.25.636-3.94 3.665-1.505-1.12-3.832 2.655-2.962-2.656-2.962 1.12-3.832-3.664-1.505-.636-3.941-3.946.25-2.265-3.271L16 3.024 12.554 1.07 10.289 4.34l-3.946-.25-.636 3.941-3.665 1.505 1.12 3.832L.508 16.33l2.656 2.962-1.12 3.832 3.664 1.504.636 3.942 3.946-.25 2.265 3.27L16 29.638l3.446 1.955z"/>
</g>
</symbol><symbol id="icon-d-ic-icon-star" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" d="M25.292 29.878l-1.775-10.346 7.517-7.327-10.388-1.51L16 1.282l-4.646 9.413-10.388 1.51 7.517 7.327-1.775 10.346L16 24.993l9.292 4.885z"/>
</g>
</symbol><symbol id="icon-d-ic-icon" viewBox="0 0 24 24">
<g fill="none">
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M11.923 19.136L5.424 22l.715-7.065-4.731-5.296 6.94-1.503L11.923 2l3.574 6.136 6.94 1.503-4.731 5.296L18.42 22z"/>
</g>
</symbol><symbol id="icon-d-ic-mask-load" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M18.01 4a11.798 11.798 0 0 0 0 1H3v24h24V14.986a8.738 8.738 0 0 0 1 0V29a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h15.01zM15 23a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-1a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
<path fill="#8a8a8a" d="M25 3h1v9h-1z"/>
<path stroke="#8a8a8a" d="M22 6l3.5-3.5L29 6"/>
</g>
</symbol><symbol id="icon-d-ic-mask" viewBox="0 0 24 24">
<g fill="none">
<circle cx="12" cy="12" r="4.5" stroke="#8a8a8a"/>
<path fill="#8a8a8a" d="M2 1h20a1 1 0 0 1 1 1v20a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zm0 1v20h20V2H2z"/>
</g>
</symbol><symbol id="icon-d-ic-redo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#8a8a8a" d="M21 6H9a6 6 0 1 0 0 12h12v1H9A7 7 0 0 1 9 5h12v1z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M19 3l2.5 2.5L19 8"/>
</g>
</symbol><symbol id="icon-d-ic-reset" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z" opacity=".5"/>
<path fill="#8a8a8a" d="M2 13v-1a7 7 0 0 1 7-7h13v1h-1v5h1v1a7 7 0 0 1-7 7H2v-1h1v-5H2zm7-7a6 6 0 0 0-6 6v6h12a6 6 0 0 0 6-6V6H9z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M19 3l2.5 2.5L19 8M5 16l-2.5 2.5L5 21"/>
</g>
</symbol><symbol id="icon-d-ic-resize" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#8a8a8a" d="M 18.988281 3.011719 C 18.800781 2.824219 18.5 2.824219 18.3125 3.011719 L 11.621094 9.707031 C 11.429688 9.894531 11.429688 10.195312 11.621094 10.378906 C 11.710938 10.472656 11.835938 10.519531 11.957031 10.519531 C 12.078125 10.519531 12.203125 10.472656 12.292969 10.378906 L 18.988281 3.6875 C 19.175781 3.5 19.175781 3.199219 18.988281 3.011719 Z M 18.988281 3.011719 "/>
<path fill="#8a8a8a" d="M 18.652344 2.867188 C 18.386719 2.867188 18.171875 3.082031 18.171875 3.347656 L 18.171875 9.085938 C 18.171875 9.347656 18.386719 9.5625 18.652344 9.5625 C 18.917969 9.5625 19.132812 9.347656 19.132812 9.085938 L 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#8a8a8a" d="M 18.652344 2.867188 L 12.914062 2.867188 C 12.652344 2.867188 12.4375 3.082031 12.4375 3.347656 C 12.4375 3.613281 12.652344 3.828125 12.914062 3.828125 L 18.652344 3.828125 C 18.917969 3.828125 19.132812 3.613281 19.132812 3.347656 C 19.132812 3.082031 18.917969 2.867188 18.652344 2.867188 Z M 18.652344 2.867188 "/>
<path fill="#8a8a8a" d="M 10.378906 11.621094 C 10.195312 11.433594 9.890625 11.433594 9.703125 11.621094 L 3.007812 18.316406 C 2.820312 18.5 2.820312 18.804688 3.007812 18.992188 C 3.105469 19.085938 3.226562 19.132812 3.347656 19.132812 C 3.46875 19.132812 3.589844 19.085938 3.683594 18.992188 L 10.378906 12.296875 C 10.566406 12.109375 10.566406 11.804688 10.378906 11.621094 Z M 10.378906 11.621094 "/>
<path fill="#8a8a8a" d="M 3.347656 12.4375 C 3.082031 12.4375 2.867188 12.652344 2.867188 12.914062 L 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 C 3.613281 19.132812 3.828125 18.917969 3.828125 18.652344 L 3.828125 12.914062 C 3.828125 12.652344 3.613281 12.4375 3.347656 12.4375 Z M 3.347656 12.4375 "/>
<path fill="#8a8a8a" d="M 9.085938 18.171875 L 3.347656 18.171875 C 3.082031 18.171875 2.867188 18.386719 2.867188 18.652344 C 2.867188 18.917969 3.082031 19.132812 3.347656 19.132812 L 9.085938 19.132812 C 9.347656 19.132812 9.5625 18.917969 9.5625 18.652344 C 9.5625 18.386719 9.347656 18.171875 9.085938 18.171875 Z M 9.085938 18.171875 "/>
<path fill="#8a8a8a" d="M 20.5625 0 L 1.4375 0 C 0.644531 0 0 0.644531 0 1.4375 L 0 20.5625 C 0 21.355469 0.644531 22 1.4375 22 L 20.5625 22 C 21.355469 22 22 21.355469 22 20.5625 L 22 1.4375 C 22 0.644531 21.355469 0 20.5625 0 Z M 21.042969 20.5625 C 21.042969 20.828125 20.828125 21.042969 20.5625 21.042969 L 1.4375 21.042969 C 1.171875 21.042969 0.957031 20.828125 0.957031 20.5625 L 0.957031 1.4375 C 0.957031 1.171875 1.171875 0.957031 1.4375 0.957031 L 20.5625 0.957031 C 20.828125 0.957031 21.042969 1.171875 21.042969 1.4375 Z M 21.042969 20.5625 "/>
</g>
</symbol><symbol id="icon-d-ic-rotate-clockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#8a8a8a" d="M29 17h-.924c0 6.627-5.373 12-12 12-6.628 0-12-5.373-12-12C4.076 10.398 9.407 5.041 16 5V4C8.82 4 3 9.82 3 17s5.82 13 13 13 13-5.82 13-13z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M16 1.5l4 3-4 3"/>
<path fill="#8a8a8a" fill-rule="nonzero" d="M16 4h4v1h-4z"/>
</g>
</symbol><symbol id="icon-d-ic-rotate-counterclockwise" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path fill="#8a8a8a" d="M3 17h.924c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.602-5.331-11.96-11.924-12V4c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 24.18 3 17z"/>
<path fill="#8a8a8a" fill-rule="nonzero" d="M12 4h4v1h-4z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M16 1.5l-4 3 4 3"/>
</g>
</symbol><symbol id="icon-d-ic-rotate" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#8a8a8a" d="M8.349 22.254a10.002 10.002 0 0 1-2.778-1.719l.65-.76a9.002 9.002 0 0 0 2.495 1.548l-.367.931zm2.873.704l.078-.997a9 9 0 1 0-.557-17.852l-.14-.99A10.076 10.076 0 0 1 12.145 3c5.523 0 10 4.477 10 10s-4.477 10-10 10c-.312 0-.62-.014-.924-.042zm-7.556-4.655a9.942 9.942 0 0 1-1.253-2.996l.973-.234a8.948 8.948 0 0 0 1.124 2.693l-.844.537zm-1.502-5.91A9.949 9.949 0 0 1 2.88 9.23l.925.382a8.954 8.954 0 0 0-.644 2.844l-.998-.062zm2.21-5.686c.687-.848 1.51-1.58 2.436-2.166l.523.852a9.048 9.048 0 0 0-2.188 1.95l-.771-.636z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M13 1l-2.5 2.5L13 6"/>
</g>
</symbol><symbol id="icon-d-ic-shape-circle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="14.5" stroke="#8a8a8a"/>
</g>
</symbol><symbol id="icon-d-ic-shape-rectangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<rect width="27" height="27" x="2.5" y="2.5" stroke="#8a8a8a" rx="1"/>
</g>
</symbol><symbol id="icon-d-ic-shape-triangle" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M16 2.5l15.5 27H.5z"/>
</g>
</symbol><symbol id="icon-d-ic-shape" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path fill="#8a8a8a" d="M14.706 8H21a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1v-4h1v4h12V9h-5.706l-.588-1z"/>
<path stroke="#8a8a8a" stroke-linecap="round" stroke-linejoin="round" d="M8.5 1.5l7.5 13H1z"/>
</g>
</symbol><symbol id="icon-d-ic-text-align-center" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M2 5h28v1H2zM8 12h16v1H8zM2 19h28v1H2zM8 26h16v1H8z"/>
</g>
</symbol><symbol id="icon-d-ic-text-align-left" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M2 5h28v1H2zM2 12h16v1H2zM2 19h28v1H2zM2 26h16v1H2z"/>
</g>
</symbol><symbol id="icon-d-ic-text-align-right" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M2 5h28v1H2zM14 12h16v1H14zM2 19h28v1H2zM14 26h16v1H14z"/>
</g>
</symbol><symbol id="icon-d-ic-text-bold" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M7 2h2v2H7zM7 28h2v2H7z"/>
<path stroke="#8a8a8a" stroke-width="2" d="M9 3v12h9a6 6 0 1 0 0-12H9zM9 15v14h10a7 7 0 0 0 0-14H9z"/>
</g>
</symbol><symbol id="icon-d-ic-text-italic" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M15 2h5v1h-5zM11 29h5v1h-5zM17 3h1l-4 26h-1z"/>
</g>
</symbol><symbol id="icon-d-ic-text-underline" viewBox="0 0 32 32">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h32v32H0z"/>
<path fill="#8a8a8a" d="M8 2v14a8 8 0 1 0 16 0V2h1v14a9 9 0 0 1-18 0V2h1zM3 29h26v1H3z"/>
<path fill="#8a8a8a" d="M5 2h5v1H5zM22 2h5v1h-5z"/>
</g>
</symbol><symbol id="icon-d-ic-text" viewBox="0 0 24 24">
<g fill="#8a8a8a" fill-rule="evenodd">
<path d="M4 3h15a1 1 0 0 1 1 1H3a1 1 0 0 1 1-1zM3 4h1v1H3zM19 4h1v1h-1z"/>
<path d="M11 3h1v18h-1z"/>
<path d="M10 20h3v1h-3z"/>
</g>
</symbol><symbol id="icon-d-ic-undo" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M24 0H0v24h24z" opacity=".5"/>
<path fill="#8a8a8a" d="M3 6h12a6 6 0 1 1 0 12H3v1h12a7 7 0 0 0 0-14H3v1z"/>
<path stroke="#8a8a8a" stroke-linecap="square" d="M5 3L2.5 5.5 5 8"/>
</g>
</symbol><symbol id="icon-d-ic-zoom-hand" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd" stroke-linejoin="round">
<path d="M0 0H24V24H0z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#8a8a8a" fill-rule="nonzero" d="M8.672 3.36c1.328 0 2.114.78 2.29 1.869l.014.101.023.006v1.042l-.638-.185c-.187-.055-.323-.211-.354-.399L10 5.713c0-.825-.42-1.353-1.328-1.353C7.695 4.36 7 5.041 7 5.713v7.941c0 .439-.524.665-.843.364l-1.868-1.761c-.595-.528-1.316-.617-1.918-.216-.522.348-.562 1.203-.18 1.8L7.738 22h11.013l.285-.518c1.247-2.326 1.897-4.259 1.96-5.785l.004-.239V8.035c0-.656-.5-1.17-1-1.17-.503 0-1 .456-1 1.17 0 .333-.32.573-.64.48L18 8.41V7.368l.086.026.042-.136c.279-.805.978-1.332 1.738-1.388L20 5.865c1.057 0 2 .967 2 2.17v7.423c0 1.929-.845 4.352-2.521 7.29-.09.156-.255.252-.435.252H7.474c-.166 0-.321-.082-.414-.219l-5.704-8.39c-.653-1.019-.584-2.486.46-3.182 1-.666 2.216-.516 3.148.31L6 12.495V5.713c0-1.18 1.058-2.263 2.49-2.348z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#8a8a8a" fill-rule="nonzero" d="M12.5 1.5c1.325 0 2.41 1.032 2.495 2.336L15 4v7.22h-1V4c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L11 4v7.22h-1V4c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
<path fill="#8a8a8a" fill-rule="nonzero" d="M16.5 3.5c1.325 0 2.41 1.032 2.495 2.336L19 6v6.3h-1V6c0-.828-.672-1.5-1.5-1.5-.78 0-1.42.595-1.493 1.356L15 6v2.44h-1V6c0-1.38 1.12-2.5 2.5-2.5z" transform="translate(-297 -290) translate(297 290)"/>
</g>
</symbol><symbol id="icon-d-ic-zoom-zoom-in" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g>
<g transform="translate(-229 -290) translate(229 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#8a8a8a"/>
<path fill="#8a8a8a" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#8a8a8a" d="M7 10H14V11H7z"/>
<path fill="#8a8a8a" d="M10 7H11V14H10z"/>
</g>
</g>
</g>
</symbol><symbol id="icon-d-ic-zoom-zoom-out" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g>
<g transform="translate(-263 -290) translate(263 290)">
<circle cx="10.5" cy="10.5" r="9" stroke="#8a8a8a"/>
<path fill="#8a8a8a" d="M18.828 15.828H19.828V22.828H18.828z" transform="rotate(-45 19.328 19.328)"/>
<path fill="#8a8a8a" d="M7 10H14V11H7z"/>
</g>
</g>
</g>
</symbol><symbol id="icon-d-img-bi" viewBox="0 0 257 26">
<g fill="#FDBA3B">
<path d="M26 5a8.001 8.001 0 0 0 0 16 8.001 8.001 0 0 0 0-16M51.893 19.812L43.676 5.396A.78.78 0 0 0 43 5a.78.78 0 0 0-.677.396l-8.218 14.418a.787.787 0 0 0 0 .792c.14.244.396.394.676.394h16.436c.28 0 .539-.15.678-.396a.796.796 0 0 0-.002-.792M15.767 5.231A.79.79 0 0 0 15.21 5H.791A.791.791 0 0 0 0 5.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M85.767 5.231A.79.79 0 0 0 85.21 5H70.791a.791.791 0 0 0-.791.79v6.42a.793.793 0 0 0 .791.79h3.21v7.21c.001.21.082.408.234.56.147.148.347.23.558.23h6.416a.788.788 0 0 0 .792-.79V13h3.006c.413 0 .611-.082.762-.232.15-.149.23-.35.231-.559V5.791a.787.787 0 0 0-.233-.56M65.942 9.948l2.17-3.76a.78.78 0 0 0 0-.792.791.791 0 0 0-.684-.396h-8.54A5.889 5.889 0 0 0 53 10.86a5.887 5.887 0 0 0 3.07 5.17l-2.184 3.782A.792.792 0 0 0 54.571 21h8.54a5.89 5.89 0 0 0 2.831-11.052M105.7 21h2.3V5h-2.3zM91 5h2.4v10.286c0 1.893 1.612 3.429 3.6 3.429s3.6-1.536 3.6-3.429V5h2.4v10.286c0 3.156-2.686 5.714-6 5.714-3.313 0-6-2.558-6-5.714V5zM252.148 21.128h-2.377V9.659h2.27v1.64c.69-1.299 1.792-1.938 3.304-1.938.497 0 .95.065 1.382.192l-.215 2.277a3.734 3.734 0 0 0-1.275-.213c-1.814 0-3.089 1.234-3.089 3.638v5.873zm-7.095-5.744a3.734 3.734 0 0 0-1.101-2.703c-.714-.766-1.6-1.149-2.658-1.149-1.058 0-1.944.383-2.679 1.149a3.803 3.803 0 0 0-1.08 2.703c0 1.063.368 1.978 1.08 2.722.735.746 1.62 1.128 2.68 1.128 1.058 0 1.943-.382 2.657-1.128.734-.744 1.101-1.659 1.101-2.722zm-9.916 0c0-1.682.583-3.086 1.729-4.256 1.166-1.17 2.635-1.767 4.428-1.767 1.793 0 3.262.597 4.407 1.767 1.167 1.17 1.75 2.574 1.75 4.256 0 1.7-.583 3.127-1.75 4.297-1.145 1.17-2.614 1.745-4.407 1.745-1.793 0-3.262-.575-4.428-1.745-1.146-1.17-1.729-2.596-1.729-4.297zm-1.5 3.233l.821 1.83c-.864.638-1.944.958-3.22.958-2.526 0-3.822-1.554-3.822-4.383V11.66h-2.01v-2h2.031V5.595h2.355v4.063h4.018v2h-4.018v5.405c0 1.469.605 2.191 1.793 2.191.626 0 1.318-.212 2.052-.638zm-12.43 2.51h2.375V9.66h-2.376v11.469zm1.23-12.977c-.929 0-1.642-.682-1.642-1.596 0-.873.713-1.554 1.643-1.554.885 0 1.576.681 1.576 1.554 0 .914-.69 1.596-1.576 1.596zm-6.49 7.234c0-1.086-.346-1.98-1.037-2.724-.692-.745-1.599-1.128-2.7-1.128-1.102 0-2.01.383-2.7 1.128-.692.744-1.037 1.638-1.037 2.724 0 1.084.345 2.02 1.036 2.766.691.744 1.6 1.105 2.7 1.105 1.102 0 2.01-.361 2.7-1.105.692-.746 1.038-1.682 1.038-2.766zm-.173-4.129V5h2.397v16.128h-2.354v-1.596c-1.015 1.255-2.333 1.873-3.91 1.873-1.663 0-3.068-.575-4.169-1.724-1.102-1.17-1.663-2.596-1.663-4.297 0-1.682.561-3.107 1.663-4.256 1.101-1.17 2.485-1.745 4.148-1.745 1.534 0 2.83.617 3.888 1.872zm-11.48 9.873h-10.218V5.405h10.195v2.318h-7.711V12h7.15v2.32h-7.15v4.489h7.733v2.319zm-23.891-9.724c-1.793 0-3.132 1.192-3.478 2.979h6.783c-.194-1.808-1.555-2.979-3.305-2.979zm5.703 3.766c0 .32-.021.703-.086 1.128h-9.095c.346 1.787 1.62 3 3.867 3 1.318 0 2.916-.49 3.953-1.234l.994 1.724c-1.189.872-3.067 1.595-5.033 1.595-4.364 0-6.243-3-6.243-6.021 0-1.724.54-3.15 1.642-4.277 1.101-1.127 2.548-1.702 4.298-1.702 1.664 0 3.046.511 4.105 1.553 1.058 1.043 1.598 2.447 1.598 4.234zm-19.949 3.894c1.08 0 1.966-.362 2.68-1.085.712-.724 1.058-1.617 1.058-2.703 0-1.084-.346-2-1.059-2.701-.713-.702-1.599-1.064-2.679-1.064-1.058 0-1.944.362-2.656 1.085-.714.702-1.059 1.596-1.059 2.68 0 1.086.345 2 1.059 2.724.712.702 1.598 1.064 2.656 1.064zm3.673-7.936V9.66h2.29v10.299c0 1.85-.584 3.32-1.728 4.404-1.146 1.085-2.68 1.638-4.58 1.638-1.945 0-3.672-.553-5.206-1.638l1.037-1.808c1.296.915 2.679 1.36 4.126 1.36 2.484 0 3.996-1.51 3.996-3.637v-.83c-1.015 1.127-2.311 1.702-3.91 1.702-1.684 0-3.089-.554-4.19-1.68-1.102-1.128-1.642-2.532-1.642-4.214 0-1.68.561-3.085 1.706-4.191 1.145-1.128 2.571-1.681 4.234-1.681 1.534 0 2.83.575 3.867 1.745zm-18.07 8.127c1.102 0 1.988-.382 2.7-1.128.714-.744 1.06-1.659 1.06-2.743 0-1.065-.346-1.98-1.06-2.724-.712-.745-1.598-1.128-2.7-1.128-1.101 0-2.008.383-2.7 1.128-.691.744-1.036 1.66-1.036 2.745 0 1.084.345 2 1.037 2.745.691.744 1.598 1.105 2.7 1.105zm3.652-8V9.66h2.29v11.469h-2.29v-1.575c-1.059 1.234-2.399 1.852-3.976 1.852-1.663 0-3.067-.575-4.168-1.745-1.102-1.17-1.642-2.617-1.642-4.34 0-1.724.54-3.128 1.642-4.256 1.1-1.128 2.505-1.681 4.168-1.681 1.577 0 2.917.617 3.976 1.872zM138.79 9.34c1.404 0 2.527.448 3.37 1.34.863.873 1.295 2.086 1.295 3.596v6.852h-2.376V14.66c0-2.021-1.036-3.128-2.657-3.128-1.727 0-2.915 1.255-2.915 3.192v6.404h-2.377v-6.426c0-1.978-1.037-3.17-2.679-3.17-1.728 0-2.937 1.277-2.937 3.234v6.362h-2.377V9.659h2.333v1.66c.692-1.212 1.988-1.979 3.522-1.979 1.533.021 2.958.767 3.586 2.107.798-1.277 2.419-2.107 4.212-2.107zm-19.517 11.788h2.484V5.405h-2.484v15.723z"/>
</g>
</symbol></svg>

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,128 @@
.border {
border: 1px solid black;
}
.body-container {
width: 1000px;
}
.tui-image-editor-controls {
min-height: 250px;
}
.menu {
padding: 0;
margin-bottom: 5px;
text-align: center;
color: #544b61;
font-weight: 400;
list-style-type: none;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.logo {
margin: 0 auto;
width: 300px;
vertical-align: middle;
}
.header .name {
padding: 10px;
line-height: 50px;
font-size: 30px;
font-weight: 100;
vertical-align: middle;
}
.header .menu {
display: inline-block;
}
.menu-item {
padding: 10px;
display: inline-block;
cursor: pointer;
vertical-align: middle;
}
.menu-item a {
text-decoration: none;
}
.menu-item.no-pointer {
cursor: default;
}
.menu-item.active,
.menu-item:hover {
background-color: #f3f3f3;
}
.menu-item.disabled {
cursor: default;
color: #bfbebe;
}
.align-left-top {
text-align: left;
vertical-align: top;
}
.range-narrow {
width: 80px;
}
.sub-menu-container {
font-size: 14px;
margin-bottom: 1em;
display: none;
}
.tui-image-editor {
height: 500px;
}
.tui-image-editor-canvas-container {
margin: 0 auto;
top: 50%;
transform: translateY(-50%);
-ms-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
border: 1px dashed black;
overflow: hidden;
}
.tui-colorpicker-container {
margin: 5px auto 0;
}
.tui-colorpicker-palette-toggle-slider {
display: none;
}
.input-wrapper {
position: relative;
}
.input-wrapper input {
cursor: pointer;
position: absolute;
font-size: 999px;
left: 0;
top: 0;
opacity: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.btn-text-style {
padding: 5px;
margin: 3px 1px;
border: 1px dashed #bfbebe;
outline: 0;
background-color: #eee;
cursor: pointer;
}
.icon-text {
font-size: 20px;
}
.select-line-type {
outline: 0;
vertical-align: middle;
}
#tui-color-picker {
display: inline-block;
vertical-align: middle;
}
#tui-text-palette {
display: none;
position: absolute;
padding: 10px;
border: 1px solid #bfbebe;
background-color: #fff;
z-index: 9999;
}
@@ -0,0 +1,203 @@
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background-color: #383838;
font-family: Sans-Serif;
}
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
input[type='button'],
button {
-webkit-appearance: none;
-moz-appearance: none;
background-color: #fff;
}
input[type='file'] {
position: absolute;
margin: 0;
padding: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
opacity: 0;
filter: alpha(opacity=0);
}
.header {
position: fixed;
left: 0;
top: 0;
width: 100%;
background-color: #fff;
text-align: center;
z-index: 9999;
}
.header .logo {
margin: 10px 5px;
width: 180px;
vertical-align: middle;
}
.header .name {
font-size: 16px;
font-weight: bold;
}
.header .menu {
padding: 10px;
background-color: #000;
}
.header .menu input {
opacity: 0;
}
.header .menu img {
width: 20px;
height: 20px;
vertical-align: middle;
}
.header .button {
position: relative;
display: inline-block;
margin: 0 5px;
padding: 0;
border-radius: 5px 5px;
width: 30px;
height: 30px;
border: 0;
background-color: #fff;
vertical-align: middle;
}
.header .button.disabled img {
opacity: 0.5;
}
.tui-image-editor {
height: 100%;
}
.tui-image-editor-canvas-container {
margin: 0 auto;
top: 50%;
transform: translateY(-50%);
-ms-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
overflow: hidden;
}
.tui-image-editor-controls {
position: fixed;
width: 100%;
left: 0;
bottom: 0;
background-color: #fff;
}
.tui-image-editor-controls .scrollable {
display: inline-block;
overflow-x: auto;
width: 100%;
height: 100%;
white-space: nowrap;
font-size: 0;
background-color: #000;
vertical-align: middle;
}
.tui-image-editor-controls .no-scrollable {
overflow-x: hidden;
}
.tui-image-editor-controls .menu-item {
display: inline-block;
height: 80px;
border-right: 1px solid #383838;
background-color: #ddd;
vertical-align: middle;
}
.tui-image-editor-controls .menu-button {
width: 80px;
height: 80px;
border: none;
vertical-align: middle;
background-color: #000;
color: #fff;
font-size: 12px;
font-weight: bold;
outline: 0;
}
.tui-image-editor-controls .submenu-button {
width: 80px;
height: 80px;
border: none;
background-color: #ddd;
vertical-align: middle;
}
.tui-image-editor-controls .hiddenmenu-button {
margin: 0 10px;
padding: 5px;
border: none;
color: #fff;
background-color: rgba(255, 255, 255, 0);
}
.tui-image-editor-controls .submenu {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
font-size: 0;
}
.tui-image-editor-controls .submenu.show {
display: block;
}
.tui-image-editor-controls .submenu .menu-item:last-child {
margin-right: 50px;
}
.tui-image-editor-controls .hiddenmenu {
position: absolute;
display: none;
padding: 40px;
width: 100%;
left: 0;
bottom: 80px;
background-color: rgba(0, 0, 0, 0.7);
text-align: center;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 9999;
}
.tui-image-editor-controls .hiddenmenu.show {
display: block;
}
.tui-image-editor-controls .hiddenmenu .top {
font-size: 12px;
color: #fff;
margin-bottom: 20px;
}
.tui-image-editor-controls .btn-prev {
display: inline-block;
width: 30px;
height: 80px;
background-color: #000;
color: #fff;
border: none;
vertical-align: middle;
}
.tui-image-editor-controls .tui-colorpicker-container {
display: inline-block;
}
.tui-image-editor-controls .msg {
position: absolute;
margin-left: 50%;
padding: 5px 10px;
left: -86px;
top: -50px;
border-radius: 5px 5px;
background-color: rgba(255, 255, 255, 0.5);
font-size: 12px;
}
.tui-image-editor-controls .msg.hide {
display: none;
}
@@ -0,0 +1,21 @@
body {
margin: 0;
padding: 0;
}
.code-description {
padding: 22px 52px;
background-color: rgba(81, 92, 230, 0.1);
line-height: 1.4em;
}
.code-description,
.code-description a {
font-family: Arial;
font-size: 14px;
color: #515ce6;
}
.code-html {
padding: 20px 52px;
}
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>0. Design</title>
<link
type="text/css"
href="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.css"
rel="stylesheet"
/>
<link type="text/css" href="../dist/tui-image-editor.css" rel="stylesheet" />
<style>
@import url(http://fonts.googleapis.com/css?family=Noto+Sans);
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<div id="tui-image-editor-container"></div>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.4.0/fabric.js"
></script>
<script
type="text/javascript"
src="https://uicdn.toast.com/tui.code-snippet/v1.5.0/tui-code-snippet.min.js"
></script>
<script
type="text/javascript"
src="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.js"
></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"
></script>
<script type="text/javascript" src="../dist/tui-image-editor.js"></script>
<script type="text/javascript" src="js/theme/white-theme.js"></script>
<script type="text/javascript" src="js/theme/black-theme.js"></script>
<script>
// Image editor
var imageEditor = new tui.ImageEditor('#tui-image-editor-container', {
includeUI: {
loadImage: {
path: 'img/sampleImage2.png',
name: 'SampleImage',
},
theme: blackTheme, // or whiteTheme
initMenu: 'filter',
menuBarPosition: 'bottom',
},
cssMaxWidth: 700,
cssMaxHeight: 500,
usageStatistics: false,
});
window.onresize = function () {
imageEditor.ui.resizeEditor();
};
</script>
</body>
</html>
@@ -0,0 +1,380 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>1. Basic</title>
<link
type="text/css"
href="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.css"
rel="stylesheet"
/>
<link type="text/css" href="css/service-basic.css" rel="stylesheet" />
</head>
<body>
<div class="body-container">
<div class="tui-image-editor-controls">
<div class="header">
<img class="logo" src="img/TOAST UI Component.png" />
<span class="name"> Image Editor</span>
<ul class="menu">
<li class="menu-item border input-wrapper">
Load
<input type="file" accept="image/*" id="input-image-file" />
</li>
<li class="menu-item border" id="btn-download">Download</li>
</ul>
</div>
<ul class="menu">
<li class="menu-item disabled" id="btn-undo">Undo</li>
<li class="menu-item disabled" id="btn-redo">Redo</li>
<li class="menu-item" id="btn-clear-objects">ClearObjects</li>
<li class="menu-item" id="btn-remove-active-object">RemoveActiveObject</li>
<li class="menu-item" id="btn-crop">Crop</li>
<li class="menu-item" id="btn-flip">Flip</li>
<li class="menu-item" id="btn-rotation">Rotation</li>
<li class="menu-item" id="btn-draw-line">DrawLine</li>
<li class="menu-item" id="btn-draw-shape">Shape</li>
<li class="menu-item" id="btn-add-icon">Icon</li>
<li class="menu-item" id="btn-text">Text</li>
<li class="menu-item" id="btn-mask-filter">Mask</li>
<li class="menu-item" id="btn-image-filter">Filter</li>
</ul>
<div class="sub-menu-container" id="crop-sub-menu">
<ul class="menu">
<li class="menu-item" id="btn-apply-crop">Apply</li>
<li class="menu-item" id="btn-cancel-crop">Cancel</li>
</ul>
</div>
<div class="sub-menu-container" id="flip-sub-menu">
<ul class="menu">
<li class="menu-item" id="btn-flip-x">FlipX</li>
<li class="menu-item" id="btn-flip-y">FlipY</li>
<li class="menu-item" id="btn-reset-flip">Reset</li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container" id="rotation-sub-menu">
<ul class="menu">
<li class="menu-item" id="btn-rotate-clockwise">Clockwise(30)</li>
<li class="menu-item" id="btn-rotate-counter-clockwise">Counter-Clockwise(-30)</li>
<li class="menu-item no-pointer">
<label>
Range input
<input id="input-rotation-range" type="range" min="-360" value="0" max="360" />
</label>
</li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container menu" id="draw-line-sub-menu">
<ul class="menu">
<li class="menu-item">
<label>
<input type="radio" name="select-line-type" value="freeDrawing" checked="checked" />
Free drawing
</label>
<label>
<input type="radio" name="select-line-type" value="lineDrawing" />
Straight line
</label>
</li>
<li class="menu-item">
<div id="tui-brush-color-picker">Brush color</div>
</li>
<li class="menu-item">
<label class="menu-item no-pointer">
Brush width
<input id="input-brush-width-range" type="range" min="5" max="30" value="12" />
</label>
</li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container" id="draw-shape-sub-menu">
<ul class="menu">
<li class="menu-item">
<label>
<input type="radio" name="select-shape-type" value="rect" checked="checked" />
rect
</label>
<label>
<input type="radio" name="select-shape-type" value="circle" />
circle
</label>
<label>
<input type="radio" name="select-shape-type" value="triangle" />
triangle
</label>
</li>
<li class="menu-item">
<select name="select-color-type">
<option value="fill">Fill</option>
<option value="stroke">Stroke</option>
</select>
<label>
<input
type="radio"
name="input-check-fill"
id="input-check-transparent"
value="transparent"
/>
transparent
</label>
<label>
<input
type="radio"
name="input-check-fill"
id="input-check-filter"
value="filter"
/>
filter
</label>
<div id="tui-shape-color-picker"></div>
</li>
<li class="menu-item">
<label class="menu-item no-pointer">
Stroke width
<input id="input-stroke-width-range" type="range" min="0" max="300" value="12" />
</label>
</li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container" id="icon-sub-menu">
<ul class="menu">
<li class="menu-item">
<div id="tui-icon-color-picker">Icon color</div>
</li>
<li class="menu-item border" id="btn-register-icon">Register custom icon</li>
<li class="menu-item icon-text" data-icon-type="arrow"></li>
<li class="menu-item icon-text" data-icon-type="cancel"></li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container" id="text-sub-menu">
<ul class="menu">
<li class="menu-item">
<div>
<button class="btn-text-style" data-style-type="b">Bold</button>
<button class="btn-text-style" data-style-type="i">Italic</button>
<button class="btn-text-style" data-style-type="u">Underline</button>
</div>
<div>
<button class="btn-text-style" data-style-type="l">Left</button>
<button class="btn-text-style" data-style-type="c">Center</button>
<button class="btn-text-style" data-style-type="r">Right</button>
</div>
</li>
<li class="menu-item">
<label class="no-pointer">
<input id="input-font-size-range" type="range" min="10" max="100" value="10" />
</label>
</li>
<li class="menu-item">
<div id="tui-text-color-picker">Text color</div>
</li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container" id="filter-sub-menu">
<ul class="menu">
<li class="menu-item border input-wrapper">
Load Mask Image
<input type="file" accept="image/*" id="input-mask-image-file" />
</li>
<li class="menu-item" id="btn-apply-mask">Apply mask filter</li>
<li class="menu-item close">Close</li>
</ul>
</div>
<div class="sub-menu-container" id="image-filter-sub-menu">
<ul class="menu">
<li class="menu-item align-left-top">
<table>
<tbody>
<tr>
<td>
<label><input type="checkbox" id="input-check-grayscale" />Grayscale</label>
</td>
<td>
<label><input type="checkbox" id="input-check-invert" />Invert</label>
</td>
<td>
<label><input type="checkbox" id="input-check-sepia" />Sepia</label>
</td>
</tr>
<tr>
<td>
<label><input type="checkbox" id="input-check-sepia2" />Sepia2</label>
</td>
<td>
<label><input type="checkbox" id="input-check-blur" />Blur</label>
</td>
<td>
<label><input type="checkbox" id="input-check-sharpen" />Sharpen</label>
</td>
</tr>
<tr>
<td>
<label><input type="checkbox" id="input-check-emboss" />Emboss</label>
</td>
</tr>
</tbody>
</table>
</li>
<li class="menu-item align-left-top">
<p>
<label>
<input type="checkbox" id="input-check-remove-white" />
RemoveWhite
</label>
<br />
<label>
Distance
<input
class="range-narrow"
id="input-range-remove-white-distance"
type="range"
min="0"
value="10"
max="255"
/>
</label>
</p>
</li>
<li class="menu-item align-left-top">
<p>
<label><input type="checkbox" id="input-check-brightness" />Brightness</label><br />
<label>
Value
<input
class="range-narrow"
id="input-range-brightness-value"
type="range"
min="-255"
value="100"
max="255"
/>
</label>
</p>
</li>
<li class="menu-item align-left-top">
<p>
<label><input type="checkbox" id="input-check-noise" />Noise</label><br />
<label>
Value
<input
class="range-narrow"
id="input-range-noise-value"
type="range"
min="0"
value="100"
max="1000"
/>
</label>
</p>
</li>
<li class="menu-item align-left-top">
<p>
<label>
<input type="checkbox" id="input-check-color-filter" />
ColorFilter
</label>
<br />
<label>
Threshold
<input
class="range-narrow"
id="input-range-color-filter-value"
type="range"
min="0"
value="45"
max="255"
/>
</label>
</p>
</li>
<li class="menu-item align-left-top">
<p>
<label><input type="checkbox" id="input-check-pixelate" />Pixelate</label><br />
<label>
Value
<input
class="range-narrow"
id="input-range-pixelate-value"
type="range"
min="2"
value="4"
max="20"
/>
</label>
</p>
</li>
<li class="menu-item align-left-top">
<p>
<label><input type="checkbox" id="input-check-tint" />Tint</label><br />
</p>
<div id="tui-tint-color-picker"></div>
<label>
Opacity
<input
class="range-narrow"
id="input-range-tint-opacity-value"
type="range"
min="0"
value="1"
max="1"
step="0.1"
/>
</label>
</li>
<li class="menu-item align-left-top">
<p>
<label><input type="checkbox" id="input-check-multiply" />Multiply</label>
</p>
<div id="tui-multiply-color-picker"></div>
</li>
<li class="menu-item align-left-top">
<p>
<label><input type="checkbox" id="input-check-blend" />Blend</label>
</p>
<div id="tui-blend-color-picker"></div>
<select name="select-blend-type">
<option value="add" selected>Add</option>
<option value="diff">Diff</option>
<option value="diff">Subtract</option>
<option value="multiply">Multiply</option>
<option value="screen">Screen</option>
<option value="lighten">Lighten</option>
<option value="darken">Darken</option>
</select>
</li>
<li class="menu-item close">Close</li>
</ul>
</div>
</div>
<div class="tui-image-editor"></div>
</div>
<script
type="text/javascript"
src="https://api-storage.cloud.toast.com/v1/AUTH_e18353c4ea5746c097143946d0644e61/toast-ui-cdn/tui-image-editor/v3.11.0/example/fabric-v4.2.0.js"
></script>
<script
type="text/javascript"
src="https://uicdn.toast.com/tui.code-snippet/v1.5.0/tui-code-snippet.min.js"
></script>
<script
type="text/javascript"
src="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.min.js"
></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"
></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"
></script>
<script type="text/javascript" src="../dist/tui-image-editor.js"></script>
<script src="js/service-basic.js"></script>
</body>
</html>
@@ -0,0 +1,249 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>2. Mobile</title>
<link
type="text/css"
href="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.css"
rel="stylesheet"
/>
<link type="text/css" href="css/service-mobile.css" rel="stylesheet" />
</head>
<body>
<!-- Image editor controls - top area -->
<div class="header">
<div>
<img class="logo" src="img/TOAST UI Component.png" /> <span class="name">Image Editor</span>
</div>
<div class="menu">
<span class="button">
<img src="img/openImage.png" style="margin-top: 5px" />
<input type="file" accept="image/*" id="input-image-file" />
</span>
<button class="button disabled" id="btn-undo"><img src="img/undo.png" /></button>
<button class="button disabled" id="btn-redo"><img src="img/redo.png" /></button>
<button class="button" id="btn-remove-active-object"><img src="img/remove.png" /></button>
<button class="button" id="btn-download"><img src="img/download.png" /></button>
</div>
</div>
<!-- Image editor area -->
<div class="tui-image-editor"></div>
<!-- Image editor controls - bottom area -->
<div class="tui-image-editor-controls">
<ul class="scrollable">
<li class="menu-item">
<button class="menu-button" id="btn-crop">Crop</button>
<div class="submenu">
<button class="btn-prev">&lt;</button>
<ul class="scrollable">
<li class="menu-item">
<button class="submenu-button" id="btn-apply-crop">Apply</button>
</li>
</ul>
</div>
</li>
<li class="menu-item">
<button class="menu-button">Orientation</button>
<div class="submenu">
<button class="btn-prev">&lt;</button>
<ul class="scrollable">
<li class="menu-item">
<button class="submenu-button" id="btn-rotate-clockwise">Rotate +90</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-rotate-counter-clockwise">Rotate -90</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-flip-x">FilpX</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-flip-y">FilpY</button>
</li>
</ul>
</div>
</li>
<li class="menu-item">
<button class="menu-button" id="btn-draw-line">Drawing</button>
<div class="submenu">
<button class="btn-prev">&lt;</button>
<ul class="scrollable">
<li class="menu-item">
<button class="submenu-button" id="btn-free-drawing">Free<br />Drawing</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-line-drawing">Line<br />Drawing</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-size">Brush<br />Size</button>
<div class="hiddenmenu">
<input id="input-brush-range" type="range" min="10" max="100" value="50" />
</div>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-text-color">Brush<br />Color</button>
<div class="hiddenmenu">
<div id="tui-brush-color-picker"></div>
</div>
</li>
</ul>
</div>
</li>
<li class="menu-item">
<button class="menu-button" id="btn-draw-shape">Shape</button>
<div class="submenu">
<button class="btn-prev">&lt;</button>
<ul class="scrollable">
<li class="menu-item">
<button class="submenu-button" id="btn-add-rect">Rectagle</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-add-square">Square</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-add-ellipse">Ellipse</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-add-circle">Circle</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-add-triangle">Triangle</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-stroke-size">Stroke<br />Size</button>
<div class="hiddenmenu">
<input id="input-stroke-range" type="range" min="1" max="100" value="10" />
</div>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-shape-color">Color</button>
<div class="hiddenmenu">
<div class="top">
<label for="fill-color"
><input
type="radio"
id="fill-color"
name="select-color-type"
value="fill"
checked="checked"
/>
Fill</label
>
<label for="stroke-color"
><input
type="radio"
id="stroke-color"
name="select-color-type"
value="stroke"
/>
Stroke</label
>
<label for="input-check-transparent"
><input type="checkbox" id="input-check-transparent" />Transparent</label
>
</div>
<div id="tui-shape-color-picker"></div>
</div>
</li>
</ul>
</div>
</li>
<li class="menu-item">
<button class="menu-button">Icon</button>
<div class="submenu">
<button class="btn-prev">&lt;</button>
<ul class="scrollable">
<li class="menu-item">
<button class="submenu-button" id="btn-add-arrow-icon">Arrow<br />Icon</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-add-cancel-icon">Cancel<br />Icon</button>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-icon-color">Color</button>
<div class="hiddenmenu">
<div id="tui-icon-color-picker"></div>
</div>
</li>
</ul>
</div>
</li>
<li class="menu-item">
<button class="menu-button" id="btn-add-text">Text</button>
<div class="submenu">
<button class="btn-prev">&lt;</button>
<ul class="scrollable">
<li class="menu-item">
<button class="submenu-button" id="btn-change-size">Size</button>
<div class="hiddenmenu">
<input id="input-text-size-range" type="range" min="10" max="240" value="120" />
</div>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-style">Style</button>
<div class="hiddenmenu">
<button class="hiddenmenu-button btn-change-text-style" data-style-type="bold">
<b>Bold</b>
</button>
<button class="hiddenmenu-button btn-change-text-style" data-style-type="italic">
<i>Italic</i>
</button>
<button
class="hiddenmenu-button btn-change-text-style"
data-style-type="underline"
>
<u>Underline</u>
</button>
</div>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-align">Align</button>
<div class="hiddenmenu">
<button class="hiddenmenu-button btn-change-text-style" data-style-type="left">
Left
</button>
<button class="hiddenmenu-button btn-change-text-style" data-style-type="center">
Center
</button>
<button class="hiddenmenu-button btn-change-text-style" data-style-type="right">
Right
</button>
</div>
</li>
<li class="menu-item">
<button class="submenu-button" id="btn-change-text-color">Color</button>
<div class="hiddenmenu">
<div id="tui-text-color-picker"></div>
</div>
</li>
</ul>
</div>
</li>
</ul>
<p class="msg">Menu Scrolling <b>Left ⇔ Right</b></p>
</div>
<script
type="text/javascript"
src="https://api-storage.cloud.toast.com/v1/AUTH_e18353c4ea5746c097143946d0644e61/toast-ui-cdn/tui-image-editor/v3.11.0/example/fabric-v4.2.0.js"
></script>
<script
type="text/javascript"
src="https://uicdn.toast.com/tui.code-snippet/v1.5.0/tui-code-snippet.min.js"
></script>
<script
type="text/javascript"
src="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.min.js"
></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"
></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"
></script>
<script type="text/javascript" src="../dist/tui-image-editor.js"></script>
<script src="js/service-mobile.js"></script>
</body>
</html>
@@ -0,0 +1,11 @@
{
"example01-includeUi": {
"title": "1. Include ui"
},
"example02-useApiDirect": {
"title": "2. Use api direct (basic)"
},
"example03-mobile": {
"title": "3. Mobile"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

@@ -0,0 +1,922 @@
/* eslint-disable vars-on-top,no-var,strict,prefer-template,prefer-arrow-callback,prefer-destructuring,object-shorthand,require-jsdoc,complexity,prefer-const,no-unused-vars */
var PIXELATE_FILTER_DEFAULT_VALUE = 20;
var supportingFileAPI = !!(window.File && window.FileList && window.FileReader);
var rImageType = /data:(image\/.+);base64,/;
var shapeOptions = {};
var shapeType;
var activeObjectId;
// Buttons
var $btns = $('.menu-item');
var $btnsActivatable = $btns.filter('.activatable');
var $inputImage = $('#input-image-file');
var $btnDownload = $('#btn-download');
var $btnUndo = $('#btn-undo');
var $btnRedo = $('#btn-redo');
var $btnClearObjects = $('#btn-clear-objects');
var $btnRemoveActiveObject = $('#btn-remove-active-object');
var $btnCrop = $('#btn-crop');
var $btnFlip = $('#btn-flip');
var $btnRotation = $('#btn-rotation');
var $btnDrawLine = $('#btn-draw-line');
var $btnDrawShape = $('#btn-draw-shape');
var $btnApplyCrop = $('#btn-apply-crop');
var $btnCancelCrop = $('#btn-cancel-crop');
var $btnFlipX = $('#btn-flip-x');
var $btnFlipY = $('#btn-flip-y');
var $btnResetFlip = $('#btn-reset-flip');
var $btnRotateClockwise = $('#btn-rotate-clockwise');
var $btnRotateCounterClockWise = $('#btn-rotate-counter-clockwise');
var $btnText = $('#btn-text');
var $btnTextStyle = $('.btn-text-style');
var $btnAddIcon = $('#btn-add-icon');
var $btnRegisterIcon = $('#btn-register-icon');
var $btnMaskFilter = $('#btn-mask-filter');
var $btnImageFilter = $('#btn-image-filter');
var $btnLoadMaskImage = $('#input-mask-image-file');
var $btnApplyMask = $('#btn-apply-mask');
var $btnClose = $('.close');
// Input etc.
var $inputRotationRange = $('#input-rotation-range');
var $inputBrushWidthRange = $('#input-brush-width-range');
var $inputFontSizeRange = $('#input-font-size-range');
var $inputStrokeWidthRange = $('#input-stroke-width-range');
var $inputCheckTransparent = $('#input-check-transparent');
var $inputCheckFilter = $('#input-check-filter');
var $inputCheckGrayscale = $('#input-check-grayscale');
var $inputCheckInvert = $('#input-check-invert');
var $inputCheckSepia = $('#input-check-sepia');
var $inputCheckSepia2 = $('#input-check-sepia2');
var $inputCheckBlur = $('#input-check-blur');
var $inputCheckSharpen = $('#input-check-sharpen');
var $inputCheckEmboss = $('#input-check-emboss');
var $inputCheckRemoveWhite = $('#input-check-remove-white');
var $inputRangeRemoveWhiteThreshold = $('#input-range-remove-white-threshold');
var $inputRangeRemoveWhiteDistance = $('#input-range-remove-white-distance');
var $inputCheckBrightness = $('#input-check-brightness');
var $inputRangeBrightnessValue = $('#input-range-brightness-value');
var $inputCheckNoise = $('#input-check-noise');
var $inputRangeNoiseValue = $('#input-range-noise-value');
var $inputCheckPixelate = $('#input-check-pixelate');
var $inputRangePixelateValue = $('#input-range-pixelate-value');
var $inputCheckTint = $('#input-check-tint');
var $inputRangeTintOpacityValue = $('#input-range-tint-opacity-value');
var $inputCheckMultiply = $('#input-check-multiply');
var $inputCheckBlend = $('#input-check-blend');
var $inputCheckColorFilter = $('#input-check-color-filter');
var $inputRangeColorFilterValue = $('#input-range-color-filter-value');
// Sub menus
var $displayingSubMenu = $();
var $cropSubMenu = $('#crop-sub-menu');
var $flipSubMenu = $('#flip-sub-menu');
var $rotationSubMenu = $('#rotation-sub-menu');
var $freeDrawingSubMenu = $('#free-drawing-sub-menu');
var $drawLineSubMenu = $('#draw-line-sub-menu');
var $drawShapeSubMenu = $('#draw-shape-sub-menu');
var $textSubMenu = $('#text-sub-menu');
var $iconSubMenu = $('#icon-sub-menu');
var $filterSubMenu = $('#filter-sub-menu');
var $imageFilterSubMenu = $('#image-filter-sub-menu');
// Select line type
var $selectLine = $('[name="select-line-type"]');
// Select shape type
var $selectShapeType = $('[name="select-shape-type"]');
// Select color of shape type
var $selectColorType = $('[name="select-color-type"]');
// Select blend type
var $selectBlendType = $('[name="select-blend-type"]');
// Image editor
var imageEditor = new tui.ImageEditor('.tui-image-editor', {
cssMaxWidth: 700,
cssMaxHeight: 500,
selectionStyle: {
cornerSize: 20,
rotatingPointOffset: 70,
},
});
// Color picker for free drawing
var brushColorpicker = tui.colorPicker.create({
container: $('#tui-brush-color-picker')[0],
color: '#000000',
});
// Color picker for text palette
var textColorpicker = tui.colorPicker.create({
container: $('#tui-text-color-picker')[0],
color: '#000000',
});
// Color picker for shape
var shapeColorpicker = tui.colorPicker.create({
container: $('#tui-shape-color-picker')[0],
color: '#000000',
});
// Color picker for icon
var iconColorpicker = tui.colorPicker.create({
container: $('#tui-icon-color-picker')[0],
color: '#000000',
});
var tintColorpicker = tui.colorPicker.create({
container: $('#tui-tint-color-picker')[0],
color: '#000000',
});
var multiplyColorpicker = tui.colorPicker.create({
container: $('#tui-multiply-color-picker')[0],
color: '#000000',
});
var blendColorpicker = tui.colorPicker.create({
container: $('#tui-blend-color-picker')[0],
color: '#00FF00',
});
// Common global functions
// HEX to RGBA
function hexToRGBa(hex, alpha) {
var r = parseInt(hex.slice(1, 3), 16);
var g = parseInt(hex.slice(3, 5), 16);
var b = parseInt(hex.slice(5, 7), 16);
var a = alpha || 1;
return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
}
function base64ToBlob(data) {
var mimeString = '';
var raw, uInt8Array, i, rawLength;
raw = data.replace(rImageType, function (header, imageType) {
mimeString = imageType;
return '';
});
raw = atob(raw);
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 });
}
function resizeEditor() {
var $editor = $('.tui-image-editor');
var $container = $('.tui-image-editor-canvas-container');
var height = parseFloat($container.css('max-height'));
$editor.height(height);
}
function getBrushSettings() {
var brushWidth = parseInt($inputBrushWidthRange.val(), 10);
var brushColor = brushColorpicker.getColor();
return {
width: brushWidth,
color: hexToRGBa(brushColor, 0.5),
};
}
function activateShapeMode() {
if (imageEditor.getDrawingMode() !== 'SHAPE') {
imageEditor.stopDrawingMode();
imageEditor.startDrawingMode('SHAPE');
}
}
function activateIconMode() {
imageEditor.stopDrawingMode();
}
function activateTextMode() {
if (imageEditor.getDrawingMode() !== 'TEXT') {
imageEditor.stopDrawingMode();
imageEditor.startDrawingMode('TEXT');
}
}
function setTextToolbar(obj) {
var fontSize = obj.fontSize;
var fontColor = obj.fill;
$inputFontSizeRange.val(fontSize);
textColorpicker.setColor(fontColor);
}
function setIconToolbar(obj) {
var iconColor = obj.fill;
iconColorpicker.setColor(iconColor);
}
function setShapeToolbar(obj) {
var fillColor, isTransparent, isFilter;
var colorType = $selectColorType.val();
var changeValue = colorType === 'stroke' ? obj.stroke : obj.fill.type;
isTransparent = changeValue === 'transparent';
isFilter = changeValue === 'filter';
if (colorType === 'stroke') {
if (!isTransparent && !isFilter) {
shapeColorpicker.setColor(changeValue);
}
} else if (colorType === 'fill') {
if (!isTransparent && !isFilter) {
fillColor = obj.fill.color;
shapeColorpicker.setColor(fillColor);
}
}
$inputCheckTransparent.prop('checked', isTransparent);
$inputCheckFilter.prop('checked', isFilter);
$inputStrokeWidthRange.val(obj.strokeWidth);
}
function showSubMenu(type) {
var $submenu;
switch (type) {
case 'shape':
$submenu = $drawShapeSubMenu;
break;
case 'icon':
$submenu = $iconSubMenu;
break;
case 'text':
$submenu = $textSubMenu;
break;
default:
$submenu = 0;
}
$displayingSubMenu.hide();
$displayingSubMenu = $submenu.show();
}
function applyOrRemoveFilter(applying, type, options) {
if (applying) {
imageEditor.applyFilter(type, options).then(function (result) {
console.log(result);
});
} else {
imageEditor.removeFilter(type);
}
}
// Attach image editor custom events
imageEditor.on({
objectAdded: function (objectProps) {
console.info(objectProps);
},
undoStackChanged: function (length) {
if (length) {
$btnUndo.removeClass('disabled');
} else {
$btnUndo.addClass('disabled');
}
resizeEditor();
},
redoStackChanged: function (length) {
if (length) {
$btnRedo.removeClass('disabled');
} else {
$btnRedo.addClass('disabled');
}
resizeEditor();
},
objectScaled: function (obj) {
if (obj.type === 'text') {
$inputFontSizeRange.val(obj.fontSize);
}
},
addText: function (pos) {
imageEditor
.addText('Double Click', {
position: pos.originPosition,
})
.then(function (objectProps) {
console.log(objectProps);
});
},
objectActivated: function (obj) {
activeObjectId = obj.id;
if (obj.type === 'rect' || obj.type === 'circle' || obj.type === 'triangle') {
showSubMenu('shape');
setShapeToolbar(obj);
activateShapeMode();
} else if (obj.type === 'icon') {
showSubMenu('icon');
setIconToolbar(obj);
activateIconMode();
} else if (obj.type === 'text') {
showSubMenu('text');
setTextToolbar(obj);
activateTextMode();
}
},
mousedown: function (event, originPointer) {
if ($imageFilterSubMenu.is(':visible') && imageEditor.hasFilter('colorFilter')) {
imageEditor.applyFilter('colorFilter', {
x: parseInt(originPointer.x, 10),
y: parseInt(originPointer.y, 10),
});
}
},
});
// Attach button click event listeners
$btns.on('click', function () {
$btnsActivatable.removeClass('active');
});
$btnsActivatable.on('click', function () {
$(this).addClass('active');
});
$btnUndo.on('click', function () {
$displayingSubMenu.hide();
if (!$(this).hasClass('disabled')) {
imageEditor.discardSelection();
imageEditor.undo();
}
});
$btnRedo.on('click', function () {
$displayingSubMenu.hide();
if (!$(this).hasClass('disabled')) {
imageEditor.discardSelection();
imageEditor.redo();
}
});
$btnClearObjects.on('click', function () {
$displayingSubMenu.hide();
imageEditor.clearObjects();
});
$btnRemoveActiveObject.on('click', function () {
$displayingSubMenu.hide();
imageEditor.removeObject(activeObjectId);
});
$btnCrop.on('click', function () {
imageEditor.startDrawingMode('CROPPER');
$displayingSubMenu.hide();
$displayingSubMenu = $cropSubMenu.show();
});
$btnFlip.on('click', function () {
imageEditor.stopDrawingMode();
$displayingSubMenu.hide();
$displayingSubMenu = $flipSubMenu.show();
});
$btnRotation.on('click', function () {
imageEditor.stopDrawingMode();
$displayingSubMenu.hide();
$displayingSubMenu = $rotationSubMenu.show();
});
$btnClose.on('click', function () {
imageEditor.stopDrawingMode();
$displayingSubMenu.hide();
});
$btnApplyCrop.on('click', function () {
imageEditor.crop(imageEditor.getCropzoneRect()).then(function () {
imageEditor.stopDrawingMode();
resizeEditor();
});
});
$btnCancelCrop.on('click', function () {
imageEditor.stopDrawingMode();
});
$btnFlipX.on('click', function () {
imageEditor.flipX().then(function (status) {
console.log('flipX: ', status.flipX);
console.log('flipY: ', status.flipY);
console.log('angle: ', status.angle);
});
});
$btnFlipY.on('click', function () {
imageEditor.flipY().then(function (status) {
console.log('flipX: ', status.flipX);
console.log('flipY: ', status.flipY);
console.log('angle: ', status.angle);
});
});
$btnResetFlip.on('click', function () {
imageEditor.resetFlip().then(function (status) {
console.log('flipX: ', status.flipX);
console.log('flipY: ', status.flipY);
console.log('angle: ', status.angle);
});
});
$btnRotateClockwise.on('click', function () {
imageEditor.rotate(30);
});
$btnRotateCounterClockWise.on('click', function () {
imageEditor.rotate(-30);
});
$inputRotationRange.on('mousedown', function () {
var changeAngle = function () {
imageEditor.setAngle(parseInt($inputRotationRange.val(), 10))['catch'](function () {});
};
$(document).on('mousemove', changeAngle);
$(document).on('mouseup', function stopChangingAngle() {
$(document).off('mousemove', changeAngle);
$(document).off('mouseup', stopChangingAngle);
});
});
$inputRotationRange.on('change', function () {
imageEditor.setAngle(parseInt($inputRotationRange.val(), 10))['catch'](function () {});
});
$inputBrushWidthRange.on('change', function () {
imageEditor.setBrush({ width: parseInt(this.value, 10) });
});
$inputImage.on('change', function (event) {
var file;
if (!supportingFileAPI) {
alert('This browser does not support file-api');
}
file = event.target.files[0];
imageEditor.loadImageFromFile(file).then(function (result) {
console.log(result);
imageEditor.clearUndoStack();
});
});
$btnDownload.on('click', function () {
var imageName = imageEditor.getImageName();
var dataURL = imageEditor.toDataURL();
var blob, type, w;
if (supportingFileAPI) {
blob = base64ToBlob(dataURL);
type = blob.type.split('/')[1];
if (imageName.split('.').pop() !== type) {
imageName += '.' + type;
}
// Library: FileSaver - saveAs
saveAs(blob, imageName); // eslint-disable-line
} else {
alert('This browser needs a file-server');
w = window.open();
w.document.body.innerHTML = '<img src="' + dataURL + '">';
}
});
// control draw line mode
$btnDrawLine.on('click', function () {
imageEditor.stopDrawingMode();
$displayingSubMenu.hide();
$displayingSubMenu = $drawLineSubMenu.show();
$selectLine.eq(0).change();
});
$selectLine.on('change', function () {
var mode = $(this).val();
var settings = getBrushSettings();
imageEditor.stopDrawingMode();
if (mode === 'freeDrawing') {
imageEditor.startDrawingMode('FREE_DRAWING', settings);
} else {
imageEditor.startDrawingMode('LINE_DRAWING', settings);
}
});
brushColorpicker.on('selectColor', function (event) {
imageEditor.setBrush({
color: hexToRGBa(event.color, 0.5),
});
});
// control draw shape mode
$btnDrawShape.on('click', function () {
showSubMenu('shape');
// step 1. get options to draw shape from toolbar
shapeType = $('[name="select-shape-type"]:checked').val();
shapeOptions.stroke = '#000000';
shapeOptions.fill = '#ffffff';
shapeOptions.strokeWidth = Number($inputStrokeWidthRange.val());
// step 2. set options to draw shape
imageEditor.setDrawingShape(shapeType, shapeOptions);
// step 3. start drawing shape mode
activateShapeMode();
});
$selectShapeType.on('change', function () {
shapeType = $(this).val();
imageEditor.setDrawingShape(shapeType);
});
$selectColorType.on('change', function () {
var colorType = $(this).val();
if (colorType === 'stroke') {
$inputCheckFilter.prop('disabled', true);
$inputCheckFilter.prop('checked', false);
} else {
$inputCheckTransparent.prop('disabled', false);
$inputCheckFilter.prop('disabled', false);
}
});
$inputCheckTransparent.on('change', onChangeShapeFill);
$inputCheckFilter.on('change', onChangeShapeFill);
shapeColorpicker.on('selectColor', function (event) {
$inputCheckTransparent.prop('checked', false);
$inputCheckFilter.prop('checked', false);
onChangeShapeFill(event);
});
function onChangeShapeFill(event) {
var colorType = $selectColorType.val();
var isTransparent = $inputCheckTransparent.prop('checked');
var isFilter = $inputCheckFilter.prop('checked');
var shapeOption;
if (event.color) {
shapeOption = event.color;
} else if (isTransparent) {
shapeOption = 'transparent';
} else if (isFilter) {
shapeOption = {
type: 'filter',
filter: [{ pixelate: PIXELATE_FILTER_DEFAULT_VALUE }],
};
}
if (colorType === 'stroke') {
imageEditor.changeShape(activeObjectId, {
stroke: shapeOption,
});
} else if (colorType === 'fill') {
imageEditor.changeShape(activeObjectId, {
fill: shapeOption,
});
}
imageEditor.setDrawingShape(shapeType, shapeOptions);
}
$inputStrokeWidthRange.on('change', function () {
var strokeWidth = Number($(this).val());
imageEditor.changeShape(activeObjectId, {
strokeWidth: strokeWidth,
});
imageEditor.setDrawingShape(shapeType, shapeOptions);
});
// control text mode
$btnText.on('click', function () {
showSubMenu('text');
activateTextMode();
});
$inputFontSizeRange.on('change', function () {
imageEditor.changeTextStyle(activeObjectId, {
fontSize: parseInt(this.value, 10),
});
});
$btnTextStyle.on('click', function (e) {
// eslint-disable-line
var styleType = $(this).attr('data-style-type');
var styleObj;
e.stopPropagation();
switch (styleType) {
case 'b':
styleObj = { fontWeight: 'bold' };
break;
case 'i':
styleObj = { fontStyle: 'italic' };
break;
case 'u':
styleObj = { underline: true };
break;
case 'l':
styleObj = { textAlign: 'left' };
break;
case 'c':
styleObj = { textAlign: 'center' };
break;
case 'r':
styleObj = { textAlign: 'right' };
break;
default:
styleObj = {};
}
imageEditor.changeTextStyle(activeObjectId, styleObj);
});
textColorpicker.on('selectColor', function (event) {
imageEditor.changeTextStyle(activeObjectId, {
fill: event.color,
});
});
// control icon
$btnAddIcon.on('click', function () {
showSubMenu('icon');
activateIconMode();
});
function onClickIconSubMenu(event) {
var element = event.target || event.srcElement;
var iconType = $(element).attr('data-icon-type');
imageEditor.once('mousedown', function (e, originPointer) {
imageEditor
.addIcon(iconType, {
left: originPointer.x,
top: originPointer.y,
})
.then(function (objectProps) {
// console.log(objectProps);
});
});
}
$btnRegisterIcon.on('click', function () {
$iconSubMenu
.find('.menu-item')
.eq(3)
.after('<li id="customArrow" class="menu-item icon-text" data-icon-type="customArrow">↑</li>');
imageEditor.registerIcons({
customArrow: 'M 60 0 L 120 60 H 90 L 75 45 V 180 H 45 V 45 L 30 60 H 0 Z',
});
$btnRegisterIcon.off('click');
$iconSubMenu.on('click', '#customArrow', onClickIconSubMenu);
});
$iconSubMenu.on('click', '.icon-text', onClickIconSubMenu);
iconColorpicker.on('selectColor', function (event) {
imageEditor.changeIconColor(activeObjectId, event.color);
});
// control mask filter
$btnMaskFilter.on('click', function () {
imageEditor.stopDrawingMode();
$displayingSubMenu.hide();
$displayingSubMenu = $filterSubMenu.show();
});
$btnImageFilter.on('click', function () {
var filters = {
grayscale: $inputCheckGrayscale,
invert: $inputCheckInvert,
sepia: $inputCheckSepia,
sepia2: $inputCheckSepia2,
blur: $inputCheckBlur,
shapren: $inputCheckSharpen,
emboss: $inputCheckEmboss,
removeWhite: $inputCheckRemoveWhite,
brightness: $inputCheckBrightness,
noise: $inputCheckNoise,
pixelate: $inputCheckPixelate,
tint: $inputCheckTint,
multiply: $inputCheckMultiply,
blend: $inputCheckBlend,
colorFilter: $inputCheckColorFilter,
};
tui.util.forEach(filters, function ($value, key) {
$value.prop('checked', imageEditor.hasFilter(key));
});
$displayingSubMenu.hide();
$displayingSubMenu = $imageFilterSubMenu.show();
});
$btnLoadMaskImage.on('change', function () {
var file;
var imgUrl;
if (!supportingFileAPI) {
alert('This browser does not support file-api');
}
file = event.target.files[0];
if (file) {
imgUrl = URL.createObjectURL(file);
imageEditor.loadImageFromURL(imageEditor.toDataURL(), 'FilterImage').then(function () {
imageEditor.addImageObject(imgUrl).then(function (objectProps) {
URL.revokeObjectURL(file);
console.log(objectProps);
});
});
}
});
$btnApplyMask.on('click', function () {
imageEditor
.applyFilter('mask', {
maskObjId: activeObjectId,
})
.then(function (result) {
console.log(result);
});
});
$inputCheckGrayscale.on('change', function () {
applyOrRemoveFilter(this.checked, 'Grayscale', null);
});
$inputCheckInvert.on('change', function () {
applyOrRemoveFilter(this.checked, 'Invert', null);
});
$inputCheckSepia.on('change', function () {
applyOrRemoveFilter(this.checked, 'Sepia', null);
});
$inputCheckSepia2.on('change', function () {
applyOrRemoveFilter(this.checked, 'vintage', null);
});
$inputCheckBlur.on('change', function () {
applyOrRemoveFilter(this.checked, 'Blur', { blur: 0.1 });
});
$inputCheckSharpen.on('change', function () {
applyOrRemoveFilter(this.checked, 'Sharpen', null);
});
$inputCheckEmboss.on('change', function () {
applyOrRemoveFilter(this.checked, 'Emboss', null);
});
$inputCheckRemoveWhite.on('change', function () {
applyOrRemoveFilter(this.checked, 'removeColor', {
color: '#FFFFFF',
useAlpha: false,
distance: parseInt($inputRangeRemoveWhiteDistance.val(), 10) / 255,
});
});
$inputRangeRemoveWhiteDistance.on('change', function () {
applyOrRemoveFilter($inputCheckRemoveWhite.is(':checked'), 'removeColor', {
distance: parseInt(this.value, 10) / 255,
});
});
$inputCheckBrightness.on('change', function () {
applyOrRemoveFilter(this.checked, 'brightness', {
brightness: parseInt($inputRangeBrightnessValue.val(), 10) / 255,
});
});
$inputRangeBrightnessValue.on('change', function () {
applyOrRemoveFilter($inputCheckBrightness.is(':checked'), 'brightness', {
brightness: parseInt(this.value, 10) / 255,
});
});
$inputCheckNoise.on('change', function () {
applyOrRemoveFilter(this.checked, 'noise', {
noise: parseInt($inputRangeNoiseValue.val(), 10),
});
});
$inputRangeNoiseValue.on('change', function () {
applyOrRemoveFilter($inputCheckNoise.is(':checked'), 'noise', {
noise: parseInt(this.value, 10),
});
});
$inputCheckPixelate.on('change', function () {
applyOrRemoveFilter(this.checked, 'pixelate', {
blocksize: parseInt($inputRangePixelateValue.val(), 10),
});
});
$inputRangePixelateValue.on('change', function () {
applyOrRemoveFilter($inputCheckPixelate.is(':checked'), 'pixelate', {
blocksize: parseInt(this.value, 10),
});
});
$inputCheckTint.on('change', function () {
applyOrRemoveFilter(this.checked, 'blendColor', {
mode: 'tint',
color: tintColorpicker.getColor(),
alpha: parseFloat($inputRangeTintOpacityValue.val()),
});
});
tintColorpicker.on('selectColor', function (e) {
applyOrRemoveFilter($inputCheckTint.is(':checked'), 'blendColor', {
color: e.color,
});
});
$inputRangeTintOpacityValue.on('change', function () {
applyOrRemoveFilter($inputCheckTint.is(':checked'), 'blendColor', {
alpha: parseFloat($inputRangeTintOpacityValue.val()),
});
});
$inputCheckMultiply.on('change', function () {
applyOrRemoveFilter(this.checked, 'blendColor', {
color: multiplyColorpicker.getColor(),
});
});
multiplyColorpicker.on('selectColor', function (e) {
applyOrRemoveFilter($inputCheckMultiply.is(':checked'), 'blendColor', {
color: e.color,
});
});
$inputCheckBlend.on('change', function () {
applyOrRemoveFilter(this.checked, 'blendColor', {
mode: $selectBlendType.val(),
color: blendColorpicker.getColor(),
});
});
blendColorpicker.on('selectColor', function (e) {
applyOrRemoveFilter($inputCheckBlend.is(':checked'), 'blendColor', {
color: e.color,
});
});
$selectBlendType.on('change', function () {
applyOrRemoveFilter($inputCheckBlend.is(':checked'), 'blendColor', {
mode: this.value,
});
});
$inputCheckColorFilter.on('change', function () {
applyOrRemoveFilter(this.checked, 'removeColor', {
color: '#FFFFFF',
distance: $inputRangeColorFilterValue.val() / 255,
});
});
$inputRangeColorFilterValue.on('change', function () {
applyOrRemoveFilter($inputCheckColorFilter.is(':checked'), 'removeColor', {
distance: this.value / 255,
});
});
// Etc..
// Load sample image
imageEditor.loadImageFromURL('img/sampleImage.jpg', 'SampleImage').then(function (sizeValue) {
console.log(sizeValue);
imageEditor.clearUndoStack();
});
// IE9 Unselectable
$('.menu').on('selectstart', function () {
return false;
});
@@ -0,0 +1,595 @@
/* eslint-disable vars-on-top,no-var,strict,prefer-template,prefer-arrow-callback,prefer-destructuring,object-shorthand,require-jsdoc,complexity */
'use strict';
var MAX_RESOLUTION = 3264 * 2448; // 8MP (Mega Pixel)
var supportingFileAPI = !!(window.File && window.FileList && window.FileReader);
var rImageType = /data:(image\/.+);base64,/;
var shapeOpt = {
fill: '#fff',
stroke: '#000',
strokeWidth: 10,
};
var activeObjectId;
// Selector of image editor controls
var submenuClass = '.submenu';
var hiddenmenuClass = '.hiddenmenu';
var $controls = $('.tui-image-editor-controls');
var $menuButtons = $controls.find('.menu-button');
var $submenuButtons = $controls.find('.submenu-button');
var $btnShowMenu = $controls.find('.btn-prev');
var $msg = $controls.find('.msg');
var $subMenus = $controls.find(submenuClass);
var $hiddenMenus = $controls.find(hiddenmenuClass);
// Image editor controls - top menu buttons
var $inputImage = $('#input-image-file');
var $btnDownload = $('#btn-download');
var $btnUndo = $('#btn-undo');
var $btnRedo = $('#btn-redo');
var $btnRemoveActiveObject = $('#btn-remove-active-object');
// Image editor controls - bottom menu buttons
var $btnCrop = $('#btn-crop');
var $btnAddText = $('#btn-add-text');
// Image editor controls - bottom submenu buttons
var $btnApplyCrop = $('#btn-apply-crop');
var $btnFlipX = $('#btn-flip-x');
var $btnFlipY = $('#btn-flip-y');
var $btnRotateClockwise = $('#btn-rotate-clockwise');
var $btnRotateCounterClockWise = $('#btn-rotate-counter-clockwise');
var $btnAddArrowIcon = $('#btn-add-arrow-icon');
var $btnAddCancelIcon = $('#btn-add-cancel-icon');
var $btnAddCustomIcon = $('#btn-add-custom-icon');
var $btnFreeDrawing = $('#btn-free-drawing');
var $btnLineDrawing = $('#btn-line-drawing');
var $btnAddRect = $('#btn-add-rect');
var $btnAddSquare = $('#btn-add-square');
var $btnAddEllipse = $('#btn-add-ellipse');
var $btnAddCircle = $('#btn-add-circle');
var $btnAddTriangle = $('#btn-add-triangle');
var $btnChangeTextStyle = $('.btn-change-text-style');
// Image editor controls - etc.
var $inputTextSizeRange = $('#input-text-size-range');
var $inputBrushWidthRange = $('#input-brush-range');
var $inputStrokeWidthRange = $('#input-stroke-range');
var $inputCheckTransparent = $('#input-check-transparent');
// Colorpicker
var iconColorpicker = tui.colorPicker.create({
container: $('#tui-icon-color-picker')[0],
color: '#000000',
});
var textColorpicker = tui.colorPicker.create({
container: $('#tui-text-color-picker')[0],
color: '#000000',
});
var brushColorpicker = tui.colorPicker.create({
container: $('#tui-brush-color-picker')[0],
color: '#000000',
});
var shapeColorpicker = tui.colorPicker.create({
container: $('#tui-shape-color-picker')[0],
color: '#000000',
});
// Create image editor
var imageEditor = new tui.ImageEditor('.tui-image-editor', {
cssMaxWidth: document.documentElement.clientWidth,
cssMaxHeight: document.documentElement.clientHeight,
selectionStyle: {
cornerSize: 50,
rotatingPointOffset: 100,
},
});
var $displayingSubMenu, $displayingHiddenMenu;
function hexToRGBa(hex, alpha) {
var r = parseInt(hex.slice(1, 3), 16);
var g = parseInt(hex.slice(3, 5), 16);
var b = parseInt(hex.slice(5, 7), 16);
var a = alpha || 1;
return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')';
}
function base64ToBlob(data) {
var mimeString = '';
var raw, uInt8Array, i, rawLength;
raw = data.replace(rImageType, function (header, imageType) {
mimeString = imageType;
return '';
});
raw = atob(raw);
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 });
}
function getBrushSettings() {
var brushWidth = $inputBrushWidthRange.val();
var brushColor = brushColorpicker.getColor();
return {
width: brushWidth,
color: hexToRGBa(brushColor, 0.5),
};
}
function activateShapeMode() {
imageEditor.stopDrawingMode();
}
function activateIconMode() {
imageEditor.stopDrawingMode();
}
function activateTextMode() {
if (imageEditor.getDrawingMode() !== 'TEXT') {
imageEditor.stopDrawingMode();
imageEditor.startDrawingMode('TEXT');
}
}
function setTextToolbar(obj) {
var fontSize = obj.fontSize;
var fontColor = obj.fill;
$inputTextSizeRange.val(fontSize);
textColorpicker.setColor(fontColor);
}
function setIconToolbar(obj) {
var iconColor = obj.fill;
iconColorpicker.setColor(iconColor);
}
function setShapeToolbar(obj) {
var strokeColor, fillColor, isTransparent;
var colorType = $('[name="select-color-type"]:checked').val();
if (colorType === 'stroke') {
strokeColor = obj.stroke;
isTransparent = strokeColor === 'transparent';
if (!isTransparent) {
shapeColorpicker.setColor(strokeColor);
}
} else if (colorType === 'fill') {
fillColor = obj.fill;
isTransparent = fillColor === 'transparent';
if (!isTransparent) {
shapeColorpicker.setColor(fillColor);
}
}
$inputCheckTransparent.prop('checked', isTransparent);
$inputStrokeWidthRange.val(obj.strokeWith);
}
function showSubMenu(type) {
var index;
switch (type) {
case 'shape':
index = 3;
break;
case 'icon':
index = 4;
break;
case 'text':
index = 5;
break;
default:
index = 0;
}
$displayingSubMenu.hide();
$displayingHiddenMenu.hide();
$displayingSubMenu = $menuButtons.eq(index).parent().find(submenuClass).show();
}
// Bind custom event of image editor
imageEditor.on({
undoStackChanged: function (length) {
if (length) {
$btnUndo.removeClass('disabled');
} else {
$btnUndo.addClass('disabled');
}
},
redoStackChanged: function (length) {
if (length) {
$btnRedo.removeClass('disabled');
} else {
$btnRedo.addClass('disabled');
}
},
objectScaled: function (obj) {
if (obj.type === 'text') {
$inputTextSizeRange.val(obj.fontSize);
}
},
objectActivated: function (obj) {
activeObjectId = obj.id;
if (obj.type === 'rect' || obj.type === 'circle' || obj.type === 'triangle') {
showSubMenu('shape');
setShapeToolbar(obj);
activateShapeMode();
} else if (obj.type === 'icon') {
showSubMenu('icon');
setIconToolbar(obj);
activateIconMode();
} else if (obj.type === 'text') {
showSubMenu('text');
setTextToolbar(obj);
activateTextMode();
}
},
});
// Image editor controls action
$menuButtons.on('click', function () {
$displayingSubMenu = $(this).parent().find(submenuClass).show();
$displayingHiddenMenu = $(this).parent().find(hiddenmenuClass);
});
$submenuButtons.on('click', function () {
$displayingHiddenMenu.hide();
$displayingHiddenMenu = $(this).parent().find(hiddenmenuClass).show();
});
$btnShowMenu.on('click', function () {
$displayingSubMenu.hide();
$displayingHiddenMenu.hide();
$msg.show();
imageEditor.stopDrawingMode();
});
// Image load action
$inputImage.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).then(function () {
imageEditor.clearUndoStack();
});
} else {
alert("Loaded image's resolution is too large!\nRecommended resolution is 3264 * 2448!");
}
URL.revokeObjectURL(file);
};
img.src = URL.createObjectURL(file);
}
});
// Undo action
$btnUndo.on('click', function () {
if (!$(this).hasClass('disabled')) {
imageEditor.undo();
}
});
// Redo action
$btnRedo.on('click', function () {
if (!$(this).hasClass('disabled')) {
imageEditor.redo();
}
});
// Remove active object action
$btnRemoveActiveObject.on('click', function () {
imageEditor.removeObject(activeObjectId);
});
// Download action
$btnDownload.on('click', function () {
var imageName = imageEditor.getImageName();
var dataURL = imageEditor.toDataURL();
var blob, type, w;
if (supportingFileAPI) {
blob = base64ToBlob(dataURL);
type = blob.type.split('/')[1];
if (imageName.split('.').pop() !== type) {
imageName += '.' + type;
}
// Library: FileSaver - saveAs
saveAs(blob, imageName); // eslint-disable-line
} else {
alert('This browser needs a file-server');
w = window.open();
w.document.body.innerHTML = '<img src=' + dataURL + '>';
}
});
// Crop menu action
$btnCrop.on('click', function () {
imageEditor.startDrawingMode('CROPPER');
});
$btnApplyCrop.on('click', function () {
imageEditor.crop(imageEditor.getCropzoneRect()).then(function () {
imageEditor.stopDrawingMode();
$subMenus.removeClass('show');
$hiddenMenus.removeClass('show');
});
});
// Orientation menu action
$btnRotateClockwise.on('click', function () {
imageEditor.rotate(90);
});
$btnRotateCounterClockWise.on('click', function () {
imageEditor.rotate(-90);
});
$btnFlipX.on('click', function () {
imageEditor.flipX();
});
$btnFlipY.on('click', function () {
imageEditor.flipY();
});
// Icon menu action
$btnAddArrowIcon.on('click', function () {
imageEditor.addIcon('arrow');
});
$btnAddCancelIcon.on('click', function () {
imageEditor.addIcon('cancel');
});
$btnAddCustomIcon.on('click', function () {
imageEditor.addIcon('customArrow');
});
iconColorpicker.on('selectColor', function (event) {
imageEditor.changeIconColor(activeObjectId, event.color);
});
// Text menu action
$btnAddText.on('click', function () {
var initText = 'DoubleClick';
imageEditor.startDrawingMode('TEXT');
imageEditor.addText(initText, {
styles: {
fontSize: parseInt($inputTextSizeRange.val(), 10),
},
});
});
$btnChangeTextStyle.on('click', function () {
var styleType = $(this).attr('data-style-type');
var styleObj = {};
var styleObjKey;
switch (styleType) {
case 'bold':
styleObjKey = 'fontWeight';
break;
case 'italic':
styleObjKey = 'fontStyle';
break;
case 'underline':
styleObjKey = 'underline';
break;
case 'left':
styleObjKey = 'textAlign';
break;
case 'center':
styleObjKey = 'textAlign';
break;
case 'right':
styleObjKey = 'textAlign';
break;
default:
styleObjKey = '';
}
styleObj[styleObjKey] = styleType;
imageEditor.changeTextStyle(activeObjectId, styleObj);
});
$inputTextSizeRange.on('change', function () {
imageEditor.changeTextStyle(activeObjectId, {
fontSize: parseInt($(this).val(), 10),
});
});
textColorpicker.on('selectColor', function (event) {
imageEditor.changeTextStyle(activeObjectId, {
fill: event.color,
});
});
// Draw line menu action
$btnFreeDrawing.on('click', function () {
var settings = getBrushSettings();
imageEditor.stopDrawingMode();
imageEditor.startDrawingMode('FREE_DRAWING', settings);
});
$btnLineDrawing.on('click', function () {
var settings = getBrushSettings();
imageEditor.stopDrawingMode();
imageEditor.startDrawingMode('LINE_DRAWING', settings);
});
$inputBrushWidthRange.on('change', function () {
imageEditor.setBrush({
width: parseInt($(this).val(), 10),
});
});
brushColorpicker.on('selectColor', function (event) {
imageEditor.setBrush({
color: hexToRGBa(event.color, 0.5),
});
});
// Add shape menu action
$btnAddRect.on('click', function () {
imageEditor.addShape(
'rect',
tui.util.extend(
{
width: 500,
height: 300,
},
shapeOpt
)
);
});
$btnAddSquare.on('click', function () {
imageEditor.addShape(
'rect',
tui.util.extend(
{
width: 400,
height: 400,
isRegular: true,
},
shapeOpt
)
);
});
$btnAddEllipse.on('click', function () {
imageEditor.addShape(
'circle',
tui.util.extend(
{
rx: 300,
ry: 200,
},
shapeOpt
)
);
});
$btnAddCircle.on('click', function () {
imageEditor.addShape(
'circle',
tui.util.extend(
{
rx: 200,
ry: 200,
isRegular: true,
},
shapeOpt
)
);
});
$btnAddTriangle.on('click', function () {
imageEditor.addShape(
'triangle',
tui.util.extend(
{
width: 500,
height: 400,
isRegular: true,
},
shapeOpt
)
);
});
$inputStrokeWidthRange.on('change', function () {
imageEditor.changeShape(activeObjectId, {
strokeWidth: parseInt($(this).val(), 10),
});
});
$inputCheckTransparent.on('change', function () {
var colorType = $('[name="select-color-type"]:checked').val();
var isTransparent = $(this).prop('checked');
var color;
if (!isTransparent) {
color = shapeColorpicker.getColor();
} else {
color = 'transparent';
}
if (colorType === 'stroke') {
imageEditor.changeShape(activeObjectId, {
stroke: color,
});
} else if (colorType === 'fill') {
imageEditor.changeShape(activeObjectId, {
fill: color,
});
}
});
shapeColorpicker.on('selectColor', function (event) {
var colorType = $('[name="select-color-type"]:checked').val();
var isTransparent = $inputCheckTransparent.prop('checked');
var color = event.color;
if (isTransparent) {
return;
}
if (colorType === 'stroke') {
imageEditor.changeShape(activeObjectId, {
stroke: color,
});
} else if (colorType === 'fill') {
imageEditor.changeShape(activeObjectId, {
fill: color,
});
}
});
// Load sample image
imageEditor.loadImageFromURL('img/sampleImage.jpg', 'SampleImage').then(function () {
imageEditor.clearUndoStack();
});
@@ -0,0 +1,76 @@
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': "'Noto Sans', sans-serif",
'loadButton.fontSize': '12px',
// download button
'downloadButton.backgroundColor': '#fdba3b',
'downloadButton.border': '1px solid #fdba3b',
'downloadButton.color': '#fff',
'downloadButton.fontFamily': "'Noto Sans', sans-serif",
'downloadButton.fontSize': '12px',
// main icons
'menu.normalIcon.color': '#8a8a8a',
'menu.activeIcon.color': '#555555',
'menu.disabledIcon.color': '#434343',
'menu.hoverIcon.color': '#e9e9e9',
'menu.iconSize.width': '24px',
'menu.iconSize.height': '24px',
// submenu icons
'submenu.normalIcon.color': '#8a8a8a',
'submenu.activeIcon.color': '#e9e9e9',
'submenu.iconSize.width': '32px',
'submenu.iconSize.height': '32px',
// submenu primary color
'submenu.backgroundColor': '#1e1e1e',
'submenu.partition.color': '#3c3c3c',
// submenu labels
'submenu.normalLabel.color': '#8a8a8a',
'submenu.normalLabel.fontWeight': 'lighter',
'submenu.activeLabel.color': '#fff',
'submenu.activeLabel.fontWeight': 'lighter',
// checkbox style
'checkbox.border': '0px',
'checkbox.backgroundColor': '#fff',
// range style
'range.pointer.color': '#fff',
'range.bar.color': '#666',
'range.subbar.color': '#d1d1d1',
'range.disabledPointer.color': '#414141',
'range.disabledBar.color': '#282828',
'range.disabledSubbar.color': '#414141',
'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',
};
@@ -0,0 +1,76 @@
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': "'Noto Sans', sans-serif",
'loadButton.fontSize': '12px',
// download button
'downloadButton.backgroundColor': '#fdba3b',
'downloadButton.border': '1px solid #fdba3b',
'downloadButton.color': '#fff',
'downloadButton.fontFamily': "'Noto Sans', sans-serif",
'downloadButton.fontSize': '12px',
// main icons
'menu.normalIcon.color': '#8a8a8a',
'menu.activeIcon.color': '#555555',
'menu.disabledIcon.color': '#434343',
'menu.hoverIcon.color': '#e9e9e9',
'menu.iconSize.width': '24px',
'menu.iconSize.height': '24px',
// submenu icons
'submenu.normalIcon.color': '#8a8a8a',
'submenu.activeIcon.color': '#555555',
'submenu.iconSize.width': '32px',
'submenu.iconSize.height': '32px',
// submenu primary color
'submenu.backgroundColor': 'transparent',
'submenu.partition.color': '#e5e5e5',
// 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.disabledPointer.color': '#d3d3d3',
'range.disabledBar.color': 'rgba(85,85,85,0.06)',
'range.disabledSubbar.color': 'rgba(51,51,51,0.2)',
'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': '0px',
'colorpicker.title.color': '#000',
};
@@ -0,0 +1,334 @@
// Type definitions for TOAST UI Image Editor v3.15.3
// TypeScript Version: 3.2.2
declare namespace tuiImageEditor {
type AngleType = number;
interface IThemeConfig {
'common.bi.image'?: string;
'common.bisize.width'?: string;
'common.bisize.height'?: string;
'common.backgroundImage'?: string;
'common.backgroundColor'?: string;
'common.border'?: string;
'header.backgroundImage'?: string;
'header.backgroundColor'?: string;
'header.border'?: string;
'loadButton.backgroundColor'?: string;
'loadButton.border'?: string;
'loadButton.color'?: string;
'loadButton.fontFamily'?: string;
'loadButton.fontSize'?: string;
'downloadButton.backgroundColor'?: string;
'downloadButton.border'?: string;
'downloadButton.color'?: string;
'downloadButton.fontFamily'?: string;
'downloadButton.fontSize'?: string;
'menu.normalIcon.path'?: string;
'menu.normalIcon.name'?: string;
'menu.activeIcon.path'?: string;
'menu.activeIcon.name'?: string;
'menu.iconSize.width'?: string;
'menu.iconSize.height'?: string;
'submenu.backgroundColor'?: string;
'submenu.partition.color'?: string;
'submenu.normalIcon.path'?: string;
'submenu.normalIcon.name'?: string;
'submenu.activeIcon.path'?: string;
'submenu.activeIcon.name'?: string;
'submenu.iconSize.width'?: string;
'submenu.iconSize.height'?: string;
'submenu.normalLabel.color'?: string;
'submenu.normalLabel.fontWeight'?: string;
'submenu.activeLabel.color'?: string;
'submenu.activeLabel.fontWeight'?: string;
'checkbox.border'?: string;
'checkbox.backgroundColor'?: string;
'range.pointer.color'?: string;
'range.bar.color'?: string;
'range.subbar.color'?: string;
'range.value.color'?: string;
'range.value.fontWeight'?: string;
'range.value.fontSize'?: string;
'range.value.border'?: string;
'range.value.backgroundColor'?: string;
'range.title.color'?: string;
'range.title.fontWeight'?: string;
'colorpicker.button.border'?: string;
'colorpicker.title.color'?: string;
}
interface IIconInfo {
[propName: string]: string;
}
interface IIconOptions {
fill?: string;
left?: number;
top?: number;
}
interface IShapeOptions {
fill?: string;
stroke?: string;
strokeWidth?: number;
width?: number;
height?: number;
rx?: number;
ry?: number;
left?: number;
top?: number;
isRegular?: boolean;
}
interface IGenerateTextOptions {
styles?: ITextStyleConfig;
position?: {
x: number;
y: number;
};
}
type IFilterOptions =
| { blur: number }
| { brightness: number }
| { noise: number }
| { blocksize: number }
| { color: string; distance: number; useAlpha?: boolean }
| { mode: string; color: string; alpha?: number }
| { maskObjId: number };
interface ITextStyleConfig {
fill?: string;
fontFamily?: string;
fontSize?: number;
fontStyle?: string;
fontWeight?: string;
textAlign?: string;
textDecoration?: string;
}
interface IRectConfig {
left: number;
top: number;
width: number;
height: number;
}
interface ICanvasSize {
width: number;
height: number;
}
interface IBrushOptions {
width: number;
color: string;
}
interface IPositionConfig {
x: number;
y: number;
originX: string;
originY: string;
}
interface IToDataURLOptions {
format?: string;
quality?: number;
multiplier?: number;
left?: number;
top?: number;
width?: number;
height?: number;
}
interface IGraphicObjectProps {
id?: number;
type?: string;
text?: string;
left?: string | number;
top?: string | number;
width?: string | number;
height?: string | number;
fill?: string;
stroke?: string;
strokeWidth?: string | number;
fontFamily?: string;
fontSize?: number;
fontStyle?: string;
fontWeight?: string;
textAlign?: string;
textDecoration?: string;
opacity?: number;
[propName: string]: number | string | boolean | undefined;
}
interface IIncludeUIOptions {
loadImage?: {
path: string;
name: string;
};
theme?: IThemeConfig;
menu?: string[];
initMenu?: string;
uiSize?: {
width: string;
height: string;
};
menuBarPosition?: string;
usageStatistics?: boolean;
}
interface ISelectionStyleConfig {
cornerStyle?: string;
cornerSize?: number;
cornerColor?: string;
cornerStrokeColor?: string;
transparentCorners?: boolean;
lineWidth?: number;
borderColor?: string;
rotatingPointOffset?: number;
}
interface IObjectProps {
// icon, shape
fill: string;
height: number;
id: number;
left: number;
opacity: number;
stroke: string | null;
strokeWidth: number | null;
top: number;
type: string;
width: number;
}
interface ITextObjectProps extends IObjectProps {
fontFamily: string;
fontSize: string;
fontStyle: string;
text: string;
textAlign: string;
textDecoration: string;
}
interface IFilterResolveObject {
type: string;
action: string;
}
interface ICropResolveObject {
oldWidth: number;
oldHeight: number;
newWidth: number;
newHeight: number;
}
interface IFlipXYResolveObject {
flipX: boolean;
flipY: boolean;
angle: AngleType;
}
interface IOptions {
includeUI?: IIncludeUIOptions;
cssMaxWidth?: number;
cssMaxHeight?: number;
usageStatistics?: boolean;
selectionStyle?: ISelectionStyleConfig;
}
interface IUIDimension {
height?: string;
width?: string;
}
interface IImageDimension {
oldHeight?: number;
oldWidth?: number;
newHeight?: number;
newWidth?: number;
}
interface IEditorSize {
uiSize?: IUIDimension;
imageSize?: IImageDimension;
}
interface UI {
resizeEditor(dimension: IEditorSize): Promise<void>;
}
class ImageEditor {
constructor(wrapper: string | Element, options: IOptions);
public ui: UI;
public addIcon(type: string, options?: IIconOptions): Promise<IObjectProps>;
public addImageObject(imgUrl: string): Promise<void>;
public addShape(type: string, options?: IShapeOptions): Promise<IObjectProps>;
public addText(text: string, options?: IGenerateTextOptions): Promise<ITextObjectProps>;
public applyFilter(
type: string,
options?: IFilterOptions,
isSilent?: boolean
): Promise<IFilterResolveObject>;
public changeCursor(cursorType: string): void;
public changeIconColor(id: number, color: string): Promise<void>;
public changeSelectableAll(selectable: boolean): void;
public changeShape(id: number, options?: IShapeOptions, isSilent?: boolean): Promise<void>;
public changeText(id: number, text?: string): Promise<void>;
public changeTextStyle(
id: number,
styleObj: ITextStyleConfig,
isSilent?: boolean
): Promise<void>;
public clearObjects(): Promise<void>;
public clearRedoStack(): void;
public clearUndoStack(): void;
public crop(rect: IRectConfig): Promise<ICropResolveObject>;
public deactivateAll(): void;
public destroy(): void;
public discardSelection(): void;
public flipX(): Promise<IFlipXYResolveObject>;
public flipY(): Promise<IFlipXYResolveObject>;
public getCanvasSize(): ICanvasSize;
public getCropzoneRect(): IRectConfig;
public getDrawingMode(): string;
public getImageName(): string;
public getObjectPosition(id: number, originX: string, originY: string): ICanvasSize;
public getObjectProperties(
id: number,
keys: string | string[] | IGraphicObjectProps
): IGraphicObjectProps;
public hasFilter(type: string): boolean;
public isEmptyRedoStack(): boolean;
public isEmptyUndoStack(): boolean;
public loadImageFromFile(imgFile: File, imageName?: string): Promise<ICropResolveObject>;
public loadImageFromURL(url: string, imageName?: string): Promise<ICropResolveObject>;
public redo(): Promise<any>;
public registerIcons(infos: IIconInfo): void;
public removeActiveObject(): void;
public removeFilter(type?: string): Promise<IFilterResolveObject>;
public removeObject(id: number): Promise<void>;
public resetFlip(): Promise<IFlipXYResolveObject>;
public resizeCanvasDimension(dimension: ICanvasSize): Promise<void>;
public rotate(angle: AngleType, isSilent?: boolean): Promise<AngleType>;
public setAngle(angle: AngleType, isSilent?: boolean): Promise<AngleType>;
public setBrush(option: IBrushOptions): void;
public setCropzoneRect(mode?: number): void;
public setDrawingShape(type: string, options?: IShapeOptions): void;
public setObjectPosition(id: number, posInfo?: IPositionConfig): Promise<void>;
public setObjectProperties(id: number, keyValue?: IGraphicObjectProps): Promise<void>;
public setObjectPropertiesQuietly(id: number, keyValue?: IGraphicObjectProps): Promise<void>;
public startDrawingMode(mode: string, option?: { width?: number; color?: string }): boolean;
public stopDrawingMode(): void;
public toDataURL(options?: IToDataURLOptions): string;
public undo(): Promise<any>;
public on(eventName: string, handler: (...args: any[]) => void): void;
}
}
declare module 'tui-image-editor' {
export = tuiImageEditor.ImageEditor;
}
@@ -0,0 +1 @@
import 'jest-canvas-mock';
@@ -0,0 +1,21 @@
const path = require('path');
const setupFile = path.resolve(__dirname, './jest-setup.js');
module.exports = {
moduleFileExtensions: ['js'],
testEnvironment: 'jsdom',
transform: {
'^.+\\.js$': 'jest-esm-transformer',
'^.+\\.svg$': '<rootDir>/__mocks__/svgMock.js',
},
transformIgnorePatterns: ['<rootDir>/node_modules/'],
testMatch: ['<rootDir>/**/*.spec.js'],
clearMocks: true,
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/js/$1',
'^@css/(.*)$': '<rootDir>/src/css/$1',
'^@svg/(.*)$': '<rootDir>/src/svg/$1',
'^fixtures/(.*)$': '<rootDir>/__mocks__/fileMock.js',
},
setupFiles: [setupFile],
};
@@ -0,0 +1,30 @@
/* eslint-disable */
const fs = require('fs');
const mkdirp = require('mkdirp');
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);
});
}
mkdirp('./dist/svg').then((path) => {
if (path) {
fs.readdir(svgDir, (err, dirs) => {
dirs.forEach((dir) => {
getFileList(dir);
});
});
}
});
@@ -0,0 +1,52 @@
{
"name": "tui-image-editor",
"version": "3.15.3",
"description": "TOAST UI ImageEditor",
"keywords": [
"nhn",
"nhn cloud",
"tui",
"component",
"image",
"editor",
"canvas",
"fabric"
],
"main": "dist/tui-image-editor.js",
"files": [
"src",
"dist",
"index.d.ts"
],
"scripts": {
"test": "jest --forceExit --detectOpenHandles",
"test:types": "tsc --project tests/types",
"build": "npm run build:clean && npm run build:svg && npm run build:prod && npm run build:minify && node tsBannerGenerator.js",
"build:clean": "rm -rf ./dist",
"build:prod": "webpack",
"build:minify": "webpack --env minify",
"build:svg": "node makesvg.js",
"serve": "webpack serve",
"doc:dev": "tuidoc --serv",
"doc": "tuidoc",
"update:wrapper": "node scripts/updateWrapper.js",
"publish:cdn": "node scripts/publishToCDN.js"
},
"homepage": "https://github.com/nhn/tui.image-editor",
"bugs": "https://github.com/nhn/tui.image-editor/issues",
"author": "NHN Cloud. FE Development Lab <dl_javascript@nhn.com>",
"repository": {
"type": "git",
"url": "https://github.com/nhn/tui.image-editor.git"
},
"license": "MIT",
"browserslist": [
"last 2 versions",
"not ie <= 9"
],
"dependencies": {
"fabric": "^4.2.0",
"tui-code-snippet": "^2.3.3",
"tui-color-picker": "^2.2.6"
}
}
@@ -0,0 +1,88 @@
/* eslint-disable */
const path = require('path');
const fs = require('fs');
const fetch = require('node-fetch');
const pkg = require('../package.json');
const LOCAL_DIST_PATH = path.join(__dirname, '../dist');
const STORAGE_API_URL = 'https://api-storage.cloud.toast.com/v1';
const IDENTITY_API_URL = 'https://api-identity.infrastructure.cloud.toast.com/v2.0';
const TOAST_CLOUD_TENANTID = process.env.TOAST_CLOUD_TENANTID;
const TOAST_CLOUD_STORAGEID = process.env.TOAST_CLOUD_STORAGEID;
const TOAST_CLOUD_USERNAME = process.env.TOAST_CLOUD_USERNAME;
const TOAST_CLOUD_PASSWORD = process.env.TOAST_CLOUD_PASSWORD;
async function getTOASTCloudContainer(token) {
const response = await fetch(`${STORAGE_API_URL}/${TOAST_CLOUD_STORAGEID}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': token,
},
});
const container = await response.text();
return `${container.trim()}/tui-image-editor`;
}
async function getTOASTCloudToken() {
const data = {
auth: {
tenantId: TOAST_CLOUD_TENANTID,
passwordCredentials: {
username: TOAST_CLOUD_USERNAME,
password: TOAST_CLOUD_PASSWORD,
},
},
};
const response = await fetch(`${IDENTITY_API_URL}/tokens`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
const result = await response.json();
return result.access.token.id;
}
function publishToCdn(token, localPath, cdnPath) {
const files = fs.readdirSync(localPath);
files.forEach((fileName) => {
const objectPath = `${cdnPath}/${fileName}`;
if (fileName.match(/.(js|css|svg)$/)) {
const readStream = fs.createReadStream(`${localPath}/${fileName}`);
const contentType = /css$/.test(fileName)
? 'text/css'
: /js$/.test(fileName)
? 'text/javascript'
: 'image/svg+xml';
fetch(`${STORAGE_API_URL}/${objectPath}`, {
method: 'PUT',
headers: {
'Content-Type': contentType,
'X-Auth-Token': token,
},
body: readStream,
});
} else {
publishToCdn(token, `${localPath}/${fileName}`, objectPath);
}
});
}
async function publish() {
const token = await getTOASTCloudToken();
const container = await getTOASTCloudContainer(token);
const cdnPath = `${TOAST_CLOUD_STORAGEID}/${container}`;
[`v${pkg.version}`, 'latest'].forEach((dir) => {
publishToCdn(token, LOCAL_DIST_PATH, `${cdnPath}/${dir}`);
});
}
publish();
@@ -0,0 +1,23 @@
/* eslint-disable */
const path = require('path');
const fs = require('fs');
const CORE_PACKAGE_JSON_PATH = path.join(__dirname, '../package.json');
const REACT_PACKAGE_JSON_PATH = path.join(__dirname, '../../react-image-editor/package.json');
const VUE_PACKAGE_JSON_PATH = path.join(__dirname, '../../vue-image-editor/package.json');
const corePackage = require(CORE_PACKAGE_JSON_PATH);
const reactPackage = require(REACT_PACKAGE_JSON_PATH);
const vuePackage = require(VUE_PACKAGE_JSON_PATH);
const version = corePackage.version;
reactPackage.version = version;
reactPackage.dependencies['tui-image-editor'] = `^${version}`;
fs.writeFileSync(REACT_PACKAGE_JSON_PATH, `${JSON.stringify(reactPackage, null, 2)}\n`);
vuePackage.version = version;
vuePackage.dependencies['tui-image-editor'] = `^${version}`;
fs.writeFileSync(VUE_PACKAGE_JSON_PATH, `${JSON.stringify(vuePackage, null, 2)}\n`);
@@ -0,0 +1,102 @@
/* 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-star-2 .{prefix}-button[data-icontype="icon-star-2"] 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,
.tie-crop-preset-button
.{prefix}-button.apply
margin-right: 24px;
.{prefix}-button.preset.active svg > use.active
display: block;
.{prefix}-button.apply.active svg > use.active
display: block;
/* RESIZE BUTTON */
.tie-resize-button,
.tie-resize-preset-button
.{prefix}-button.apply
margin-right: 24px;
.{prefix}-button.preset.active svg > use.active
display: block;
.{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
&.tie-text-align-left .{prefix}-button.left svg > use.active,
&.tie-text-align-center .{prefix}-button.center svg > use.active,
&.tie-text-align-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;
/* FLIP BUTTON */
.tie-zoom-button
&.resetFlip .{prefix}-button.resetFlip,
&.flipX .{prefix}-button.flipX,
&.flipY .{prefix}-button.flipY
svg > use.normal
display: none;
svg > use.active
display: block;
@@ -0,0 +1,86 @@
/* 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;
> label > span
color: #fff;
height: 14px;
position: relative;
input + label:before,
> label > span:before
content: '';
position: absolute;
width: 14px;
height: 14px;
background-color: #fff;
top: 6px;
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 + span: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: 28px;
margin-top: 4px;
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 10px;
.{prefix}-selectlist
display: none;
position: relative;
top: -1px;
border: 1px solid #ccc;
background-color: #fff;
border-top: 0px;
padding: 4px 0;
li
display: block;
text-align: left;
padding: 7px 10px;
font-family: 'Noto Sans', sans-serif;
li:hover
background-color: rgba(81, 92, 230, 0.05);
.{prefix}-selectlist-wrap:before
content: '';
position: absolute;
display: inline-block;
width: 14px;
height: 14px;
right: 5px;
top: 10px;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHlJREFUKBVjYBgFOEOAEVkmPDxc89+/f6eAYjzI4kD2FyYmJrOVK1deh4kzwRggGiQBVJCELAZig8SQNYHEmEEEMrh69eo1HR0dfqCYJUickZGxf9WqVf3IakBsFBthklpaWmVA9mEQhrJhUoTp0NBQCRAmrHL4qgAAuu4cWZOZIGsAAAAASUVORK5CYII=');
background-size: cover;
.{prefix}-selectlist-wrap select::-ms-expand
display:none;
@@ -0,0 +1,98 @@
/* 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;
font-family: 'Noto Sans', sans-serif;
.tui-colorpicker-palette-hex[value='#ffffff'] + .tui-colorpicker-palette-preview,
.tui-colorpicker-palette-hex[value=''] + .tui-colorpicker-palette-preview
border: 1px solid #ccc;
.tui-colorpicker-palette-hex[value=''] + .tui-colorpicker-palette-preview
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');
.tui-colorpicker-palette-preview
border-radius: 100%;
float: left;
width: 17px;
height: 17px;
border: 0;
.color-picker-control
position: absolute;
display: none;
z-index: 99;
width: 192px;
background-color: #fff;
box-shadow: 0 3px 22px 6px rgba(0, 0, 0, .15);
padding: 16px;
border-radius: 2px;
.tui-colorpicker-palette-toggle-slider
display: none;
.tui-colorpicker-palette-button
border: 0;
border-radius: 100%;
margin: 2px;
background-size: cover;
font-size: 1px;
&[title='#ffffff']
border: 1px solid #ccc;
&[title='']
border: 1px solid #ccc;
.triangle
width: 0;
height: 0;
border-right: 7px solid transparent;
border-top: 8px solid #fff;
border-left: 7px solid transparent;
position: absolute;
bottom: -8px;
left: 84px;
.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
.tui-image-editor-checkbox
margin-top: 0;
input + label:before,
> label:before
left: -16px;
.color-picker
width: 100%;
height: auto;
.color-picker-value
width: 32px;
height: 32px;
border: 0px;
border-radius: 100%;
margin: auto;
margin-bottom: 1px;
&.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;
@@ -0,0 +1,45 @@
/* GRID VISUAL OF FLIP AND ROTATE MENU */
.{prefix}-container
.{prefix}-grid-visual
display: none;
position: absolute;
width: 100%;
height: 100%;
border: 1px solid rgba(255,255,255,0.7);
.{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,
.{prefix}-main.{prefix}-menu-resize .{prefix}-grid-visual
display: block;
.{prefix}-grid-visual
table
width: 100%;
height: 100%;
border-collapse: collapse;
td
border: 1px solid rgba(255,255,255,0.3);
td.dot:before
content: '';
position: absolute;
box-sizing: border-box;
width: 10px;
height: 10px;
border: 0;
box-shadow: 0 0 1px 0 rgba(0,0,0,0.3);
border-radius: 100%;
background-color: #fff;
td.dot.left-top:before
top: -5px;
left: -5px;
td.dot.right-top:before
top: -5px;
right: -5px;
td.dot.left-bottom:before
bottom: -5px;
left: -5px;
td.dot.right-bottom:before
bottom: -5px;
right: -5px;
@@ -0,0 +1,44 @@
/* ICON */
.{prefix}-container
.tie-icon-add-button .{prefix}-button
min-width: 42px;
.svg_ic-menu
.svg_ic-helpmenu
width: 24px;
height: 24px;
.svg_ic-submenu
width: 32px;
height: 32px;
.svg_img-bi
width: 257px;
height: 26px;
.{prefix}-help-menu
.{prefix}-controls
svg > use
display: none;
.enabled svg:hover > use.hover
.normal svg:hover > use.hover
display: block;
.active svg:hover > use.hover
display: none;
.on svg > use.hover,
.opened svg > use.hover
display: block;
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;
.help svg > use.disabled,
.help.enabled svg > use.normal
display: block;
.help.enabled svg > use.disabled
display: none;
.{prefix}-controls:hover
z-index: 3;
@@ -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;
@@ -0,0 +1,163 @@
body > textarea
position: fixed !important;
+prefix-classes(prefix)
.-container
margin: 0;
padding: 0;
box-sizing: border-box;
min-height: 300px;
height: 100%;
position: relative;
background-color: #282828;
overflow: hidden;
letter-spacing: 0.3px;
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,
.-header-buttons div,
.-controls-buttons button,
.-controls-buttons div
display: inline-block;
position: relative;
width: 120px;
height: 40px;
padding: 0;
line-height: 40px;
outline: none;
border-radius: 20px;
border: 1px solid #ddd;
font-family: 'Noto Sans', sans-serif;
font-size: 12px;
font-weight: bold;
cursor: pointer;
vertical-align: middle;
letter-spacing: 0.3px;
text-align: center;
.-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: 0;
width: 100%;
overflow: auto;
.-size-wrap
display: table;
width: 100%;
height: 100%
.-align-wrap
display: table-cell;
vertical-align: middle;
.
position: relative;
display: inline-block;
/* BIG MENU */
.{prefix}-container
.{prefix}-menu, .{prefix}-help-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[tooltip-content]: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;
left: 13px;
top: -2px;
&:after
content: attr(tooltip-content);
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: -25px;
left: 0;
min-width: 24px;
> .{prefix}-item.active
background-color: #fff;
transition: all .3s ease;
.{prefix}-wrap
position: absolute;
@@ -0,0 +1,309 @@
/* POSITION LEFT */
.{prefix}-container
&.left
.{prefix}-menu
> .{prefix}-item[tooltip-content]
&:before
left: 28px;
top: 11px;
border-right: 7px solid #2f2f2f;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
&:after
top: 7px;
left: 42px;
white-space: nowrap;
.{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: inherit;
.{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;
/* POSITION RIGHT */
.{prefix}-container
&.right
.{prefix}-menu
> .{prefix}-item[tooltip-content]
&:before
left: -3px;
top: 11px;
border-left: 7px solid #2f2f2f;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
&:after
top: 7px;
left: unset;
right: 43px;
white-space: nowrap;
.{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: -8px;
border-right: 7px solid transparent;
border-top: 0px;
border-left: 7px solid transparent;
border-bottom: 8px solid #fff;
.{prefix}-size-wrap
height: 100%;
.{prefix}-main-container
bottom: 0;
.{prefix}-menu
> .{prefix}-item[tooltip-content]
&:before
left: 13px;
border-top: 0;
border-bottom: 7px solid #2f2f2f;
top: 33px;
&:after
top: 38px;
.{prefix}-submenu
top: 0;
bottom: auto;
> div
padding-top: 24px;
vertical-align: top;
.{prefix}-controls-logo
display: table-cell;
.{prefix}-controls-buttons
display: table-cell;
.{prefix}-main
top: 64px;
height: calc(100% - 64px);
.{prefix}-controls
top: 0;
bottom: inherit;
/* HELP MENUBAR POSITION TOP */
.{prefix}-container
.{prefix}-help-menu
&.top
white-space: nowrap;
width: 506px;
height: 40px;
top: 8px;
left: 50%;
transform: translateX(-50%);
.tie-panel-history
top: 45px;
.opened .tie-panel-history:before
border-right: 8px solid transparent;
border-left: 8px solid transparent;
border-bottom: 8px solid #fff;
left: 90px;
top: -8px;
> .{prefix}-item[tooltip-content]
&:before
left: 13px;
top: 35px;
border: none;
border-bottom: 7px solid #2f2f2f;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
&:after
top: 41px;
left: -4px;
white-space: nowrap;
> .{prefix}-item[tooltip-content].opened
&:before,
&:after
content: none;
/* HELP MENUBAR POSITION BOTTOM */
.{prefix}-container
.{prefix}-help-menu
&.bottom
white-space: nowrap;
width: 506px;
height: 40px;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
.tie-panel-history
bottom: 45px;
.opened .tie-panel-history:before
border-right: 8px solid transparent;
border-left: 8px solid transparent;
border-top: 8px solid #fff;
left: 90px;
bottom: -8px;
> .{prefix}-item[tooltip-content]
&:before
left: 13px;
top: auto;
bottom: 36px;
border: none;
border-top: 7px solid #2f2f2f;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
&:after
top: auto;
left: -4px;
bottom: 41px;
white-space: nowrap;
> .{prefix}-item[tooltip-content].opened
&:before,
&:after
content: none;
/* HELP MENUBAR POSITION LEFT */
.{prefix}-container
.{prefix}-help-menu
&.left
white-space: inherit;
width: 40px;
height: 506px;
left: 8px;
top: 50%;
transform: translateY(-50%);
.tie-panel-history
left: 140px;
top: -4px;
.opened .tie-panel-history:before
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid #fff;
left: -8px;
top: 14px;
.{prefix}-item
margin: 4px auto;
padding: 6px 8px;
> .{prefix}-item[tooltip-content]
&:before
left: 27px;
top: 11px;
border: none;
border-right: 7px solid #2f2f2f;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
&:after
top: 7px;
left: 40px;
white-space: nowrap;
> .{prefix}-item[tooltip-content].opened
&:before,
&:after
content: none;
/* HELP MENUBAR POSITION RIGHT */
.{prefix}-container
.{prefix}-help-menu
&.right
white-space: inherit;
width: 40px;
height: 506px;
right: 8px;
top: 50%;
transform: translateY(-50%);
.tie-panel-history
right: -30px;
top: -4px;
.opened .tie-panel-history:before
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-left: 8px solid #fff;
right: -8px;
top: 14px;
.{prefix}-item
margin: 4px auto;
padding: 6px 8px;
> .{prefix}-item[tooltip-content]
&:before
left: -6px;
top: 11px;
border: none;
border-left: 7px solid #2f2f2f;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
&:after
top: 7px;
left: auto;
right: 39px;
white-space: nowrap;
> .{prefix}-item[tooltip-content].opened
&:before,
&:after
content: none;
@@ -0,0 +1,91 @@
/* VIRTUAL RANGE */
.{prefix}-container
.{prefix}-virtual-range-bar
.{prefix}-virtual-range-subbar
.{prefix}-virtual-range-pointer
.{prefix}-disabled
backbround-color: red;
.{prefix}-range
position: relative;
top: 5px;
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;
margin-left: 4px;
&.short .tui-image-editor-range
width: 100px;
.color-picker-control
.{prefix}-range
width: 108px;
margin-left: 10px;
.{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: -2px;
margin-left: 19px;
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: 'Noto Sans', sans-serif;
margin-top: 15px;
margin-left: 4px;
.{prefix}-controls
position: absolute;
background-color: #151515;
width: 100%;
height: 64px;
display: table;
bottom: 0;
z-index: 2;
.{prefix}-icpartition
display: inline-block;
background-color: #444;
width: 1px;
height: 24px;
@@ -0,0 +1,168 @@
/* SUBMENU */
.{prefix}-container
.{prefix}-submenu
display: none;
position: absolute;
bottom: 0;
width:100%;
height: 150px;
white-space: nowrap;
z-index: 2;
.{prefix}-button:hover svg > use.active
display: block;
.{prefix}-submenu-item
li
display: inline-block;
vertical-align: top;
.{prefix}-newline
display: block;
margin-top: 0;
.{prefix}-button
position: relative;
cursor: pointer;
display: inline-block;
font-weight: normal;
font-size: 11px;
margin: 0 9px 0 9px;
.{prefix}-button.preset
margin: 0 9px 20px 5px;
label > span
display: inline-block;
cursor: pointer;
padding-top: 5px;
font-family: "Noto Sans", sans-serif;
font-size: 11px;
.{prefix}-button.apply label,
.{prefix}-button.cancel label
vertical-align: 7px;
> div
display: none;
vertical-align: bottom;
.{prefix}-submenu-style
opacity: 0.95;
z-index: -1;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: block;
.{prefix}-partition > div
width: 1px;
height: 52px;
border-left: 1px solid #3c3c3c;
margin: 0 8px 0 8px;
.{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 > span
width: 55px;
white-space: nowrap;
.{prefix}-submenu-align:first-child
margin-right: 0;
label > span
width: 70px;
.{prefix}-main.{prefix}-menu-crop .{prefix}-submenu > div.{prefix}-menu-crop,
.{prefix}-main.{prefix}-menu-resize .{prefix}-submenu > div.{prefix}-menu-resize,
.{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,
.{prefix}-main.{prefix}-menu-zoom .{prefix}-submenu > div.{prefix}-menu-zoom
display: table-cell;
.{prefix}-main.{prefix}-menu-crop,
.{prefix}-main.{prefix}-menu-resize,
.{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}-main.{prefix}-menu-zoom
.{prefix}-submenu
display: table;
/* Help menu bar */
.{prefix}-container
.{prefix}-help-menu
list-style: none;
padding: 0;
margin: 0 auto;
text-align: center;
vertical-align: middle;
border-radius: 20px;
background-color: rgba(255, 255, 255, 0.06);
z-index: 2;
position: absolute;
.tie-panel-history
display: none;
background-color: #fff;
color: #444;
position: absolute;
width: 196px;
height: 276px;
padding: 4px 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.15);
cursor: auto;
transform: translateX(calc(-50% + 12px));
.history-list
height: 268px;
padding: 0;
overflow: hidden scroll;
list-style: none;
.history-item
height: 24px;
font-size: 11px;
line-height: 24px;
.{prefix}-history-item
position: relative;
height: 24px;
cursor: pointer;
svg
width: 24px;
height: 24px;
span
display: inline-block;
width: 128px;
height: 24px;
text-align: left;
.history-item-icon
display: inline-block;
width: 24px;
height: 24px;
position: absolute;
top: 6px;
left: 6px;
.history-item-checkbox
display: none;
width: 24px;
height: 24px;
position: absolute;
top: 5px;
right: -6px;
&.selected-item
background-color: rgba(119, 119, 119, 0.12);
.history-item-checkbox
display: inline-block;
&.disabled-item
color: #333;
opacity: 0.3;
.opened .tie-panel-history
display: block;
&:before
content: '';
position: absolute;
display: inline-block;
margin: 0 auto;
width: 0;
height: 0;
@@ -0,0 +1,29 @@
import '@/polyfill';
import ImageEditor from '@/imageEditor';
import '@css/index.styl';
// commands
import '@/command/addIcon';
import '@/command/addImageObject';
import '@/command/addObject';
import '@/command/addShape';
import '@/command/addText';
import '@/command/applyFilter';
import '@/command/changeIconColor';
import '@/command/changeShape';
import '@/command/changeText';
import '@/command/changeTextStyle';
import '@/command/clearObjects';
import '@/command/flip';
import '@/command/loadImage';
import '@/command/removeFilter';
import '@/command/removeObject';
import '@/command/resizeCanvasDimension';
import '@/command/rotate';
import '@/command/setObjectProperties';
import '@/command/setObjectPosition';
import '@/command/changeSelection';
import '@/command/resize';
export default ImageEditor;
export { ImageEditor };
@@ -0,0 +1,718 @@
import extend from 'tui-code-snippet/object/extend';
import Imagetracer from '@/helper/imagetracer';
import { isSupportFileApi, base64ToBlob, toInteger, isEmptyCropzone, includes } from '@/util';
import { eventNames, historyNames, drawingModes, drawingMenuNames, zoomModes } from '@/consts';
export default {
/**
* Get ui actions
* @returns {Object} actions for ui
* @private
*/
getActions() {
return {
main: this._mainAction(),
shape: this._shapeAction(),
crop: this._cropAction(),
resize: this._resizeAction(),
flip: this._flipAction(),
rotate: this._rotateAction(),
text: this._textAction(),
mask: this._maskAction(),
draw: this._drawAction(),
icon: this._iconAction(),
filter: this._filterAction(),
history: this._historyAction(),
};
},
/**
* Main Action
* @returns {Object} actions for ui main
* @private
*/
_mainAction() {
const exitCropOnAction = () => {
if (this.ui.submenu === 'crop') {
this.stopDrawingMode();
this.ui.changeMenu('crop');
}
};
const setAngleRangeBarOnAction = (angle) => {
if (this.ui.submenu === 'rotate') {
this.ui.rotate.setRangeBarAngle('setAngle', angle);
}
};
const setFilterStateRangeBarOnAction = (filterOptions) => {
if (this.ui.submenu === 'filter') {
this.ui.filter.setFilterState(filterOptions);
}
};
const onEndUndoRedo = (result) => {
setAngleRangeBarOnAction(result);
setFilterStateRangeBarOnAction(result);
return result;
};
const toggleZoomMode = () => {
const zoomMode = this._graphics.getZoomMode();
this.stopDrawingMode();
if (zoomMode !== zoomModes.ZOOM) {
this.startDrawingMode(drawingModes.ZOOM);
this._graphics.startZoomInMode();
} else {
this._graphics.endZoomInMode();
}
};
const toggleHandMode = () => {
const zoomMode = this._graphics.getZoomMode();
this.stopDrawingMode();
if (zoomMode !== zoomModes.HAND) {
this.startDrawingMode(drawingModes.ZOOM);
this._graphics.startHandMode();
} else {
this._graphics.endHandMode();
}
};
const initFilterState = () => {
if (this.ui.filter) {
this.ui.filter.initFilterCheckBoxState();
}
};
return extend(
{
initLoadImage: (imagePath, imageName) =>
this.loadImageFromURL(imagePath, imageName).then((sizeValue) => {
exitCropOnAction();
this.ui.initializeImgUrl = imagePath;
this.ui.resizeEditor({ imageSize: sizeValue });
this.clearUndoStack();
this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.LOAD_IMAGE);
}),
undo: () => {
if (!this.isEmptyUndoStack()) {
exitCropOnAction();
this.deactivateAll();
this.undo().then(onEndUndoRedo);
}
},
redo: () => {
if (!this.isEmptyRedoStack()) {
exitCropOnAction();
this.deactivateAll();
this.redo().then(onEndUndoRedo);
}
},
reset: () => {
exitCropOnAction();
this.loadImageFromURL(this.ui.initializeImgUrl, 'resetImage').then((sizeValue) => {
exitCropOnAction();
initFilterState();
this.ui.resizeEditor({ imageSize: sizeValue });
this.clearUndoStack();
this._initHistory();
});
},
delete: () => {
this.ui.changeHelpButtonEnabled('delete', false);
exitCropOnAction();
this.removeActiveObject();
this.activeObjectId = null;
},
deleteAll: () => {
exitCropOnAction();
this.clearObjects();
this.ui.changeHelpButtonEnabled('delete', false);
this.ui.changeHelpButtonEnabled('deleteAll', false);
},
load: (file) => {
if (!isSupportFileApi()) {
alert('This browser does not support file-api');
}
this.ui.initializeImgUrl = URL.createObjectURL(file);
this.loadImageFromFile(file)
.then((sizeValue) => {
exitCropOnAction();
initFilterState();
this.clearUndoStack();
this.ui.activeMenuEvent();
this.ui.resizeEditor({ imageSize: sizeValue });
this._clearHistory();
this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.LOAD_IMAGE);
})
['catch']((message) => Promise.reject(message));
},
download: () => {
const dataURL = this.toDataURL();
let imageName = this.getImageName();
let blob, type, w;
if (isSupportFileApi() && window.saveAs) {
blob = 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 = `<img src='${dataURL}'>`;
}
},
history: (event) => {
this.ui.toggleHistoryMenu(event);
},
zoomIn: () => {
this.ui.toggleZoomButtonStatus('zoomIn');
this.deactivateAll();
toggleZoomMode();
},
zoomOut: () => {
this._graphics.zoomOut();
},
hand: () => {
this.ui.offZoomInButtonStatus();
this.ui.toggleZoomButtonStatus('hand');
this.deactivateAll();
toggleHandMode();
},
},
this._commonAction()
);
},
/**
* Icon Action
* @returns {Object} actions for ui icon
* @private
*/
_iconAction() {
return extend(
{
changeColor: (color) => {
if (this.activeObjectId) {
this.changeIconColor(this.activeObjectId, color);
}
},
addIcon: (iconType, iconColor) => {
this.startDrawingMode('ICON');
this.setDrawingIcon(iconType, iconColor);
},
cancelAddIcon: () => {
this.ui.icon.clearIconType();
this.changeSelectableAll(true);
this.changeCursor('default');
this.stopDrawingMode();
},
registerDefaultIcons: (type, path) => {
const iconObj = {};
iconObj[type] = path;
this.registerIcons(iconObj);
},
registerCustomIcon: (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) => {
return this.loadImageFromURL(this.toDataURL(), 'FilterImage').then(() => {
this.addImageObject(imgUrl).then(() => {
URL.revokeObjectURL(file);
});
this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.LOAD_MASK_IMAGE);
});
},
applyFilter: () => {
this.applyFilter('mask', {
maskObjId: this.activeObjectId,
});
},
},
this._commonAction()
);
},
/**
* Text Action
* @returns {Object} actions for ui text
* @private
*/
_textAction() {
return extend(
{
changeTextStyle: (styleObj, isSilent) => {
if (this.activeObjectId) {
this.changeTextStyle(this.activeObjectId, styleObj, isSilent);
}
},
},
this._commonAction()
);
},
/**
* Rotate Action
* @returns {Object} actions for ui rotate
* @private
*/
_rotateAction() {
return extend(
{
rotate: (angle, isSilent) => {
this.rotate(angle, isSilent);
this.ui.resizeEditor();
this.ui.rotate.setRangeBarAngle('rotate', angle);
},
setAngle: (angle, isSilent) => {
this.setAngle(angle, isSilent);
this.ui.resizeEditor();
this.ui.rotate.setRangeBarAngle('setAngle', angle);
},
},
this._commonAction()
);
},
/**
* Shape Action
* @returns {Object} actions for ui shape
* @private
*/
_shapeAction() {
return extend(
{
changeShape: (changeShapeObject, isSilent) => {
if (this.activeObjectId) {
this.changeShape(this.activeObjectId, changeShapeObject, isSilent);
}
},
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 && !isEmptyCropzone(cropRect)) {
this.crop(cropRect)
.then(() => {
this.stopDrawingMode();
this.ui.resizeEditor();
this.ui.changeMenu('crop');
this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.CROP);
})
['catch']((message) => Promise.reject(message));
}
},
cancel: () => {
this.stopDrawingMode();
this.ui.changeMenu('crop');
},
/* eslint-disable */
preset: (presetType) => {
switch (presetType) {
case 'preset-square':
this.setCropzoneRect(1 / 1);
break;
case 'preset-3-2':
this.setCropzoneRect(3 / 2);
break;
case 'preset-4-3':
this.setCropzoneRect(4 / 3);
break;
case 'preset-5-4':
this.setCropzoneRect(5 / 4);
break;
case 'preset-7-5':
this.setCropzoneRect(7 / 5);
break;
case 'preset-16-9':
this.setCropzoneRect(16 / 9);
break;
default:
this.setCropzoneRect();
this.ui.crop.changeApplyButtonStatus(false);
break;
}
},
},
this._commonAction()
);
},
/**
* Resize Action
* @returns {Object} actions for ui resize
* @private
*/
_resizeAction() {
return extend(
{
getCurrentDimensions: () => this._graphics.getCurrentDimensions(),
preview: (actor, value, lockState) => {
const currentDimensions = this._graphics.getCurrentDimensions();
const calcAspectRatio = () => currentDimensions.width / currentDimensions.height;
let dimensions = {};
switch (actor) {
case 'width':
dimensions.width = value;
if (lockState) {
dimensions.height = value / calcAspectRatio();
} else {
dimensions.height = currentDimensions.height;
}
break;
case 'height':
dimensions.height = value;
if (lockState) {
dimensions.width = value * calcAspectRatio();
} else {
dimensions.width = currentDimensions.width;
}
break;
default:
dimensions = currentDimensions;
}
this._graphics.resize(dimensions).then(() => {
this.ui.resizeEditor();
});
if (lockState) {
this.ui.resize.setWidthValue(dimensions.width);
this.ui.resize.setHeightValue(dimensions.height);
}
},
lockAspectRatio: (lockState, min, max) => {
const { width, height } = this._graphics.getCurrentDimensions();
const aspectRatio = width / height;
if (lockState) {
if (width > height) {
const pMax = max / aspectRatio;
const pMin = min * aspectRatio;
this.ui.resize.setLimit({
minWidth: pMin > min ? pMin : min,
minHeight: min,
maxWidth: max,
maxHeight: pMax < max ? pMax : max,
});
} else {
const pMax = max * aspectRatio;
const pMin = min / aspectRatio;
this.ui.resize.setLimit({
minWidth: min,
minHeight: pMin > min ? pMin : min,
maxWidth: pMax < max ? pMax : max,
maxHeight: max,
});
}
} else {
this.ui.resize.setLimit({
minWidth: min,
minHeight: min,
maxWidth: max,
maxHeight: max,
});
}
},
resize: (dimensions = null) => {
if (!dimensions) {
dimensions = this._graphics.getCurrentDimensions();
}
this.resize(dimensions)
.then(() => {
this._graphics.setOriginalDimensions(dimensions);
this.stopDrawingMode();
this.ui.resizeEditor();
this.ui.changeMenu('resize');
})
['catch']((message) => Promise.reject(message));
},
reset: (standByMode = false) => {
const dimensions = this._graphics.getOriginalDimensions();
this.ui.resize.setWidthValue(dimensions.width, true);
this.ui.resize.setHeightValue(dimensions.height, true);
this._graphics.resize(dimensions).then(() => {
if (!standByMode) {
this.stopDrawingMode();
this.ui.resizeEditor();
this.ui.changeMenu('resize');
}
});
},
},
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, isSilent) => {
if (applying) {
this.applyFilter(type, options, isSilent);
} else if (this.hasFilter(type)) {
this.removeFilter(type);
}
},
},
this._commonAction()
);
},
/**
* Image Editor Event Observer
*/
setReAction() {
this.on({
undoStackChanged: (length) => {
if (length) {
this.ui.changeHelpButtonEnabled('undo', true);
this.ui.changeHelpButtonEnabled('reset', true);
} else {
this.ui.changeHelpButtonEnabled('undo', false);
this.ui.changeHelpButtonEnabled('reset', false);
}
this.ui.resizeEditor();
},
redoStackChanged: (length) => {
if (length) {
this.ui.changeHelpButtonEnabled('redo', true);
} else {
this.ui.changeHelpButtonEnabled('redo', false);
}
this.ui.resizeEditor();
},
/* eslint-disable complexity */
objectActivated: (obj) => {
this.activeObjectId = obj.id;
this.ui.changeHelpButtonEnabled('delete', true);
this.ui.changeHelpButtonEnabled('deleteAll', 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);
}
this.ui.text.setTextStyleStateOnAction(obj);
} 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) => {
const { textColor: fill, fontSize, fontStyle, fontWeight, underline } = this.ui.text;
const fontFamily = 'Noto Sans';
this.addText('Double Click', {
position: pos.originPosition,
styles: { fill, fontSize, fontFamily, fontStyle, fontWeight, underline },
}).then(() => {
this.changeCursor('default');
});
},
addObjectAfter: (obj) => {
if (obj.type === 'icon') {
this.ui.icon.changeStandbyMode();
} else 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 = 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 (!includes(['draw', 'crop', 'resize'], this.ui.submenu)) {
this.stopDrawingMode();
}
},
});
},
/**
* History Action
* @returns {Object} history actions for ui
* @private
*/
_historyAction() {
return {
undo: (count) => this.undo(count),
redo: (count) => this.redo(count),
};
},
/**
* Common Action
* @returns {Object} common actions for ui
* @private
*/
_commonAction() {
const { TEXT, CROPPER, SHAPE, ZOOM, RESIZE } = drawingModes;
return {
modeChange: (menu) => {
switch (menu) {
case drawingMenuNames.TEXT:
this._changeActivateMode(TEXT);
break;
case drawingMenuNames.CROP:
this.startDrawingMode(CROPPER);
break;
case drawingMenuNames.SHAPE:
this._changeActivateMode(SHAPE);
this.setDrawingShape(this.ui.shape.type, this.ui.shape.options);
break;
case drawingMenuNames.ZOOM:
this.startDrawingMode(ZOOM);
break;
case drawingMenuNames.RESIZE:
this.startDrawingMode(RESIZE);
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);
},
};
@@ -0,0 +1,42 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { ICON } = componentNames;
const command = {
name: commandNames.ADD_ICON,
/**
* Add an icon
* @param {Graphics} graphics - Graphics instance
* @param {string} type - Icon type ('arrow', 'cancel', custom icon name)
* @param {Object} options - Icon options
* @param {string} [options.fill] - Icon foreground color
* @param {string} [options.left] - Icon x position
* @param {string} [options.top] - Icon y position
* @returns {Promise}
*/
execute(graphics, type, options) {
const iconComp = graphics.getComponent(ICON);
return iconComp.add(type, options).then((objectProps) => {
this.undoData.object = graphics.getObject(objectProps.id);
return objectProps;
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
graphics.remove(this.undoData.object);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,34 @@
import commandFactory from '@/factory/command';
import { commandNames } from '@/consts';
const command = {
name: commandNames.ADD_IMAGE_OBJECT,
/**
* Add an image object
* @param {Graphics} graphics - Graphics instance
* @param {string} imgUrl - Image url to make object
* @returns {Promise}
*/
execute(graphics, imgUrl) {
return graphics.addImageObject(imgUrl).then((objectProps) => {
this.undoData.object = graphics.getObject(objectProps.id);
return objectProps;
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
graphics.remove(this.undoData.object);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,43 @@
import commandFactory from '@/factory/command';
import { commandNames, rejectMessages } from '@/consts';
const command = {
name: commandNames.ADD_OBJECT,
/**
* Add an object
* @param {Graphics} graphics - Graphics instance
* @param {Object} object - Fabric object
* @returns {Promise}
*/
execute(graphics, object) {
return new Promise((resolve, reject) => {
if (!graphics.contains(object)) {
graphics.add(object);
resolve(object);
} else {
reject(rejectMessages.addedObject);
}
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @param {Object} object - Fabric object
* @returns {Promise}
*/
undo(graphics, object) {
return new Promise((resolve, reject) => {
if (graphics.contains(object)) {
graphics.remove(object);
resolve(object);
} else {
reject(rejectMessages.noObject);
}
});
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,51 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { SHAPE } = componentNames;
const command = {
name: commandNames.ADD_SHAPE,
/**
* Add a shape
* @param {Graphics} graphics - Graphics instance
* @param {string} type - Shape type (ex: 'rect', 'circle', 'triangle')
* @param {Object} options - Shape options
* @param {string} [options.fill] - Shape foreground color (ex: '#fff', 'transparent')
* @param {string} [options.stroke] - Shape outline color
* @param {number} [options.strokeWidth] - Shape outline width
* @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
* @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
* @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
* @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
* @param {number} [options.left] - Shape x position
* @param {number} [options.top] - Shape y position
* @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
* @returns {Promise}
*/
execute(graphics, type, options) {
const shapeComp = graphics.getComponent(SHAPE);
return shapeComp.add(type, options).then((objectProps) => {
const { id } = objectProps;
this.undoData.object = graphics.getObject(id);
return objectProps;
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
graphics.remove(this.undoData.object);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,73 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames, rejectMessages } from '@/consts';
import {
setCachedUndoDataForDimension,
makeSelectionUndoData,
makeSelectionUndoDatum,
} from '@/helper/selectionModifyHelper';
const { TEXT } = componentNames;
const command = {
name: commandNames.ADD_TEXT,
/**
* Add a text object
* @param {Graphics} graphics - Graphics instance
* @param {string} text - Initial input text
* @param {Object} [options] Options for text styles
* @param {Object} [options.styles] Initial styles
* @param {string} [options.styles.fill] Color
* @param {string} [options.styles.fontFamily] Font type for text
* @param {number} [options.styles.fontSize] Size
* @param {string} [options.styles.fontStyle] Type of inclination (normal / italic)
* @param {string} [options.styles.fontWeight] Type of thicker or thinner looking (normal / bold)
* @param {string} [options.styles.textAlign] Type of text align (left / center / right)
* @param {string} [options.styles.textDecoration] Type of line (underline / line-through / overline)
* @param {{x: number, y: number}} [options.position] - Initial position
* @returns {Promise}
*/
execute(graphics, text, options) {
const textComp = graphics.getComponent(TEXT);
if (this.undoData.object) {
const undoObject = this.undoData.object;
return new Promise((resolve, reject) => {
if (!graphics.contains(undoObject)) {
graphics.add(undoObject);
resolve(undoObject);
} else {
reject(rejectMessages.redo);
}
});
}
return textComp.add(text, options).then((objectProps) => {
const { id } = objectProps;
const textObject = graphics.getObject(id);
this.undoData.object = textObject;
setCachedUndoDataForDimension(
makeSelectionUndoData(textObject, () => makeSelectionUndoDatum(id, textObject, false))
);
return objectProps;
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
graphics.remove(this.undoData.object);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,95 @@
import extend from 'tui-code-snippet/object/extend';
import commandFactory from '@/factory/command';
import { componentNames, rejectMessages, commandNames } from '@/consts';
const { FILTER } = componentNames;
/**
* Cached data for undo
* @type {Object}
*/
let cachedUndoDataForSilent = null;
/**
* Make undoData
* @param {string} type - Filter type
* @param {Object} prevfilterOption - prev Filter options
* @param {Object} options - Filter options
* @returns {object} - undo data
*/
function makeUndoData(type, prevfilterOption, options) {
const undoData = {};
if (type === 'mask') {
undoData.object = options.mask;
}
undoData.options = prevfilterOption;
return undoData;
}
const command = {
name: commandNames.APPLY_FILTER,
/**
* Apply a filter into an image
* @param {Graphics} graphics - Graphics instance
* @param {string} type - Filter type
* @param {Object} options - Filter options
* @param {number} options.maskObjId - masking image object id
* @param {boolean} isSilent - is silent execution or not
* @returns {Promise}
*/
execute(graphics, type, options, isSilent) {
const filterComp = graphics.getComponent(FILTER);
if (type === 'mask') {
const maskObj = graphics.getObject(options.maskObjId);
if (!(maskObj && maskObj.isType('image'))) {
return Promise.reject(rejectMessages.invalidParameters);
}
extend(options, { mask: maskObj });
graphics.remove(options.mask);
}
if (!this.isRedo) {
const prevfilterOption = filterComp.getOptions(type);
const undoData = makeUndoData(type, prevfilterOption, options);
cachedUndoDataForSilent = this.setUndoData(undoData, cachedUndoDataForSilent, isSilent);
}
return filterComp.add(type, options);
},
/**
* @param {Graphics} graphics - Graphics instance
* @param {string} type - Filter type
* @returns {Promise}
*/
undo(graphics, type) {
const filterComp = graphics.getComponent(FILTER);
if (type === 'mask') {
const mask = this.undoData.object;
graphics.add(mask);
graphics.setActiveObject(mask);
return filterComp.remove(type);
}
// options changed case
if (this.undoData.options) {
return filterComp.add(type, this.undoData.options);
}
// filter added case
return filterComp.remove(type);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,48 @@
import commandFactory from '@/factory/command';
import { componentNames, rejectMessages, commandNames } from '@/consts';
const { ICON } = componentNames;
const command = {
name: commandNames.CHANGE_ICON_COLOR,
/**
* Change icon color
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @param {string} color - Color for icon
* @returns {Promise}
*/
execute(graphics, id, color) {
return new Promise((resolve, reject) => {
const iconComp = graphics.getComponent(ICON);
const targetObj = graphics.getObject(id);
if (!targetObj) {
reject(rejectMessages.noObject);
}
this.undoData.object = targetObj;
this.undoData.color = iconComp.getColor(targetObj);
iconComp.setColor(color, targetObj);
resolve();
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const iconComp = graphics.getComponent(ICON);
const { object: icon, color } = this.undoData;
iconComp.setColor(color, icon);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,31 @@
import commandFactory from '@/factory/command';
import { commandNames } from '@/consts';
import { getCachedUndoDataForDimension } from '@/helper/selectionModifyHelper';
const command = {
name: commandNames.CHANGE_SELECTION,
execute(graphics, props) {
if (this.isRedo) {
props.forEach((prop) => {
graphics.setObjectProperties(prop.id, prop);
});
} else {
this.undoData = getCachedUndoDataForDimension();
}
return Promise.resolve();
},
undo(graphics) {
this.undoData.forEach((datum) => {
graphics.setObjectProperties(datum.id, datum);
});
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,84 @@
import forEachOwnProperties from 'tui-code-snippet/collection/forEachOwnProperties';
import commandFactory from '@/factory/command';
import { componentNames, rejectMessages, commandNames } from '@/consts';
const { SHAPE } = componentNames;
/**
* Cached data for undo
* @type {Object}
*/
let cachedUndoDataForSilent = null;
/**
* Make undoData
* @param {object} options - shape options
* @param {Component} targetObj - shape component
* @returns {object} - undo data
*/
function makeUndoData(options, targetObj) {
const undoData = {
object: targetObj,
options: {},
};
forEachOwnProperties(options, (value, key) => {
undoData.options[key] = targetObj[key];
});
return undoData;
}
const command = {
name: commandNames.CHANGE_SHAPE,
/**
* Change a shape
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @param {Object} options - Shape options
* @param {string} [options.fill] - Shape foreground color (ex: '#fff', 'transparent')
* @param {string} [options.stroke] - Shape outline color
* @param {number} [options.strokeWidth] - Shape outline width
* @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
* @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
* @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
* @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
* @param {number} [options.left] - Shape x position
* @param {number} [options.top] - Shape y position
* @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not
* @param {boolean} isSilent - is silent execution or not
* @returns {Promise}
*/
execute(graphics, id, options, isSilent) {
const shapeComp = graphics.getComponent(SHAPE);
const targetObj = graphics.getObject(id);
if (!targetObj) {
return Promise.reject(rejectMessages.noObject);
}
if (!this.isRedo) {
const undoData = makeUndoData(options, targetObj);
cachedUndoDataForSilent = this.setUndoData(undoData, cachedUndoDataForSilent, isSilent);
}
return shapeComp.change(targetObj, options);
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const shapeComp = graphics.getComponent(SHAPE);
const { object: shape, options } = this.undoData;
return shapeComp.change(shape, options);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,44 @@
import commandFactory from '@/factory/command';
import { componentNames, rejectMessages, commandNames } from '@/consts';
const { TEXT } = componentNames;
const command = {
name: commandNames.CHANGE_TEXT,
/**
* Change a text
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @param {string} text - Changing text
* @returns {Promise}
*/
execute(graphics, id, text) {
const textComp = graphics.getComponent(TEXT);
const targetObj = graphics.getObject(id);
if (!targetObj) {
return Promise.reject(rejectMessages.noObject);
}
this.undoData.object = targetObj;
this.undoData.text = textComp.getText(targetObj);
return textComp.change(targetObj, text);
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const textComp = graphics.getComponent(TEXT);
const { object: textObj, text } = this.undoData;
return textComp.change(textObj, text);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,80 @@
import forEachOwnProperties from 'tui-code-snippet/collection/forEachOwnProperties';
import commandFactory from '@/factory/command';
import { componentNames, rejectMessages, commandNames } from '@/consts';
const { TEXT } = componentNames;
/**
* Cached data for undo
* @type {Object}
*/
let cachedUndoDataForSilent = null;
/**
* Make undoData
* @param {object} styles - text styles
* @param {Component} targetObj - text component
* @returns {object} - undo data
*/
function makeUndoData(styles, targetObj) {
const undoData = {
object: targetObj,
styles: {},
};
forEachOwnProperties(styles, (value, key) => {
const undoValue = targetObj[key];
undoData.styles[key] = undoValue;
});
return undoData;
}
const command = {
name: commandNames.CHANGE_TEXT_STYLE,
/**
* Change text styles
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @param {Object} styles - text styles
* @param {string} [styles.fill] Color
* @param {string} [styles.fontFamily] Font type for text
* @param {number} [styles.fontSize] Size
* @param {string} [styles.fontStyle] Type of inclination (normal / italic)
* @param {string} [styles.fontWeight] Type of thicker or thinner looking (normal / bold)
* @param {string} [styles.textAlign] Type of text align (left / center / right)
* @param {string} [styles.textDecoration] Type of line (underline / line-through / overline)
* @param {boolean} isSilent - is silent execution or not
* @returns {Promise}
*/
execute(graphics, id, styles, isSilent) {
const textComp = graphics.getComponent(TEXT);
const targetObj = graphics.getObject(id);
if (!targetObj) {
return Promise.reject(rejectMessages.noObject);
}
if (!this.isRedo) {
const undoData = makeUndoData(styles, targetObj);
cachedUndoDataForSilent = this.setUndoData(undoData, cachedUndoDataForSilent, isSilent);
}
return textComp.setStyle(targetObj, styles);
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const textComp = graphics.getComponent(TEXT);
const { object: textObj, styles } = this.undoData;
return textComp.setStyle(textObj, styles);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,33 @@
import commandFactory from '@/factory/command';
import { commandNames } from '@/consts';
const command = {
name: commandNames.CLEAR_OBJECTS,
/**
* Clear all objects without background (main) image
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
execute(graphics) {
return new Promise((resolve) => {
this.undoData.objects = graphics.removeAll();
resolve();
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
* @ignore
*/
undo(graphics) {
graphics.add(this.undoData.objects);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,36 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { FLIP } = componentNames;
const command = {
name: commandNames.FLIP_IMAGE,
/**
* flip an image
* @param {Graphics} graphics - Graphics instance
* @param {string} type - 'flipX' or 'flipY' or 'reset'
* @returns {Promise}
*/
execute(graphics, type) {
const flipComp = graphics.getComponent(FLIP);
this.undoData.setting = flipComp.getCurrentSetting();
return flipComp[type]();
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const flipComp = graphics.getComponent(FLIP);
return flipComp.set(this.undoData.setting);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,58 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { IMAGE_LOADER } = componentNames;
const command = {
name: commandNames.LOAD_IMAGE,
/**
* Load a background (main) image
* @param {Graphics} graphics - Graphics instance
* @param {string} imageName - Image name
* @param {string} imgUrl - Image Url
* @returns {Promise}
*/
execute(graphics, imageName, imgUrl) {
const loader = graphics.getComponent(IMAGE_LOADER);
const prevImage = loader.getCanvasImage();
const prevImageWidth = prevImage ? prevImage.width : 0;
const prevImageHeight = prevImage ? prevImage.height : 0;
const objects = graphics.removeAll(true).filter((objectItem) => objectItem.type !== 'cropzone');
objects.forEach((objectItem) => {
objectItem.evented = true;
});
this.undoData = {
name: loader.getImageName(),
image: prevImage,
objects,
};
return loader.load(imageName, imgUrl).then((newImage) => ({
oldWidth: prevImageWidth,
oldHeight: prevImageHeight,
newWidth: newImage.width,
newHeight: newImage.height,
}));
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const loader = graphics.getComponent(IMAGE_LOADER);
const { objects, name, image } = this.undoData;
graphics.removeAll(true);
graphics.add(objects);
return loader.load(name, image);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,38 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { FILTER } = componentNames;
const command = {
name: commandNames.REMOVE_FILTER,
/**
* Remove a filter from an image
* @param {Graphics} graphics - Graphics instance
* @param {string} type - Filter type
* @returns {Promise}
*/
execute(graphics, type) {
const filterComp = graphics.getComponent(FILTER);
this.undoData.options = filterComp.getOptions(type);
return filterComp.remove(type);
},
/**
* @param {Graphics} graphics - Graphics instance
* @param {string} type - Filter type
* @returns {Promise}
*/
undo(graphics, type) {
const filterComp = graphics.getComponent(FILTER);
const { options } = this.undoData;
return filterComp.add(type, options);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,37 @@
import commandFactory from '@/factory/command';
import { commandNames, rejectMessages } from '@/consts';
const command = {
name: commandNames.REMOVE_OBJECT,
/**
* Remove an object
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @returns {Promise}
*/
execute(graphics, id) {
return new Promise((resolve, reject) => {
this.undoData.objects = graphics.removeObjectById(id);
if (this.undoData.objects.length) {
resolve();
} else {
reject(rejectMessages.noObject);
}
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
graphics.add(this.undoData.objects);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,41 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { RESIZE } = componentNames;
const command = {
name: commandNames.RESIZE_IMAGE,
/**
* Resize an image
* @param {Graphics} graphics - Graphics instance
* @param {object} dimensions - Image Dimensions
* @returns {Promise}
*/
execute(graphics, dimensions) {
const resizeComp = graphics.getComponent(RESIZE);
let originalDimensions = resizeComp.getOriginalDimensions();
if (!originalDimensions) {
originalDimensions = resizeComp.getCurrentDimensions();
}
this.undoData.dimensions = originalDimensions;
return resizeComp.resize(dimensions);
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const resizeComp = graphics.getComponent(RESIZE);
return resizeComp.resize(this.undoData.dimensions);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,40 @@
import commandFactory from '@/factory/command';
import { commandNames } from '@/consts';
const command = {
name: commandNames.RESIZE_CANVAS_DIMENSION,
/**
* resize the canvas with given dimension
* @param {Graphics} graphics - Graphics instance
* @param {{width: number, height: number}} dimension - Max width & height
* @returns {Promise}
*/
execute(graphics, dimension) {
return new Promise((resolve) => {
this.undoData.size = {
width: graphics.cssMaxWidth,
height: graphics.cssMaxHeight,
};
graphics.setCssMaxDimension(dimension);
graphics.adjustCanvasDimension();
resolve();
});
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
graphics.setCssMaxDimension(this.undoData.size);
graphics.adjustCanvasDimension();
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,64 @@
import commandFactory from '@/factory/command';
import { componentNames, commandNames } from '@/consts';
const { ROTATION } = componentNames;
/**
* Cached data for undo
* @type {Object}
*/
let cachedUndoDataForSilent = null;
/**
* Make undo data
* @param {Component} rotationComp - rotation component
* @returns {object} - undodata
*/
function makeUndoData(rotationComp) {
return {
angle: rotationComp.getCurrentAngle(),
};
}
const command = {
name: commandNames.ROTATE_IMAGE,
/**
* Rotate an image
* @param {Graphics} graphics - Graphics instance
* @param {string} type - 'rotate' or 'setAngle'
* @param {number} angle - angle value (degree)
* @param {boolean} isSilent - is silent execution or not
* @returns {Promise}
*/
execute(graphics, type, angle, isSilent) {
const rotationComp = graphics.getComponent(ROTATION);
if (!this.isRedo) {
const undoData = makeUndoData(rotationComp);
cachedUndoDataForSilent = this.setUndoData(undoData, cachedUndoDataForSilent, isSilent);
}
return rotationComp[type](angle);
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const rotationComp = graphics.getComponent(ROTATION);
const [, type, angle] = this.args;
if (type === 'setAngle') {
return rotationComp[type](this.undoData.angle);
}
return rotationComp.rotate(-angle);
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,50 @@
import commandFactory from '@/factory/command';
import { commandNames, rejectMessages } from '@/consts';
const command = {
name: commandNames.SET_OBJECT_POSITION,
/**
* Set object properties
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @param {Object} posInfo - position object
* @param {number} posInfo.x - x position
* @param {number} posInfo.y - y position
* @param {string} posInfo.originX - can be 'left', 'center', 'right'
* @param {string} posInfo.originY - can be 'top', 'center', 'bottom'
* @returns {Promise}
*/
execute(graphics, id, posInfo) {
const targetObj = graphics.getObject(id);
if (!targetObj) {
return Promise.reject(rejectMessages.noObject);
}
this.undoData.objectId = id;
this.undoData.props = graphics.getObjectProperties(id, ['left', 'top']);
graphics.setObjectPosition(id, posInfo);
graphics.renderAll();
return Promise.resolve();
},
/**
* @param {Graphics} graphics - Graphics instance
* @returns {Promise}
*/
undo(graphics) {
const { objectId, props } = this.undoData;
graphics.setObjectProperties(objectId, props);
graphics.renderAll();
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,55 @@
import forEachOwnProperties from 'tui-code-snippet/collection/forEachOwnProperties';
import commandFactory from '@/factory/command';
import { commandNames, rejectMessages } from '@/consts';
const command = {
name: commandNames.SET_OBJECT_PROPERTIES,
/**
* Set object properties
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @param {Object} props - properties
* @param {string} [props.fill] Color
* @param {string} [props.fontFamily] Font type for text
* @param {number} [props.fontSize] Size
* @param {string} [props.fontStyle] Type of inclination (normal / italic)
* @param {string} [props.fontWeight] Type of thicker or thinner looking (normal / bold)
* @param {string} [props.textAlign] Type of text align (left / center / right)
* @param {string} [props.textDecoration] Type of line (underline / line-through / overline)
* @returns {Promise}
*/
execute(graphics, id, props) {
const targetObj = graphics.getObject(id);
if (!targetObj) {
return Promise.reject(rejectMessages.noObject);
}
this.undoData.props = {};
forEachOwnProperties(props, (value, key) => {
this.undoData.props[key] = targetObj[key];
});
graphics.setObjectProperties(id, props);
return Promise.resolve();
},
/**
* @param {Graphics} graphics - Graphics instance
* @param {number} id - object id
* @returns {Promise}
*/
undo(graphics, id) {
const { props } = this.undoData;
graphics.setObjectProperties(id, props);
return Promise.resolve();
},
};
commandFactory.register(command);
export default command;
@@ -0,0 +1,407 @@
import { fabric } from 'fabric';
import extend from 'tui-code-snippet/object/extend';
import Component from '@/interface/component';
import Cropzone from '@/extension/cropzone';
import { keyCodes, componentNames, CROPZONE_DEFAULT_OPTIONS } from '@/consts';
import { clamp, fixFloatingPoint } from '@/util';
const MOUSE_MOVE_THRESHOLD = 10;
const DEFAULT_OPTION = {
presetRatio: null,
top: -10,
left: -10,
height: 1,
width: 1,
};
/**
* Cropper components
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @class Cropper
* @ignore
*/
class Cropper extends Component {
constructor(graphics) {
super(componentNames.CROPPER, graphics);
/**
* Cropzone
* @type {Cropzone}
* @private
*/
this._cropzone = null;
/**
* StartX of Cropzone
* @type {number}
* @private
*/
this._startX = null;
/**
* StartY of Cropzone
* @type {number}
* @private
*/
this._startY = null;
/**
* State whether shortcut key is pressed or not
* @type {boolean}
* @private
*/
this._withShiftKey = false;
/**
* Listeners
* @type {object.<string, function>}
* @private
*/
this._listeners = {
keydown: this._onKeyDown.bind(this),
keyup: this._onKeyUp.bind(this),
mousedown: this._onFabricMouseDown.bind(this),
mousemove: this._onFabricMouseMove.bind(this),
mouseup: this._onFabricMouseUp.bind(this),
};
}
/**
* Start cropping
*/
start() {
if (this._cropzone) {
return;
}
const canvas = this.getCanvas();
canvas.forEachObject((obj) => {
// {@link http://fabricjs.com/docs/fabric.Object.html#evented}
obj.evented = false;
});
this._cropzone = new Cropzone(
canvas,
extend(
{
left: 0,
top: 0,
width: 0.5,
height: 0.5,
strokeWidth: 0, // {@link https://github.com/kangax/fabric.js/issues/2860}
cornerSize: 10,
cornerColor: 'black',
fill: 'transparent',
},
CROPZONE_DEFAULT_OPTIONS,
this.graphics.cropSelectionStyle
)
);
canvas.discardActiveObject();
canvas.add(this._cropzone);
canvas.on('mouse:down', this._listeners.mousedown);
canvas.selection = false;
canvas.defaultCursor = 'crosshair';
fabric.util.addListener(document, 'keydown', this._listeners.keydown);
fabric.util.addListener(document, 'keyup', this._listeners.keyup);
}
/**
* End cropping
*/
end() {
const canvas = this.getCanvas();
const cropzone = this._cropzone;
if (!cropzone) {
return;
}
canvas.remove(cropzone);
canvas.selection = true;
canvas.defaultCursor = 'default';
canvas.off('mouse:down', this._listeners.mousedown);
canvas.forEachObject((obj) => {
obj.evented = true;
});
this._cropzone = null;
fabric.util.removeListener(document, 'keydown', this._listeners.keydown);
fabric.util.removeListener(document, 'keyup', this._listeners.keyup);
}
/**
* Change cropzone visible
* @param {boolean} visible - cropzone visible state
*/
changeVisibility(visible) {
if (this._cropzone) {
this._cropzone.set({ visible });
}
}
/**
* onMousedown handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onFabricMouseDown(fEvent) {
const canvas = this.getCanvas();
if (fEvent.target) {
return;
}
canvas.selection = false;
const coord = canvas.getPointer(fEvent.e);
this._startX = coord.x;
this._startY = coord.y;
canvas.on({
'mouse:move': this._listeners.mousemove,
'mouse:up': this._listeners.mouseup,
});
}
/**
* onMousemove handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onFabricMouseMove(fEvent) {
const canvas = this.getCanvas();
const pointer = canvas.getPointer(fEvent.e);
const { x, y } = pointer;
const cropzone = this._cropzone;
if (Math.abs(x - this._startX) + Math.abs(y - this._startY) > MOUSE_MOVE_THRESHOLD) {
canvas.remove(cropzone);
cropzone.set(this._calcRectDimensionFromPoint(x, y, cropzone.presetRatio));
canvas.add(cropzone);
canvas.setActiveObject(cropzone);
}
}
/**
* Get rect dimension setting from Canvas-Mouse-Position(x, y)
* @param {number} x - Canvas-Mouse-Position x
* @param {number} y - Canvas-Mouse-Position Y
* @param {number|null} presetRatio - fixed aspect ratio (width/height) of the cropzone (null if not set)
* @returns {{left: number, top: number, width: number, height: number}}
* @private
*/
_calcRectDimensionFromPoint(x, y, presetRatio = null) {
const canvas = this.getCanvas();
const canvasWidth = canvas.getWidth();
const canvasHeight = canvas.getHeight();
const startX = this._startX;
const startY = this._startY;
let left = clamp(x, 0, startX);
let top = clamp(y, 0, startY);
let width = clamp(x, startX, canvasWidth) - left; // (startX <= x(mouse) <= canvasWidth) - left
let height = clamp(y, startY, canvasHeight) - top; // (startY <= y(mouse) <= canvasHeight) - top
if (this._withShiftKey && !presetRatio) {
// make fixed ratio cropzone
if (width > height) {
height = width;
} else if (height > width) {
width = height;
}
if (startX >= x) {
left = startX - width;
}
if (startY >= y) {
top = startY - height;
}
} else if (presetRatio) {
// Restrict cropzone to given presetRatio
height = width / presetRatio;
// If moving in a direction where the top left corner moves (ie. top-left, bottom-left, top-right)
// the left and/or top values has to be changed based on the new height/width
if (startX >= x) {
left = clamp(startX - width, 0, canvasWidth);
}
if (startY >= y) {
top = clamp(startY - height, 0, canvasHeight);
}
// Check if the new height is too large
if (top + height > canvasHeight) {
height = canvasHeight - top; // Set height to max available height
width = height * presetRatio; // Restrict cropzone to given presetRatio based on the new height
// If moving in a direction where the top left corner moves (ie. top-left, bottom-left, top-right)
// the left and/or top values has to be changed based on the new height/width
if (startX >= x) {
left = clamp(startX - width, 0, canvasWidth);
}
if (startY >= y) {
top = clamp(startY - height, 0, canvasHeight);
}
}
}
return {
left,
top,
width,
height,
};
}
/**
* onMouseup handler in fabric canvas
* @private
*/
_onFabricMouseUp() {
const cropzone = this._cropzone;
const listeners = this._listeners;
const canvas = this.getCanvas();
canvas.setActiveObject(cropzone);
canvas.off({
'mouse:move': listeners.mousemove,
'mouse:up': listeners.mouseup,
});
}
/**
* Get cropped image data
* @param {Object} cropRect cropzone rect
* @param {Number} cropRect.left left position
* @param {Number} cropRect.top top position
* @param {Number} cropRect.width width
* @param {Number} cropRect.height height
* @returns {?{imageName: string, url: string}} cropped Image data
*/
getCroppedImageData(cropRect) {
const canvas = this.getCanvas();
const containsCropzone = canvas.contains(this._cropzone);
if (!cropRect) {
return null;
}
if (containsCropzone) {
canvas.remove(this._cropzone);
}
const imageData = {
imageName: this.getImageName(),
url: canvas.toDataURL(cropRect),
};
if (containsCropzone) {
canvas.add(this._cropzone);
}
return imageData;
}
/**
* Get cropped rect
* @returns {Object} rect
*/
getCropzoneRect() {
const cropzone = this._cropzone;
if (!cropzone.isValid()) {
return null;
}
return {
left: cropzone.left,
top: cropzone.top,
width: cropzone.width,
height: cropzone.height,
};
}
/**
* Set a cropzone square
* @param {number} [presetRatio] - preset ratio
*/
setCropzoneRect(presetRatio) {
const canvas = this.getCanvas();
const cropzone = this._cropzone;
canvas.discardActiveObject();
canvas.selection = false;
canvas.remove(cropzone);
cropzone.set(presetRatio ? this._getPresetPropertiesForCropSize(presetRatio) : DEFAULT_OPTION);
canvas.add(cropzone);
canvas.selection = true;
if (presetRatio) {
canvas.setActiveObject(cropzone);
}
}
/**
* get a cropzone square info
* @param {number} presetRatio - preset ratio
* @returns {{presetRatio: number, left: number, top: number, width: number, height: number}}
* @private
*/
_getPresetPropertiesForCropSize(presetRatio) {
const canvas = this.getCanvas();
const originalWidth = canvas.getWidth();
const originalHeight = canvas.getHeight();
const standardSize = originalWidth >= originalHeight ? originalWidth : originalHeight;
const getScale = (value, orignalValue) => (value > orignalValue ? orignalValue / value : 1);
let width = standardSize * presetRatio;
let height = standardSize;
const scaleWidth = getScale(width, originalWidth);
[width, height] = [width, height].map((sizeValue) => sizeValue * scaleWidth);
const scaleHeight = getScale(height, originalHeight);
[width, height] = [width, height].map((sizeValue) => fixFloatingPoint(sizeValue * scaleHeight));
return {
presetRatio,
top: (originalHeight - height) / 2,
left: (originalWidth - width) / 2,
width,
height,
};
}
/**
* Keydown event handler
* @param {KeyboardEvent} e - Event object
* @private
*/
_onKeyDown(e) {
if (e.keyCode === keyCodes.SHIFT) {
this._withShiftKey = true;
}
}
/**
* Keyup event handler
* @param {KeyboardEvent} e - Event object
* @private
*/
_onKeyUp(e) {
if (e.keyCode === keyCodes.SHIFT) {
this._withShiftKey = false;
}
}
}
export default Cropper;
@@ -0,0 +1,229 @@
import isUndefined from 'tui-code-snippet/type/isUndefined';
import extend from 'tui-code-snippet/object/extend';
import forEach from 'tui-code-snippet/collection/forEach';
import { fabric } from 'fabric';
import Component from '@/interface/component';
import { rejectMessages, componentNames } from '@/consts';
import Mask from '@/extension/mask';
import Sharpen from '@/extension/sharpen';
import Emboss from '@/extension/emboss';
import ColorFilter from '@/extension/colorFilter';
const { filters } = fabric.Image;
filters.Mask = Mask;
filters.Sharpen = Sharpen;
filters.Emboss = Emboss;
filters.ColorFilter = ColorFilter;
/**
* Filter
* @class Filter
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
class Filter extends Component {
constructor(graphics) {
super(componentNames.FILTER, graphics);
}
/**
* Add filter to source image (a specific filter is added on fabric.js)
* @param {string} type - Filter type
* @param {Object} [options] - Options of filter
* @returns {Promise}
*/
add(type, options) {
return new Promise((resolve, reject) => {
const sourceImg = this._getSourceImage();
const canvas = this.getCanvas();
let imgFilter = this._getFilter(sourceImg, type);
if (!imgFilter) {
imgFilter = this._createFilter(sourceImg, type, options);
}
if (!imgFilter) {
reject(rejectMessages.invalidParameters);
}
this._changeFilterValues(imgFilter, options);
this._apply(sourceImg, () => {
canvas.renderAll();
resolve({
type,
action: 'add',
options,
});
});
});
}
/**
* Remove filter to source image
* @param {string} type - Filter type
* @returns {Promise}
*/
remove(type) {
return new Promise((resolve, reject) => {
const sourceImg = this._getSourceImage();
const canvas = this.getCanvas();
const options = this.getOptions(type);
if (!sourceImg.filters.length) {
reject(rejectMessages.unsupportedOperation);
}
this._removeFilter(sourceImg, type);
this._apply(sourceImg, () => {
canvas.renderAll();
resolve({
type,
action: 'remove',
options,
});
});
});
}
/**
* Whether this has the filter or not
* @param {string} type - Filter type
* @returns {boolean} true if it has the filter
*/
hasFilter(type) {
return !!this._getFilter(this._getSourceImage(), type);
}
/**
* Get a filter options
* @param {string} type - Filter type
* @returns {Object} filter options or null if there is no that filter
*/
getOptions(type) {
const sourceImg = this._getSourceImage();
const imgFilter = this._getFilter(sourceImg, type);
if (!imgFilter) {
return null;
}
return extend({}, imgFilter.options);
}
/**
* Change filter values
* @param {Object} imgFilter object of filter
* @param {Object} options object
* @private
*/
_changeFilterValues(imgFilter, options) {
forEach(options, (value, key) => {
if (!isUndefined(imgFilter[key])) {
imgFilter[key] = value;
}
});
forEach(imgFilter.options, (value, key) => {
if (!isUndefined(options[key])) {
imgFilter.options[key] = options[key];
}
});
}
/**
* Apply filter
* @param {fabric.Image} sourceImg - Source image to apply filter
* @param {function} callback - Executed function after applying filter
* @private
*/
_apply(sourceImg, callback) {
sourceImg.filters.push();
const result = sourceImg.applyFilters();
if (result) {
callback();
}
}
/**
* Get source image on canvas
* @returns {fabric.Image} Current source image on canvas
* @private
*/
_getSourceImage() {
return this.getCanvasImage();
}
/**
* Create filter instance
* @param {fabric.Image} sourceImg - Source image to apply filter
* @param {string} type - Filter type
* @param {Object} [options] - Options of filter
* @returns {Object} Fabric object of filter
* @private
*/
_createFilter(sourceImg, type, options) {
let filterObj;
// capitalize first letter for matching with fabric image filter name
const fabricType = this._getFabricFilterType(type);
const ImageFilter = fabric.Image.filters[fabricType];
if (ImageFilter) {
filterObj = new ImageFilter(options);
filterObj.options = options;
sourceImg.filters.push(filterObj);
}
return filterObj;
}
/**
* Get applied filter instance
* @param {fabric.Image} sourceImg - Source image to apply filter
* @param {string} type - Filter type
* @returns {Object} Fabric object of filter
* @private
*/
_getFilter(sourceImg, type) {
let imgFilter = null;
if (sourceImg) {
const fabricType = this._getFabricFilterType(type);
const { length } = sourceImg.filters;
let item, i;
for (i = 0; i < length; i += 1) {
item = sourceImg.filters[i];
if (item.type === fabricType) {
imgFilter = item;
break;
}
}
}
return imgFilter;
}
/**
* Remove applied filter instance
* @param {fabric.Image} sourceImg - Source image to apply filter
* @param {string} type - Filter type
* @private
*/
_removeFilter(sourceImg, type) {
const fabricType = this._getFabricFilterType(type);
sourceImg.filters = sourceImg.filters.filter((value) => value.type !== fabricType);
}
/**
* Change filter class name to fabric's, especially capitalizing first letter
* @param {string} type - Filter type
* @example
* 'grayscale' -> 'Grayscale'
* @returns {string} Fabric filter class name
*/
_getFabricFilterType(type) {
return type.charAt(0).toUpperCase() + type.slice(1);
}
}
export default Filter;
@@ -0,0 +1,146 @@
import extend from 'tui-code-snippet/object/extend';
import Component from '@/interface/component';
import { componentNames, rejectMessages } from '@/consts';
/**
* Flip
* @class Flip
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
class Flip extends Component {
constructor(graphics) {
super(componentNames.FLIP, graphics);
}
/**
* Get current flip settings
* @returns {{flipX: Boolean, flipY: Boolean}}
*/
getCurrentSetting() {
const canvasImage = this.getCanvasImage();
return {
flipX: canvasImage.flipX,
flipY: canvasImage.flipY,
};
}
/**
* Set flipX, flipY
* @param {{flipX: Boolean, flipY: Boolean}} newSetting - Flip setting
* @returns {Promise}
*/
set(newSetting) {
const setting = this.getCurrentSetting();
const isChangingFlipX = setting.flipX !== newSetting.flipX;
const isChangingFlipY = setting.flipY !== newSetting.flipY;
if (!isChangingFlipX && !isChangingFlipY) {
return Promise.reject(rejectMessages.flip);
}
extend(setting, newSetting);
this.setImageProperties(setting, true);
this._invertAngle(isChangingFlipX, isChangingFlipY);
this._flipObjects(isChangingFlipX, isChangingFlipY);
return Promise.resolve({
flipX: setting.flipX,
flipY: setting.flipY,
angle: this.getCanvasImage().angle,
});
}
/**
* Invert image angle for flip
* @param {boolean} isChangingFlipX - Change flipX
* @param {boolean} isChangingFlipY - Change flipY
*/
_invertAngle(isChangingFlipX, isChangingFlipY) {
const canvasImage = this.getCanvasImage();
let { angle } = canvasImage;
if (isChangingFlipX) {
angle *= -1;
}
if (isChangingFlipY) {
angle *= -1;
}
canvasImage.rotate(parseFloat(angle)).setCoords(); // parseFloat for -0 to 0
}
/**
* Flip objects
* @param {boolean} isChangingFlipX - Change flipX
* @param {boolean} isChangingFlipY - Change flipY
* @private
*/
_flipObjects(isChangingFlipX, isChangingFlipY) {
const canvas = this.getCanvas();
if (isChangingFlipX) {
canvas.forEachObject((obj) => {
obj
.set({
angle: parseFloat(obj.angle * -1), // parseFloat for -0 to 0
flipX: !obj.flipX,
left: canvas.width - obj.left,
})
.setCoords();
});
}
if (isChangingFlipY) {
canvas.forEachObject((obj) => {
obj
.set({
angle: parseFloat(obj.angle * -1), // parseFloat for -0 to 0
flipY: !obj.flipY,
top: canvas.height - obj.top,
})
.setCoords();
});
}
canvas.renderAll();
}
/**
* Reset flip settings
* @returns {Promise}
*/
reset() {
return this.set({
flipX: false,
flipY: false,
});
}
/**
* Flip x
* @returns {Promise}
*/
flipX() {
const current = this.getCurrentSetting();
return this.set({
flipX: !current.flipX,
flipY: current.flipY,
});
}
/**
* Flip y
* @returns {Promise}
*/
flipY() {
const current = this.getCurrentSetting();
return this.set({
flipX: current.flipX,
flipY: !current.flipY,
});
}
}
export default Flip;
@@ -0,0 +1,66 @@
import { fabric } from 'fabric';
import Component from '@/interface/component';
import { componentNames } from '@/consts';
/**
* FreeDrawing
* @class FreeDrawing
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
class FreeDrawing extends Component {
constructor(graphics) {
super(componentNames.FREE_DRAWING, graphics);
/**
* Brush width
* @type {number}
*/
this.width = 12;
/**
* fabric.Color instance for brush color
* @type {fabric.Color}
*/
this.oColor = new fabric.Color('rgba(0, 0, 0, 0.5)');
}
/**
* Start free drawing mode
* @param {{width: ?number, color: ?string}} [setting] - Brush width & color
*/
start(setting) {
const canvas = this.getCanvas();
canvas.isDrawingMode = true;
this.setBrush(setting);
}
/**
* Set brush
* @param {{width: ?number, color: ?string}} [setting] - Brush width & color
*/
setBrush(setting) {
const brush = this.getCanvas().freeDrawingBrush;
setting = setting || {};
this.width = setting.width || this.width;
if (setting.color) {
this.oColor = new fabric.Color(setting.color);
}
brush.width = this.width;
brush.color = this.oColor.toRgba();
}
/**
* End free drawing mode
*/
end() {
const canvas = this.getCanvas();
canvas.isDrawingMode = false;
}
}
export default FreeDrawing;
@@ -0,0 +1,246 @@
import { fabric } from 'fabric';
import extend from 'tui-code-snippet/object/extend';
import forEach from 'tui-code-snippet/collection/forEach';
import Component from '@/interface/component';
import { eventNames as events, rejectMessages, componentNames, fObjectOptions } from '@/consts';
const pathMap = {
arrow: 'M 0 90 H 105 V 120 L 160 60 L 105 0 V 30 H 0 Z',
cancel:
'M 0 30 L 30 60 L 0 90 L 30 120 L 60 90 L 90 120 L 120 90 ' +
'L 90 60 L 120 30 L 90 0 L 60 30 L 30 0 Z',
};
/**
* Icon
* @class Icon
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
class Icon extends Component {
constructor(graphics) {
super(componentNames.ICON, graphics);
/**
* Default icon color
* @type {string}
*/
this._oColor = '#000000';
/**
* Path value of each icon type
* @type {Object}
*/
this._pathMap = pathMap;
/**
* Type of the drawing icon
* @type {string}
* @private
*/
this._type = null;
/**
* Color of the drawing icon
* @type {string}
* @private
*/
this._iconColor = null;
/**
* Event handler list
* @type {Object}
* @private
*/
this._handlers = {
mousedown: this._onFabricMouseDown.bind(this),
mousemove: this._onFabricMouseMove.bind(this),
mouseup: this._onFabricMouseUp.bind(this),
};
}
/**
* Set states of the current drawing shape
* @ignore
* @param {string} type - Icon type ('arrow', 'cancel', custom icon name)
* @param {string} iconColor - Icon foreground color
*/
setStates(type, iconColor) {
this._type = type;
this._iconColor = iconColor;
}
/**
* Start to draw the icon on canvas
* @ignore
*/
start() {
const canvas = this.getCanvas();
canvas.selection = false;
canvas.on('mouse:down', this._handlers.mousedown);
}
/**
* End to draw the icon on canvas
* @ignore
*/
end() {
const canvas = this.getCanvas();
canvas.selection = true;
canvas.off({
'mouse:down': this._handlers.mousedown,
});
}
/**
* Add icon
* @param {string} type - Icon type
* @param {Object} options - Icon options
* @param {string} [options.fill] - Icon foreground color
* @param {string} [options.left] - Icon x position
* @param {string} [options.top] - Icon y position
* @returns {Promise}
*/
add(type, options) {
return new Promise((resolve, reject) => {
const canvas = this.getCanvas();
const path = this._pathMap[type];
const selectionStyle = fObjectOptions.SELECTION_STYLE;
const icon = path ? this._createIcon(path) : null;
this._icon = icon;
if (!icon) {
reject(rejectMessages.invalidParameters);
}
icon.set(
extend(
{
type: 'icon',
fill: this._oColor,
},
selectionStyle,
options,
this.graphics.controlStyle
)
);
canvas.add(icon).setActiveObject(icon);
resolve(this.graphics.createObjectProperties(icon));
});
}
/**
* Register icon paths
* @param {{key: string, value: string}} pathInfos - Path infos
*/
registerPaths(pathInfos) {
forEach(
pathInfos,
(path, type) => {
this._pathMap[type] = path;
},
this
);
}
/**
* Set icon object color
* @param {string} color - Color to set
* @param {fabric.Path}[obj] - Current activated path object
*/
setColor(color, obj) {
this._oColor = color;
if (obj && obj.get('type') === 'icon') {
obj.set({ fill: this._oColor });
this.getCanvas().renderAll();
}
}
/**
* Get icon color
* @param {fabric.Path}[obj] - Current activated path object
* @returns {string} color
*/
getColor(obj) {
return obj.fill;
}
/**
* Create icon object
* @param {string} path - Path value to create icon
* @returns {fabric.Path} Path object
*/
_createIcon(path) {
return new fabric.Path(path);
}
/**
* MouseDown event handler on canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
* @private
*/
_onFabricMouseDown(fEvent) {
const canvas = this.getCanvas();
this._startPoint = canvas.getPointer(fEvent.e);
const { x: left, y: top } = this._startPoint;
this.add(this._type, {
left,
top,
fill: this._iconColor,
}).then(() => {
this.fire(events.ADD_OBJECT, this.graphics.createObjectProperties(this._icon));
canvas.on('mouse:move', this._handlers.mousemove);
canvas.on('mouse:up', this._handlers.mouseup);
});
}
/**
* MouseMove event handler on canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
* @private
*/
_onFabricMouseMove(fEvent) {
const canvas = this.getCanvas();
if (!this._icon) {
return;
}
const moveOriginPointer = canvas.getPointer(fEvent.e);
const scaleX = (moveOriginPointer.x - this._startPoint.x) / this._icon.width;
const scaleY = (moveOriginPointer.y - this._startPoint.y) / this._icon.height;
this._icon.set({
scaleX: Math.abs(scaleX * 2),
scaleY: Math.abs(scaleY * 2),
});
this._icon.setCoords();
canvas.renderAll();
}
/**
* MouseUp event handler on canvas
* @private
*/
_onFabricMouseUp() {
const canvas = this.getCanvas();
this.fire(events.OBJECT_ADDED, this.graphics.createObjectProperties(this._icon));
this._icon = null;
canvas.off('mouse:down', this._handlers.mousedown);
canvas.off('mouse:move', this._handlers.mousemove);
canvas.off('mouse:up', this._handlers.mouseup);
}
}
export default Icon;
@@ -0,0 +1,84 @@
import Component from '@/interface/component';
import { componentNames, rejectMessages } from '@/consts';
const imageOption = {
padding: 0,
crossOrigin: 'Anonymous',
};
/**
* ImageLoader components
* @extends {Component}
* @class ImageLoader
* @param {Graphics} graphics - Graphics instance
* @ignore
*/
class ImageLoader extends Component {
constructor(graphics) {
super(componentNames.IMAGE_LOADER, graphics);
}
/**
* Load image from url
* @param {?string} imageName - File name
* @param {?(fabric.Image|string)} img - fabric.Image instance or URL of an image
* @returns {Promise}
*/
load(imageName, img) {
let promise;
if (!imageName && !img) {
// Back to the initial state, not error.
const canvas = this.getCanvas();
canvas.backgroundImage = null;
canvas.renderAll();
promise = new Promise((resolve) => {
this.setCanvasImage('', null);
resolve();
});
} else {
promise = this._setBackgroundImage(img).then((oImage) => {
this.setCanvasImage(imageName, oImage);
this.adjustCanvasDimension();
return oImage;
});
}
return promise;
}
/**
* Set background image
* @param {?(fabric.Image|String)} img fabric.Image instance or URL of an image to set background to
* @returns {Promise}
* @private
*/
_setBackgroundImage(img) {
if (!img) {
return Promise.reject(rejectMessages.loadImage);
}
return new Promise((resolve, reject) => {
const canvas = this.getCanvas();
canvas.setBackgroundImage(
img,
() => {
const oImage = canvas.backgroundImage;
if (oImage && oImage.getElement()) {
resolve(oImage);
} else {
reject(rejectMessages.loadingImageFailed);
}
},
imageOption
);
});
}
}
export default ImageLoader;
@@ -0,0 +1,205 @@
import { fabric } from 'fabric';
import extend from 'tui-code-snippet/object/extend';
import Component from '@/interface/component';
import ArrowLine from '@/extension/arrowLine';
import { eventNames, componentNames, fObjectOptions } from '@/consts';
/**
* Line
* @class Line
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
class Line extends Component {
constructor(graphics) {
super(componentNames.LINE, graphics);
/**
* Brush width
* @type {number}
* @private
*/
this._width = 12;
/**
* fabric.Color instance for brush color
* @type {fabric.Color}
* @private
*/
this._oColor = new fabric.Color('rgba(0, 0, 0, 0.5)');
/**
* Listeners
* @type {object.<string, function>}
* @private
*/
this._listeners = {
mousedown: this._onFabricMouseDown.bind(this),
mousemove: this._onFabricMouseMove.bind(this),
mouseup: this._onFabricMouseUp.bind(this),
};
}
/**
* Start drawing line mode
* @param {{width: ?number, color: ?string}} [setting] - Brush width & color
*/
setHeadOption(setting) {
const {
arrowType = {
head: null,
tail: null,
},
} = setting;
this._arrowType = arrowType;
}
/**
* Start drawing line mode
* @param {{width: ?number, color: ?string}} [setting] - Brush width & color
*/
start(setting = {}) {
const canvas = this.getCanvas();
canvas.defaultCursor = 'crosshair';
canvas.selection = false;
this.setHeadOption(setting);
this.setBrush(setting);
canvas.forEachObject((obj) => {
obj.set({
evented: false,
});
});
canvas.on({
'mouse:down': this._listeners.mousedown,
});
}
/**
* Set brush
* @param {{width: ?number, color: ?string}} [setting] - Brush width & color
*/
setBrush(setting) {
const brush = this.getCanvas().freeDrawingBrush;
setting = setting || {};
this._width = setting.width || this._width;
if (setting.color) {
this._oColor = new fabric.Color(setting.color);
}
brush.width = this._width;
brush.color = this._oColor.toRgba();
}
/**
* End drawing line mode
*/
end() {
const canvas = this.getCanvas();
canvas.defaultCursor = 'default';
canvas.selection = true;
canvas.forEachObject((obj) => {
obj.set({
evented: true,
});
});
canvas.off('mouse:down', this._listeners.mousedown);
}
/**
* Mousedown event handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
* @private
*/
_onFabricMouseDown(fEvent) {
const canvas = this.getCanvas();
const { x, y } = canvas.getPointer(fEvent.e);
const points = [x, y, x, y];
this._line = new ArrowLine(points, {
stroke: this._oColor.toRgba(),
strokeWidth: this._width,
arrowType: this._arrowType,
evented: false,
});
this._line.set(fObjectOptions.SELECTION_STYLE);
canvas.add(this._line);
canvas.on({
'mouse:move': this._listeners.mousemove,
'mouse:up': this._listeners.mouseup,
});
this.fire(eventNames.ADD_OBJECT, this._createLineEventObjectProperties());
}
/**
* Mousemove event handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
* @private
*/
_onFabricMouseMove(fEvent) {
const canvas = this.getCanvas();
const pointer = canvas.getPointer(fEvent.e);
this._line.set({
x2: pointer.x,
y2: pointer.y,
});
this._line.setCoords();
canvas.renderAll();
}
/**
* Mouseup event handler in fabric canvas
* @private
*/
_onFabricMouseUp() {
const canvas = this.getCanvas();
this.fire(eventNames.OBJECT_ADDED, this._createLineEventObjectProperties());
this._line = null;
canvas.off({
'mouse:move': this._listeners.mousemove,
'mouse:up': this._listeners.mouseup,
});
}
/**
* create line event object properties
* @returns {Object} properties line object
* @private
*/
_createLineEventObjectProperties() {
const params = this.graphics.createObjectProperties(this._line);
const { x1, x2, y1, y2 } = this._line;
return extend({}, params, {
startPosition: {
x: x1,
y: y1,
},
endPosition: {
x: x2,
y: y2,
},
});
}
}
export default Line;
@@ -0,0 +1,103 @@
import Component from '@/interface/component';
import { componentNames } from '@/consts';
/**
* Resize components
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @class Resize
* @ignore
*/
class Resize extends Component {
constructor(graphics) {
super(componentNames.RESIZE, graphics);
/**
* Current dimensions
* @type {Object}
* @private
*/
this._dimensions = null;
/**
* Original dimensions
* @type {Object}
* @private
*/
this._originalDimensions = null;
}
/**
* Get current dimensions
* @returns {object}
*/
getCurrentDimensions() {
const canvasImage = this.getCanvasImage();
if (!this._dimensions && canvasImage) {
const { width, height } = canvasImage;
this._dimensions = { width, height };
}
return this._dimensions;
}
/**
* Get original dimensions
* @returns {object}
*/
getOriginalDimensions() {
return this._originalDimensions;
}
/**
* Set original dimensions
* @param {object} dimensions - Dimensions
*/
setOriginalDimensions(dimensions) {
this._originalDimensions = dimensions;
}
/**
* Resize Image
* @param {Object} dimensions - Resize dimensions
* @returns {Promise}
*/
resize(dimensions) {
const canvasImage = this.getCanvasImage();
const { width, height, scaleX, scaleY } = canvasImage;
const { width: dimensionsWidth, height: dimensionsHeight } = dimensions;
const scaleValues = {
scaleX: dimensionsWidth ? dimensionsWidth / width : scaleX,
scaleY: dimensionsHeight ? dimensionsHeight / height : scaleY,
};
if (scaleX !== scaleValues.scaleX || scaleY !== scaleValues.scaleY) {
canvasImage.set(scaleValues).setCoords();
this._dimensions = {
width: canvasImage.width * canvasImage.scaleX,
height: canvasImage.height * canvasImage.scaleY,
};
}
this.adjustCanvasDimensionBase();
return Promise.resolve();
}
/**
* Start resizing
*/
start() {
const dimensions = this.getCurrentDimensions();
this.setOriginalDimensions(dimensions);
}
/**
* End resizing
*/
end() {}
}
export default Resize;
@@ -0,0 +1,91 @@
import { fabric } from 'fabric';
import Component from '@/interface/component';
import { componentNames } from '@/consts';
/**
* Image Rotation component
* @class Rotation
* @extends {Component}
* @param {Graphics} graphics - Graphics instance
* @ignore
*/
class Rotation extends Component {
constructor(graphics) {
super(componentNames.ROTATION, graphics);
}
/**
* Get current angle
* @returns {Number}
*/
getCurrentAngle() {
return this.getCanvasImage().angle;
}
/**
* Set angle of the image
*
* Do not call "this.setImageProperties" for setting angle directly.
* Before setting angle, The originX,Y of image should be set to center.
* See "http://fabricjs.com/docs/fabric.Object.html#setAngle"
*
* @param {number} angle - Angle value
* @returns {Promise}
*/
setAngle(angle) {
const oldAngle = this.getCurrentAngle() % 360; // The angle is lower than 2*PI(===360 degrees)
angle %= 360;
const canvasImage = this.getCanvasImage();
const oldImageCenter = canvasImage.getCenterPoint();
canvasImage.set({ angle }).setCoords();
this.adjustCanvasDimension();
const newImageCenter = canvasImage.getCenterPoint();
this._rotateForEachObject(oldImageCenter, newImageCenter, angle - oldAngle);
return Promise.resolve(angle);
}
/**
* Rotate for each object
* @param {fabric.Point} oldImageCenter - Image center point before rotation
* @param {fabric.Point} newImageCenter - Image center point after rotation
* @param {number} angleDiff - Image angle difference after rotation
* @private
*/
_rotateForEachObject(oldImageCenter, newImageCenter, angleDiff) {
const canvas = this.getCanvas();
const centerDiff = {
x: oldImageCenter.x - newImageCenter.x,
y: oldImageCenter.y - newImageCenter.y,
};
canvas.forEachObject((obj) => {
const objCenter = obj.getCenterPoint();
const radian = fabric.util.degreesToRadians(angleDiff);
const newObjCenter = fabric.util.rotatePoint(objCenter, oldImageCenter, radian);
obj.set({
left: newObjCenter.x - centerDiff.x,
top: newObjCenter.y - centerDiff.y,
angle: (obj.angle + angleDiff) % 360,
});
obj.setCoords();
});
canvas.renderAll();
}
/**
* Rotate the image
* @param {number} additionalAngle - Additional angle
* @returns {Promise}
*/
rotate(additionalAngle) {
const current = this.getCurrentAngle();
return this.setAngle(current + additionalAngle);
}
}
export default Rotation;
@@ -0,0 +1,601 @@
import { fabric } from 'fabric';
import extend from 'tui-code-snippet/object/extend';
import Component from '@/interface/component';
import resizeHelper from '@/helper/shapeResizeHelper';
import {
getFillImageFromShape,
rePositionFilterTypeFillImage,
reMakePatternImageSource,
makeFillPatternForFilter,
makeFilterOptionFromFabricImage,
resetFillPatternCanvas,
} from '@/helper/shapeFilterFillHelper';
import {
changeOrigin,
getCustomProperty,
getFillTypeFromOption,
getFillTypeFromObject,
isShape,
} from '@/util';
import {
rejectMessages,
eventNames,
keyCodes as KEY_CODES,
componentNames,
fObjectOptions,
SHAPE_DEFAULT_OPTIONS,
SHAPE_FILL_TYPE,
} from '@/consts';
const SHAPE_INIT_OPTIONS = extend(
{
strokeWidth: 1,
stroke: '#000000',
fill: '#ffffff',
width: 1,
height: 1,
rx: 0,
ry: 0,
},
SHAPE_DEFAULT_OPTIONS
);
const DEFAULT_TYPE = 'rect';
const DEFAULT_WIDTH = 20;
const DEFAULT_HEIGHT = 20;
/**
* Make fill option
* @param {Object} options - Options to create the shape
* @param {Object.Image} canvasImage - canvas background image
* @param {Function} createStaticCanvas - static canvas creater
* @returns {Object} - shape option
* @private
*/
function makeFabricFillOption(options, canvasImage, createStaticCanvas) {
const fillOption = options.fill;
const fillType = getFillTypeFromOption(options.fill);
let fill = fillOption;
if (fillOption.color) {
fill = fillOption.color;
}
let extOption = null;
if (fillType === 'filter') {
const newStaticCanvas = createStaticCanvas();
extOption = makeFillPatternForFilter(canvasImage, fillOption.filter, newStaticCanvas);
} else {
extOption = { fill };
}
return extend({}, options, extOption);
}
/**
* Shape
* @class Shape
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
export default class Shape extends Component {
constructor(graphics) {
super(componentNames.SHAPE, graphics);
/**
* Object of The drawing shape
* @type {fabric.Object}
* @private
*/
this._shapeObj = null;
/**
* Type of the drawing shape
* @type {string}
* @private
*/
this._type = DEFAULT_TYPE;
/**
* Options to draw the shape
* @type {Object}
* @private
*/
this._options = extend({}, SHAPE_INIT_OPTIONS);
/**
* Whether the shape object is selected or not
* @type {boolean}
* @private
*/
this._isSelected = false;
/**
* Pointer for drawing shape (x, y)
* @type {Object}
* @private
*/
this._startPoint = {};
/**
* Using shortcut on drawing shape
* @type {boolean}
* @private
*/
this._withShiftKey = false;
/**
* Event handler list
* @type {Object}
* @private
*/
this._handlers = {
mousedown: this._onFabricMouseDown.bind(this),
mousemove: this._onFabricMouseMove.bind(this),
mouseup: this._onFabricMouseUp.bind(this),
keydown: this._onKeyDown.bind(this),
keyup: this._onKeyUp.bind(this),
};
}
/**
* Start to draw the shape on canvas
* @ignore
*/
start() {
const canvas = this.getCanvas();
this._isSelected = false;
canvas.defaultCursor = 'crosshair';
canvas.selection = false;
canvas.uniformScaling = true;
canvas.on({
'mouse:down': this._handlers.mousedown,
});
fabric.util.addListener(document, 'keydown', this._handlers.keydown);
fabric.util.addListener(document, 'keyup', this._handlers.keyup);
}
/**
* End to draw the shape on canvas
* @ignore
*/
end() {
const canvas = this.getCanvas();
this._isSelected = false;
canvas.defaultCursor = 'default';
canvas.selection = true;
canvas.uniformScaling = false;
canvas.off({
'mouse:down': this._handlers.mousedown,
});
fabric.util.removeListener(document, 'keydown', this._handlers.keydown);
fabric.util.removeListener(document, 'keyup', this._handlers.keyup);
}
/**
* Set states of the current drawing shape
* @ignore
* @param {string} type - Shape type (ex: 'rect', 'circle')
* @param {Object} [options] - Shape options
* @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
* Shape foreground color (ex: '#fff', 'transparent')
* @param {string} [options.stoke] - Shape outline color
* @param {number} [options.strokeWidth] - Shape outline width
* @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
* @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
* @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
* @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
*/
setStates(type, options) {
this._type = type;
if (options) {
this._options = extend(this._options, options);
}
}
/**
* Add the shape
* @ignore
* @param {string} type - Shape type (ex: 'rect', 'circle')
* @param {Object} options - Shape options
* @param {(ShapeFillOption | string)} [options.fill] - ShapeFillOption or Shape foreground color (ex: '#fff', 'transparent') or ShapeFillOption object
* @param {string} [options.stroke] - Shape outline color
* @param {number} [options.strokeWidth] - Shape outline width
* @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
* @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
* @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
* @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
* @param {number} [options.isRegular] - Whether scaling shape has 1:1 ratio or not
* @returns {Promise}
*/
add(type, options) {
return new Promise((resolve) => {
const canvas = this.getCanvas();
const extendOption = this._extendOptions(options);
const shapeObj = this._createInstance(type, extendOption);
const objectProperties = this.graphics.createObjectProperties(shapeObj);
this._bindEventOnShape(shapeObj);
canvas.add(shapeObj).setActiveObject(shapeObj);
this._resetPositionFillFilter(shapeObj);
resolve(objectProperties);
});
}
/**
* Change the shape
* @ignore
* @param {fabric.Object} shapeObj - Selected shape object on canvas
* @param {Object} options - Shape options
* @param {(ShapeFillOption | string)} [options.fill] - {@link ShapeFillOption} or
* Shape foreground color (ex: '#fff', 'transparent')
* @param {string} [options.stroke] - Shape outline color
* @param {number} [options.strokeWidth] - Shape outline width
* @param {number} [options.width] - Width value (When type option is 'rect', this options can use)
* @param {number} [options.height] - Height value (When type option is 'rect', this options can use)
* @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use)
* @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use)
* @param {number} [options.isRegular] - Whether scaling shape has 1:1 ratio or not
* @returns {Promise}
*/
change(shapeObj, options) {
return new Promise((resolve, reject) => {
if (!isShape(shapeObj)) {
reject(rejectMessages.unsupportedType);
}
const hasFillOption = getFillTypeFromOption(options.fill) === 'filter';
const { canvasImage, createStaticCanvas } = this.graphics;
shapeObj.set(
hasFillOption ? makeFabricFillOption(options, canvasImage, createStaticCanvas) : options
);
if (hasFillOption) {
this._resetPositionFillFilter(shapeObj);
}
this.getCanvas().renderAll();
resolve();
});
}
/**
* make fill property for user event
* @param {fabric.Object} shapeObj - fabric object
* @returns {Object}
*/
makeFillPropertyForUserEvent(shapeObj) {
const fillType = getFillTypeFromObject(shapeObj);
const fillProp = {};
if (fillType === SHAPE_FILL_TYPE.FILTER) {
const fillImage = getFillImageFromShape(shapeObj);
const filterOption = makeFilterOptionFromFabricImage(fillImage);
fillProp.type = fillType;
fillProp.filter = filterOption;
} else {
fillProp.type = SHAPE_FILL_TYPE.COLOR;
fillProp.color = shapeObj.fill || 'transparent';
}
return fillProp;
}
/**
* Copy object handling.
* @param {fabric.Object} shapeObj - Shape object
* @param {fabric.Object} originalShapeObj - Shape object
*/
processForCopiedObject(shapeObj, originalShapeObj) {
this._bindEventOnShape(shapeObj);
if (getFillTypeFromObject(shapeObj) === 'filter') {
const fillImage = getFillImageFromShape(originalShapeObj);
const filterOption = makeFilterOptionFromFabricImage(fillImage);
const newStaticCanvas = this.graphics.createStaticCanvas();
shapeObj.set(
makeFillPatternForFilter(this.graphics.canvasImage, filterOption, newStaticCanvas)
);
this._resetPositionFillFilter(shapeObj);
}
}
/**
* Create the instance of shape
* @param {string} type - Shape type
* @param {Object} options - Options to creat the shape
* @returns {fabric.Object} Shape instance
* @private
*/
_createInstance(type, options) {
let instance;
switch (type) {
case 'rect':
instance = new fabric.Rect(options);
break;
case 'circle':
instance = new fabric.Ellipse(
extend(
{
type: 'circle',
},
options
)
);
break;
case 'triangle':
instance = new fabric.Triangle(options);
break;
default:
instance = {};
}
return instance;
}
/**
* Get the options to create the shape
* @param {Object} options - Options to creat the shape
* @returns {Object} Shape options
* @private
*/
_extendOptions(options) {
const selectionStyles = fObjectOptions.SELECTION_STYLE;
const { canvasImage, createStaticCanvas } = this.graphics;
options = extend({}, SHAPE_INIT_OPTIONS, this._options, selectionStyles, options);
return makeFabricFillOption(options, canvasImage, createStaticCanvas);
}
/**
* Bind fabric events on the creating shape object
* @param {fabric.Object} shapeObj - Shape object
* @private
*/
_bindEventOnShape(shapeObj) {
const self = this;
const canvas = this.getCanvas();
shapeObj.on({
added() {
self._shapeObj = this;
resizeHelper.setOrigins(self._shapeObj);
},
selected() {
self._isSelected = true;
self._shapeObj = this;
canvas.uniformScaling = true;
canvas.defaultCursor = 'default';
resizeHelper.setOrigins(self._shapeObj);
},
deselected() {
self._isSelected = false;
self._shapeObj = null;
canvas.defaultCursor = 'crosshair';
canvas.uniformScaling = false;
},
modified() {
const currentObj = self._shapeObj;
resizeHelper.adjustOriginToCenter(currentObj);
resizeHelper.setOrigins(currentObj);
},
modifiedInGroup(activeSelection) {
self._fillFilterRePositionInGroupSelection(shapeObj, activeSelection);
},
moving() {
self._resetPositionFillFilter(this);
},
rotating() {
self._resetPositionFillFilter(this);
},
scaling(fEvent) {
const pointer = canvas.getPointer(fEvent.e);
const currentObj = self._shapeObj;
canvas.setCursor('crosshair');
resizeHelper.resize(currentObj, pointer, true);
self._resetPositionFillFilter(this);
},
});
}
/**
* MouseDown event handler on canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
* @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);
canvas.on({
'mouse:move': this._handlers.mousemove,
'mouse:up': this._handlers.mouseup,
});
}
}
/**
* MouseDown event handler on canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event object
* @private
*/
_onFabricMouseMove(fEvent) {
const canvas = this.getCanvas();
const pointer = canvas.getPointer(fEvent.e);
const startPointX = this._startPoint.x;
const startPointY = this._startPoint.y;
const width = startPointX - pointer.x;
const height = startPointY - pointer.y;
const shape = this._shapeObj;
if (!shape) {
this.add(this._type, {
left: startPointX,
top: startPointY,
width,
height,
}).then((objectProps) => {
this.fire(eventNames.ADD_OBJECT, objectProps);
});
} else {
this._shapeObj.set({
isRegular: this._withShiftKey,
});
resizeHelper.resize(shape, pointer);
canvas.renderAll();
this._resetPositionFillFilter(shape);
}
}
/**
* MouseUp event handler on canvas
* @private
*/
_onFabricMouseUp() {
const canvas = this.getCanvas();
const startPointX = this._startPoint.x;
const startPointY = this._startPoint.y;
const shape = this._shapeObj;
if (!shape) {
this.add(this._type, {
left: startPointX,
top: startPointY,
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
}).then((objectProps) => {
this.fire(eventNames.ADD_OBJECT, objectProps);
});
} else if (shape) {
resizeHelper.adjustOriginToCenter(shape);
this.fire(eventNames.OBJECT_ADDED, this.graphics.createObjectProperties(shape));
}
canvas.off({
'mouse:move': this._handlers.mousemove,
'mouse:up': this._handlers.mouseup,
});
}
/**
* Keydown event handler on document
* @param {KeyboardEvent} e - Event object
* @private
*/
_onKeyDown(e) {
if (e.keyCode === KEY_CODES.SHIFT) {
this._withShiftKey = true;
if (this._shapeObj) {
this._shapeObj.isRegular = true;
}
}
}
/**
* Keyup event handler on document
* @param {KeyboardEvent} e - Event object
* @private
*/
_onKeyUp(e) {
if (e.keyCode === KEY_CODES.SHIFT) {
this._withShiftKey = false;
if (this._shapeObj) {
this._shapeObj.isRegular = false;
}
}
}
/**
* Reset shape position and internal proportions in the filter type fill area.
* @param {fabric.Object} shapeObj - Shape object
* @private
*/
_resetPositionFillFilter(shapeObj) {
if (getFillTypeFromObject(shapeObj) !== 'filter') {
return;
}
const { patternSourceCanvas } = getCustomProperty(shapeObj, 'patternSourceCanvas');
const fillImage = getFillImageFromShape(shapeObj);
const { originalAngle } = getCustomProperty(fillImage, 'originalAngle');
if (this.graphics.canvasImage.angle !== originalAngle) {
reMakePatternImageSource(shapeObj, this.graphics.canvasImage);
}
const { originX, originY } = shapeObj;
resizeHelper.adjustOriginToCenter(shapeObj);
shapeObj.width *= shapeObj.scaleX;
shapeObj.height *= shapeObj.scaleY;
shapeObj.rx *= shapeObj.scaleX;
shapeObj.ry *= shapeObj.scaleY;
shapeObj.scaleX = 1;
shapeObj.scaleY = 1;
rePositionFilterTypeFillImage(shapeObj);
changeOrigin(shapeObj, {
originX,
originY,
});
resetFillPatternCanvas(patternSourceCanvas);
}
/**
* Reset filter area position within group selection.
* @param {fabric.Object} shapeObj - Shape object
* @param {fabric.ActiveSelection} activeSelection - Shape object
* @private
*/
_fillFilterRePositionInGroupSelection(shapeObj, activeSelection) {
if (activeSelection.scaleX !== 1 || activeSelection.scaleY !== 1) {
// This is necessary because the group's scale transition state affects the relative size of the fill area.
// The only way to reset the object transformation scale state to neutral.
// {@link https://github.com/fabricjs/fabric.js/issues/5372}
activeSelection.addWithUpdate();
}
const { angle, left, top } = shapeObj;
fabric.util.addTransformToObject(shapeObj, activeSelection.calcTransformMatrix());
this._resetPositionFillFilter(shapeObj);
shapeObj.set({
angle,
left,
top,
});
}
}
@@ -0,0 +1,572 @@
import { fabric } from 'fabric';
import extend from 'tui-code-snippet/object/extend';
import isExisty from 'tui-code-snippet/type/isExisty';
import forEach from 'tui-code-snippet/collection/forEach';
import Component from '@/interface/component';
import { stamp } from '@/util';
import { componentNames, eventNames as events, fObjectOptions } from '@/consts';
const defaultStyles = {
fill: '#000000',
left: 0,
top: 0,
};
const resetStyles = {
fill: '#000000',
fontStyle: 'normal',
fontWeight: 'normal',
textAlign: 'tie-text-align-left',
underline: false,
};
const DBCLICK_TIME = 500;
/**
* Text
* @class Text
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @ignore
*/
class Text extends Component {
constructor(graphics) {
super(componentNames.TEXT, graphics);
/**
* Default text style
* @type {Object}
*/
this._defaultStyles = defaultStyles;
/**
* Selected state
* @type {boolean}
*/
this._isSelected = false;
/**
* Selected text object
* @type {Object}
*/
this._selectedObj = {};
/**
* Editing text object
* @type {Object}
*/
this._editingObj = {};
/**
* Listeners for fabric event
* @type {Object}
*/
this._listeners = {
mousedown: this._onFabricMouseDown.bind(this),
select: this._onFabricSelect.bind(this),
selectClear: this._onFabricSelectClear.bind(this),
scaling: this._onFabricScaling.bind(this),
textChanged: this._onFabricTextChanged.bind(this),
};
/**
* Textarea element for editing
* @type {HTMLElement}
*/
this._textarea = null;
/**
* Ratio of current canvas
* @type {number}
*/
this._ratio = 1;
/**
* Last click time
* @type {Date}
*/
this._lastClickTime = new Date().getTime();
/**
* Text object infos before editing
* @type {Object}
*/
this._editingObjInfos = {};
/**
* Previous state of editing
* @type {boolean}
*/
this.isPrevEditing = false;
}
/**
* Start input text mode
*/
start() {
const canvas = this.getCanvas();
canvas.selection = false;
canvas.defaultCursor = 'text';
canvas.on({
'mouse:down': this._listeners.mousedown,
'selection:created': this._listeners.select,
'selection:updated': this._listeners.select,
'before:selection:cleared': this._listeners.selectClear,
'object:scaling': this._listeners.scaling,
'text:changed': this._listeners.textChanged,
});
canvas.forEachObject((obj) => {
if (obj.type === 'i-text') {
this.adjustOriginPosition(obj, 'start');
}
});
this.setCanvasRatio();
}
/**
* End input text mode
*/
end() {
const canvas = this.getCanvas();
canvas.selection = true;
canvas.defaultCursor = 'default';
canvas.forEachObject((obj) => {
if (obj.type === 'i-text') {
if (obj.text === '') {
canvas.remove(obj);
} else {
this.adjustOriginPosition(obj, 'end');
}
}
});
canvas.off({
'mouse:down': this._listeners.mousedown,
'selection:created': this._listeners.select,
'selection:updated': this._listeners.select,
'before:selection:cleared': this._listeners.selectClear,
'object:selected': this._listeners.select,
'object:scaling': this._listeners.scaling,
'text:changed': this._listeners.textChanged,
});
}
/**
* Adjust the origin position
* @param {fabric.Object} text - text object
* @param {string} editStatus - 'start' or 'end'
*/
adjustOriginPosition(text, editStatus) {
let [originX, originY] = ['center', 'center'];
if (editStatus === 'start') {
[originX, originY] = ['left', 'top'];
}
const { x: left, y: top } = text.getPointByOrigin(originX, originY);
text.set({
left,
top,
originX,
originY,
});
text.setCoords();
}
/**
* Add new text on canvas image
* @param {string} text - Initial input text
* @param {Object} options - Options for generating text
* @param {Object} [options.styles] Initial styles
* @param {string} [options.styles.fill] Color
* @param {string} [options.styles.fontFamily] Font type for text
* @param {number} [options.styles.fontSize] Size
* @param {string} [options.styles.fontStyle] Type of inclination (normal / italic)
* @param {string} [options.styles.fontWeight] Type of thicker or thinner looking (normal / bold)
* @param {string} [options.styles.textAlign] Type of text align (left / center / right)
* @param {string} [options.styles.textDecoration] Type of line (underline / line-through / overline)
* @param {{x: number, y: number}} [options.position] - Initial position
* @returns {Promise}
*/
add(text, options) {
return new Promise((resolve) => {
const canvas = this.getCanvas();
let newText = null;
let selectionStyle = fObjectOptions.SELECTION_STYLE;
let styles = this._defaultStyles;
this._setInitPos(options.position);
if (options.styles) {
styles = extend(styles, options.styles);
}
if (!isExisty(options.autofocus)) {
options.autofocus = true;
}
newText = new fabric.IText(text, styles);
selectionStyle = extend({}, selectionStyle, {
originX: 'left',
originY: 'top',
});
newText.set(selectionStyle);
newText.on({
mouseup: this._onFabricMouseUp.bind(this),
});
canvas.add(newText);
if (options.autofocus) {
newText.enterEditing();
newText.selectAll();
}
if (!canvas.getActiveObject()) {
canvas.setActiveObject(newText);
}
this.isPrevEditing = true;
resolve(this.graphics.createObjectProperties(newText));
});
}
/**
* Change text of activate object on canvas image
* @param {Object} activeObj - Current selected text object
* @param {string} text - Changed text
* @returns {Promise}
*/
change(activeObj, text) {
return new Promise((resolve) => {
activeObj.set('text', text);
this.getCanvas().renderAll();
resolve();
});
}
/**
* Set style
* @param {Object} activeObj - Current selected text object
* @param {Object} styleObj - Initial styles
* @param {string} [styleObj.fill] Color
* @param {string} [styleObj.fontFamily] Font type for text
* @param {number} [styleObj.fontSize] Size
* @param {string} [styleObj.fontStyle] Type of inclination (normal / italic)
* @param {string} [styleObj.fontWeight] Type of thicker or thinner looking (normal / bold)
* @param {string} [styleObj.textAlign] Type of text align (left / center / right)
* @param {string} [styleObj.textDecoration] Type of line (underline / line-through / overline)
* @returns {Promise}
*/
setStyle(activeObj, styleObj) {
return new Promise((resolve) => {
forEach(
styleObj,
(val, key) => {
if (activeObj[key] === val && key !== 'fontSize') {
styleObj[key] = resetStyles[key] || '';
}
},
this
);
if ('textDecoration' in styleObj) {
extend(styleObj, this._getTextDecorationAdaptObject(styleObj.textDecoration));
}
activeObj.set(styleObj);
this.getCanvas().renderAll();
resolve();
});
}
/**
* Get the text
* @param {Object} activeObj - Current selected text object
* @returns {String} text
*/
getText(activeObj) {
return activeObj.text;
}
/**
* Set infos of the current selected object
* @param {fabric.Text} obj - Current selected text object
* @param {boolean} state - State of selecting
*/
setSelectedInfo(obj, state) {
this._selectedObj = obj;
this._isSelected = state;
}
/**
* Whether object is selected or not
* @returns {boolean} State of selecting
*/
isSelected() {
return this._isSelected;
}
/**
* Get current selected text object
* @returns {fabric.Text} Current selected text object
*/
getSelectedObj() {
return this._selectedObj;
}
/**
* Set ratio value of canvas
*/
setCanvasRatio() {
const canvasElement = this.getCanvasElement();
const cssWidth = parseInt(canvasElement.style.maxWidth, 10);
const originWidth = canvasElement.width;
this._ratio = originWidth / cssWidth;
}
/**
* Get ratio value of canvas
* @returns {number} Ratio value
*/
getCanvasRatio() {
return this._ratio;
}
/**
* Get text decoration adapt object
* @param {string} textDecoration - text decoration option string
* @returns {object} adapt object for override
*/
_getTextDecorationAdaptObject(textDecoration) {
return {
underline: textDecoration === 'underline',
linethrough: textDecoration === 'line-through',
overline: textDecoration === 'overline',
};
}
/**
* Set initial position on canvas image
* @param {{x: number, y: number}} [position] - Selected position
* @private
*/
_setInitPos(position) {
position = position || this.getCanvasImage().getCenterPoint();
this._defaultStyles.left = position.x;
this._defaultStyles.top = position.y;
}
/**
* Input event handler
* @private
*/
_onInput() {
const ratio = this.getCanvasRatio();
const obj = this._editingObj;
const textareaStyle = this._textarea.style;
textareaStyle.width = `${Math.ceil(obj.width / ratio)}px`;
textareaStyle.height = `${Math.ceil(obj.height / ratio)}px`;
}
/**
* Keydown event handler
* @private
*/
_onKeyDown() {
const ratio = this.getCanvasRatio();
const obj = this._editingObj;
const textareaStyle = this._textarea.style;
setTimeout(() => {
obj.text(this._textarea.value);
textareaStyle.width = `${Math.ceil(obj.width / ratio)}px`;
textareaStyle.height = `${Math.ceil(obj.height / ratio)}px`;
}, 0);
}
/**
* Blur event handler
* @private
*/
_onBlur() {
const ratio = this.getCanvasRatio();
const editingObj = this._editingObj;
const editingObjInfos = this._editingObjInfos;
const textContent = this._textarea.value;
let transWidth = editingObj.width / ratio - editingObjInfos.width / ratio;
let transHeight = editingObj.height / ratio - editingObjInfos.height / ratio;
if (ratio === 1) {
transWidth /= 2;
transHeight /= 2;
}
this._textarea.style.display = 'none';
editingObj.set({
left: editingObjInfos.left + transWidth,
top: editingObjInfos.top + transHeight,
});
if (textContent.length) {
this.getCanvas().add(editingObj);
const params = {
id: stamp(editingObj),
type: editingObj.type,
text: textContent,
};
this.fire(events.TEXT_CHANGED, params);
}
}
/**
* Scroll event handler
* @private
*/
_onScroll() {
this._textarea.scrollLeft = 0;
this._textarea.scrollTop = 0;
}
/**
* Fabric scaling event handler
* @param {fabric.Event} fEvent - Current scaling event on selected object
* @private
*/
_onFabricScaling(fEvent) {
const obj = fEvent.target;
obj.fontSize = obj.fontSize * obj.scaleY;
obj.scaleX = 1;
obj.scaleY = 1;
}
/**
* textChanged event handler
* @param {{target: fabric.Object}} props - changed text object
* @private
*/
_onFabricTextChanged(props) {
this.fire(events.TEXT_CHANGED, props.target);
}
/**
* onSelectClear handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onFabricSelectClear(fEvent) {
const obj = this.getSelectedObj();
this.isPrevEditing = true;
this.setSelectedInfo(fEvent.target, false);
if (obj) {
// obj is empty object at initial time, will be set fabric object
if (obj.text === '') {
this.getCanvas().remove(obj);
}
}
}
/**
* onSelect handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onFabricSelect(fEvent) {
this.isPrevEditing = true;
this.setSelectedInfo(fEvent.target, true);
}
/**
* Fabric 'mousedown' event handler
* @param {fabric.Event} fEvent - Current mousedown event on selected object
* @private
*/
_onFabricMouseDown(fEvent) {
const obj = fEvent.target;
if (obj && !obj.isType('text')) {
return;
}
if (this.isPrevEditing) {
this.isPrevEditing = false;
return;
}
this._fireAddText(fEvent);
}
/**
* Fire 'addText' event if object is not selected.
* @param {fabric.Event} fEvent - Current mousedown event on selected object
* @private
*/
_fireAddText(fEvent) {
const obj = fEvent.target;
const e = fEvent.e || {};
const originPointer = this.getCanvas().getPointer(e);
if (!obj) {
this.fire(events.ADD_TEXT, {
originPosition: {
x: originPointer.x,
y: originPointer.y,
},
clientPosition: {
x: e.clientX || 0,
y: e.clientY || 0,
},
});
}
}
/**
* Fabric mouseup event handler
* @param {fabric.Event} fEvent - Current mousedown event on selected object
* @private
*/
_onFabricMouseUp(fEvent) {
const { target } = fEvent;
const newClickTime = new Date().getTime();
if (this._isDoubleClick(newClickTime) && !target.isEditing) {
target.enterEditing();
}
if (target.isEditing) {
this.fire(events.TEXT_EDITING); // fire editing text event
}
this._lastClickTime = newClickTime;
}
/**
* Get state of firing double click event
* @param {Date} newClickTime - Current clicked time
* @returns {boolean} Whether double clicked or not
* @private
*/
_isDoubleClick(newClickTime) {
return newClickTime - this._lastClickTime < DBCLICK_TIME;
}
}
export default Text;
@@ -0,0 +1,708 @@
import { fabric } from 'fabric';
import Component from '@/interface/component';
import { clamp } from '@/util';
import { componentNames, eventNames, keyCodes, zoomModes } from '@/consts';
const MOUSE_MOVE_THRESHOLD = 10;
const DEFAULT_SCROLL_OPTION = {
left: 0,
top: 0,
width: 0,
height: 0,
stroke: '#000000',
strokeWidth: 0,
fill: '#000000',
opacity: 0.4,
evented: false,
selectable: false,
hoverCursor: 'auto',
};
const DEFAULT_VERTICAL_SCROLL_RATIO = {
SIZE: 0.0045,
MARGIN: 0.003,
BORDER_RADIUS: 0.003,
};
const DEFAULT_HORIZONTAL_SCROLL_RATIO = {
SIZE: 0.0066,
MARGIN: 0.0044,
BORDER_RADIUS: 0.003,
};
const DEFAULT_ZOOM_LEVEL = 1.0;
const {
ZOOM_CHANGED,
ADD_TEXT,
TEXT_EDITING,
OBJECT_MODIFIED,
KEY_DOWN,
KEY_UP,
HAND_STARTED,
HAND_STOPPED,
} = eventNames;
/**
* Zoom components
* @param {Graphics} graphics - Graphics instance
* @extends {Component}
* @class Zoom
* @ignore
*/
class Zoom extends Component {
constructor(graphics) {
super(componentNames.ZOOM, graphics);
/**
* zoomArea
* @type {?fabric.Rect}
* @private
*/
this.zoomArea = null;
/**
* Start point of zoom area
* @type {?{x: number, y: number}}
*/
this._startPoint = null;
/**
* Center point of every zoom
* @type {Array.<{prevZoomLevel: number, zoomLevel: number, x: number, y: number}>}
*/
this._centerPoints = [];
/**
* Zoom level (default: 100%(1.0), max: 400%(4.0))
* @type {number}
*/
this.zoomLevel = DEFAULT_ZOOM_LEVEL;
/**
* Zoom mode ('normal', 'zoom', 'hand')
* @type {string}
*/
this.zoomMode = zoomModes.DEFAULT;
/**
* Listeners
* @type {Object.<string, Function>}
* @private
*/
this._listeners = {
startZoom: this._onMouseDownWithZoomMode.bind(this),
moveZoom: this._onMouseMoveWithZoomMode.bind(this),
stopZoom: this._onMouseUpWithZoomMode.bind(this),
startHand: this._onMouseDownWithHandMode.bind(this),
moveHand: this._onMouseMoveWithHandMode.bind(this),
stopHand: this._onMouseUpWithHandMode.bind(this),
zoomChanged: this._changeScrollState.bind(this),
keydown: this._startHandModeWithSpaceBar.bind(this),
keyup: this._endHandModeWithSpaceBar.bind(this),
};
const canvas = this.getCanvas();
/**
* Width:Height ratio (ex. width=1.5, height=1 -> aspectRatio=1.5)
* @private
*/
this.aspectRatio = canvas.width / canvas.height;
/**
* vertical scroll bar
* @type {fabric.Rect}
* @private
*/
this._verticalScroll = new fabric.Rect(DEFAULT_SCROLL_OPTION);
/**
* horizontal scroll bar
* @type {fabric.Rect}
* @private
*/
this._horizontalScroll = new fabric.Rect(DEFAULT_SCROLL_OPTION);
canvas.on(ZOOM_CHANGED, this._listeners.zoomChanged);
this.graphics.on(ADD_TEXT, this._startTextEditingHandler.bind(this));
this.graphics.on(TEXT_EDITING, this._startTextEditingHandler.bind(this));
this.graphics.on(OBJECT_MODIFIED, this._stopTextEditingHandler.bind(this));
}
/**
* Attach zoom keyboard events
*/
attachKeyboardZoomEvents() {
fabric.util.addListener(document, KEY_DOWN, this._listeners.keydown);
fabric.util.addListener(document, KEY_UP, this._listeners.keyup);
}
/**
* Detach zoom keyboard events
*/
detachKeyboardZoomEvents() {
fabric.util.removeListener(document, KEY_DOWN, this._listeners.keydown);
fabric.util.removeListener(document, KEY_UP, this._listeners.keyup);
}
/**
* Handler when you started editing text
* @private
*/
_startTextEditingHandler() {
this.isTextEditing = true;
}
/**
* Handler when you stopped editing text
* @private
*/
_stopTextEditingHandler() {
this.isTextEditing = false;
}
/**
* Handler who turns on hand mode when the space bar is down
* @param {KeyboardEvent} e - Event object
* @private
*/
_startHandModeWithSpaceBar(e) {
if (this.withSpace || this.isTextEditing) {
return;
}
if (e.keyCode === keyCodes.SPACE) {
this.withSpace = true;
this.startHandMode();
}
}
/**
* Handler who turns off hand mode when space bar is up
* @param {KeyboardEvent} e - Event object
* @private
*/
_endHandModeWithSpaceBar(e) {
if (e.keyCode === keyCodes.SPACE) {
this.withSpace = false;
this.endHandMode();
}
}
/**
* Start zoom-in mode
*/
startZoomInMode() {
if (this.zoomArea) {
return;
}
this.endHandMode();
this.zoomMode = zoomModes.ZOOM;
const canvas = this.getCanvas();
this._changeObjectsEventedState(false);
this.zoomArea = new fabric.Rect({
left: 0,
top: 0,
width: 0.5,
height: 0.5,
stroke: 'black',
strokeWidth: 1,
fill: 'transparent',
hoverCursor: 'zoom-in',
});
canvas.discardActiveObject();
canvas.add(this.zoomArea);
canvas.on('mouse:down', this._listeners.startZoom);
canvas.selection = false;
canvas.defaultCursor = 'zoom-in';
}
/**
* End zoom-in mode
*/
endZoomInMode() {
this.zoomMode = zoomModes.DEFAULT;
const canvas = this.getCanvas();
const { startZoom, moveZoom, stopZoom } = this._listeners;
canvas.selection = true;
canvas.defaultCursor = 'auto';
canvas.off({
'mouse:down': startZoom,
'mouse:move': moveZoom,
'mouse:up': stopZoom,
});
this._changeObjectsEventedState(true);
canvas.remove(this.zoomArea);
this.zoomArea = null;
}
/**
* Start zoom drawing mode
*/
start() {
this.zoomArea = null;
this._startPoint = null;
this._startHandPoint = null;
}
/**
* Stop zoom drawing mode
*/
end() {
this.endZoomInMode();
this.endHandMode();
}
/**
* Start hand mode
*/
startHandMode() {
this.endZoomInMode();
this.zoomMode = zoomModes.HAND;
const canvas = this.getCanvas();
this._changeObjectsEventedState(false);
canvas.discardActiveObject();
canvas.off('mouse:down', this._listeners.startHand);
canvas.on('mouse:down', this._listeners.startHand);
canvas.selection = false;
canvas.defaultCursor = 'grab';
canvas.fire(HAND_STARTED);
}
/**
* Stop hand mode
*/
endHandMode() {
this.zoomMode = zoomModes.DEFAULT;
const canvas = this.getCanvas();
this._changeObjectsEventedState(true);
canvas.off('mouse:down', this._listeners.startHand);
canvas.selection = true;
canvas.defaultCursor = 'auto';
this._startHandPoint = null;
canvas.fire(HAND_STOPPED);
}
/**
* onMousedown handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onMouseDownWithZoomMode({ target, e }) {
if (target) {
return;
}
const canvas = this.getCanvas();
canvas.selection = false;
this._startPoint = canvas.getPointer(e);
this.zoomArea.set({ width: 0, height: 0 });
const { moveZoom, stopZoom } = this._listeners;
canvas.on({
'mouse:move': moveZoom,
'mouse:up': stopZoom,
});
}
/**
* onMousemove handler in fabric canvas
* @param {{e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onMouseMoveWithZoomMode({ e }) {
const canvas = this.getCanvas();
const pointer = canvas.getPointer(e);
const { x, y } = pointer;
const { zoomArea, _startPoint } = this;
const deltaX = Math.abs(x - _startPoint.x);
const deltaY = Math.abs(y - _startPoint.y);
if (deltaX + deltaY > MOUSE_MOVE_THRESHOLD) {
canvas.remove(zoomArea);
zoomArea.set(this._calcRectDimensionFromPoint(x, y));
canvas.add(zoomArea);
}
}
/**
* Get rect dimension setting from Canvas-Mouse-Position(x, y)
* @param {number} x - Canvas-Mouse-Position x
* @param {number} y - Canvas-Mouse-Position Y
* @returns {{left: number, top: number, width: number, height: number}}
* @private
*/
_calcRectDimensionFromPoint(x, y) {
const canvas = this.getCanvas();
const canvasWidth = canvas.getWidth();
const canvasHeight = canvas.getHeight();
const { x: startX, y: startY } = this._startPoint;
const { min } = Math;
const left = min(startX, x);
const top = min(startY, y);
const width = clamp(x, startX, canvasWidth) - left; // (startX <= x(mouse) <= canvasWidth) - left
const height = clamp(y, startY, canvasHeight) - top; // (startY <= y(mouse) <= canvasHeight) - top
return { left, top, width, height };
}
/**
* onMouseup handler in fabric canvas
* @private
*/
_onMouseUpWithZoomMode() {
let { zoomLevel } = this;
const { zoomArea } = this;
const { moveZoom, stopZoom } = this._listeners;
const canvas = this.getCanvas();
const center = this._getCenterPoint();
const { x, y } = center;
if (!this._isMaxZoomLevel()) {
this._centerPoints.push({
x,
y,
prevZoomLevel: zoomLevel,
zoomLevel: zoomLevel + 1,
});
zoomLevel += 1;
canvas.zoomToPoint({ x, y }, zoomLevel);
this._fireZoomChanged(canvas, zoomLevel);
this.zoomLevel = zoomLevel;
}
canvas.off({
'mouse:move': moveZoom,
'mouse:up': stopZoom,
});
canvas.remove(zoomArea);
this._startPoint = null;
}
/**
* Get center point
* @returns {{x: number, y: number}}
* @private
*/
_getCenterPoint() {
const { left, top, width, height } = this.zoomArea;
const { x, y } = this._startPoint;
const { aspectRatio } = this;
if (width < MOUSE_MOVE_THRESHOLD && height < MOUSE_MOVE_THRESHOLD) {
return { x, y };
}
return width > height
? { x: left + (aspectRatio * height) / 2, y: top + height / 2 }
: { x: left + width / 2, y: top + width / aspectRatio / 2 };
}
/**
* Zoom the canvas
* @param {{x: number, y: number}} center - center of zoom
* @param {?number} zoomLevel - zoom level
*/
zoom({ x, y }, zoomLevel = this.zoomLevel) {
const canvas = this.getCanvas();
const centerPoints = this._centerPoints;
for (let i = centerPoints.length - 1; i >= 0; i -= 1) {
if (centerPoints[i].zoomLevel < zoomLevel) {
break;
}
const { x: prevX, y: prevY, prevZoomLevel } = centerPoints.pop();
canvas.zoomToPoint({ x: prevX, y: prevY }, prevZoomLevel);
this.zoomLevel = prevZoomLevel;
}
canvas.zoomToPoint({ x, y }, zoomLevel);
if (!this._isDefaultZoomLevel(zoomLevel)) {
this._centerPoints.push({
x,
y,
zoomLevel,
prevZoomLevel: this.zoomLevel,
});
}
this.zoomLevel = zoomLevel;
this._fireZoomChanged(canvas, zoomLevel);
}
/**
* Zoom out one step
*/
zoomOut() {
const centerPoints = this._centerPoints;
if (!centerPoints.length) {
return;
}
const canvas = this.getCanvas();
const point = centerPoints.pop();
const { x, y, prevZoomLevel } = point;
if (this._isDefaultZoomLevel(prevZoomLevel)) {
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
} else {
canvas.zoomToPoint({ x, y }, prevZoomLevel);
}
this.zoomLevel = prevZoomLevel;
this._fireZoomChanged(canvas, this.zoomLevel);
}
/**
* Zoom reset
*/
resetZoom() {
const canvas = this.getCanvas();
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
this.zoomLevel = DEFAULT_ZOOM_LEVEL;
this._centerPoints = [];
this._fireZoomChanged(canvas, this.zoomLevel);
}
/**
* Whether zoom level is max (5.0)
* @returns {boolean}
* @private
*/
_isMaxZoomLevel() {
return this.zoomLevel >= 5.0;
}
/**
* Move point of zoom
* @param {{x: number, y: number}} delta - move amount
* @private
*/
_movePointOfZoom({ x: deltaX, y: deltaY }) {
const centerPoints = this._centerPoints;
if (!centerPoints.length) {
return;
}
const canvas = this.getCanvas();
const { zoomLevel } = this;
const point = centerPoints.pop();
const { x: originX, y: originY, prevZoomLevel } = point;
const x = originX - deltaX;
const y = originY - deltaY;
canvas.zoomToPoint({ x: originX, y: originY }, prevZoomLevel);
canvas.zoomToPoint({ x, y }, zoomLevel);
centerPoints.push({ x, y, prevZoomLevel, zoomLevel });
this._fireZoomChanged(canvas, zoomLevel);
}
/**
* onMouseDown handler in fabric canvas
* @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onMouseDownWithHandMode({ target, e }) {
if (target) {
return;
}
const canvas = this.getCanvas();
if (this.zoomLevel <= DEFAULT_ZOOM_LEVEL) {
return;
}
canvas.selection = false;
this._startHandPoint = canvas.getPointer(e);
const { moveHand, stopHand } = this._listeners;
canvas.on({
'mouse:move': moveHand,
'mouse:up': stopHand,
});
}
/**
* onMouseMove handler in fabric canvas
* @param {{e: MouseEvent}} fEvent - Fabric event
* @private
*/
_onMouseMoveWithHandMode({ e }) {
const canvas = this.getCanvas();
const { x, y } = canvas.getPointer(e);
const deltaX = x - this._startHandPoint.x;
const deltaY = y - this._startHandPoint.y;
this._movePointOfZoom({ x: deltaX, y: deltaY });
}
/**
* onMouseUp handler in fabric canvas
* @private
*/
_onMouseUpWithHandMode() {
const canvas = this.getCanvas();
const { moveHand, stopHand } = this._listeners;
canvas.off({
'mouse:move': moveHand,
'mouse:up': stopHand,
});
this._startHandPoint = null;
}
/**
* onChangeZoom handler in fabric canvas
* @private
*/
_changeScrollState({ viewport, zoomLevel }) {
const canvas = this.getCanvas();
canvas.remove(this._verticalScroll);
canvas.remove(this._horizontalScroll);
if (this._isDefaultZoomLevel(zoomLevel)) {
return;
}
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
const { tl, tr, bl } = viewport;
const viewportWidth = tr.x - tl.x;
const viewportHeight = bl.y - tl.y;
const horizontalScrollWidth = (viewportWidth * viewportWidth) / canvasWidth;
const horizontalScrollHeight = viewportHeight * DEFAULT_HORIZONTAL_SCROLL_RATIO.SIZE;
const horizontalScrollLeft = clamp(
tl.x + (tl.x / canvasWidth) * viewportWidth,
tl.x,
tr.x - horizontalScrollWidth
);
const horizontalScrollMargin = viewportHeight * DEFAULT_HORIZONTAL_SCROLL_RATIO.MARGIN;
const horizontalScrollBorderRadius =
viewportHeight * DEFAULT_HORIZONTAL_SCROLL_RATIO.BORDER_RADIUS;
this._horizontalScroll.set({
left: horizontalScrollLeft,
top: bl.y - horizontalScrollHeight - horizontalScrollMargin,
width: horizontalScrollWidth,
height: horizontalScrollHeight,
rx: horizontalScrollBorderRadius,
ry: horizontalScrollBorderRadius,
});
const verticalScrollWidth = viewportWidth * DEFAULT_VERTICAL_SCROLL_RATIO.SIZE;
const verticalScrollHeight = (viewportHeight * viewportHeight) / canvasHeight;
const verticalScrollTop = clamp(
tl.y + (tl.y / canvasHeight) * viewportHeight,
tr.y,
bl.y - verticalScrollHeight
);
const verticalScrollMargin = viewportWidth * DEFAULT_VERTICAL_SCROLL_RATIO.MARGIN;
const verticalScrollBorderRadius = viewportWidth * DEFAULT_VERTICAL_SCROLL_RATIO.BORDER_RADIUS;
this._verticalScroll.set({
left: tr.x - verticalScrollWidth - verticalScrollMargin,
top: verticalScrollTop,
width: verticalScrollWidth,
height: verticalScrollHeight,
rx: verticalScrollBorderRadius,
ry: verticalScrollBorderRadius,
});
this._addScrollBar();
}
/**
* Change objects 'evented' state
* @param {boolean} [evented=true] - objects 'evented' state
*/
_changeObjectsEventedState(evented = true) {
const canvas = this.getCanvas();
canvas.forEachObject((obj) => {
// {@link http://fabricjs.com/docs/fabric.Object.html#evented}
obj.evented = evented;
});
}
/**
* Add scroll bar and set remove timer
*/
_addScrollBar() {
const canvas = this.getCanvas();
canvas.add(this._horizontalScroll);
canvas.add(this._verticalScroll);
if (this.scrollBarTid) {
clearTimeout(this.scrollBarTid);
}
this.scrollBarTid = setTimeout(() => {
canvas.remove(this._horizontalScroll);
canvas.remove(this._verticalScroll);
}, 3000);
}
/**
* Check zoom level is default zoom level (1.0)
* @param {number} zoomLevel - zoom level
* @returns {boolean} - whether zoom level is 1.0
*/
_isDefaultZoomLevel(zoomLevel) {
return zoomLevel === DEFAULT_ZOOM_LEVEL;
}
/**
* Fire 'zoomChanged' event
* @param {fabric.Canvas} canvas - fabric canvas
* @param {number} zoomLevel - 'zoomChanged' event params
*/
_fireZoomChanged(canvas, zoomLevel) {
canvas.fire(ZOOM_CHANGED, { viewport: canvas.calcViewportBoundaries(), zoomLevel });
}
/**
* Get zoom mode
*/
get mode() {
return this.zoomMode;
}
}
export default Zoom;
@@ -0,0 +1,406 @@
import { keyMirror } from '@/util';
/**
* Help features for zoom
* @type {Array.<string>}
*/
export const ZOOM_HELP_MENUS = ['zoomIn', 'zoomOut', 'hand'];
/**
* Help features for command
* @type {Array.<string>}
*/
export const COMMAND_HELP_MENUS = ['history', 'undo', 'redo', 'reset'];
/**
* Help features for delete
* @type {Array.<string>}
*/
export const DELETE_HELP_MENUS = ['delete', 'deleteAll'];
/**
* Editor help features
* @type {Array.<string>}
*/
export const HELP_MENUS = [...ZOOM_HELP_MENUS, ...COMMAND_HELP_MENUS, ...DELETE_HELP_MENUS];
/**
* Fill type for shape
* @type {Object.<string, string>}
*/
export const SHAPE_FILL_TYPE = {
FILTER: 'filter',
COLOR: 'color',
};
/**
* Shape type list
* @type {Array.<string>}
*/
export const SHAPE_TYPE = ['rect', 'circle', 'triangle'];
/**
* Object type
* @type {Object.<string, string>}
*/
export const OBJ_TYPE = {
CROPZONE: 'cropzone',
};
/**
* Filter type map
* @type {Object.<string, string>}
*/
export const filterType = {
VINTAGE: 'vintage',
SEPIA2: 'sepia2',
REMOVE_COLOR: 'removeColor',
COLOR_FILTER: 'colorFilter',
REMOVE_WHITE: 'removeWhite',
BLEND_COLOR: 'blendColor',
BLEND: 'blend',
};
/**
* Component names
* @type {Object.<string, string>}
*/
export const componentNames = keyMirror(
'IMAGE_LOADER',
'CROPPER',
'FLIP',
'ROTATION',
'FREE_DRAWING',
'LINE',
'TEXT',
'ICON',
'FILTER',
'SHAPE',
'ZOOM',
'RESIZE'
);
/**
* Shape default option
* @type {Object}
*/
export const SHAPE_DEFAULT_OPTIONS = {
lockSkewingX: true,
lockSkewingY: true,
bringForward: true,
isRegular: false,
};
/**
* Cropzone default option
* @type {Object}
*/
export const CROPZONE_DEFAULT_OPTIONS = {
hasRotatingPoint: false,
hasBorders: false,
lockScalingFlip: true,
lockRotation: true,
lockSkewingX: true,
lockSkewingY: true,
};
/**
* Command names
* @type {Object.<string, string>}
*/
export const commandNames = {
CLEAR_OBJECTS: 'clearObjects',
LOAD_IMAGE: 'loadImage',
FLIP_IMAGE: 'flip',
ROTATE_IMAGE: 'rotate',
ADD_OBJECT: 'addObject',
REMOVE_OBJECT: 'removeObject',
APPLY_FILTER: 'applyFilter',
REMOVE_FILTER: 'removeFilter',
ADD_ICON: 'addIcon',
CHANGE_ICON_COLOR: 'changeIconColor',
ADD_SHAPE: 'addShape',
CHANGE_SHAPE: 'changeShape',
ADD_TEXT: 'addText',
CHANGE_TEXT: 'changeText',
CHANGE_TEXT_STYLE: 'changeTextStyle',
ADD_IMAGE_OBJECT: 'addImageObject',
RESIZE_CANVAS_DIMENSION: 'resizeCanvasDimension',
SET_OBJECT_PROPERTIES: 'setObjectProperties',
SET_OBJECT_POSITION: 'setObjectPosition',
CHANGE_SELECTION: 'changeSelection',
RESIZE_IMAGE: 'resize',
};
/**
* Event names
* @type {Object.<string, string>}
*/
export const eventNames = {
OBJECT_ACTIVATED: 'objectActivated',
OBJECT_MOVED: 'objectMoved',
OBJECT_SCALED: 'objectScaled',
OBJECT_CREATED: 'objectCreated',
OBJECT_ROTATED: 'objectRotated',
OBJECT_ADDED: 'objectAdded',
OBJECT_MODIFIED: 'objectModified',
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',
SELECTION_CLEARED: 'selectionCleared',
SELECTION_CREATED: 'selectionCreated',
EXECUTE_COMMAND: 'executeCommand',
AFTER_UNDO: 'afterUndo',
AFTER_REDO: 'afterRedo',
ZOOM_CHANGED: 'zoomChanged',
HAND_STARTED: 'handStarted',
HAND_STOPPED: 'handStopped',
KEY_DOWN: 'keydown',
KEY_UP: 'keyup',
INPUT_BOX_EDITING_STARTED: 'inputBoxEditingStarted',
INPUT_BOX_EDITING_STOPPED: 'inputBoxEditingStopped',
FOCUS: 'focus',
BLUR: 'blur',
IMAGE_RESIZED: 'imageResized',
};
/**
* Selector names
* @type {Object.<string, string>}
*/
export const selectorNames = {
COLOR_PICKER_INPUT_BOX: '.tui-colorpicker-palette-hex',
};
/**
* History names
* @type {Object.<string, string>}
*/
export const historyNames = {
LOAD_IMAGE: 'Load',
LOAD_MASK_IMAGE: 'Mask',
ADD_MASK_IMAGE: 'Mask',
ADD_IMAGE_OBJECT: 'Mask',
CROP: 'Crop',
RESIZE: 'Resize',
APPLY_FILTER: 'Filter',
REMOVE_FILTER: 'Filter',
CHANGE_SHAPE: 'Shape',
CHANGE_ICON_COLOR: 'Icon',
ADD_TEXT: 'Text',
CHANGE_TEXT_STYLE: 'Text',
REMOVE_OBJECT: 'Delete',
CLEAR_OBJECTS: 'Delete',
};
/**
* Editor states
* @type {Object.<string, string>}
*/
export const drawingModes = keyMirror(
'NORMAL',
'CROPPER',
'FREE_DRAWING',
'LINE_DRAWING',
'TEXT',
'SHAPE',
'ICON',
'ZOOM',
'RESIZE'
);
/**
* Menu names with drawing mode
* @type {Object.<string, string>}
*/
export const drawingMenuNames = {
TEXT: 'text',
CROP: 'crop',
RESIZE: 'resize',
SHAPE: 'shape',
ZOOM: 'zoom',
};
/**
* Zoom modes
* @type {Object.<string, string>}
*/
export const zoomModes = {
DEFAULT: 'normal',
ZOOM: 'zoom',
HAND: 'hand',
};
/**
* Shortcut key values
* @type {Object.<string, number>}
*/
export const keyCodes = {
Z: 90,
Y: 89,
C: 67,
V: 86,
SHIFT: 16,
BACKSPACE: 8,
DEL: 46,
ARROW_DOWN: 40,
ARROW_UP: 38,
SPACE: 32,
DIGIT_0: 48,
DIGIT_9: 57,
};
/**
* Fabric object options
* @type {Object.<string, Object>}
*/
export const fObjectOptions = {
SELECTION_STYLE: {
borderColor: 'red',
cornerColor: 'green',
cornerSize: 10,
originX: 'center',
originY: 'center',
transparentCorners: false,
},
};
/**
* Promise reject messages
* @type {Object.<string, string>}
*/
export const rejectMessages = {
addedObject: 'The object is already added.',
flip: 'The flipX and flipY setting values are not changed.',
invalidDrawingMode: 'This operation is not supported in the drawing mode.',
invalidParameters: 'Invalid parameters.',
isLock: 'The executing command state is locked.',
loadImage: 'The background image is empty.',
loadingImageFailed: 'Invalid image loaded.',
noActiveObject: 'There is no active object.',
noObject: 'The object is not in canvas.',
redo: 'The promise of redo command is reject.',
rotation: 'The current angle is same the old angle.',
undo: 'The promise of undo command is reject.',
unsupportedOperation: 'Unsupported operation.',
unsupportedType: 'Unsupported object type.',
};
/**
* Default icon menu svg path
* @type {Object.<string, string>}
*/
export const 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',
};
export const defaultRotateRangeValues = {
realTimeEvent: true,
min: -360,
max: 360,
value: 0,
};
export const defaultDrawRangeValues = {
min: 5,
max: 30,
value: 12,
};
export const defaultShapeStrokeValues = {
realTimeEvent: true,
min: 2,
max: 300,
value: 3,
};
export const defaultTextRangeValues = {
realTimeEvent: true,
min: 10,
max: 100,
value: 50,
};
export const defaultFilterRangeValues = {
tintOpacityRange: {
realTimeEvent: true,
min: 0,
max: 1,
value: 0.7,
useDecimal: true,
},
removewhiteDistanceRange: {
realTimeEvent: true,
min: 0,
max: 1,
value: 0.2,
useDecimal: true,
},
brightnessRange: {
realTimeEvent: true,
min: -1,
max: 1,
value: 0,
useDecimal: true,
},
noiseRange: {
realTimeEvent: true,
min: 0,
max: 1000,
value: 100,
},
pixelateRange: {
realTimeEvent: true,
min: 2,
max: 20,
value: 4,
},
colorfilterThresholdRange: {
realTimeEvent: true,
min: 0,
max: 1,
value: 0.2,
useDecimal: true,
},
blurFilterRange: {
value: 0.1,
},
};
export const emptyCropRectValues = {
LEFT: 0,
TOP: 0,
WIDTH: 0.5,
HEIGHT: 0.5,
};
export const defaultResizePixelValues = {
realTimeEvent: true,
min: 32,
max: 4088,
value: 800,
};
@@ -0,0 +1,35 @@
import DrawingMode from '@/interface/drawingMode';
import { drawingModes, componentNames as components } from '@/consts';
/**
* CropperDrawingMode class
* @class
* @ignore
*/
class CropperDrawingMode extends DrawingMode {
constructor() {
super(drawingModes.CROPPER);
}
/**
* start this drawing mode
* @param {Graphics} graphics - Graphics instance
* @override
*/
start(graphics) {
const cropper = graphics.getComponent(components.CROPPER);
cropper.start();
}
/**
* stop this drawing mode
* @param {Graphics} graphics - Graphics instance
* @override
*/
end(graphics) {
const cropper = graphics.getComponent(components.CROPPER);
cropper.end();
}
}
export default CropperDrawingMode;
@@ -0,0 +1,36 @@
import DrawingMode from '@/interface/drawingMode';
import { drawingModes, componentNames as components } from '@/consts';
/**
* FreeDrawingMode class
* @class
* @ignore
*/
class FreeDrawingMode extends DrawingMode {
constructor() {
super(drawingModes.FREE_DRAWING);
}
/**
* start this drawing mode
* @param {Graphics} graphics - Graphics instance
* @param {{width: ?number, color: ?string}} [options] - Brush width & color
* @override
*/
start(graphics, options) {
const freeDrawing = graphics.getComponent(components.FREE_DRAWING);
freeDrawing.start(options);
}
/**
* stop this drawing mode
* @param {Graphics} graphics - Graphics instance
* @override
*/
end(graphics) {
const freeDrawing = graphics.getComponent(components.FREE_DRAWING);
freeDrawing.end();
}
}
export default FreeDrawingMode;
@@ -0,0 +1,35 @@
import DrawingMode from '@/interface/drawingMode';
import { drawingModes, componentNames as components } from '@/consts';
/**
* IconDrawingMode class
* @class
* @ignore
*/
class IconDrawingMode extends DrawingMode {
constructor() {
super(drawingModes.ICON);
}
/**
* start this drawing mode
* @param {Graphics} graphics - Graphics instance
* @override
*/
start(graphics) {
const icon = graphics.getComponent(components.ICON);
icon.start();
}
/**
* stop this drawing mode
* @param {Graphics} graphics - Graphics instance
* @override
*/
end(graphics) {
const icon = graphics.getComponent(components.ICON);
icon.end();
}
}
export default IconDrawingMode;
@@ -0,0 +1,36 @@
import DrawingMode from '@/interface/drawingMode';
import { drawingModes, componentNames as components } from '@/consts';
/**
* LineDrawingMode class
* @class
* @ignore
*/
class LineDrawingMode extends DrawingMode {
constructor() {
super(drawingModes.LINE_DRAWING);
}
/**
* start this drawing mode
* @param {Graphics} graphics - Graphics instance
* @param {{width: ?number, color: ?string}} [options] - Brush width & color
* @override
*/
start(graphics, options) {
const lineDrawing = graphics.getComponent(components.LINE);
lineDrawing.start(options);
}
/**
* stop this drawing mode
* @param {Graphics} graphics - Graphics instance
* @override
*/
end(graphics) {
const lineDrawing = graphics.getComponent(components.LINE);
lineDrawing.end();
}
}
export default LineDrawingMode;

Some files were not shown because too many files have changed in this diff Show More