[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": \"airbnb-base\",\n  \"env\": {\n    \"browser\": true\n  },\n  \"root\": true,\n  \"rules\": {\n    \"import/no-extraneous-dependencies\": \"off\",\n    \"no-param-reassign\": \"off\",\n    \"no-restricted-properties\": \"off\",\n    \"valid-jsdoc\": [\"error\", {\n      \"requireReturn\": false\n    }]\n  },\n  \"overrides\": [\n    {\n      \"files\": \"test/**/*.js\",\n      \"env\": {\n        \"jquery\": true,\n        \"mocha\": true\n      },\n      \"globals\": {\n        \"expect\": true\n      },\n      \"rules\": {\n        \"no-unused-expressions\": \"off\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.\n\nWe are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.\n\nExamples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.\n\nProject 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. Project maintainers who do not follow the Code of Conduct may be removed from the project team.\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.\n\nThis Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at https://www.contributor-covenant.org/version/1/0/0/code-of-conduct.html\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing to Cropper\n\n> Based on [Angular's contributing guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md).\n\nWe would love for you to contribute to Cropper and help make it even better than it is today! As a contributor, here are the guidelines we would like you to follow:\n\n- [Code of Conduct](#code-of-conduct)\n- [Question or Problem](#question-or-problem)\n- [Issues and Bugs](#issues-and-bugs)\n- [Feature Requests](#feature-requests)\n- [Submission Guidelines](#submission-guidelines)\n- [Coding Rules](#coding-rules)\n- [Commit Message Guidelines](#commit-message-guidelines)\n\n## Code of Conduct\n\nHelp us keep Cropper open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md).\n\n## Question or Problem\n\nDo not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [Stack Overflow](https://stackoverflow.com/questions/tagged/cropper) where the questions should be tagged with tag `cropper`.\n\nStack Overflow is a much better place to ask questions since:\n\n- There are thousands of people willing to help on Stack Overflow.\n- Questions and answers stay available for public viewing so your question / answer might help someone else.\n- Stack Overflow's voting system assures that the best answers are prominently visible.\n\nTo save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.\n\n## Issues and Bugs\n\nIf you find a bug in the source code, you can help us by [submitting an issue](#submitting-an-issue) to our [GitHub Repository](https://github.com/fengyuanchen/cropper). Even better, you can [submit a Pull Request](#submitting-a-pull-request-pr) with a fix.\n\n## Feature Requests\n\nYou can *request* a new feature by [submitting an issue](#submitting-an-issue) to our [GitHub Repository](https://github.com/fengyuanchen/cropper). If you would like to *implement* a new feature, please submit an issue with a proposal for your work first, to be sure that we can use it.\n\nPlease consider what kind of change it is:\n\n- For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed. This will also allow us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.\n- **Small Features** can be crafted and directly [submitted as a Pull Request](#submitting-a-pull-request-pr).\n\n## Submission Guidelines\n\n### Submitting an Issue\n\nBefore you submit an issue, please search the [issue tracker](https://github.com/fengyuanchen/cropper/issues), maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.\n\nWe want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using [CodePen](https://codepen.io/pen). Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:\n\n- version of Cropper used\n- 3rd-party libraries and their versions\n- and most importantly - a use-case that fails\n\nA minimal reproduce scenario using [CodePen](https://codepen.io/pen) allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If [CodePen](https://codepen.io/pen) is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.\n\nWe will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal reproduce scenario. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.\n\nUnfortunately we are not able to investigate / fix bugs without a minimal reproduce scenario, so if we don't hear back from you we are going to close an issue that don't have enough info to be reproduced.\n\nYou can file new issues by filling out our [new issue form](https://github.com/fengyuanchen/cropper/issues/new).\n\n### Submitting a Pull Request (PR)\n\nBefore you submit your Pull Request (PR) consider the following guidelines:\n\n1. Search [GitHub](https://github.com/fengyuanchen/cropper/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate effort.\n1. Fork the **fengyuanchen/cropper** repo.\n1. Make your changes in a new git branch:\n\n    ```shell\n    git checkout -b my-fix-branch master\n    ```\n\n1. Create your patch, **including appropriate test cases**.\n1. Follow our [Coding Rules](#coding-rules).\n1. Run the full Cropper test suite, and ensure that all tests pass.\n1. Commit your changes using a descriptive commit message that follows our [Commit Message Guidelines](#commit-message-guidelines). Adherence to these guidelines is necessary because release notes are automatically generated from these messages.\n\n    ```shell\n    git commit -a\n    ```\n\n    Note: the optional commit `-a` command line option will automatically \"add\" and \"rm\" edited files.\n1. Push your branch to GitHub:\n\n    ```shell\n    git push origin my-fix-branch\n    ```\n\n1. In GitHub, send a pull request to `cropper:master`.\n1. If we suggest changes then:\n    - Make the required updates.\n    - Re-run the Cropper test suites to ensure tests are still passing.\n    - Rebase your branch and force push to your GitHub repository (this will update your Pull Request):\n\n    ```shell\n    git rebase master -i\n    git push -f\n    ```\n\nThat's it! Thank you for your contribution!\n\n#### After your pull request is merged\n\nAfter your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:\n\n1. Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:\n\n    ```shell\n    git push origin --delete my-fix-branch\n    ```\n\n1. Check out the master branch:\n\n    ```shell\n    git checkout master -f\n    ```\n\n1. Delete the local branch:\n\n    ```shell\n    git branch -D my-fix-branch\n    ```\n\n1. Update your master with the latest upstream version:\n\n    ```shell\n    git pull --ff upstream master\n    ```\n\n## Coding Rules\n\nTo ensure consistency throughout the source code, keep these rules in mind as you are working:\n\n- All features or bug fixes **must be tested** by one or more specs (unit-tests).\n- All public API methods **must be documented**.\n- We follow [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript).\n\n## Commit Message Guidelines\n\n### Commit Message Format\n\nA commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:\n\n```\n<type>(<scope>): <subject>\n<BLANK LINE>\n<body>\n<BLANK LINE>\n<footer>\n```\n\nThe **header** is mandatory and the **scope** of the header is optional.\n\nAny line of the commit message cannot be longer 100 characters! This allows the message to be easier to read on GitHub as well as in various git tools.\n\nThe footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.\n\nHere are some [samples](https://github.com/fengyuanchen/cropper/commits/master).\n\n### Revert\n\nIf the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.\n\n### Type\n\nMust be one of the following:\n\n- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)\n- **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)\n- **docs**: Documentation only changes\n- **feat**: A new feature\n- **fix**: A bug fix\n- **perf**: A code change that improves performance\n- **refactor**: A code change that neither fixes a bug nor adds a feature\n- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)\n- **test**: Adding missing tests or correcting existing tests\n\n### Scope\n\nThe scope could be anything specifying place of the commit change. For example `move`, `zoom`, `rotate`, etc...\n\n### Subject\n\nThe subject contains succinct description of the change:\n\n- Use the imperative, present tense: \"change\" not \"changed\" nor \"changes\".\n- Don't capitalize first letter.\n- No dot (.) at the end.\n\n### Body\n\nJust as in the **subject**, use the imperative, present tense: \"change\" not \"changed\" nor \"changes\". The body should include the motivation for the change and contrast this with previous behavior.\n\n### Footer\n\nThe footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.\n\n**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## PR Checklist\n\nPlease check if your PR fulfills the following requirements:\n\n- [ ] The commit message follows our [guidelines](https://github.com/fengyuanchen/cropper/blob/master/.github/CONTRIBUTING.md#commit-message-guidelines).\n- [ ] Tests for the changes have been added (for bug fixes / features)\n- [ ] Docs have been added / updated (for bug fixes / features)\n\n## PR Type\n\nWhat kind of change does this PR introduce?\n\n<!-- Please check the one that applies to this PR using \"x\". -->\n\n- [ ] Bugfix\n- [ ] Feature\n- [ ] Code style update\n- [ ] Refactor\n- [ ] Build related changes\n- [ ] Documentation content changes\n- [ ] Other, Please describe:\n\n## What is the current behavior?\n\n<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->\n\nIssue Number: N/A\n\n## What is the new behavior?\n\n<!-- Describe what the new behavior would be. -->\n\n## Does this PR introduce a breaking change?\n\n- [ ] Yes\n- [ ] No\n\n<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->\n\n## Other information\n"
  },
  {
    "path": ".gitignore",
    "content": "*.local\n*.map\ncoverage\nnode_modules\n"
  },
  {
    "path": ".stylelintrc",
    "content": "{\n  \"extends\": \"stylelint-config-standard\",\n  \"plugins\": [\n    \"stylelint-order\"\n  ],\n  \"rules\": {\n    \"no-descending-specificity\": null,\n    \"order/properties-alphabetical-order\": true\n  }\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js: node\ncache: npm\nscript:\n  - npm run lint\n  - npm run build\n  - npm test\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## 4.1.0 (Oct 12, 2019)\n\n- Upgrade the built-in Cropper.js to v1.5.6.\n\n## 4.0.0 (Apr 1, 2018)\n\n- Upgrade the built-in Cropper.js to v1.3.4.\n\n## 4.0.0-beta (Mar 3, 2018)\n\n- Upgrade the built-in Cropper.js to v1.3.2.\n\n## 4.0.0-alpha (Mar 1, 2018)\n\n- The core code of Cropper is replaced with [Cropper.js](https://github.com/fengyuanchen/cropperjs) now.\n- Migration from Cropper 3.x:\n  - **Before:**\n    ```js\n    $().cropper({\n      crop(event) {\n        console.log(\n          event.x,\n          event.y,\n          event.width,\n          event.height,\n          event.rotate,\n          event.scaleX,\n          event.scaleY,\n        );\n      },\n    });\n    ```\n  - **After:**\n    ```js\n    $().cropper({\n      crop(event) {\n        console.log(\n          event.detail.x,\n          event.detail.y,\n          event.detail.width,\n          event.detail.height,\n          event.detail.rotate,\n          event.detail.scaleX,\n          event.detail.scaleY,\n        );\n      },\n    });\n    ```\n\n## 3.1.6 (Mar 1, 2018)\n\n- Fixed a bug of incorrect behavior of `viewMode: 2`.\n\n## 3.1.5 (Feb 25, 2018)\n\n- Fixed a bug of `getCroppedCanvas` method when provide `maxWidth` or `maxHeight` options (#953).\n\n## 3.1.4 (Jan 13, 2018)\n\n- Fixed a bug of rotation (#938).\n\n## 3.1.3 (Oct 21, 2017)\n\n- Fixed a bug of render when disable one of `rotatable` and `scalable` options.\n\n## 3.1.2 (Oct 18, 2017)\n\n- Normalize related decimal numbers when crop an image with canvas (#918).\n- Ignore unnecessary files when publish to NPM (#928).\n\n## 3.1.1 (Oct 11, 2017)\n\n- Supports to load in node environment.\n\n## 3.1.0 (Oct 8, 2017)\n\n- Added 4 new options to `getCroppedCanvas` method:  `minWidth`, `minHeight`, `maxWidth` and `maxHeight`.\n- Enhanced image scaling: the `scaleX` and `scaleY` values should only be `1` or `-1` before, but now they can be any numbers.\n- Improved crop box resizing behavior in the northeast, northwest, southeast and southwest directions.\n\n## 3.0.0 (Sep 3, 2017)\n\n- Improved crop box resizing behavior in the east, west, south and north directions.\n\n## 3.0.0-rc.3 (Jul 7, 2017)\n\n- Added two new options (`imageSmoothingEnabled` and `imageSmoothingQuality`) to `getCroppedCanvas` method.\n\n## 3.0.0-rc.2 (May 30, 2017)\n\n- Improved performance for large images(#856).\n- Fixed the issue of `ArrayBuffer` reference error in IE9 (#885).\n- Fixed an issue of canvas box initialization.\n\n## 3.0.0-rc.1 (Apr 30, 2017)\n\n- Use `window.jQuery` instead of `window.$` for browser side usage (#876).\n- Change the `main` field value from `dist/cropper.js` (UMD) to `dist/cropper.common.js` (CommonJS).\n- Added `module` and `browser` fields to `package.json`.\n\n## 3.0.0-rc (Mar 25, 2017)\n\n- Clear cached pointers correctly to avoid touch zoom problem.\n- Improve the `responsive` option (only available when the container width/height great than the minContainerWidth/Height)\n- Improve the `toggleDragModeOnDblclick` option (only available when the `dragMode` option is set to `crop` or `move`)\n\n## 3.0.0-beta (Feb 25, 2017)\n\n- Fixed the bug of rotate square image lead image shrink.\n- Improved RegExps for DataURL processing.\n\n## 3.0.0-alpha.1 (Jan 21, 2017)\n\n- Use CSS3 2D Transforms instead of `left` and `top` for better performance.\n- Set `withCredentials` attribute when read the image data by XMLHttpRequest.\n\n## 3.0.0-alpha (Jan 15, 2017)\n\n- Removed `build` event.\n- Renamed `built` event to `ready`.\n- Removed event namespace.\n- Ported code to ECMAScript 6.\n- Dropped IE8 support.\n- Improved event handler for Pointer Events (#824).\n- Improved setCropBoxData method.\n- Fixed a bug of auto crop when replace the image.\n\n## 2.3.4 (Sep 3, 2016)\n\n- Fixed a bug of cropping in view mode 1 and 2.\n- Fixed a bug of calling `ready` event twice when call `replace` method.\n- Fixed dependencies problem in the package.json file.\n\n## 2.3.3 (Aug 10, 2016)\n\n- Allow scroll when the `dragMode` is \"none\" on touch screens (#727).\n- Fixed the issue of orientation transform (#740).\n\n## 2.3.2 (Jun 8, 2016)\n\n- Fixed wrong property reference (#705)\n- Fixed the wrong place of the crop event triggering (#706).\n- Fixed the calling order of `scale` and `rotate` (#709).\n\n## 2.3.1 (May 28, 2016)\n\n- Improved the rotate and scale transform behavior (#633, idea by afeibus).\n- Improved the `getCroppedCanvas` method to return the whole canvas if it is not cropped (#666, PR by @vinnymac).\n- Check cross origin setting when load image by XMLHTTPRequest (#669)\n\n## 2.3.0 (Feb 22, 2016)\n\n- Added a new parameter to the `replace` method for applying filters.\n- Improved the image initializing for Safari (#120, #509).\n- Fixed incorrect size limitation of the crop box.\n- Fixed incorrect cropped canvas when scaleX or scaleY great than 1 (#598).\n\n## 2.2.5 (Jan 18, 2016)\n\n- Fixed crossOriginUrl undefined error when exists the `crossOrigin` property.\n\n## 2.2.4 (Jan 1, 2016)\n\n- Fixed a dimension bug in the \"getCroppedCanvas\" method.\n- Added an example for cropping round image.\n\n## 2.2.3 (Dec 28, 2015)\n\n- Supports to zoom from event triggering point.\n\n## 2.2.2 (Dec 24, 2015)\n\n- Limit wheel speed to prevent zoom too fast\n- Improve the `setCropBoxData` method\n\n## 2.2.1 (Dec 12, 2015)\n\n- Handle Data URL (Fixed #540: avoid to use XMLHttpRequest to open a Data URL)\n- Handle ajax error when load ArrayBuffer\n- Not to transform the image to base64 when Orientation equals to `1`\n\n## 2.2.0 (Dec 6, 2015)\n\n- Added a new option: `checkOrientation` (#120, #509)\n- Added a timestamp to the url of preview image (#531)\n\n## 2.1.0 (Dec 2, 2015)\n\n- Added new `restore` option\n\n## 2.0.2 (Nov 30, 2015)\n\n- Fixed #476: Floor the numerical parameters for `CanvasRenderingContext2D.drawImage`\n\n## 2.0.1 (Nov 18, 2015)\n\n- Improved new crop box creating\n\n## 2.0.0 (Nov 11, 2015)\n\n### Common\n\n- Supports four modes\n- Supports three drag modes\n- Improved the experience of cropping\n- Makes the crop box's borders and handlers visible when overflow\n- Fixed an issue of canvas limitation\n- Fixed an issue of cropping\n\n### Options\n\n- Added `viewMode`\n- Added `dragMode`\n- Renamed `touchDragZoom` to `zoomOnTouch`\n- Renamed `mouseWheelZoom` to `zoomOnWheel`\n- Renamed `doubleClickToggle` to `toggleDragModeOnDblclick`\n- Renamed `checkImageOrigin` to `checkCrossOrigin`\n- Removed `strict` (supported by `viewMode: 1`)\n- Removed `dragCrop` (supported by `dragMode: 'crop'`)\n\n### Methods\n\n- Added `moveTo`\n- Added `zoomTo`\n- Added `rotateTo`\n- Added `scaleX`\n- Added `scaleY`\n- Improved `getCanvasData` (added `naturalWidth` and `naturalHeight`)\n\n### Events\n\n- Improved `zoom` (changed `event.ratio` and added `event.oldRatio`)\n\n## 1.0.0 (Oct 10, 2015)\n\n- Improved canvas limitation\n- Improved preview\n- Improved test\n- Fixed an error in the `clear` method (missed parameters)\n- Fixed the issue of crop box limitation (#430)\n\n## 1.0.0-rc.1 (Sep 5, 2015)\n\n- Moved from Less to Sass\n- Fixed the issue of `destroy` method (#434)\n- Fixed the issue on IE8 (#319)\n- Added an example for customizing preview\n- Added download button to documentation\n- Added FAQ\n\n## 0.11.1 (Aug 22, 2015)\n\n- Optimize \"built\" and \"crop\" events\n- Improve the starting speed (#422)\n- Improve the building process (#428)\n- Fix event issue on IE8 (#319)\n\n## 0.11.0 (Aug 10, 2015)\n\n- Improve `setCropBoxData` method (#385)\n- Fix event issue on IE10 (#394)\n- Optimize code (use `var` for per variable)\n\n### Options\n\n- Add \"scalable\" option\n- Add \"wheelZoomRatio\" option\n- Convert \"crop\" option to \"crop\" event\n\n### Methods\n\n- Add \"scale\" method\n- Improve \"move\" method (the `offsetY` parameter is optional now)\n\n### Events\n\n- Rename \"dragstart\" to \"cropstart\"\n- Rename \"dragmove\" to \"cropmove\"\n- Rename \"dragend\" to \"cropend\"\n- Merge \"zoomin\" and \"zoomout\" to \"zoom\"\n- Merge \"crop\" option and \"change\" event to \"crop\" event\n\n## 0.10.1 (Jul 5, 2015)\n\n- Add Pointer Events support (#328)\n- Add RTL support (#342)\n- Add one new option: \"center\" (#367)\n- Allow cropper to grow vertically (#350)\n\n## 0.10.0 (Jun 8, 2015)\n\n- Add three new options: \"change\", \"cropBoxMovable\", \"doubleClickToggle\"\n- Change \"movable\" option (only for image)\n- Rename \"resizable\" to \"cropBoxResizable\"\n- Add one new event: \"change.cropper\"\n- Locking aspect ratio in \"free mode\" by holding shift key (#259)\n- Sync drag mode to crop box when it is not movable (#300)\n\n## 0.9.3 (May 10, 2015)\n\n- Add one new option: \"data\"\n- Add two new methods: \"setData\" (#290, #296), \"crop\" (#275)\n- Fix incorrect minWidth/Height size of canvas (#295)\n- Fix the strict mode bug (#280)\n- Fix the crop box resizing bug (#277)\n\n## 0.9.2 (Apr 18, 2015)\n\n- Improve strict mode to show full image\n- Add two new options: \"minCanvasWidth\" and \"minCanvasHeight\"\n- Reverse mouse wheeling zoom\n- Fix incorrect cursor in disabled state\n\n## 0.9.1 (Mar 21, 2015)\n\n- Fix the touch zoom issue (#206)\n- Fix the reset issue (#246)\n\n## 0.9.0 (Mar 15, 2015)\n\n- Wraps image with a virtual canvas (for zooming and rotating).\n- Limits image position and size in strict mode.\n- Supports multiple global croppers by default.\n- Outputs cropped canvas for display or get Data URL or get Blob\n- Identifies drag events with \"event.dragType\" property\n- Added zoom events for controlling the canvas (image) size.\n- Improved responsiveness for window resizing.\n\n### Options\n\n- Change \"minContainerWidth\" (default value: 300 -> 200)\n- Change \"minContainerHeight\" (default value: 150 -> 100)\n- Add \"strict\"\n- Add \"zoomin\"\n- Add \"zoomout\"\n- remove \"global\"\n\n### Methods\n\n- Change \"replace\" (not to change the original image any more)\n- Change \"getImageData\" (move rotation-related properties to canvas data)\n- Add \"getContainerData\"\n- Add \"getCanvasData\"\n- Add \"setCanvasData\"\n- Add \"getCroppedCanvas\"\n- Remove \"setImageData\" (replace with \"getCanvasData\")\n- Remove \"getDataURL\" (replace with \"getCroppedCanvas\")\n\n### Events\n\n- Add \"event.dragType\" property to drag events\n- Add \"zoomin.cropper\"\n- Add \"zoomout.cropper\"\n\n## 0.8.0 (Feb 19, 2015)\n\n- Refactored source code.\n- Compiles CSS with [Less](http://lesscss.org) CSS preprocessors.\n- Supports fixed container.\n- Supports rotation with CSS3 2D Transforms.\n\n### Options\n\n- Change the default value of \"aspectRatio\"\n- Rename \"done\" to \"crop\"\n- Rename \"dashed\" to \"guides\"\n- Rename \"multiple\" to \"global\"\n- Add \"background\"\n- Add \"highlight\"\n- Add \"responsive\"\n- Add \"mouseWheelZoom\"\n- Add \"touchDragZoom\"\n- Add \"minCropBoxWidth\"\n- Add \"minCropBoxHeight\"\n- Add \"minContainerWidth\"\n- Add \"minContainerHeight\"\n- Remove \"data\"\n- Remove \"minWidth\"\n- Remove \"minHeight\"\n- Remove \"maxWidth\"\n- Remove \"maxHeight\"\n\n### Methods\n\n- Change \"reset\"\n- Add \"setImageData\"\n- Add \"getCropBoxData\"\n- Add \"setCropBoxData\"\n- Add \"move\"\n- Remove \"setData\"\n\n## 0.7.9 (Feb 19, 2015)\n\n- Improve preview.\n- Improve rotation.\n- Improve responsiveness (#157).\n- Enable to move the image when the size of the crop box is the same as the container's (#186).\n\n## 0.7.8 (Feb 8, 2015)\n\n- Add two new options: \"minContainerWidth\" and \"minContainerHeight\".\n- Improve three methods: \"setAspectRatio\", \"destroy\" and \"disable\".\n- Improve mouse wheel zoom.\n- Improve drag resizing.\n\n## 0.7.7 (Jan 10, 2015)\n\n- Fix a bug of \"dragCrop\" option.\n- Add a timestamp to the url to bust cache when it's a cross-origin image (#148).\n- Fix the issue of \"autoCropArea\" option (#144).\n\n## 0.7.6 (Dec 20, 2014)\n\n- Fix events binding bugs.\n- Change the \"done\" option and the \"getData\" method (returns floating-point number) (#130).\n- Fix the rotation issue after replace the image (#139).\n\n## 0.7.5 (Nov 27, 2014)\n\n- Reset the ratio when replace the image.\n- Add a new option: \"checkImageOrigin\" (#119).\n- Prevent to call the \"done\" option when it's disabled (#107).\n- Improve the preview (#95).\n\n## 0.7.4 (Nov 24, 2014)\n\n- Improve \"getDataURL\" method, enable to customize the image sizes (#105).\n- Fix the issue of destroy (#101).\n- Fix the issue of canvas (#106).\n\n## 0.7.3 (Nov 15, 2014)\n\n- Supports cross-origin image (#96, #97).\n- Add a new option: \"autoCropArea\".\n- Improve \"movable\" option.\n- Output rotation degree by \"getImageData\" method (#94).\n\n## 0.7.2 (Nov 11, 2014)\n\n- Fix the image rotation error in Firefox (#92).\n\n## 0.7.1 (Nov 8, 2014)\n\n- Rebuild \"rotate\" method (#88).\n- Fix the issue of free ratio (#87).\n- Improve \"getDataURL\" method (#86).\n- Optimize event listeners.\n\n## 0.7.0 (Oct 12, 2014)\n\n- Supports zoom (#36, #79).\n- Supports rotation (#1, #81).\n- Add two new options: \"zoomable\" and \"rotatable\".\n- Add six new methods: \"enable\", \"disable\", \"zoom\", \"rotate\", \"getDataURL\" (#80) and \"setDragMode\".\n- Rename \"release\" method to \"clear\".\n- Rename \"setImgSrc\" method to \"replace\".\n- Rename \"getImgInfo\" method to \"getImageData\".\n- Some other improvements.\n\n## 0.6.2 (Oct 11, 2014)\n\n- Hide the modal when release the crop box.\n- Improve touch events.\n\n## 0.6.1 (Oct 3, 2014)\n\n- Fix an event error.\n\n## 0.6.0 (Sep 20, 2014)\n\n- Add six new options: \"dashed\", \"build\", \"built\", \"dragstart\", \"dragmove\" and \"dragend\".\n- Add three new events: \"dragstart.cropper\", \"dragmove.cropper\" and \"dragend.cropper\".\n- Remove an old event: \"render.cropper\".\n- Supports to toggle the dashed lines by \"dashed\" option (#68).\n- Fix the issue of events (#71).\n- Optimize the source code.\n\n## 0.5.5 (Sep 8, 2014)\n\n- Improve the render when the mouse out of the cropper container (#54).\n\n## 0.5.4 (Aug 30, 2014)\n\n- Fix typos: replace \"resizeable\" with \"resizable\" and \"moveable\" with \"movable\".\n\n## 0.5.3 (Aug 23, 2014)\n\n- Fix the issue (#64) that the crop box could not move after multiple touches.\n\n## 0.5.2 (Aug 16, 2014)\n\n- Fix a bug of type checking in the options.\n- Compress the cropper template string.\n\n## 0.5.1 (Aug 12, 2014)\n\n- Supports canvas (#55).\n\n## 0.5.0 (Aug 10, 2014)\n\n- Add a new option: \"multiple\".\n\n...\n\n## 0.4.0 (Jul 26, 2014)\n\n- Add eight new options: \"autoCrop\", \"dragCrop\", \"moveable\", \"resizeable\", \"maxWidth\", \"maxHeight\", \"minWidth\" and \"minHeight\".\n- Add three new methods: \"reset\", \"release\" and \"destroy\".\n- Add three new events: \"build.cropper\", \"built.cropper\" and \"render.cropper\".\n- Remove two old methods: \"enable\" and \"disable\".\n- Remove three old events: \"dragstart\", \"dragmove\" and \"dragend\".\n- Supports no conflict with the \"$.fn.cropper.noConflict\" method.\n\n...\n\n## 0.3.0 (May 18, 2014)\n\n- Supports touch.\n- Supports events.\n- Add three events: \"dargstart\", \"dargmove\" and \"dargend\".\n- Add a new method: \"setImgSrc\".\n\n...\n\n## 0.2.0 (Apr 23, 2014)\n\n- Supports free ratio.\n- Add a new option: \"data\".\n- Add four new methods: \"getData\", \"setData\", \"getImgInfo\" and \"setAspectRatio\".\n\n...\n\n## 0.1.0 (Feb 19, 2014)\n\n- Supports four options: \"aspectRatio\", \"done\", \"modal\" and \"preview\".\n- Supports two methods: \"enable\" and \"disable\".\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright 2014-present Chen Fengyuan and contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Cropper\n\n[![Build Status](https://img.shields.io/travis/fengyuanchen/cropper.svg)](https://travis-ci.org/fengyuanchen/cropper) [![Downloads](https://img.shields.io/npm/dm/cropper.svg)](https://www.npmjs.com/package/cropper) [![Version](https://img.shields.io/npm/v/cropper.svg)](https://www.npmjs.com/package/cropper) [![Dependencies](https://img.shields.io/david/fengyuanchen/cropper.svg)](https://www.npmjs.com/package/cropper)\n\n> A simple jQuery image cropping plugin. As of v4.0.0, the core code of Cropper is replaced with [Cropper.js](https://github.com/fengyuanchen/cropperjs).\n\n- [Demo](https://fengyuanchen.github.io/cropper)\n- [Cropper.js](https://github.com/fengyuanchen/cropperjs) - JavaScript image cropper (**recommended**)\n- [jquery-cropper](https://github.com/fengyuanchen/jquery-cropper) - A jQuery plugin wrapper for Cropper.js (**recommended** for jQuery users to use this instead of Cropper)\n\n## Main\n\n```text\ndist/\n├── cropper.css\n├── cropper.min.css   (compressed)\n├── cropper.js        (UMD)\n├── cropper.min.js    (UMD, compressed)\n├── cropper.common.js (CommonJS, default)\n└── cropper.esm.js    (ES Module)\n```\n\n## Getting started\n\n### Installation\n\n```shell\nnpm install cropper jquery\n```\n\nInclude files:\n\n```html\n<script src=\"/path/to/jquery.js\"></script><!-- jQuery is required -->\n<link  href=\"/path/to/cropper.css\" rel=\"stylesheet\">\n<script src=\"/path/to/cropper.js\"></script>\n```\n\n### Usage\n\nInitialize with `$.fn.cropper` method.\n\n```html\n<!-- Wrap the image or canvas element with a block element (container) -->\n<div>\n  <img id=\"image\" src=\"picture.jpg\">\n</div>\n```\n\n```css\n/* Limit image width to avoid overflow the container */\nimg {\n  max-width: 100%; /* This rule is very important, please do not ignore this! */\n}\n```\n\n```js\nvar $image = $('#image');\n\n$image.cropper({\n  aspectRatio: 16 / 9,\n  crop: function(event) {\n    console.log(event.detail.x);\n    console.log(event.detail.y);\n    console.log(event.detail.width);\n    console.log(event.detail.height);\n    console.log(event.detail.rotate);\n    console.log(event.detail.scaleX);\n    console.log(event.detail.scaleY);\n  }\n});\n\n// Get the Cropper.js instance after initialized\nvar cropper = $image.data('cropper');\n```\n\n## Options\n\nSee the available [options](https://github.com/fengyuanchen/cropperjs#options) of Cropper.js.\n\n```js\n$('img').cropper(options);\n```\n\n## Methods\n\nSee the available [methods](https://github.com/fengyuanchen/cropperjs#methods) of Cropper.js.\n\n```js\n$('img').once('ready', function () {\n  $(this).cropper('method', argument1, , argument2, ..., argumentN);\n});\n```\n\n## Events\n\nSee the available [events](https://github.com/fengyuanchen/cropperjs#events) of Cropper.js.\n\n```js\n$('img').on('event', handler);\n```\n\n## No conflict\n\nIf you have to use other plugin with the same namespace, just call the `$.fn.cropper.noConflict` method to revert to it.\n\n```html\n<script src=\"other-plugin.js\"></script>\n<script src=\"cropper.js\"></script>\n<script>\n  $.fn.cropper.noConflict();\n  // Code that uses other plugin's \"$('img').cropper\" can follow here.\n</script>\n```\n\n## Browser support\n\nIt is the same as the [browser support of Cropper.js](https://github.com/fengyuanchen/cropperjs#browser-support). As a jQuery plugin, you also need to see the [jQuery Browser Support](https://jquery.com/browser-support/).\n\n## Contributing\n\nPlease read through our [contributing guidelines](.github/CONTRIBUTING.md).\n\n## Versioning\n\nMaintained under the [Semantic Versioning guidelines](https://semver.org/).\n\n## License\n\n[MIT](https://opensource.org/licenses/MIT) © [Chen Fengyuan](https://chenfengyuan.com/)\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        modules: false,\n      },\n    ],\n  ],\n  env: {\n    test: {\n      plugins: [\n        'istanbul',\n      ],\n    },\n  },\n};\n"
  },
  {
    "path": "composer.json",
    "content": "{\n  \"name\": \"fengyuanchen/cropper\",\n  \"license\": \"MIT\",\n  \"repositories\": [\n    {\n      \"type\": \"vcs\",\n      \"url\": \"https://github.com/fengyuanchen/cropper\"\n    }\n  ]\n}\n"
  },
  {
    "path": "dist/cropper.common.js",
    "content": "/*!\n * Cropper v4.1.0\n * https://fengyuanchen.github.io/cropper\n *\n * Copyright 2014-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2019-10-12T07:43:51.850Z\n */\n\n'use strict';\n\nfunction _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }\n\nvar $ = _interopDefault(require('jquery'));\n\nfunction _typeof(obj) {\n  if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n    _typeof = function (obj) {\n      return typeof obj;\n    };\n  } else {\n    _typeof = function (obj) {\n      return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n    };\n  }\n\n  return _typeof(obj);\n}\n\nfunction _classCallCheck(instance, Constructor) {\n  if (!(instance instanceof Constructor)) {\n    throw new TypeError(\"Cannot call a class as a function\");\n  }\n}\n\nfunction _defineProperties(target, props) {\n  for (var i = 0; i < props.length; i++) {\n    var descriptor = props[i];\n    descriptor.enumerable = descriptor.enumerable || false;\n    descriptor.configurable = true;\n    if (\"value\" in descriptor) descriptor.writable = true;\n    Object.defineProperty(target, descriptor.key, descriptor);\n  }\n}\n\nfunction _createClass(Constructor, protoProps, staticProps) {\n  if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n  if (staticProps) _defineProperties(Constructor, staticProps);\n  return Constructor;\n}\n\nfunction _defineProperty(obj, key, value) {\n  if (key in obj) {\n    Object.defineProperty(obj, key, {\n      value: value,\n      enumerable: true,\n      configurable: true,\n      writable: true\n    });\n  } else {\n    obj[key] = value;\n  }\n\n  return obj;\n}\n\nfunction ownKeys(object, enumerableOnly) {\n  var keys = Object.keys(object);\n\n  if (Object.getOwnPropertySymbols) {\n    var symbols = Object.getOwnPropertySymbols(object);\n    if (enumerableOnly) symbols = symbols.filter(function (sym) {\n      return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n    });\n    keys.push.apply(keys, symbols);\n  }\n\n  return keys;\n}\n\nfunction _objectSpread2(target) {\n  for (var i = 1; i < arguments.length; i++) {\n    var source = arguments[i] != null ? arguments[i] : {};\n\n    if (i % 2) {\n      ownKeys(source, true).forEach(function (key) {\n        _defineProperty(target, key, source[key]);\n      });\n    } else if (Object.getOwnPropertyDescriptors) {\n      Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n    } else {\n      ownKeys(source).forEach(function (key) {\n        Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n      });\n    }\n  }\n\n  return target;\n}\n\nfunction _toConsumableArray(arr) {\n  return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n}\n\nfunction _arrayWithoutHoles(arr) {\n  if (Array.isArray(arr)) {\n    for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n    return arr2;\n  }\n}\n\nfunction _iterableToArray(iter) {\n  if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter);\n}\n\nfunction _nonIterableSpread() {\n  throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n}\n\nvar IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';\nvar WINDOW = IS_BROWSER ? window : {};\nvar IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false;\nvar HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;\nvar NAMESPACE = 'cropper'; // Actions\n\nvar ACTION_ALL = 'all';\nvar ACTION_CROP = 'crop';\nvar ACTION_MOVE = 'move';\nvar ACTION_ZOOM = 'zoom';\nvar ACTION_EAST = 'e';\nvar ACTION_WEST = 'w';\nvar ACTION_SOUTH = 's';\nvar ACTION_NORTH = 'n';\nvar ACTION_NORTH_EAST = 'ne';\nvar ACTION_NORTH_WEST = 'nw';\nvar ACTION_SOUTH_EAST = 'se';\nvar ACTION_SOUTH_WEST = 'sw'; // Classes\n\nvar CLASS_CROP = \"\".concat(NAMESPACE, \"-crop\");\nvar CLASS_DISABLED = \"\".concat(NAMESPACE, \"-disabled\");\nvar CLASS_HIDDEN = \"\".concat(NAMESPACE, \"-hidden\");\nvar CLASS_HIDE = \"\".concat(NAMESPACE, \"-hide\");\nvar CLASS_INVISIBLE = \"\".concat(NAMESPACE, \"-invisible\");\nvar CLASS_MODAL = \"\".concat(NAMESPACE, \"-modal\");\nvar CLASS_MOVE = \"\".concat(NAMESPACE, \"-move\"); // Data keys\n\nvar DATA_ACTION = \"\".concat(NAMESPACE, \"Action\");\nvar DATA_PREVIEW = \"\".concat(NAMESPACE, \"Preview\"); // Drag modes\n\nvar DRAG_MODE_CROP = 'crop';\nvar DRAG_MODE_MOVE = 'move';\nvar DRAG_MODE_NONE = 'none'; // Events\n\nvar EVENT_CROP = 'crop';\nvar EVENT_CROP_END = 'cropend';\nvar EVENT_CROP_MOVE = 'cropmove';\nvar EVENT_CROP_START = 'cropstart';\nvar EVENT_DBLCLICK = 'dblclick';\nvar EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';\nvar EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';\nvar EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';\nvar EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;\nvar EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;\nvar EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;\nvar EVENT_READY = 'ready';\nvar EVENT_RESIZE = 'resize';\nvar EVENT_WHEEL = 'wheel';\nvar EVENT_ZOOM = 'zoom'; // Mime types\n\nvar MIME_TYPE_JPEG = 'image/jpeg'; // RegExps\n\nvar REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;\nvar REGEXP_DATA_URL = /^data:/;\nvar REGEXP_DATA_URL_JPEG = /^data:image\\/jpeg;base64,/;\nvar REGEXP_TAG_NAME = /^img|canvas$/i; // Misc\n// Inspired by the default width and height of a canvas element.\n\nvar MIN_CONTAINER_WIDTH = 200;\nvar MIN_CONTAINER_HEIGHT = 100;\n\nvar DEFAULTS = {\n  // Define the view mode of the cropper\n  viewMode: 0,\n  // 0, 1, 2, 3\n  // Define the dragging mode of the cropper\n  dragMode: DRAG_MODE_CROP,\n  // 'crop', 'move' or 'none'\n  // Define the initial aspect ratio of the crop box\n  initialAspectRatio: NaN,\n  // Define the aspect ratio of the crop box\n  aspectRatio: NaN,\n  // An object with the previous cropping result data\n  data: null,\n  // A selector for adding extra containers to preview\n  preview: '',\n  // Re-render the cropper when resize the window\n  responsive: true,\n  // Restore the cropped area after resize the window\n  restore: true,\n  // Check if the current image is a cross-origin image\n  checkCrossOrigin: true,\n  // Check the current image's Exif Orientation information\n  checkOrientation: true,\n  // Show the black modal\n  modal: true,\n  // Show the dashed lines for guiding\n  guides: true,\n  // Show the center indicator for guiding\n  center: true,\n  // Show the white modal to highlight the crop box\n  highlight: true,\n  // Show the grid background\n  background: true,\n  // Enable to crop the image automatically when initialize\n  autoCrop: true,\n  // Define the percentage of automatic cropping area when initializes\n  autoCropArea: 0.8,\n  // Enable to move the image\n  movable: true,\n  // Enable to rotate the image\n  rotatable: true,\n  // Enable to scale the image\n  scalable: true,\n  // Enable to zoom the image\n  zoomable: true,\n  // Enable to zoom the image by dragging touch\n  zoomOnTouch: true,\n  // Enable to zoom the image by wheeling mouse\n  zoomOnWheel: true,\n  // Define zoom ratio when zoom the image by wheeling mouse\n  wheelZoomRatio: 0.1,\n  // Enable to move the crop box\n  cropBoxMovable: true,\n  // Enable to resize the crop box\n  cropBoxResizable: true,\n  // Toggle drag mode between \"crop\" and \"move\" when click twice on the cropper\n  toggleDragModeOnDblclick: true,\n  // Size limitation\n  minCanvasWidth: 0,\n  minCanvasHeight: 0,\n  minCropBoxWidth: 0,\n  minCropBoxHeight: 0,\n  minContainerWidth: 200,\n  minContainerHeight: 100,\n  // Shortcuts of events\n  ready: null,\n  cropstart: null,\n  cropmove: null,\n  cropend: null,\n  crop: null,\n  zoom: null\n};\n\nvar TEMPLATE = '<div class=\"cropper-container\" touch-action=\"none\">' + '<div class=\"cropper-wrap-box\">' + '<div class=\"cropper-canvas\"></div>' + '</div>' + '<div class=\"cropper-drag-box\"></div>' + '<div class=\"cropper-crop-box\">' + '<span class=\"cropper-view-box\"></span>' + '<span class=\"cropper-dashed dashed-h\"></span>' + '<span class=\"cropper-dashed dashed-v\"></span>' + '<span class=\"cropper-center\"></span>' + '<span class=\"cropper-face\"></span>' + '<span class=\"cropper-line line-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-line line-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-line line-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-line line-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-point point-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-point point-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-point point-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-ne\" data-cropper-action=\"ne\"></span>' + '<span class=\"cropper-point point-nw\" data-cropper-action=\"nw\"></span>' + '<span class=\"cropper-point point-sw\" data-cropper-action=\"sw\"></span>' + '<span class=\"cropper-point point-se\" data-cropper-action=\"se\"></span>' + '</div>' + '</div>';\n\n/**\n * Check if the given value is not a number.\n */\n\nvar isNaN = Number.isNaN || WINDOW.isNaN;\n/**\n * Check if the given value is a number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a number, else `false`.\n */\n\nfunction isNumber(value) {\n  return typeof value === 'number' && !isNaN(value);\n}\n/**\n * Check if the given value is a positive number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a positive number, else `false`.\n */\n\nvar isPositiveNumber = function isPositiveNumber(value) {\n  return value > 0 && value < Infinity;\n};\n/**\n * Check if the given value is undefined.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is undefined, else `false`.\n */\n\nfunction isUndefined(value) {\n  return typeof value === 'undefined';\n}\n/**\n * Check if the given value is an object.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is an object, else `false`.\n */\n\nfunction isObject(value) {\n  return _typeof(value) === 'object' && value !== null;\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n/**\n * Check if the given value is a plain object.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.\n */\n\nfunction isPlainObject(value) {\n  if (!isObject(value)) {\n    return false;\n  }\n\n  try {\n    var _constructor = value.constructor;\n    var prototype = _constructor.prototype;\n    return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');\n  } catch (error) {\n    return false;\n  }\n}\n/**\n * Check if the given value is a function.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a function, else `false`.\n */\n\nfunction isFunction(value) {\n  return typeof value === 'function';\n}\nvar slice = Array.prototype.slice;\n/**\n * Convert array-like or iterable object to an array.\n * @param {*} value - The value to convert.\n * @returns {Array} Returns a new array.\n */\n\nfunction toArray(value) {\n  return Array.from ? Array.from(value) : slice.call(value);\n}\n/**\n * Iterate the given data.\n * @param {*} data - The data to iterate.\n * @param {Function} callback - The process function for each element.\n * @returns {*} The original data.\n */\n\nfunction forEach(data, callback) {\n  if (data && isFunction(callback)) {\n    if (Array.isArray(data) || isNumber(data.length)\n    /* array-like */\n    ) {\n        toArray(data).forEach(function (value, key) {\n          callback.call(data, value, key, data);\n        });\n      } else if (isObject(data)) {\n      Object.keys(data).forEach(function (key) {\n        callback.call(data, data[key], key, data);\n      });\n    }\n  }\n\n  return data;\n}\n/**\n * Extend the given object.\n * @param {*} target - The target object to extend.\n * @param {*} args - The rest objects for merging to the target object.\n * @returns {Object} The extended object.\n */\n\nvar assign = Object.assign || function assign(target) {\n  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n    args[_key - 1] = arguments[_key];\n  }\n\n  if (isObject(target) && args.length > 0) {\n    args.forEach(function (arg) {\n      if (isObject(arg)) {\n        Object.keys(arg).forEach(function (key) {\n          target[key] = arg[key];\n        });\n      }\n    });\n  }\n\n  return target;\n};\nvar REGEXP_DECIMALS = /\\.\\d*(?:0|9){12}\\d*$/;\n/**\n * Normalize decimal number.\n * Check out {@link http://0.30000000000000004.com/}\n * @param {number} value - The value to normalize.\n * @param {number} [times=100000000000] - The times for normalizing.\n * @returns {number} Returns the normalized number.\n */\n\nfunction normalizeDecimalNumber(value) {\n  var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;\n  return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;\n}\nvar REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;\n/**\n * Apply styles to the given element.\n * @param {Element} element - The target element.\n * @param {Object} styles - The styles for applying.\n */\n\nfunction setStyle(element, styles) {\n  var style = element.style;\n  forEach(styles, function (value, property) {\n    if (REGEXP_SUFFIX.test(property) && isNumber(value)) {\n      value = \"\".concat(value, \"px\");\n    }\n\n    style[property] = value;\n  });\n}\n/**\n * Check if the given element has a special class.\n * @param {Element} element - The element to check.\n * @param {string} value - The class to search.\n * @returns {boolean} Returns `true` if the special class was found.\n */\n\nfunction hasClass(element, value) {\n  return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;\n}\n/**\n * Add classes to the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be added.\n */\n\nfunction addClass(element, value) {\n  if (!value) {\n    return;\n  }\n\n  if (isNumber(element.length)) {\n    forEach(element, function (elem) {\n      addClass(elem, value);\n    });\n    return;\n  }\n\n  if (element.classList) {\n    element.classList.add(value);\n    return;\n  }\n\n  var className = element.className.trim();\n\n  if (!className) {\n    element.className = value;\n  } else if (className.indexOf(value) < 0) {\n    element.className = \"\".concat(className, \" \").concat(value);\n  }\n}\n/**\n * Remove classes from the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be removed.\n */\n\nfunction removeClass(element, value) {\n  if (!value) {\n    return;\n  }\n\n  if (isNumber(element.length)) {\n    forEach(element, function (elem) {\n      removeClass(elem, value);\n    });\n    return;\n  }\n\n  if (element.classList) {\n    element.classList.remove(value);\n    return;\n  }\n\n  if (element.className.indexOf(value) >= 0) {\n    element.className = element.className.replace(value, '');\n  }\n}\n/**\n * Add or remove classes from the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be toggled.\n * @param {boolean} added - Add only.\n */\n\nfunction toggleClass(element, value, added) {\n  if (!value) {\n    return;\n  }\n\n  if (isNumber(element.length)) {\n    forEach(element, function (elem) {\n      toggleClass(elem, value, added);\n    });\n    return;\n  } // IE10-11 doesn't support the second parameter of `classList.toggle`\n\n\n  if (added) {\n    addClass(element, value);\n  } else {\n    removeClass(element, value);\n  }\n}\nvar REGEXP_CAMEL_CASE = /([a-z\\d])([A-Z])/g;\n/**\n * Transform the given string from camelCase to kebab-case\n * @param {string} value - The value to transform.\n * @returns {string} The transformed value.\n */\n\nfunction toParamCase(value) {\n  return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();\n}\n/**\n * Get data from the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to get.\n * @returns {string} The data value.\n */\n\nfunction getData(element, name) {\n  if (isObject(element[name])) {\n    return element[name];\n  }\n\n  if (element.dataset) {\n    return element.dataset[name];\n  }\n\n  return element.getAttribute(\"data-\".concat(toParamCase(name)));\n}\n/**\n * Set data to the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to set.\n * @param {string} data - The data value.\n */\n\nfunction setData(element, name, data) {\n  if (isObject(data)) {\n    element[name] = data;\n  } else if (element.dataset) {\n    element.dataset[name] = data;\n  } else {\n    element.setAttribute(\"data-\".concat(toParamCase(name)), data);\n  }\n}\n/**\n * Remove data from the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to remove.\n */\n\nfunction removeData(element, name) {\n  if (isObject(element[name])) {\n    try {\n      delete element[name];\n    } catch (error) {\n      element[name] = undefined;\n    }\n  } else if (element.dataset) {\n    // #128 Safari not allows to delete dataset property\n    try {\n      delete element.dataset[name];\n    } catch (error) {\n      element.dataset[name] = undefined;\n    }\n  } else {\n    element.removeAttribute(\"data-\".concat(toParamCase(name)));\n  }\n}\nvar REGEXP_SPACES = /\\s\\s*/;\n\nvar onceSupported = function () {\n  var supported = false;\n\n  if (IS_BROWSER) {\n    var once = false;\n\n    var listener = function listener() {};\n\n    var options = Object.defineProperty({}, 'once', {\n      get: function get() {\n        supported = true;\n        return once;\n      },\n\n      /**\n       * This setter can fix a `TypeError` in strict mode\n       * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}\n       * @param {boolean} value - The value to set\n       */\n      set: function set(value) {\n        once = value;\n      }\n    });\n    WINDOW.addEventListener('test', listener, options);\n    WINDOW.removeEventListener('test', listener, options);\n  }\n\n  return supported;\n}();\n/**\n * Remove event listener from the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Function} listener - The event listener.\n * @param {Object} options - The event options.\n */\n\n\nfunction removeListener(element, type, listener) {\n  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n  var handler = listener;\n  type.trim().split(REGEXP_SPACES).forEach(function (event) {\n    if (!onceSupported) {\n      var listeners = element.listeners;\n\n      if (listeners && listeners[event] && listeners[event][listener]) {\n        handler = listeners[event][listener];\n        delete listeners[event][listener];\n\n        if (Object.keys(listeners[event]).length === 0) {\n          delete listeners[event];\n        }\n\n        if (Object.keys(listeners).length === 0) {\n          delete element.listeners;\n        }\n      }\n    }\n\n    element.removeEventListener(event, handler, options);\n  });\n}\n/**\n * Add event listener to the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Function} listener - The event listener.\n * @param {Object} options - The event options.\n */\n\nfunction addListener(element, type, listener) {\n  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n  var _handler = listener;\n  type.trim().split(REGEXP_SPACES).forEach(function (event) {\n    if (options.once && !onceSupported) {\n      var _element$listeners = element.listeners,\n          listeners = _element$listeners === void 0 ? {} : _element$listeners;\n\n      _handler = function handler() {\n        delete listeners[event][listener];\n        element.removeEventListener(event, _handler, options);\n\n        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n          args[_key2] = arguments[_key2];\n        }\n\n        listener.apply(element, args);\n      };\n\n      if (!listeners[event]) {\n        listeners[event] = {};\n      }\n\n      if (listeners[event][listener]) {\n        element.removeEventListener(event, listeners[event][listener], options);\n      }\n\n      listeners[event][listener] = _handler;\n      element.listeners = listeners;\n    }\n\n    element.addEventListener(event, _handler, options);\n  });\n}\n/**\n * Dispatch event on the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Object} data - The additional event data.\n * @returns {boolean} Indicate if the event is default prevented or not.\n */\n\nfunction dispatchEvent(element, type, data) {\n  var event; // Event and CustomEvent on IE9-11 are global objects, not constructors\n\n  if (isFunction(Event) && isFunction(CustomEvent)) {\n    event = new CustomEvent(type, {\n      detail: data,\n      bubbles: true,\n      cancelable: true\n    });\n  } else {\n    event = document.createEvent('CustomEvent');\n    event.initCustomEvent(type, true, true, data);\n  }\n\n  return element.dispatchEvent(event);\n}\n/**\n * Get the offset base on the document.\n * @param {Element} element - The target element.\n * @returns {Object} The offset data.\n */\n\nfunction getOffset(element) {\n  var box = element.getBoundingClientRect();\n  return {\n    left: box.left + (window.pageXOffset - document.documentElement.clientLeft),\n    top: box.top + (window.pageYOffset - document.documentElement.clientTop)\n  };\n}\nvar location = WINDOW.location;\nvar REGEXP_ORIGINS = /^(\\w+:)\\/\\/([^:/?#]*):?(\\d*)/i;\n/**\n * Check if the given URL is a cross origin URL.\n * @param {string} url - The target URL.\n * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.\n */\n\nfunction isCrossOriginURL(url) {\n  var parts = url.match(REGEXP_ORIGINS);\n  return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);\n}\n/**\n * Add timestamp to the given URL.\n * @param {string} url - The target URL.\n * @returns {string} The result URL.\n */\n\nfunction addTimestamp(url) {\n  var timestamp = \"timestamp=\".concat(new Date().getTime());\n  return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;\n}\n/**\n * Get transforms base on the given object.\n * @param {Object} obj - The target object.\n * @returns {string} A string contains transform values.\n */\n\nfunction getTransforms(_ref) {\n  var rotate = _ref.rotate,\n      scaleX = _ref.scaleX,\n      scaleY = _ref.scaleY,\n      translateX = _ref.translateX,\n      translateY = _ref.translateY;\n  var values = [];\n\n  if (isNumber(translateX) && translateX !== 0) {\n    values.push(\"translateX(\".concat(translateX, \"px)\"));\n  }\n\n  if (isNumber(translateY) && translateY !== 0) {\n    values.push(\"translateY(\".concat(translateY, \"px)\"));\n  } // Rotate should come first before scale to match orientation transform\n\n\n  if (isNumber(rotate) && rotate !== 0) {\n    values.push(\"rotate(\".concat(rotate, \"deg)\"));\n  }\n\n  if (isNumber(scaleX) && scaleX !== 1) {\n    values.push(\"scaleX(\".concat(scaleX, \")\"));\n  }\n\n  if (isNumber(scaleY) && scaleY !== 1) {\n    values.push(\"scaleY(\".concat(scaleY, \")\"));\n  }\n\n  var transform = values.length ? values.join(' ') : 'none';\n  return {\n    WebkitTransform: transform,\n    msTransform: transform,\n    transform: transform\n  };\n}\n/**\n * Get the max ratio of a group of pointers.\n * @param {string} pointers - The target pointers.\n * @returns {number} The result ratio.\n */\n\nfunction getMaxZoomRatio(pointers) {\n  var pointers2 = _objectSpread2({}, pointers);\n\n  var ratios = [];\n  forEach(pointers, function (pointer, pointerId) {\n    delete pointers2[pointerId];\n    forEach(pointers2, function (pointer2) {\n      var x1 = Math.abs(pointer.startX - pointer2.startX);\n      var y1 = Math.abs(pointer.startY - pointer2.startY);\n      var x2 = Math.abs(pointer.endX - pointer2.endX);\n      var y2 = Math.abs(pointer.endY - pointer2.endY);\n      var z1 = Math.sqrt(x1 * x1 + y1 * y1);\n      var z2 = Math.sqrt(x2 * x2 + y2 * y2);\n      var ratio = (z2 - z1) / z1;\n      ratios.push(ratio);\n    });\n  });\n  ratios.sort(function (a, b) {\n    return Math.abs(a) < Math.abs(b);\n  });\n  return ratios[0];\n}\n/**\n * Get a pointer from an event object.\n * @param {Object} event - The target event object.\n * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.\n * @returns {Object} The result pointer contains start and/or end point coordinates.\n */\n\nfunction getPointer(_ref2, endOnly) {\n  var pageX = _ref2.pageX,\n      pageY = _ref2.pageY;\n  var end = {\n    endX: pageX,\n    endY: pageY\n  };\n  return endOnly ? end : _objectSpread2({\n    startX: pageX,\n    startY: pageY\n  }, end);\n}\n/**\n * Get the center point coordinate of a group of pointers.\n * @param {Object} pointers - The target pointers.\n * @returns {Object} The center point coordinate.\n */\n\nfunction getPointersCenter(pointers) {\n  var pageX = 0;\n  var pageY = 0;\n  var count = 0;\n  forEach(pointers, function (_ref3) {\n    var startX = _ref3.startX,\n        startY = _ref3.startY;\n    pageX += startX;\n    pageY += startY;\n    count += 1;\n  });\n  pageX /= count;\n  pageY /= count;\n  return {\n    pageX: pageX,\n    pageY: pageY\n  };\n}\n/**\n * Get the max sizes in a rectangle under the given aspect ratio.\n * @param {Object} data - The original sizes.\n * @param {string} [type='contain'] - The adjust type.\n * @returns {Object} The result sizes.\n */\n\nfunction getAdjustedSizes(_ref4) // or 'cover'\n{\n  var aspectRatio = _ref4.aspectRatio,\n      height = _ref4.height,\n      width = _ref4.width;\n  var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';\n  var isValidWidth = isPositiveNumber(width);\n  var isValidHeight = isPositiveNumber(height);\n\n  if (isValidWidth && isValidHeight) {\n    var adjustedWidth = height * aspectRatio;\n\n    if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {\n      height = width / aspectRatio;\n    } else {\n      width = height * aspectRatio;\n    }\n  } else if (isValidWidth) {\n    height = width / aspectRatio;\n  } else if (isValidHeight) {\n    width = height * aspectRatio;\n  }\n\n  return {\n    width: width,\n    height: height\n  };\n}\n/**\n * Get the new sizes of a rectangle after rotated.\n * @param {Object} data - The original sizes.\n * @returns {Object} The result sizes.\n */\n\nfunction getRotatedSizes(_ref5) {\n  var width = _ref5.width,\n      height = _ref5.height,\n      degree = _ref5.degree;\n  degree = Math.abs(degree) % 180;\n\n  if (degree === 90) {\n    return {\n      width: height,\n      height: width\n    };\n  }\n\n  var arc = degree % 90 * Math.PI / 180;\n  var sinArc = Math.sin(arc);\n  var cosArc = Math.cos(arc);\n  var newWidth = width * cosArc + height * sinArc;\n  var newHeight = width * sinArc + height * cosArc;\n  return degree > 90 ? {\n    width: newHeight,\n    height: newWidth\n  } : {\n    width: newWidth,\n    height: newHeight\n  };\n}\n/**\n * Get a canvas which drew the given image.\n * @param {HTMLImageElement} image - The image for drawing.\n * @param {Object} imageData - The image data.\n * @param {Object} canvasData - The canvas data.\n * @param {Object} options - The options.\n * @returns {HTMLCanvasElement} The result canvas.\n */\n\nfunction getSourceCanvas(image, _ref6, _ref7, _ref8) {\n  var imageAspectRatio = _ref6.aspectRatio,\n      imageNaturalWidth = _ref6.naturalWidth,\n      imageNaturalHeight = _ref6.naturalHeight,\n      _ref6$rotate = _ref6.rotate,\n      rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,\n      _ref6$scaleX = _ref6.scaleX,\n      scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,\n      _ref6$scaleY = _ref6.scaleY,\n      scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;\n  var aspectRatio = _ref7.aspectRatio,\n      naturalWidth = _ref7.naturalWidth,\n      naturalHeight = _ref7.naturalHeight;\n  var _ref8$fillColor = _ref8.fillColor,\n      fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor,\n      _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,\n      imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,\n      _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,\n      imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ,\n      _ref8$maxWidth = _ref8.maxWidth,\n      maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,\n      _ref8$maxHeight = _ref8.maxHeight,\n      maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,\n      _ref8$minWidth = _ref8.minWidth,\n      minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,\n      _ref8$minHeight = _ref8.minHeight,\n      minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;\n  var canvas = document.createElement('canvas');\n  var context = canvas.getContext('2d');\n  var maxSizes = getAdjustedSizes({\n    aspectRatio: aspectRatio,\n    width: maxWidth,\n    height: maxHeight\n  });\n  var minSizes = getAdjustedSizes({\n    aspectRatio: aspectRatio,\n    width: minWidth,\n    height: minHeight\n  }, 'cover');\n  var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));\n  var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as\n  // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90\n\n  var destMaxSizes = getAdjustedSizes({\n    aspectRatio: imageAspectRatio,\n    width: maxWidth,\n    height: maxHeight\n  });\n  var destMinSizes = getAdjustedSizes({\n    aspectRatio: imageAspectRatio,\n    width: minWidth,\n    height: minHeight\n  }, 'cover');\n  var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));\n  var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));\n  var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];\n  canvas.width = normalizeDecimalNumber(width);\n  canvas.height = normalizeDecimalNumber(height);\n  context.fillStyle = fillColor;\n  context.fillRect(0, 0, width, height);\n  context.save();\n  context.translate(width / 2, height / 2);\n  context.rotate(rotate * Math.PI / 180);\n  context.scale(scaleX, scaleY);\n  context.imageSmoothingEnabled = imageSmoothingEnabled;\n  context.imageSmoothingQuality = imageSmoothingQuality;\n  context.drawImage.apply(context, [image].concat(_toConsumableArray(params.map(function (param) {\n    return Math.floor(normalizeDecimalNumber(param));\n  }))));\n  context.restore();\n  return canvas;\n}\nvar fromCharCode = String.fromCharCode;\n/**\n * Get string from char code in data view.\n * @param {DataView} dataView - The data view for read.\n * @param {number} start - The start index.\n * @param {number} length - The read length.\n * @returns {string} The read result.\n */\n\nfunction getStringFromCharCode(dataView, start, length) {\n  var str = '';\n  length += start;\n\n  for (var i = start; i < length; i += 1) {\n    str += fromCharCode(dataView.getUint8(i));\n  }\n\n  return str;\n}\nvar REGEXP_DATA_URL_HEAD = /^data:.*,/;\n/**\n * Transform Data URL to array buffer.\n * @param {string} dataURL - The Data URL to transform.\n * @returns {ArrayBuffer} The result array buffer.\n */\n\nfunction dataURLToArrayBuffer(dataURL) {\n  var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n  var binary = atob(base64);\n  var arrayBuffer = new ArrayBuffer(binary.length);\n  var uint8 = new Uint8Array(arrayBuffer);\n  forEach(uint8, function (value, i) {\n    uint8[i] = binary.charCodeAt(i);\n  });\n  return arrayBuffer;\n}\n/**\n * Transform array buffer to Data URL.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.\n * @param {string} mimeType - The mime type of the Data URL.\n * @returns {string} The result Data URL.\n */\n\nfunction arrayBufferToDataURL(arrayBuffer, mimeType) {\n  var chunks = []; // Chunk Typed Array for better performance (#435)\n\n  var chunkSize = 8192;\n  var uint8 = new Uint8Array(arrayBuffer);\n\n  while (uint8.length > 0) {\n    // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9\n    // eslint-disable-next-line prefer-spread\n    chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));\n    uint8 = uint8.subarray(chunkSize);\n  }\n\n  return \"data:\".concat(mimeType, \";base64,\").concat(btoa(chunks.join('')));\n}\n/**\n * Get orientation value from given array buffer.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to read.\n * @returns {number} The read orientation value.\n */\n\nfunction resetAndGetOrientation(arrayBuffer) {\n  var dataView = new DataView(arrayBuffer);\n  var orientation; // Ignores range error when the image does not have correct Exif information\n\n  try {\n    var littleEndian;\n    var app1Start;\n    var ifdStart; // Only handle JPEG image (start by 0xFFD8)\n\n    if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n      var length = dataView.byteLength;\n      var offset = 2;\n\n      while (offset + 1 < length) {\n        if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n          app1Start = offset;\n          break;\n        }\n\n        offset += 1;\n      }\n    }\n\n    if (app1Start) {\n      var exifIDCode = app1Start + 4;\n      var tiffOffset = app1Start + 10;\n\n      if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n        var endianness = dataView.getUint16(tiffOffset);\n        littleEndian = endianness === 0x4949;\n\n        if (littleEndian || endianness === 0x4D4D\n        /* bigEndian */\n        ) {\n            if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n              var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n              if (firstIFDOffset >= 0x00000008) {\n                ifdStart = tiffOffset + firstIFDOffset;\n              }\n            }\n          }\n      }\n    }\n\n    if (ifdStart) {\n      var _length = dataView.getUint16(ifdStart, littleEndian);\n\n      var _offset;\n\n      var i;\n\n      for (i = 0; i < _length; i += 1) {\n        _offset = ifdStart + i * 12 + 2;\n\n        if (dataView.getUint16(_offset, littleEndian) === 0x0112\n        /* Orientation */\n        ) {\n            // 8 is the offset of the current tag's value\n            _offset += 8; // Get the original orientation value\n\n            orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value\n\n            dataView.setUint16(_offset, 1, littleEndian);\n            break;\n          }\n      }\n    }\n  } catch (error) {\n    orientation = 1;\n  }\n\n  return orientation;\n}\n/**\n * Parse Exif Orientation value.\n * @param {number} orientation - The orientation to parse.\n * @returns {Object} The parsed result.\n */\n\nfunction parseOrientation(orientation) {\n  var rotate = 0;\n  var scaleX = 1;\n  var scaleY = 1;\n\n  switch (orientation) {\n    // Flip horizontal\n    case 2:\n      scaleX = -1;\n      break;\n    // Rotate left 180°\n\n    case 3:\n      rotate = -180;\n      break;\n    // Flip vertical\n\n    case 4:\n      scaleY = -1;\n      break;\n    // Flip vertical and rotate right 90°\n\n    case 5:\n      rotate = 90;\n      scaleY = -1;\n      break;\n    // Rotate right 90°\n\n    case 6:\n      rotate = 90;\n      break;\n    // Flip horizontal and rotate right 90°\n\n    case 7:\n      rotate = 90;\n      scaleX = -1;\n      break;\n    // Rotate left 90°\n\n    case 8:\n      rotate = -90;\n      break;\n\n    default:\n  }\n\n  return {\n    rotate: rotate,\n    scaleX: scaleX,\n    scaleY: scaleY\n  };\n}\n\nvar render = {\n  render: function render() {\n    this.initContainer();\n    this.initCanvas();\n    this.initCropBox();\n    this.renderCanvas();\n\n    if (this.cropped) {\n      this.renderCropBox();\n    }\n  },\n  initContainer: function initContainer() {\n    var element = this.element,\n        options = this.options,\n        container = this.container,\n        cropper = this.cropper;\n    addClass(cropper, CLASS_HIDDEN);\n    removeClass(element, CLASS_HIDDEN);\n    var containerData = {\n      width: Math.max(container.offsetWidth, Number(options.minContainerWidth) || 200),\n      height: Math.max(container.offsetHeight, Number(options.minContainerHeight) || 100)\n    };\n    this.containerData = containerData;\n    setStyle(cropper, {\n      width: containerData.width,\n      height: containerData.height\n    });\n    addClass(element, CLASS_HIDDEN);\n    removeClass(cropper, CLASS_HIDDEN);\n  },\n  // Canvas (image wrapper)\n  initCanvas: function initCanvas() {\n    var containerData = this.containerData,\n        imageData = this.imageData;\n    var viewMode = this.options.viewMode;\n    var rotated = Math.abs(imageData.rotate) % 180 === 90;\n    var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;\n    var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;\n    var aspectRatio = naturalWidth / naturalHeight;\n    var canvasWidth = containerData.width;\n    var canvasHeight = containerData.height;\n\n    if (containerData.height * aspectRatio > containerData.width) {\n      if (viewMode === 3) {\n        canvasWidth = containerData.height * aspectRatio;\n      } else {\n        canvasHeight = containerData.width / aspectRatio;\n      }\n    } else if (viewMode === 3) {\n      canvasHeight = containerData.width / aspectRatio;\n    } else {\n      canvasWidth = containerData.height * aspectRatio;\n    }\n\n    var canvasData = {\n      aspectRatio: aspectRatio,\n      naturalWidth: naturalWidth,\n      naturalHeight: naturalHeight,\n      width: canvasWidth,\n      height: canvasHeight\n    };\n    canvasData.left = (containerData.width - canvasWidth) / 2;\n    canvasData.top = (containerData.height - canvasHeight) / 2;\n    canvasData.oldLeft = canvasData.left;\n    canvasData.oldTop = canvasData.top;\n    this.canvasData = canvasData;\n    this.limited = viewMode === 1 || viewMode === 2;\n    this.limitCanvas(true, true);\n    this.initialImageData = assign({}, imageData);\n    this.initialCanvasData = assign({}, canvasData);\n  },\n  limitCanvas: function limitCanvas(sizeLimited, positionLimited) {\n    var options = this.options,\n        containerData = this.containerData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData;\n    var viewMode = options.viewMode;\n    var aspectRatio = canvasData.aspectRatio;\n    var cropped = this.cropped && cropBoxData;\n\n    if (sizeLimited) {\n      var minCanvasWidth = Number(options.minCanvasWidth) || 0;\n      var minCanvasHeight = Number(options.minCanvasHeight) || 0;\n\n      if (viewMode > 1) {\n        minCanvasWidth = Math.max(minCanvasWidth, containerData.width);\n        minCanvasHeight = Math.max(minCanvasHeight, containerData.height);\n\n        if (viewMode === 3) {\n          if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n            minCanvasWidth = minCanvasHeight * aspectRatio;\n          } else {\n            minCanvasHeight = minCanvasWidth / aspectRatio;\n          }\n        }\n      } else if (viewMode > 0) {\n        if (minCanvasWidth) {\n          minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);\n        } else if (minCanvasHeight) {\n          minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);\n        } else if (cropped) {\n          minCanvasWidth = cropBoxData.width;\n          minCanvasHeight = cropBoxData.height;\n\n          if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n            minCanvasWidth = minCanvasHeight * aspectRatio;\n          } else {\n            minCanvasHeight = minCanvasWidth / aspectRatio;\n          }\n        }\n      }\n\n      var _getAdjustedSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: minCanvasWidth,\n        height: minCanvasHeight\n      });\n\n      minCanvasWidth = _getAdjustedSizes.width;\n      minCanvasHeight = _getAdjustedSizes.height;\n      canvasData.minWidth = minCanvasWidth;\n      canvasData.minHeight = minCanvasHeight;\n      canvasData.maxWidth = Infinity;\n      canvasData.maxHeight = Infinity;\n    }\n\n    if (positionLimited) {\n      if (viewMode > (cropped ? 0 : 1)) {\n        var newCanvasLeft = containerData.width - canvasData.width;\n        var newCanvasTop = containerData.height - canvasData.height;\n        canvasData.minLeft = Math.min(0, newCanvasLeft);\n        canvasData.minTop = Math.min(0, newCanvasTop);\n        canvasData.maxLeft = Math.max(0, newCanvasLeft);\n        canvasData.maxTop = Math.max(0, newCanvasTop);\n\n        if (cropped && this.limited) {\n          canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));\n          canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));\n          canvasData.maxLeft = cropBoxData.left;\n          canvasData.maxTop = cropBoxData.top;\n\n          if (viewMode === 2) {\n            if (canvasData.width >= containerData.width) {\n              canvasData.minLeft = Math.min(0, newCanvasLeft);\n              canvasData.maxLeft = Math.max(0, newCanvasLeft);\n            }\n\n            if (canvasData.height >= containerData.height) {\n              canvasData.minTop = Math.min(0, newCanvasTop);\n              canvasData.maxTop = Math.max(0, newCanvasTop);\n            }\n          }\n        }\n      } else {\n        canvasData.minLeft = -canvasData.width;\n        canvasData.minTop = -canvasData.height;\n        canvasData.maxLeft = containerData.width;\n        canvasData.maxTop = containerData.height;\n      }\n    }\n  },\n  renderCanvas: function renderCanvas(changed, transformed) {\n    var canvasData = this.canvasData,\n        imageData = this.imageData;\n\n    if (transformed) {\n      var _getRotatedSizes = getRotatedSizes({\n        width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),\n        height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),\n        degree: imageData.rotate || 0\n      }),\n          naturalWidth = _getRotatedSizes.width,\n          naturalHeight = _getRotatedSizes.height;\n\n      var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);\n      var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);\n      canvasData.left -= (width - canvasData.width) / 2;\n      canvasData.top -= (height - canvasData.height) / 2;\n      canvasData.width = width;\n      canvasData.height = height;\n      canvasData.aspectRatio = naturalWidth / naturalHeight;\n      canvasData.naturalWidth = naturalWidth;\n      canvasData.naturalHeight = naturalHeight;\n      this.limitCanvas(true, false);\n    }\n\n    if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {\n      canvasData.left = canvasData.oldLeft;\n    }\n\n    if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {\n      canvasData.top = canvasData.oldTop;\n    }\n\n    canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);\n    canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);\n    this.limitCanvas(false, true);\n    canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);\n    canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);\n    canvasData.oldLeft = canvasData.left;\n    canvasData.oldTop = canvasData.top;\n    setStyle(this.canvas, assign({\n      width: canvasData.width,\n      height: canvasData.height\n    }, getTransforms({\n      translateX: canvasData.left,\n      translateY: canvasData.top\n    })));\n    this.renderImage(changed);\n\n    if (this.cropped && this.limited) {\n      this.limitCropBox(true, true);\n    }\n  },\n  renderImage: function renderImage(changed) {\n    var canvasData = this.canvasData,\n        imageData = this.imageData;\n    var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);\n    var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);\n    assign(imageData, {\n      width: width,\n      height: height,\n      left: (canvasData.width - width) / 2,\n      top: (canvasData.height - height) / 2\n    });\n    setStyle(this.image, assign({\n      width: imageData.width,\n      height: imageData.height\n    }, getTransforms(assign({\n      translateX: imageData.left,\n      translateY: imageData.top\n    }, imageData))));\n\n    if (changed) {\n      this.output();\n    }\n  },\n  initCropBox: function initCropBox() {\n    var options = this.options,\n        canvasData = this.canvasData;\n    var aspectRatio = options.aspectRatio || options.initialAspectRatio;\n    var autoCropArea = Number(options.autoCropArea) || 0.8;\n    var cropBoxData = {\n      width: canvasData.width,\n      height: canvasData.height\n    };\n\n    if (aspectRatio) {\n      if (canvasData.height * aspectRatio > canvasData.width) {\n        cropBoxData.height = cropBoxData.width / aspectRatio;\n      } else {\n        cropBoxData.width = cropBoxData.height * aspectRatio;\n      }\n    }\n\n    this.cropBoxData = cropBoxData;\n    this.limitCropBox(true, true); // Initialize auto crop area\n\n    cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n    cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than \"minWidth/Height\"\n\n    cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);\n    cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);\n    cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;\n    cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;\n    cropBoxData.oldLeft = cropBoxData.left;\n    cropBoxData.oldTop = cropBoxData.top;\n    this.initialCropBoxData = assign({}, cropBoxData);\n  },\n  limitCropBox: function limitCropBox(sizeLimited, positionLimited) {\n    var options = this.options,\n        containerData = this.containerData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData,\n        limited = this.limited;\n    var aspectRatio = options.aspectRatio;\n\n    if (sizeLimited) {\n      var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;\n      var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;\n      var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;\n      var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height\n\n      minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);\n      minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);\n\n      if (aspectRatio) {\n        if (minCropBoxWidth && minCropBoxHeight) {\n          if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n            minCropBoxHeight = minCropBoxWidth / aspectRatio;\n          } else {\n            minCropBoxWidth = minCropBoxHeight * aspectRatio;\n          }\n        } else if (minCropBoxWidth) {\n          minCropBoxHeight = minCropBoxWidth / aspectRatio;\n        } else if (minCropBoxHeight) {\n          minCropBoxWidth = minCropBoxHeight * aspectRatio;\n        }\n\n        if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n          maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n        } else {\n          maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n        }\n      } // The minWidth/Height must be less than maxWidth/Height\n\n\n      cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);\n      cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);\n      cropBoxData.maxWidth = maxCropBoxWidth;\n      cropBoxData.maxHeight = maxCropBoxHeight;\n    }\n\n    if (positionLimited) {\n      if (limited) {\n        cropBoxData.minLeft = Math.max(0, canvasData.left);\n        cropBoxData.minTop = Math.max(0, canvasData.top);\n        cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;\n        cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;\n      } else {\n        cropBoxData.minLeft = 0;\n        cropBoxData.minTop = 0;\n        cropBoxData.maxLeft = containerData.width - cropBoxData.width;\n        cropBoxData.maxTop = containerData.height - cropBoxData.height;\n      }\n    }\n  },\n  renderCropBox: function renderCropBox() {\n    var options = this.options,\n        containerData = this.containerData,\n        cropBoxData = this.cropBoxData;\n\n    if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {\n      cropBoxData.left = cropBoxData.oldLeft;\n    }\n\n    if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {\n      cropBoxData.top = cropBoxData.oldTop;\n    }\n\n    cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n    cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);\n    this.limitCropBox(false, true);\n    cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);\n    cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);\n    cropBoxData.oldLeft = cropBoxData.left;\n    cropBoxData.oldTop = cropBoxData.top;\n\n    if (options.movable && options.cropBoxMovable) {\n      // Turn to move the canvas when the crop box is equal to the container\n      setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);\n    }\n\n    setStyle(this.cropBox, assign({\n      width: cropBoxData.width,\n      height: cropBoxData.height\n    }, getTransforms({\n      translateX: cropBoxData.left,\n      translateY: cropBoxData.top\n    })));\n\n    if (this.cropped && this.limited) {\n      this.limitCanvas(true, true);\n    }\n\n    if (!this.disabled) {\n      this.output();\n    }\n  },\n  output: function output() {\n    this.preview();\n    dispatchEvent(this.element, EVENT_CROP, this.getData());\n  }\n};\n\nvar preview = {\n  initPreview: function initPreview() {\n    var element = this.element,\n        crossOrigin = this.crossOrigin;\n    var preview = this.options.preview;\n    var url = crossOrigin ? this.crossOriginUrl : this.url;\n    var alt = element.alt || 'The image to preview';\n    var image = document.createElement('img');\n\n    if (crossOrigin) {\n      image.crossOrigin = crossOrigin;\n    }\n\n    image.src = url;\n    image.alt = alt;\n    this.viewBox.appendChild(image);\n    this.viewBoxImage = image;\n\n    if (!preview) {\n      return;\n    }\n\n    var previews = preview;\n\n    if (typeof preview === 'string') {\n      previews = element.ownerDocument.querySelectorAll(preview);\n    } else if (preview.querySelector) {\n      previews = [preview];\n    }\n\n    this.previews = previews;\n    forEach(previews, function (el) {\n      var img = document.createElement('img'); // Save the original size for recover\n\n      setData(el, DATA_PREVIEW, {\n        width: el.offsetWidth,\n        height: el.offsetHeight,\n        html: el.innerHTML\n      });\n\n      if (crossOrigin) {\n        img.crossOrigin = crossOrigin;\n      }\n\n      img.src = url;\n      img.alt = alt;\n      /**\n       * Override img element styles\n       * Add `display:block` to avoid margin top issue\n       * Add `height:auto` to override `height` attribute on IE8\n       * (Occur only when margin-top <= -height)\n       */\n\n      img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;\"';\n      el.innerHTML = '';\n      el.appendChild(img);\n    });\n  },\n  resetPreview: function resetPreview() {\n    forEach(this.previews, function (element) {\n      var data = getData(element, DATA_PREVIEW);\n      setStyle(element, {\n        width: data.width,\n        height: data.height\n      });\n      element.innerHTML = data.html;\n      removeData(element, DATA_PREVIEW);\n    });\n  },\n  preview: function preview() {\n    var imageData = this.imageData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData;\n    var cropBoxWidth = cropBoxData.width,\n        cropBoxHeight = cropBoxData.height;\n    var width = imageData.width,\n        height = imageData.height;\n    var left = cropBoxData.left - canvasData.left - imageData.left;\n    var top = cropBoxData.top - canvasData.top - imageData.top;\n\n    if (!this.cropped || this.disabled) {\n      return;\n    }\n\n    setStyle(this.viewBoxImage, assign({\n      width: width,\n      height: height\n    }, getTransforms(assign({\n      translateX: -left,\n      translateY: -top\n    }, imageData))));\n    forEach(this.previews, function (element) {\n      var data = getData(element, DATA_PREVIEW);\n      var originalWidth = data.width;\n      var originalHeight = data.height;\n      var newWidth = originalWidth;\n      var newHeight = originalHeight;\n      var ratio = 1;\n\n      if (cropBoxWidth) {\n        ratio = originalWidth / cropBoxWidth;\n        newHeight = cropBoxHeight * ratio;\n      }\n\n      if (cropBoxHeight && newHeight > originalHeight) {\n        ratio = originalHeight / cropBoxHeight;\n        newWidth = cropBoxWidth * ratio;\n        newHeight = originalHeight;\n      }\n\n      setStyle(element, {\n        width: newWidth,\n        height: newHeight\n      });\n      setStyle(element.getElementsByTagName('img')[0], assign({\n        width: width * ratio,\n        height: height * ratio\n      }, getTransforms(assign({\n        translateX: -left * ratio,\n        translateY: -top * ratio\n      }, imageData))));\n    });\n  }\n};\n\nvar events = {\n  bind: function bind() {\n    var element = this.element,\n        options = this.options,\n        cropper = this.cropper;\n\n    if (isFunction(options.cropstart)) {\n      addListener(element, EVENT_CROP_START, options.cropstart);\n    }\n\n    if (isFunction(options.cropmove)) {\n      addListener(element, EVENT_CROP_MOVE, options.cropmove);\n    }\n\n    if (isFunction(options.cropend)) {\n      addListener(element, EVENT_CROP_END, options.cropend);\n    }\n\n    if (isFunction(options.crop)) {\n      addListener(element, EVENT_CROP, options.crop);\n    }\n\n    if (isFunction(options.zoom)) {\n      addListener(element, EVENT_ZOOM, options.zoom);\n    }\n\n    addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));\n\n    if (options.zoomable && options.zoomOnWheel) {\n      addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {\n        passive: false,\n        capture: true\n      });\n    }\n\n    if (options.toggleDragModeOnDblclick) {\n      addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));\n    }\n\n    addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));\n    addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));\n\n    if (options.responsive) {\n      addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));\n    }\n  },\n  unbind: function unbind() {\n    var element = this.element,\n        options = this.options,\n        cropper = this.cropper;\n\n    if (isFunction(options.cropstart)) {\n      removeListener(element, EVENT_CROP_START, options.cropstart);\n    }\n\n    if (isFunction(options.cropmove)) {\n      removeListener(element, EVENT_CROP_MOVE, options.cropmove);\n    }\n\n    if (isFunction(options.cropend)) {\n      removeListener(element, EVENT_CROP_END, options.cropend);\n    }\n\n    if (isFunction(options.crop)) {\n      removeListener(element, EVENT_CROP, options.crop);\n    }\n\n    if (isFunction(options.zoom)) {\n      removeListener(element, EVENT_ZOOM, options.zoom);\n    }\n\n    removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);\n\n    if (options.zoomable && options.zoomOnWheel) {\n      removeListener(cropper, EVENT_WHEEL, this.onWheel, {\n        passive: false,\n        capture: true\n      });\n    }\n\n    if (options.toggleDragModeOnDblclick) {\n      removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);\n    }\n\n    removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);\n    removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);\n\n    if (options.responsive) {\n      removeListener(window, EVENT_RESIZE, this.onResize);\n    }\n  }\n};\n\nvar handlers = {\n  resize: function resize() {\n    var options = this.options,\n        container = this.container,\n        containerData = this.containerData;\n    var minContainerWidth = Number(options.minContainerWidth) || MIN_CONTAINER_WIDTH;\n    var minContainerHeight = Number(options.minContainerHeight) || MIN_CONTAINER_HEIGHT;\n\n    if (this.disabled || containerData.width <= minContainerWidth || containerData.height <= minContainerHeight) {\n      return;\n    }\n\n    var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed\n\n    if (ratio !== 1 || container.offsetHeight !== containerData.height) {\n      var canvasData;\n      var cropBoxData;\n\n      if (options.restore) {\n        canvasData = this.getCanvasData();\n        cropBoxData = this.getCropBoxData();\n      }\n\n      this.render();\n\n      if (options.restore) {\n        this.setCanvasData(forEach(canvasData, function (n, i) {\n          canvasData[i] = n * ratio;\n        }));\n        this.setCropBoxData(forEach(cropBoxData, function (n, i) {\n          cropBoxData[i] = n * ratio;\n        }));\n      }\n    }\n  },\n  dblclick: function dblclick() {\n    if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {\n      return;\n    }\n\n    this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);\n  },\n  wheel: function wheel(event) {\n    var _this = this;\n\n    var ratio = Number(this.options.wheelZoomRatio) || 0.1;\n    var delta = 1;\n\n    if (this.disabled) {\n      return;\n    }\n\n    event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)\n\n    if (this.wheeling) {\n      return;\n    }\n\n    this.wheeling = true;\n    setTimeout(function () {\n      _this.wheeling = false;\n    }, 50);\n\n    if (event.deltaY) {\n      delta = event.deltaY > 0 ? 1 : -1;\n    } else if (event.wheelDelta) {\n      delta = -event.wheelDelta / 120;\n    } else if (event.detail) {\n      delta = event.detail > 0 ? 1 : -1;\n    }\n\n    this.zoom(-delta * ratio, event);\n  },\n  cropStart: function cropStart(event) {\n    var buttons = event.buttons,\n        button = event.button;\n\n    if (this.disabled // Handle mouse event and pointer event and ignore touch event\n    || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && ( // No primary button (Usually the left button)\n    isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu\n    || event.ctrlKey)) {\n      return;\n    }\n\n    var options = this.options,\n        pointers = this.pointers;\n    var action;\n\n    if (event.changedTouches) {\n      // Handle touch event\n      forEach(event.changedTouches, function (touch) {\n        pointers[touch.identifier] = getPointer(touch);\n      });\n    } else {\n      // Handle mouse event and pointer event\n      pointers[event.pointerId || 0] = getPointer(event);\n    }\n\n    if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {\n      action = ACTION_ZOOM;\n    } else {\n      action = getData(event.target, DATA_ACTION);\n    }\n\n    if (!REGEXP_ACTIONS.test(action)) {\n      return;\n    }\n\n    if (dispatchEvent(this.element, EVENT_CROP_START, {\n      originalEvent: event,\n      action: action\n    }) === false) {\n      return;\n    } // This line is required for preventing page zooming in iOS browsers\n\n\n    event.preventDefault();\n    this.action = action;\n    this.cropping = false;\n\n    if (action === ACTION_CROP) {\n      this.cropping = true;\n      addClass(this.dragBox, CLASS_MODAL);\n    }\n  },\n  cropMove: function cropMove(event) {\n    var action = this.action;\n\n    if (this.disabled || !action) {\n      return;\n    }\n\n    var pointers = this.pointers;\n    event.preventDefault();\n\n    if (dispatchEvent(this.element, EVENT_CROP_MOVE, {\n      originalEvent: event,\n      action: action\n    }) === false) {\n      return;\n    }\n\n    if (event.changedTouches) {\n      forEach(event.changedTouches, function (touch) {\n        // The first parameter should not be undefined (#432)\n        assign(pointers[touch.identifier] || {}, getPointer(touch, true));\n      });\n    } else {\n      assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));\n    }\n\n    this.change(event);\n  },\n  cropEnd: function cropEnd(event) {\n    if (this.disabled) {\n      return;\n    }\n\n    var action = this.action,\n        pointers = this.pointers;\n\n    if (event.changedTouches) {\n      forEach(event.changedTouches, function (touch) {\n        delete pointers[touch.identifier];\n      });\n    } else {\n      delete pointers[event.pointerId || 0];\n    }\n\n    if (!action) {\n      return;\n    }\n\n    event.preventDefault();\n\n    if (!Object.keys(pointers).length) {\n      this.action = '';\n    }\n\n    if (this.cropping) {\n      this.cropping = false;\n      toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);\n    }\n\n    dispatchEvent(this.element, EVENT_CROP_END, {\n      originalEvent: event,\n      action: action\n    });\n  }\n};\n\nvar change = {\n  change: function change(event) {\n    var options = this.options,\n        canvasData = this.canvasData,\n        containerData = this.containerData,\n        cropBoxData = this.cropBoxData,\n        pointers = this.pointers;\n    var action = this.action;\n    var aspectRatio = options.aspectRatio;\n    var left = cropBoxData.left,\n        top = cropBoxData.top,\n        width = cropBoxData.width,\n        height = cropBoxData.height;\n    var right = left + width;\n    var bottom = top + height;\n    var minLeft = 0;\n    var minTop = 0;\n    var maxWidth = containerData.width;\n    var maxHeight = containerData.height;\n    var renderable = true;\n    var offset; // Locking aspect ratio in \"free mode\" by holding shift key\n\n    if (!aspectRatio && event.shiftKey) {\n      aspectRatio = width && height ? width / height : 1;\n    }\n\n    if (this.limited) {\n      minLeft = cropBoxData.minLeft;\n      minTop = cropBoxData.minTop;\n      maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);\n      maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);\n    }\n\n    var pointer = pointers[Object.keys(pointers)[0]];\n    var range = {\n      x: pointer.endX - pointer.startX,\n      y: pointer.endY - pointer.startY\n    };\n\n    var check = function check(side) {\n      switch (side) {\n        case ACTION_EAST:\n          if (right + range.x > maxWidth) {\n            range.x = maxWidth - right;\n          }\n\n          break;\n\n        case ACTION_WEST:\n          if (left + range.x < minLeft) {\n            range.x = minLeft - left;\n          }\n\n          break;\n\n        case ACTION_NORTH:\n          if (top + range.y < minTop) {\n            range.y = minTop - top;\n          }\n\n          break;\n\n        case ACTION_SOUTH:\n          if (bottom + range.y > maxHeight) {\n            range.y = maxHeight - bottom;\n          }\n\n          break;\n\n        default:\n      }\n    };\n\n    switch (action) {\n      // Move crop box\n      case ACTION_ALL:\n        left += range.x;\n        top += range.y;\n        break;\n      // Resize crop box\n\n      case ACTION_EAST:\n        if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_EAST);\n        width += range.x;\n\n        if (width < 0) {\n          action = ACTION_WEST;\n          width = -width;\n          left -= width;\n        }\n\n        if (aspectRatio) {\n          height = width / aspectRatio;\n          top += (cropBoxData.height - height) / 2;\n        }\n\n        break;\n\n      case ACTION_NORTH:\n        if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_NORTH);\n        height -= range.y;\n        top += range.y;\n\n        if (height < 0) {\n          action = ACTION_SOUTH;\n          height = -height;\n          top -= height;\n        }\n\n        if (aspectRatio) {\n          width = height * aspectRatio;\n          left += (cropBoxData.width - width) / 2;\n        }\n\n        break;\n\n      case ACTION_WEST:\n        if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_WEST);\n        width -= range.x;\n        left += range.x;\n\n        if (width < 0) {\n          action = ACTION_EAST;\n          width = -width;\n          left -= width;\n        }\n\n        if (aspectRatio) {\n          height = width / aspectRatio;\n          top += (cropBoxData.height - height) / 2;\n        }\n\n        break;\n\n      case ACTION_SOUTH:\n        if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_SOUTH);\n        height += range.y;\n\n        if (height < 0) {\n          action = ACTION_NORTH;\n          height = -height;\n          top -= height;\n        }\n\n        if (aspectRatio) {\n          width = height * aspectRatio;\n          left += (cropBoxData.width - width) / 2;\n        }\n\n        break;\n\n      case ACTION_NORTH_EAST:\n        if (aspectRatio) {\n          if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n          width = height * aspectRatio;\n        } else {\n          check(ACTION_NORTH);\n          check(ACTION_EAST);\n\n          if (range.x >= 0) {\n            if (right < maxWidth) {\n              width += range.x;\n            } else if (range.y <= 0 && top <= minTop) {\n              renderable = false;\n            }\n          } else {\n            width += range.x;\n          }\n\n          if (range.y <= 0) {\n            if (top > minTop) {\n              height -= range.y;\n              top += range.y;\n            }\n          } else {\n            height -= range.y;\n            top += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_SOUTH_WEST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_NORTH_WEST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_SOUTH_EAST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n\n      case ACTION_NORTH_WEST:\n        if (aspectRatio) {\n          if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n          width = height * aspectRatio;\n          left += cropBoxData.width - width;\n        } else {\n          check(ACTION_NORTH);\n          check(ACTION_WEST);\n\n          if (range.x <= 0) {\n            if (left > minLeft) {\n              width -= range.x;\n              left += range.x;\n            } else if (range.y <= 0 && top <= minTop) {\n              renderable = false;\n            }\n          } else {\n            width -= range.x;\n            left += range.x;\n          }\n\n          if (range.y <= 0) {\n            if (top > minTop) {\n              height -= range.y;\n              top += range.y;\n            }\n          } else {\n            height -= range.y;\n            top += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_SOUTH_EAST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_NORTH_EAST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_SOUTH_WEST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n\n      case ACTION_SOUTH_WEST:\n        if (aspectRatio) {\n          if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_WEST);\n          width -= range.x;\n          left += range.x;\n          height = width / aspectRatio;\n        } else {\n          check(ACTION_SOUTH);\n          check(ACTION_WEST);\n\n          if (range.x <= 0) {\n            if (left > minLeft) {\n              width -= range.x;\n              left += range.x;\n            } else if (range.y >= 0 && bottom >= maxHeight) {\n              renderable = false;\n            }\n          } else {\n            width -= range.x;\n            left += range.x;\n          }\n\n          if (range.y >= 0) {\n            if (bottom < maxHeight) {\n              height += range.y;\n            }\n          } else {\n            height += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_NORTH_EAST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_SOUTH_EAST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_NORTH_WEST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n\n      case ACTION_SOUTH_EAST:\n        if (aspectRatio) {\n          if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_EAST);\n          width += range.x;\n          height = width / aspectRatio;\n        } else {\n          check(ACTION_SOUTH);\n          check(ACTION_EAST);\n\n          if (range.x >= 0) {\n            if (right < maxWidth) {\n              width += range.x;\n            } else if (range.y >= 0 && bottom >= maxHeight) {\n              renderable = false;\n            }\n          } else {\n            width += range.x;\n          }\n\n          if (range.y >= 0) {\n            if (bottom < maxHeight) {\n              height += range.y;\n            }\n          } else {\n            height += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_NORTH_WEST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_SOUTH_WEST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_NORTH_EAST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n      // Move canvas\n\n      case ACTION_MOVE:\n        this.move(range.x, range.y);\n        renderable = false;\n        break;\n      // Zoom canvas\n\n      case ACTION_ZOOM:\n        this.zoom(getMaxZoomRatio(pointers), event);\n        renderable = false;\n        break;\n      // Create crop box\n\n      case ACTION_CROP:\n        if (!range.x || !range.y) {\n          renderable = false;\n          break;\n        }\n\n        offset = getOffset(this.cropper);\n        left = pointer.startX - offset.left;\n        top = pointer.startY - offset.top;\n        width = cropBoxData.minWidth;\n        height = cropBoxData.minHeight;\n\n        if (range.x > 0) {\n          action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n        } else if (range.x < 0) {\n          left -= width;\n          action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n        }\n\n        if (range.y < 0) {\n          top -= height;\n        } // Show the crop box if is hidden\n\n\n        if (!this.cropped) {\n          removeClass(this.cropBox, CLASS_HIDDEN);\n          this.cropped = true;\n\n          if (this.limited) {\n            this.limitCropBox(true, true);\n          }\n        }\n\n        break;\n\n      default:\n    }\n\n    if (renderable) {\n      cropBoxData.width = width;\n      cropBoxData.height = height;\n      cropBoxData.left = left;\n      cropBoxData.top = top;\n      this.action = action;\n      this.renderCropBox();\n    } // Override\n\n\n    forEach(pointers, function (p) {\n      p.startX = p.endX;\n      p.startY = p.endY;\n    });\n  }\n};\n\nvar methods = {\n  // Show the crop box manually\n  crop: function crop() {\n    if (this.ready && !this.cropped && !this.disabled) {\n      this.cropped = true;\n      this.limitCropBox(true, true);\n\n      if (this.options.modal) {\n        addClass(this.dragBox, CLASS_MODAL);\n      }\n\n      removeClass(this.cropBox, CLASS_HIDDEN);\n      this.setCropBoxData(this.initialCropBoxData);\n    }\n\n    return this;\n  },\n  // Reset the image and crop box to their initial states\n  reset: function reset() {\n    if (this.ready && !this.disabled) {\n      this.imageData = assign({}, this.initialImageData);\n      this.canvasData = assign({}, this.initialCanvasData);\n      this.cropBoxData = assign({}, this.initialCropBoxData);\n      this.renderCanvas();\n\n      if (this.cropped) {\n        this.renderCropBox();\n      }\n    }\n\n    return this;\n  },\n  // Clear the crop box\n  clear: function clear() {\n    if (this.cropped && !this.disabled) {\n      assign(this.cropBoxData, {\n        left: 0,\n        top: 0,\n        width: 0,\n        height: 0\n      });\n      this.cropped = false;\n      this.renderCropBox();\n      this.limitCanvas(true, true); // Render canvas after crop box rendered\n\n      this.renderCanvas();\n      removeClass(this.dragBox, CLASS_MODAL);\n      addClass(this.cropBox, CLASS_HIDDEN);\n    }\n\n    return this;\n  },\n\n  /**\n   * Replace the image's src and rebuild the cropper\n   * @param {string} url - The new URL.\n   * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.\n   * @returns {Cropper} this\n   */\n  replace: function replace(url) {\n    var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n    if (!this.disabled && url) {\n      if (this.isImg) {\n        this.element.src = url;\n      }\n\n      if (hasSameSize) {\n        this.url = url;\n        this.image.src = url;\n\n        if (this.ready) {\n          this.viewBoxImage.src = url;\n          forEach(this.previews, function (element) {\n            element.getElementsByTagName('img')[0].src = url;\n          });\n        }\n      } else {\n        if (this.isImg) {\n          this.replaced = true;\n        }\n\n        this.options.data = null;\n        this.uncreate();\n        this.load(url);\n      }\n    }\n\n    return this;\n  },\n  // Enable (unfreeze) the cropper\n  enable: function enable() {\n    if (this.ready && this.disabled) {\n      this.disabled = false;\n      removeClass(this.cropper, CLASS_DISABLED);\n    }\n\n    return this;\n  },\n  // Disable (freeze) the cropper\n  disable: function disable() {\n    if (this.ready && !this.disabled) {\n      this.disabled = true;\n      addClass(this.cropper, CLASS_DISABLED);\n    }\n\n    return this;\n  },\n\n  /**\n   * Destroy the cropper and remove the instance from the image\n   * @returns {Cropper} this\n   */\n  destroy: function destroy() {\n    var element = this.element;\n\n    if (!element[NAMESPACE]) {\n      return this;\n    }\n\n    element[NAMESPACE] = undefined;\n\n    if (this.isImg && this.replaced) {\n      element.src = this.originalUrl;\n    }\n\n    this.uncreate();\n    return this;\n  },\n\n  /**\n   * Move the canvas with relative offsets\n   * @param {number} offsetX - The relative offset distance on the x-axis.\n   * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.\n   * @returns {Cropper} this\n   */\n  move: function move(offsetX) {\n    var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;\n    var _this$canvasData = this.canvasData,\n        left = _this$canvasData.left,\n        top = _this$canvasData.top;\n    return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));\n  },\n\n  /**\n   * Move the canvas to an absolute point\n   * @param {number} x - The x-axis coordinate.\n   * @param {number} [y=x] - The y-axis coordinate.\n   * @returns {Cropper} this\n   */\n  moveTo: function moveTo(x) {\n    var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;\n    var canvasData = this.canvasData;\n    var changed = false;\n    x = Number(x);\n    y = Number(y);\n\n    if (this.ready && !this.disabled && this.options.movable) {\n      if (isNumber(x)) {\n        canvasData.left = x;\n        changed = true;\n      }\n\n      if (isNumber(y)) {\n        canvasData.top = y;\n        changed = true;\n      }\n\n      if (changed) {\n        this.renderCanvas(true);\n      }\n    }\n\n    return this;\n  },\n\n  /**\n   * Zoom the canvas with a relative ratio\n   * @param {number} ratio - The target ratio.\n   * @param {Event} _originalEvent - The original event if any.\n   * @returns {Cropper} this\n   */\n  zoom: function zoom(ratio, _originalEvent) {\n    var canvasData = this.canvasData;\n    ratio = Number(ratio);\n\n    if (ratio < 0) {\n      ratio = 1 / (1 - ratio);\n    } else {\n      ratio = 1 + ratio;\n    }\n\n    return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);\n  },\n\n  /**\n   * Zoom the canvas to an absolute ratio\n   * @param {number} ratio - The target ratio.\n   * @param {Object} pivot - The zoom pivot point coordinate.\n   * @param {Event} _originalEvent - The original event if any.\n   * @returns {Cropper} this\n   */\n  zoomTo: function zoomTo(ratio, pivot, _originalEvent) {\n    var options = this.options,\n        canvasData = this.canvasData;\n    var width = canvasData.width,\n        height = canvasData.height,\n        naturalWidth = canvasData.naturalWidth,\n        naturalHeight = canvasData.naturalHeight;\n    ratio = Number(ratio);\n\n    if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {\n      var newWidth = naturalWidth * ratio;\n      var newHeight = naturalHeight * ratio;\n\n      if (dispatchEvent(this.element, EVENT_ZOOM, {\n        ratio: ratio,\n        oldRatio: width / naturalWidth,\n        originalEvent: _originalEvent\n      }) === false) {\n        return this;\n      }\n\n      if (_originalEvent) {\n        var pointers = this.pointers;\n        var offset = getOffset(this.cropper);\n        var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {\n          pageX: _originalEvent.pageX,\n          pageY: _originalEvent.pageY\n        }; // Zoom from the triggering point of the event\n\n        canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);\n        canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);\n      } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {\n        canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);\n        canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);\n      } else {\n        // Zoom from the center of the canvas\n        canvasData.left -= (newWidth - width) / 2;\n        canvasData.top -= (newHeight - height) / 2;\n      }\n\n      canvasData.width = newWidth;\n      canvasData.height = newHeight;\n      this.renderCanvas(true);\n    }\n\n    return this;\n  },\n\n  /**\n   * Rotate the canvas with a relative degree\n   * @param {number} degree - The rotate degree.\n   * @returns {Cropper} this\n   */\n  rotate: function rotate(degree) {\n    return this.rotateTo((this.imageData.rotate || 0) + Number(degree));\n  },\n\n  /**\n   * Rotate the canvas to an absolute degree\n   * @param {number} degree - The rotate degree.\n   * @returns {Cropper} this\n   */\n  rotateTo: function rotateTo(degree) {\n    degree = Number(degree);\n\n    if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {\n      this.imageData.rotate = degree % 360;\n      this.renderCanvas(true, true);\n    }\n\n    return this;\n  },\n\n  /**\n   * Scale the image on the x-axis.\n   * @param {number} scaleX - The scale ratio on the x-axis.\n   * @returns {Cropper} this\n   */\n  scaleX: function scaleX(_scaleX) {\n    var scaleY = this.imageData.scaleY;\n    return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);\n  },\n\n  /**\n   * Scale the image on the y-axis.\n   * @param {number} scaleY - The scale ratio on the y-axis.\n   * @returns {Cropper} this\n   */\n  scaleY: function scaleY(_scaleY) {\n    var scaleX = this.imageData.scaleX;\n    return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);\n  },\n\n  /**\n   * Scale the image\n   * @param {number} scaleX - The scale ratio on the x-axis.\n   * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.\n   * @returns {Cropper} this\n   */\n  scale: function scale(scaleX) {\n    var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;\n    var imageData = this.imageData;\n    var transformed = false;\n    scaleX = Number(scaleX);\n    scaleY = Number(scaleY);\n\n    if (this.ready && !this.disabled && this.options.scalable) {\n      if (isNumber(scaleX)) {\n        imageData.scaleX = scaleX;\n        transformed = true;\n      }\n\n      if (isNumber(scaleY)) {\n        imageData.scaleY = scaleY;\n        transformed = true;\n      }\n\n      if (transformed) {\n        this.renderCanvas(true, true);\n      }\n    }\n\n    return this;\n  },\n\n  /**\n   * Get the cropped area position and size data (base on the original image)\n   * @param {boolean} [rounded=false] - Indicate if round the data values or not.\n   * @returns {Object} The result cropped data.\n   */\n  getData: function getData() {\n    var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n    var options = this.options,\n        imageData = this.imageData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData;\n    var data;\n\n    if (this.ready && this.cropped) {\n      data = {\n        x: cropBoxData.left - canvasData.left,\n        y: cropBoxData.top - canvasData.top,\n        width: cropBoxData.width,\n        height: cropBoxData.height\n      };\n      var ratio = imageData.width / imageData.naturalWidth;\n      forEach(data, function (n, i) {\n        data[i] = n / ratio;\n      });\n\n      if (rounded) {\n        // In case rounding off leads to extra 1px in right or bottom border\n        // we should round the top-left corner and the dimension (#343).\n        var bottom = Math.round(data.y + data.height);\n        var right = Math.round(data.x + data.width);\n        data.x = Math.round(data.x);\n        data.y = Math.round(data.y);\n        data.width = right - data.x;\n        data.height = bottom - data.y;\n      }\n    } else {\n      data = {\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      };\n    }\n\n    if (options.rotatable) {\n      data.rotate = imageData.rotate || 0;\n    }\n\n    if (options.scalable) {\n      data.scaleX = imageData.scaleX || 1;\n      data.scaleY = imageData.scaleY || 1;\n    }\n\n    return data;\n  },\n\n  /**\n   * Set the cropped area position and size with new data\n   * @param {Object} data - The new data.\n   * @returns {Cropper} this\n   */\n  setData: function setData(data) {\n    var options = this.options,\n        imageData = this.imageData,\n        canvasData = this.canvasData;\n    var cropBoxData = {};\n\n    if (this.ready && !this.disabled && isPlainObject(data)) {\n      var transformed = false;\n\n      if (options.rotatable) {\n        if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {\n          imageData.rotate = data.rotate;\n          transformed = true;\n        }\n      }\n\n      if (options.scalable) {\n        if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {\n          imageData.scaleX = data.scaleX;\n          transformed = true;\n        }\n\n        if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {\n          imageData.scaleY = data.scaleY;\n          transformed = true;\n        }\n      }\n\n      if (transformed) {\n        this.renderCanvas(true, true);\n      }\n\n      var ratio = imageData.width / imageData.naturalWidth;\n\n      if (isNumber(data.x)) {\n        cropBoxData.left = data.x * ratio + canvasData.left;\n      }\n\n      if (isNumber(data.y)) {\n        cropBoxData.top = data.y * ratio + canvasData.top;\n      }\n\n      if (isNumber(data.width)) {\n        cropBoxData.width = data.width * ratio;\n      }\n\n      if (isNumber(data.height)) {\n        cropBoxData.height = data.height * ratio;\n      }\n\n      this.setCropBoxData(cropBoxData);\n    }\n\n    return this;\n  },\n\n  /**\n   * Get the container size data.\n   * @returns {Object} The result container data.\n   */\n  getContainerData: function getContainerData() {\n    return this.ready ? assign({}, this.containerData) : {};\n  },\n\n  /**\n   * Get the image position and size data.\n   * @returns {Object} The result image data.\n   */\n  getImageData: function getImageData() {\n    return this.sized ? assign({}, this.imageData) : {};\n  },\n\n  /**\n   * Get the canvas position and size data.\n   * @returns {Object} The result canvas data.\n   */\n  getCanvasData: function getCanvasData() {\n    var canvasData = this.canvasData;\n    var data = {};\n\n    if (this.ready) {\n      forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {\n        data[n] = canvasData[n];\n      });\n    }\n\n    return data;\n  },\n\n  /**\n   * Set the canvas position and size with new data.\n   * @param {Object} data - The new canvas data.\n   * @returns {Cropper} this\n   */\n  setCanvasData: function setCanvasData(data) {\n    var canvasData = this.canvasData;\n    var aspectRatio = canvasData.aspectRatio;\n\n    if (this.ready && !this.disabled && isPlainObject(data)) {\n      if (isNumber(data.left)) {\n        canvasData.left = data.left;\n      }\n\n      if (isNumber(data.top)) {\n        canvasData.top = data.top;\n      }\n\n      if (isNumber(data.width)) {\n        canvasData.width = data.width;\n        canvasData.height = data.width / aspectRatio;\n      } else if (isNumber(data.height)) {\n        canvasData.height = data.height;\n        canvasData.width = data.height * aspectRatio;\n      }\n\n      this.renderCanvas(true);\n    }\n\n    return this;\n  },\n\n  /**\n   * Get the crop box position and size data.\n   * @returns {Object} The result crop box data.\n   */\n  getCropBoxData: function getCropBoxData() {\n    var cropBoxData = this.cropBoxData;\n    var data;\n\n    if (this.ready && this.cropped) {\n      data = {\n        left: cropBoxData.left,\n        top: cropBoxData.top,\n        width: cropBoxData.width,\n        height: cropBoxData.height\n      };\n    }\n\n    return data || {};\n  },\n\n  /**\n   * Set the crop box position and size with new data.\n   * @param {Object} data - The new crop box data.\n   * @returns {Cropper} this\n   */\n  setCropBoxData: function setCropBoxData(data) {\n    var cropBoxData = this.cropBoxData;\n    var aspectRatio = this.options.aspectRatio;\n    var widthChanged;\n    var heightChanged;\n\n    if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {\n      if (isNumber(data.left)) {\n        cropBoxData.left = data.left;\n      }\n\n      if (isNumber(data.top)) {\n        cropBoxData.top = data.top;\n      }\n\n      if (isNumber(data.width) && data.width !== cropBoxData.width) {\n        widthChanged = true;\n        cropBoxData.width = data.width;\n      }\n\n      if (isNumber(data.height) && data.height !== cropBoxData.height) {\n        heightChanged = true;\n        cropBoxData.height = data.height;\n      }\n\n      if (aspectRatio) {\n        if (widthChanged) {\n          cropBoxData.height = cropBoxData.width / aspectRatio;\n        } else if (heightChanged) {\n          cropBoxData.width = cropBoxData.height * aspectRatio;\n        }\n      }\n\n      this.renderCropBox();\n    }\n\n    return this;\n  },\n\n  /**\n   * Get a canvas drawn the cropped image.\n   * @param {Object} [options={}] - The config options.\n   * @returns {HTMLCanvasElement} - The result canvas.\n   */\n  getCroppedCanvas: function getCroppedCanvas() {\n    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n    if (!this.ready || !window.HTMLCanvasElement) {\n      return null;\n    }\n\n    var canvasData = this.canvasData;\n    var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.\n\n    if (!this.cropped) {\n      return source;\n    }\n\n    var _this$getData = this.getData(),\n        initialX = _this$getData.x,\n        initialY = _this$getData.y,\n        initialWidth = _this$getData.width,\n        initialHeight = _this$getData.height;\n\n    var ratio = source.width / Math.floor(canvasData.naturalWidth);\n\n    if (ratio !== 1) {\n      initialX *= ratio;\n      initialY *= ratio;\n      initialWidth *= ratio;\n      initialHeight *= ratio;\n    }\n\n    var aspectRatio = initialWidth / initialHeight;\n    var maxSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.maxWidth || Infinity,\n      height: options.maxHeight || Infinity\n    });\n    var minSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.minWidth || 0,\n      height: options.minHeight || 0\n    }, 'cover');\n\n    var _getAdjustedSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.width || (ratio !== 1 ? source.width : initialWidth),\n      height: options.height || (ratio !== 1 ? source.height : initialHeight)\n    }),\n        width = _getAdjustedSizes.width,\n        height = _getAdjustedSizes.height;\n\n    width = Math.min(maxSizes.width, Math.max(minSizes.width, width));\n    height = Math.min(maxSizes.height, Math.max(minSizes.height, height));\n    var canvas = document.createElement('canvas');\n    var context = canvas.getContext('2d');\n    canvas.width = normalizeDecimalNumber(width);\n    canvas.height = normalizeDecimalNumber(height);\n    context.fillStyle = options.fillColor || 'transparent';\n    context.fillRect(0, 0, width, height);\n    var _options$imageSmoothi = options.imageSmoothingEnabled,\n        imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,\n        imageSmoothingQuality = options.imageSmoothingQuality;\n    context.imageSmoothingEnabled = imageSmoothingEnabled;\n\n    if (imageSmoothingQuality) {\n      context.imageSmoothingQuality = imageSmoothingQuality;\n    } // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n\n\n    var sourceWidth = source.width;\n    var sourceHeight = source.height; // Source canvas parameters\n\n    var srcX = initialX;\n    var srcY = initialY;\n    var srcWidth;\n    var srcHeight; // Destination canvas parameters\n\n    var dstX;\n    var dstY;\n    var dstWidth;\n    var dstHeight;\n\n    if (srcX <= -initialWidth || srcX > sourceWidth) {\n      srcX = 0;\n      srcWidth = 0;\n      dstX = 0;\n      dstWidth = 0;\n    } else if (srcX <= 0) {\n      dstX = -srcX;\n      srcX = 0;\n      srcWidth = Math.min(sourceWidth, initialWidth + srcX);\n      dstWidth = srcWidth;\n    } else if (srcX <= sourceWidth) {\n      dstX = 0;\n      srcWidth = Math.min(initialWidth, sourceWidth - srcX);\n      dstWidth = srcWidth;\n    }\n\n    if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {\n      srcY = 0;\n      srcHeight = 0;\n      dstY = 0;\n      dstHeight = 0;\n    } else if (srcY <= 0) {\n      dstY = -srcY;\n      srcY = 0;\n      srcHeight = Math.min(sourceHeight, initialHeight + srcY);\n      dstHeight = srcHeight;\n    } else if (srcY <= sourceHeight) {\n      dstY = 0;\n      srcHeight = Math.min(initialHeight, sourceHeight - srcY);\n      dstHeight = srcHeight;\n    }\n\n    var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid \"IndexSizeError\"\n\n    if (dstWidth > 0 && dstHeight > 0) {\n      var scale = width / initialWidth;\n      params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);\n    } // All the numerical parameters should be integer for `drawImage`\n    // https://github.com/fengyuanchen/cropper/issues/476\n\n\n    context.drawImage.apply(context, [source].concat(_toConsumableArray(params.map(function (param) {\n      return Math.floor(normalizeDecimalNumber(param));\n    }))));\n    return canvas;\n  },\n\n  /**\n   * Change the aspect ratio of the crop box.\n   * @param {number} aspectRatio - The new aspect ratio.\n   * @returns {Cropper} this\n   */\n  setAspectRatio: function setAspectRatio(aspectRatio) {\n    var options = this.options;\n\n    if (!this.disabled && !isUndefined(aspectRatio)) {\n      // 0 -> NaN\n      options.aspectRatio = Math.max(0, aspectRatio) || NaN;\n\n      if (this.ready) {\n        this.initCropBox();\n\n        if (this.cropped) {\n          this.renderCropBox();\n        }\n      }\n    }\n\n    return this;\n  },\n\n  /**\n   * Change the drag mode.\n   * @param {string} mode - The new drag mode.\n   * @returns {Cropper} this\n   */\n  setDragMode: function setDragMode(mode) {\n    var options = this.options,\n        dragBox = this.dragBox,\n        face = this.face;\n\n    if (this.ready && !this.disabled) {\n      var croppable = mode === DRAG_MODE_CROP;\n      var movable = options.movable && mode === DRAG_MODE_MOVE;\n      mode = croppable || movable ? mode : DRAG_MODE_NONE;\n      options.dragMode = mode;\n      setData(dragBox, DATA_ACTION, mode);\n      toggleClass(dragBox, CLASS_CROP, croppable);\n      toggleClass(dragBox, CLASS_MOVE, movable);\n\n      if (!options.cropBoxMovable) {\n        // Sync drag mode to crop box when it is not movable\n        setData(face, DATA_ACTION, mode);\n        toggleClass(face, CLASS_CROP, croppable);\n        toggleClass(face, CLASS_MOVE, movable);\n      }\n    }\n\n    return this;\n  }\n};\n\nvar AnotherCropper = WINDOW.Cropper;\n\nvar Cropper =\n/*#__PURE__*/\nfunction () {\n  /**\n   * Create a new Cropper.\n   * @param {Element} element - The target element for cropping.\n   * @param {Object} [options={}] - The configuration options.\n   */\n  function Cropper(element) {\n    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n    _classCallCheck(this, Cropper);\n\n    if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {\n      throw new Error('The first argument is required and must be an <img> or <canvas> element.');\n    }\n\n    this.element = element;\n    this.options = assign({}, DEFAULTS, isPlainObject(options) && options);\n    this.cropped = false;\n    this.disabled = false;\n    this.pointers = {};\n    this.ready = false;\n    this.reloading = false;\n    this.replaced = false;\n    this.sized = false;\n    this.sizing = false;\n    this.init();\n  }\n\n  _createClass(Cropper, [{\n    key: \"init\",\n    value: function init() {\n      var element = this.element;\n      var tagName = element.tagName.toLowerCase();\n      var url;\n\n      if (element[NAMESPACE]) {\n        return;\n      }\n\n      element[NAMESPACE] = this;\n\n      if (tagName === 'img') {\n        this.isImg = true; // e.g.: \"img/picture.jpg\"\n\n        url = element.getAttribute('src') || '';\n        this.originalUrl = url; // Stop when it's a blank image\n\n        if (!url) {\n          return;\n        } // e.g.: \"http://example.com/img/picture.jpg\"\n\n\n        url = element.src;\n      } else if (tagName === 'canvas' && window.HTMLCanvasElement) {\n        url = element.toDataURL();\n      }\n\n      this.load(url);\n    }\n  }, {\n    key: \"load\",\n    value: function load(url) {\n      var _this = this;\n\n      if (!url) {\n        return;\n      }\n\n      this.url = url;\n      this.imageData = {};\n      var element = this.element,\n          options = this.options;\n\n      if (!options.rotatable && !options.scalable) {\n        options.checkOrientation = false;\n      } // Only IE10+ supports Typed Arrays\n\n\n      if (!options.checkOrientation || !window.ArrayBuffer) {\n        this.clone();\n        return;\n      } // Detect the mime type of the image directly if it is a Data URL\n\n\n      if (REGEXP_DATA_URL.test(url)) {\n        // Read ArrayBuffer from Data URL of JPEG images directly for better performance\n        if (REGEXP_DATA_URL_JPEG.test(url)) {\n          this.read(dataURLToArrayBuffer(url));\n        } else {\n          // Only a JPEG image may contains Exif Orientation information,\n          // the rest types of Data URLs are not necessary to check orientation at all.\n          this.clone();\n        }\n\n        return;\n      } // 1. Detect the mime type of the image by a XMLHttpRequest.\n      // 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.\n\n\n      var xhr = new XMLHttpRequest();\n      var clone = this.clone.bind(this);\n      this.reloading = true;\n      this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:\n      // http, https, data, chrome, chrome-extension.\n      // 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy\n      // in some browsers as IE11 and Safari.\n\n      xhr.onabort = clone;\n      xhr.onerror = clone;\n      xhr.ontimeout = clone;\n\n      xhr.onprogress = function () {\n        // Abort the request directly if it not a JPEG image for better performance\n        if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {\n          xhr.abort();\n        }\n      };\n\n      xhr.onload = function () {\n        _this.read(xhr.response);\n      };\n\n      xhr.onloadend = function () {\n        _this.reloading = false;\n        _this.xhr = null;\n      }; // Bust cache when there is a \"crossOrigin\" property to avoid browser cache error\n\n\n      if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {\n        url = addTimestamp(url);\n      }\n\n      xhr.open('GET', url);\n      xhr.responseType = 'arraybuffer';\n      xhr.withCredentials = element.crossOrigin === 'use-credentials';\n      xhr.send();\n    }\n  }, {\n    key: \"read\",\n    value: function read(arrayBuffer) {\n      var options = this.options,\n          imageData = this.imageData; // Reset the orientation value to its default value 1\n      // as some iOS browsers will render image with its orientation\n\n      var orientation = resetAndGetOrientation(arrayBuffer);\n      var rotate = 0;\n      var scaleX = 1;\n      var scaleY = 1;\n\n      if (orientation > 1) {\n        // Generate a new URL which has the default orientation value\n        this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);\n\n        var _parseOrientation = parseOrientation(orientation);\n\n        rotate = _parseOrientation.rotate;\n        scaleX = _parseOrientation.scaleX;\n        scaleY = _parseOrientation.scaleY;\n      }\n\n      if (options.rotatable) {\n        imageData.rotate = rotate;\n      }\n\n      if (options.scalable) {\n        imageData.scaleX = scaleX;\n        imageData.scaleY = scaleY;\n      }\n\n      this.clone();\n    }\n  }, {\n    key: \"clone\",\n    value: function clone() {\n      var element = this.element,\n          url = this.url;\n      var crossOrigin = element.crossOrigin;\n      var crossOriginUrl = url;\n\n      if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {\n        if (!crossOrigin) {\n          crossOrigin = 'anonymous';\n        } // Bust cache when there is not a \"crossOrigin\" property (#519)\n\n\n        crossOriginUrl = addTimestamp(url);\n      }\n\n      this.crossOrigin = crossOrigin;\n      this.crossOriginUrl = crossOriginUrl;\n      var image = document.createElement('img');\n\n      if (crossOrigin) {\n        image.crossOrigin = crossOrigin;\n      }\n\n      image.src = crossOriginUrl || url;\n      image.alt = element.alt || 'The image to crop';\n      this.image = image;\n      image.onload = this.start.bind(this);\n      image.onerror = this.stop.bind(this);\n      addClass(image, CLASS_HIDE);\n      element.parentNode.insertBefore(image, element.nextSibling);\n    }\n  }, {\n    key: \"start\",\n    value: function start() {\n      var _this2 = this;\n\n      var image = this.image;\n      image.onload = null;\n      image.onerror = null;\n      this.sizing = true; // Match all browsers that use WebKit as the layout engine in iOS devices,\n      // such as Safari for iOS, Chrome for iOS, and in-app browsers.\n\n      var isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);\n\n      var done = function done(naturalWidth, naturalHeight) {\n        assign(_this2.imageData, {\n          naturalWidth: naturalWidth,\n          naturalHeight: naturalHeight,\n          aspectRatio: naturalWidth / naturalHeight\n        });\n        _this2.sizing = false;\n        _this2.sized = true;\n\n        _this2.build();\n      }; // Most modern browsers (excepts iOS WebKit)\n\n\n      if (image.naturalWidth && !isIOSWebKit) {\n        done(image.naturalWidth, image.naturalHeight);\n        return;\n      }\n\n      var sizingImage = document.createElement('img');\n      var body = document.body || document.documentElement;\n      this.sizingImage = sizingImage;\n\n      sizingImage.onload = function () {\n        done(sizingImage.width, sizingImage.height);\n\n        if (!isIOSWebKit) {\n          body.removeChild(sizingImage);\n        }\n      };\n\n      sizingImage.src = image.src; // iOS WebKit will convert the image automatically\n      // with its orientation once append it into DOM (#279)\n\n      if (!isIOSWebKit) {\n        sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';\n        body.appendChild(sizingImage);\n      }\n    }\n  }, {\n    key: \"stop\",\n    value: function stop() {\n      var image = this.image;\n      image.onload = null;\n      image.onerror = null;\n      image.parentNode.removeChild(image);\n      this.image = null;\n    }\n  }, {\n    key: \"build\",\n    value: function build() {\n      if (!this.sized || this.ready) {\n        return;\n      }\n\n      var element = this.element,\n          options = this.options,\n          image = this.image; // Create cropper elements\n\n      var container = element.parentNode;\n      var template = document.createElement('div');\n      template.innerHTML = TEMPLATE;\n      var cropper = template.querySelector(\".\".concat(NAMESPACE, \"-container\"));\n      var canvas = cropper.querySelector(\".\".concat(NAMESPACE, \"-canvas\"));\n      var dragBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-drag-box\"));\n      var cropBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-crop-box\"));\n      var face = cropBox.querySelector(\".\".concat(NAMESPACE, \"-face\"));\n      this.container = container;\n      this.cropper = cropper;\n      this.canvas = canvas;\n      this.dragBox = dragBox;\n      this.cropBox = cropBox;\n      this.viewBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-view-box\"));\n      this.face = face;\n      canvas.appendChild(image); // Hide the original image\n\n      addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image\n\n      container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden\n\n      if (!this.isImg) {\n        removeClass(image, CLASS_HIDE);\n      }\n\n      this.initPreview();\n      this.bind();\n      options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;\n      options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;\n      options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;\n      addClass(cropBox, CLASS_HIDDEN);\n\n      if (!options.guides) {\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-dashed\")), CLASS_HIDDEN);\n      }\n\n      if (!options.center) {\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-center\")), CLASS_HIDDEN);\n      }\n\n      if (options.background) {\n        addClass(cropper, \"\".concat(NAMESPACE, \"-bg\"));\n      }\n\n      if (!options.highlight) {\n        addClass(face, CLASS_INVISIBLE);\n      }\n\n      if (options.cropBoxMovable) {\n        addClass(face, CLASS_MOVE);\n        setData(face, DATA_ACTION, ACTION_ALL);\n      }\n\n      if (!options.cropBoxResizable) {\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-line\")), CLASS_HIDDEN);\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-point\")), CLASS_HIDDEN);\n      }\n\n      this.render();\n      this.ready = true;\n      this.setDragMode(options.dragMode);\n\n      if (options.autoCrop) {\n        this.crop();\n      }\n\n      this.setData(options.data);\n\n      if (isFunction(options.ready)) {\n        addListener(element, EVENT_READY, options.ready, {\n          once: true\n        });\n      }\n\n      dispatchEvent(element, EVENT_READY);\n    }\n  }, {\n    key: \"unbuild\",\n    value: function unbuild() {\n      if (!this.ready) {\n        return;\n      }\n\n      this.ready = false;\n      this.unbind();\n      this.resetPreview();\n      this.cropper.parentNode.removeChild(this.cropper);\n      removeClass(this.element, CLASS_HIDDEN);\n    }\n  }, {\n    key: \"uncreate\",\n    value: function uncreate() {\n      if (this.ready) {\n        this.unbuild();\n        this.ready = false;\n        this.cropped = false;\n      } else if (this.sizing) {\n        this.sizingImage.onload = null;\n        this.sizing = false;\n        this.sized = false;\n      } else if (this.reloading) {\n        this.xhr.onabort = null;\n        this.xhr.abort();\n      } else if (this.image) {\n        this.stop();\n      }\n    }\n    /**\n     * Get the no conflict cropper class.\n     * @returns {Cropper} The cropper class.\n     */\n\n  }], [{\n    key: \"noConflict\",\n    value: function noConflict() {\n      window.Cropper = AnotherCropper;\n      return Cropper;\n    }\n    /**\n     * Change the default options.\n     * @param {Object} options - The new default options.\n     */\n\n  }, {\n    key: \"setDefaults\",\n    value: function setDefaults(options) {\n      assign(DEFAULTS, isPlainObject(options) && options);\n    }\n  }]);\n\n  return Cropper;\n}();\n\nassign(Cropper.prototype, render, preview, events, handlers, change, methods);\n\nif ($.fn) {\n  var AnotherCropper$1 = $.fn.cropper;\n  var NAMESPACE$1 = 'cropper';\n\n  $.fn.cropper = function jQueryCropper(option) {\n    for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    var result;\n    this.each(function (i, element) {\n      var $element = $(element);\n      var isDestroy = option === 'destroy';\n      var cropper = $element.data(NAMESPACE$1);\n\n      if (!cropper) {\n        if (isDestroy) {\n          return;\n        }\n\n        var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);\n        cropper = new Cropper(element, options);\n        $element.data(NAMESPACE$1, cropper);\n      }\n\n      if (typeof option === 'string') {\n        var fn = cropper[option];\n\n        if ($.isFunction(fn)) {\n          result = fn.apply(cropper, args);\n\n          if (result === cropper) {\n            result = undefined;\n          }\n\n          if (isDestroy) {\n            $element.removeData(NAMESPACE$1);\n          }\n        }\n      }\n    });\n    return result !== undefined ? result : this;\n  };\n\n  $.fn.cropper.Constructor = Cropper;\n  $.fn.cropper.setDefaults = Cropper.setDefaults;\n\n  $.fn.cropper.noConflict = function noConflict() {\n    $.fn.cropper = AnotherCropper$1;\n    return this;\n  };\n}\n"
  },
  {
    "path": "dist/cropper.css",
    "content": "/*!\n * Cropper v4.1.0\n * https://fengyuanchen.github.io/cropper\n *\n * Copyright 2014-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2019-10-12T07:43:47.783Z\n */\n\n.cropper-container {\n  direction: ltr;\n  font-size: 0;\n  line-height: 0;\n  position: relative;\n  -ms-touch-action: none;\n  touch-action: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.cropper-container img {\n  display: block;\n  height: 100%;\n  image-orientation: 0deg;\n  max-height: none !important;\n  max-width: none !important;\n  min-height: 0 !important;\n  min-width: 0 !important;\n  width: 100%;\n}\n\n.cropper-wrap-box,\n.cropper-canvas,\n.cropper-drag-box,\n.cropper-crop-box,\n.cropper-modal {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n\n.cropper-wrap-box,\n.cropper-canvas {\n  overflow: hidden;\n}\n\n.cropper-drag-box {\n  background-color: #fff;\n  opacity: 0;\n}\n\n.cropper-modal {\n  background-color: #000;\n  opacity: 0.5;\n}\n\n.cropper-view-box {\n  display: block;\n  height: 100%;\n  outline: 1px solid #39f;\n  outline-color: rgba(51, 153, 255, 0.75);\n  overflow: hidden;\n  width: 100%;\n}\n\n.cropper-dashed {\n  border: 0 dashed #eee;\n  display: block;\n  opacity: 0.5;\n  position: absolute;\n}\n\n.cropper-dashed.dashed-h {\n  border-bottom-width: 1px;\n  border-top-width: 1px;\n  height: calc(100% / 3);\n  left: 0;\n  top: calc(100% / 3);\n  width: 100%;\n}\n\n.cropper-dashed.dashed-v {\n  border-left-width: 1px;\n  border-right-width: 1px;\n  height: 100%;\n  left: calc(100% / 3);\n  top: 0;\n  width: calc(100% / 3);\n}\n\n.cropper-center {\n  display: block;\n  height: 0;\n  left: 50%;\n  opacity: 0.75;\n  position: absolute;\n  top: 50%;\n  width: 0;\n}\n\n.cropper-center::before,\n.cropper-center::after {\n  background-color: #eee;\n  content: ' ';\n  display: block;\n  position: absolute;\n}\n\n.cropper-center::before {\n  height: 1px;\n  left: -3px;\n  top: 0;\n  width: 7px;\n}\n\n.cropper-center::after {\n  height: 7px;\n  left: 0;\n  top: -3px;\n  width: 1px;\n}\n\n.cropper-face,\n.cropper-line,\n.cropper-point {\n  display: block;\n  height: 100%;\n  opacity: 0.1;\n  position: absolute;\n  width: 100%;\n}\n\n.cropper-face {\n  background-color: #fff;\n  left: 0;\n  top: 0;\n}\n\n.cropper-line {\n  background-color: #39f;\n}\n\n.cropper-line.line-e {\n  cursor: ew-resize;\n  right: -3px;\n  top: 0;\n  width: 5px;\n}\n\n.cropper-line.line-n {\n  cursor: ns-resize;\n  height: 5px;\n  left: 0;\n  top: -3px;\n}\n\n.cropper-line.line-w {\n  cursor: ew-resize;\n  left: -3px;\n  top: 0;\n  width: 5px;\n}\n\n.cropper-line.line-s {\n  bottom: -3px;\n  cursor: ns-resize;\n  height: 5px;\n  left: 0;\n}\n\n.cropper-point {\n  background-color: #39f;\n  height: 5px;\n  opacity: 0.75;\n  width: 5px;\n}\n\n.cropper-point.point-e {\n  cursor: ew-resize;\n  margin-top: -3px;\n  right: -3px;\n  top: 50%;\n}\n\n.cropper-point.point-n {\n  cursor: ns-resize;\n  left: 50%;\n  margin-left: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-w {\n  cursor: ew-resize;\n  left: -3px;\n  margin-top: -3px;\n  top: 50%;\n}\n\n.cropper-point.point-s {\n  bottom: -3px;\n  cursor: s-resize;\n  left: 50%;\n  margin-left: -3px;\n}\n\n.cropper-point.point-ne {\n  cursor: nesw-resize;\n  right: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-nw {\n  cursor: nwse-resize;\n  left: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-sw {\n  bottom: -3px;\n  cursor: nesw-resize;\n  left: -3px;\n}\n\n.cropper-point.point-se {\n  bottom: -3px;\n  cursor: nwse-resize;\n  height: 20px;\n  opacity: 1;\n  right: -3px;\n  width: 20px;\n}\n\n@media (min-width: 768px) {\n  .cropper-point.point-se {\n    height: 15px;\n    width: 15px;\n  }\n}\n\n@media (min-width: 992px) {\n  .cropper-point.point-se {\n    height: 10px;\n    width: 10px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .cropper-point.point-se {\n    height: 5px;\n    opacity: 0.75;\n    width: 5px;\n  }\n}\n\n.cropper-point.point-se::before {\n  background-color: #39f;\n  bottom: -50%;\n  content: ' ';\n  display: block;\n  height: 200%;\n  opacity: 0;\n  position: absolute;\n  right: -50%;\n  width: 200%;\n}\n\n.cropper-invisible {\n  opacity: 0;\n}\n\n.cropper-bg {\n  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');\n}\n\n.cropper-hide {\n  display: block;\n  height: 0;\n  position: absolute;\n  width: 0;\n}\n\n.cropper-hidden {\n  display: none !important;\n}\n\n.cropper-move {\n  cursor: move;\n}\n\n.cropper-crop {\n  cursor: crosshair;\n}\n\n.cropper-disabled .cropper-drag-box,\n.cropper-disabled .cropper-face,\n.cropper-disabled .cropper-line,\n.cropper-disabled .cropper-point {\n  cursor: not-allowed;\n}\n"
  },
  {
    "path": "dist/cropper.esm.js",
    "content": "/*!\n * Cropper v4.1.0\n * https://fengyuanchen.github.io/cropper\n *\n * Copyright 2014-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2019-10-12T07:43:51.850Z\n */\n\nimport $ from 'jquery';\n\nfunction _typeof(obj) {\n  if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n    _typeof = function (obj) {\n      return typeof obj;\n    };\n  } else {\n    _typeof = function (obj) {\n      return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n    };\n  }\n\n  return _typeof(obj);\n}\n\nfunction _classCallCheck(instance, Constructor) {\n  if (!(instance instanceof Constructor)) {\n    throw new TypeError(\"Cannot call a class as a function\");\n  }\n}\n\nfunction _defineProperties(target, props) {\n  for (var i = 0; i < props.length; i++) {\n    var descriptor = props[i];\n    descriptor.enumerable = descriptor.enumerable || false;\n    descriptor.configurable = true;\n    if (\"value\" in descriptor) descriptor.writable = true;\n    Object.defineProperty(target, descriptor.key, descriptor);\n  }\n}\n\nfunction _createClass(Constructor, protoProps, staticProps) {\n  if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n  if (staticProps) _defineProperties(Constructor, staticProps);\n  return Constructor;\n}\n\nfunction _defineProperty(obj, key, value) {\n  if (key in obj) {\n    Object.defineProperty(obj, key, {\n      value: value,\n      enumerable: true,\n      configurable: true,\n      writable: true\n    });\n  } else {\n    obj[key] = value;\n  }\n\n  return obj;\n}\n\nfunction ownKeys(object, enumerableOnly) {\n  var keys = Object.keys(object);\n\n  if (Object.getOwnPropertySymbols) {\n    var symbols = Object.getOwnPropertySymbols(object);\n    if (enumerableOnly) symbols = symbols.filter(function (sym) {\n      return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n    });\n    keys.push.apply(keys, symbols);\n  }\n\n  return keys;\n}\n\nfunction _objectSpread2(target) {\n  for (var i = 1; i < arguments.length; i++) {\n    var source = arguments[i] != null ? arguments[i] : {};\n\n    if (i % 2) {\n      ownKeys(source, true).forEach(function (key) {\n        _defineProperty(target, key, source[key]);\n      });\n    } else if (Object.getOwnPropertyDescriptors) {\n      Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n    } else {\n      ownKeys(source).forEach(function (key) {\n        Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n      });\n    }\n  }\n\n  return target;\n}\n\nfunction _toConsumableArray(arr) {\n  return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n}\n\nfunction _arrayWithoutHoles(arr) {\n  if (Array.isArray(arr)) {\n    for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n    return arr2;\n  }\n}\n\nfunction _iterableToArray(iter) {\n  if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter);\n}\n\nfunction _nonIterableSpread() {\n  throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n}\n\nvar IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';\nvar WINDOW = IS_BROWSER ? window : {};\nvar IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false;\nvar HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;\nvar NAMESPACE = 'cropper'; // Actions\n\nvar ACTION_ALL = 'all';\nvar ACTION_CROP = 'crop';\nvar ACTION_MOVE = 'move';\nvar ACTION_ZOOM = 'zoom';\nvar ACTION_EAST = 'e';\nvar ACTION_WEST = 'w';\nvar ACTION_SOUTH = 's';\nvar ACTION_NORTH = 'n';\nvar ACTION_NORTH_EAST = 'ne';\nvar ACTION_NORTH_WEST = 'nw';\nvar ACTION_SOUTH_EAST = 'se';\nvar ACTION_SOUTH_WEST = 'sw'; // Classes\n\nvar CLASS_CROP = \"\".concat(NAMESPACE, \"-crop\");\nvar CLASS_DISABLED = \"\".concat(NAMESPACE, \"-disabled\");\nvar CLASS_HIDDEN = \"\".concat(NAMESPACE, \"-hidden\");\nvar CLASS_HIDE = \"\".concat(NAMESPACE, \"-hide\");\nvar CLASS_INVISIBLE = \"\".concat(NAMESPACE, \"-invisible\");\nvar CLASS_MODAL = \"\".concat(NAMESPACE, \"-modal\");\nvar CLASS_MOVE = \"\".concat(NAMESPACE, \"-move\"); // Data keys\n\nvar DATA_ACTION = \"\".concat(NAMESPACE, \"Action\");\nvar DATA_PREVIEW = \"\".concat(NAMESPACE, \"Preview\"); // Drag modes\n\nvar DRAG_MODE_CROP = 'crop';\nvar DRAG_MODE_MOVE = 'move';\nvar DRAG_MODE_NONE = 'none'; // Events\n\nvar EVENT_CROP = 'crop';\nvar EVENT_CROP_END = 'cropend';\nvar EVENT_CROP_MOVE = 'cropmove';\nvar EVENT_CROP_START = 'cropstart';\nvar EVENT_DBLCLICK = 'dblclick';\nvar EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';\nvar EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';\nvar EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';\nvar EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;\nvar EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;\nvar EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;\nvar EVENT_READY = 'ready';\nvar EVENT_RESIZE = 'resize';\nvar EVENT_WHEEL = 'wheel';\nvar EVENT_ZOOM = 'zoom'; // Mime types\n\nvar MIME_TYPE_JPEG = 'image/jpeg'; // RegExps\n\nvar REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;\nvar REGEXP_DATA_URL = /^data:/;\nvar REGEXP_DATA_URL_JPEG = /^data:image\\/jpeg;base64,/;\nvar REGEXP_TAG_NAME = /^img|canvas$/i; // Misc\n// Inspired by the default width and height of a canvas element.\n\nvar MIN_CONTAINER_WIDTH = 200;\nvar MIN_CONTAINER_HEIGHT = 100;\n\nvar DEFAULTS = {\n  // Define the view mode of the cropper\n  viewMode: 0,\n  // 0, 1, 2, 3\n  // Define the dragging mode of the cropper\n  dragMode: DRAG_MODE_CROP,\n  // 'crop', 'move' or 'none'\n  // Define the initial aspect ratio of the crop box\n  initialAspectRatio: NaN,\n  // Define the aspect ratio of the crop box\n  aspectRatio: NaN,\n  // An object with the previous cropping result data\n  data: null,\n  // A selector for adding extra containers to preview\n  preview: '',\n  // Re-render the cropper when resize the window\n  responsive: true,\n  // Restore the cropped area after resize the window\n  restore: true,\n  // Check if the current image is a cross-origin image\n  checkCrossOrigin: true,\n  // Check the current image's Exif Orientation information\n  checkOrientation: true,\n  // Show the black modal\n  modal: true,\n  // Show the dashed lines for guiding\n  guides: true,\n  // Show the center indicator for guiding\n  center: true,\n  // Show the white modal to highlight the crop box\n  highlight: true,\n  // Show the grid background\n  background: true,\n  // Enable to crop the image automatically when initialize\n  autoCrop: true,\n  // Define the percentage of automatic cropping area when initializes\n  autoCropArea: 0.8,\n  // Enable to move the image\n  movable: true,\n  // Enable to rotate the image\n  rotatable: true,\n  // Enable to scale the image\n  scalable: true,\n  // Enable to zoom the image\n  zoomable: true,\n  // Enable to zoom the image by dragging touch\n  zoomOnTouch: true,\n  // Enable to zoom the image by wheeling mouse\n  zoomOnWheel: true,\n  // Define zoom ratio when zoom the image by wheeling mouse\n  wheelZoomRatio: 0.1,\n  // Enable to move the crop box\n  cropBoxMovable: true,\n  // Enable to resize the crop box\n  cropBoxResizable: true,\n  // Toggle drag mode between \"crop\" and \"move\" when click twice on the cropper\n  toggleDragModeOnDblclick: true,\n  // Size limitation\n  minCanvasWidth: 0,\n  minCanvasHeight: 0,\n  minCropBoxWidth: 0,\n  minCropBoxHeight: 0,\n  minContainerWidth: 200,\n  minContainerHeight: 100,\n  // Shortcuts of events\n  ready: null,\n  cropstart: null,\n  cropmove: null,\n  cropend: null,\n  crop: null,\n  zoom: null\n};\n\nvar TEMPLATE = '<div class=\"cropper-container\" touch-action=\"none\">' + '<div class=\"cropper-wrap-box\">' + '<div class=\"cropper-canvas\"></div>' + '</div>' + '<div class=\"cropper-drag-box\"></div>' + '<div class=\"cropper-crop-box\">' + '<span class=\"cropper-view-box\"></span>' + '<span class=\"cropper-dashed dashed-h\"></span>' + '<span class=\"cropper-dashed dashed-v\"></span>' + '<span class=\"cropper-center\"></span>' + '<span class=\"cropper-face\"></span>' + '<span class=\"cropper-line line-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-line line-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-line line-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-line line-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-point point-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-point point-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-point point-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-ne\" data-cropper-action=\"ne\"></span>' + '<span class=\"cropper-point point-nw\" data-cropper-action=\"nw\"></span>' + '<span class=\"cropper-point point-sw\" data-cropper-action=\"sw\"></span>' + '<span class=\"cropper-point point-se\" data-cropper-action=\"se\"></span>' + '</div>' + '</div>';\n\n/**\n * Check if the given value is not a number.\n */\n\nvar isNaN = Number.isNaN || WINDOW.isNaN;\n/**\n * Check if the given value is a number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a number, else `false`.\n */\n\nfunction isNumber(value) {\n  return typeof value === 'number' && !isNaN(value);\n}\n/**\n * Check if the given value is a positive number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a positive number, else `false`.\n */\n\nvar isPositiveNumber = function isPositiveNumber(value) {\n  return value > 0 && value < Infinity;\n};\n/**\n * Check if the given value is undefined.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is undefined, else `false`.\n */\n\nfunction isUndefined(value) {\n  return typeof value === 'undefined';\n}\n/**\n * Check if the given value is an object.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is an object, else `false`.\n */\n\nfunction isObject(value) {\n  return _typeof(value) === 'object' && value !== null;\n}\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n/**\n * Check if the given value is a plain object.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.\n */\n\nfunction isPlainObject(value) {\n  if (!isObject(value)) {\n    return false;\n  }\n\n  try {\n    var _constructor = value.constructor;\n    var prototype = _constructor.prototype;\n    return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');\n  } catch (error) {\n    return false;\n  }\n}\n/**\n * Check if the given value is a function.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a function, else `false`.\n */\n\nfunction isFunction(value) {\n  return typeof value === 'function';\n}\nvar slice = Array.prototype.slice;\n/**\n * Convert array-like or iterable object to an array.\n * @param {*} value - The value to convert.\n * @returns {Array} Returns a new array.\n */\n\nfunction toArray(value) {\n  return Array.from ? Array.from(value) : slice.call(value);\n}\n/**\n * Iterate the given data.\n * @param {*} data - The data to iterate.\n * @param {Function} callback - The process function for each element.\n * @returns {*} The original data.\n */\n\nfunction forEach(data, callback) {\n  if (data && isFunction(callback)) {\n    if (Array.isArray(data) || isNumber(data.length)\n    /* array-like */\n    ) {\n        toArray(data).forEach(function (value, key) {\n          callback.call(data, value, key, data);\n        });\n      } else if (isObject(data)) {\n      Object.keys(data).forEach(function (key) {\n        callback.call(data, data[key], key, data);\n      });\n    }\n  }\n\n  return data;\n}\n/**\n * Extend the given object.\n * @param {*} target - The target object to extend.\n * @param {*} args - The rest objects for merging to the target object.\n * @returns {Object} The extended object.\n */\n\nvar assign = Object.assign || function assign(target) {\n  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n    args[_key - 1] = arguments[_key];\n  }\n\n  if (isObject(target) && args.length > 0) {\n    args.forEach(function (arg) {\n      if (isObject(arg)) {\n        Object.keys(arg).forEach(function (key) {\n          target[key] = arg[key];\n        });\n      }\n    });\n  }\n\n  return target;\n};\nvar REGEXP_DECIMALS = /\\.\\d*(?:0|9){12}\\d*$/;\n/**\n * Normalize decimal number.\n * Check out {@link http://0.30000000000000004.com/}\n * @param {number} value - The value to normalize.\n * @param {number} [times=100000000000] - The times for normalizing.\n * @returns {number} Returns the normalized number.\n */\n\nfunction normalizeDecimalNumber(value) {\n  var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;\n  return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;\n}\nvar REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;\n/**\n * Apply styles to the given element.\n * @param {Element} element - The target element.\n * @param {Object} styles - The styles for applying.\n */\n\nfunction setStyle(element, styles) {\n  var style = element.style;\n  forEach(styles, function (value, property) {\n    if (REGEXP_SUFFIX.test(property) && isNumber(value)) {\n      value = \"\".concat(value, \"px\");\n    }\n\n    style[property] = value;\n  });\n}\n/**\n * Check if the given element has a special class.\n * @param {Element} element - The element to check.\n * @param {string} value - The class to search.\n * @returns {boolean} Returns `true` if the special class was found.\n */\n\nfunction hasClass(element, value) {\n  return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;\n}\n/**\n * Add classes to the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be added.\n */\n\nfunction addClass(element, value) {\n  if (!value) {\n    return;\n  }\n\n  if (isNumber(element.length)) {\n    forEach(element, function (elem) {\n      addClass(elem, value);\n    });\n    return;\n  }\n\n  if (element.classList) {\n    element.classList.add(value);\n    return;\n  }\n\n  var className = element.className.trim();\n\n  if (!className) {\n    element.className = value;\n  } else if (className.indexOf(value) < 0) {\n    element.className = \"\".concat(className, \" \").concat(value);\n  }\n}\n/**\n * Remove classes from the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be removed.\n */\n\nfunction removeClass(element, value) {\n  if (!value) {\n    return;\n  }\n\n  if (isNumber(element.length)) {\n    forEach(element, function (elem) {\n      removeClass(elem, value);\n    });\n    return;\n  }\n\n  if (element.classList) {\n    element.classList.remove(value);\n    return;\n  }\n\n  if (element.className.indexOf(value) >= 0) {\n    element.className = element.className.replace(value, '');\n  }\n}\n/**\n * Add or remove classes from the given element.\n * @param {Element} element - The target element.\n * @param {string} value - The classes to be toggled.\n * @param {boolean} added - Add only.\n */\n\nfunction toggleClass(element, value, added) {\n  if (!value) {\n    return;\n  }\n\n  if (isNumber(element.length)) {\n    forEach(element, function (elem) {\n      toggleClass(elem, value, added);\n    });\n    return;\n  } // IE10-11 doesn't support the second parameter of `classList.toggle`\n\n\n  if (added) {\n    addClass(element, value);\n  } else {\n    removeClass(element, value);\n  }\n}\nvar REGEXP_CAMEL_CASE = /([a-z\\d])([A-Z])/g;\n/**\n * Transform the given string from camelCase to kebab-case\n * @param {string} value - The value to transform.\n * @returns {string} The transformed value.\n */\n\nfunction toParamCase(value) {\n  return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();\n}\n/**\n * Get data from the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to get.\n * @returns {string} The data value.\n */\n\nfunction getData(element, name) {\n  if (isObject(element[name])) {\n    return element[name];\n  }\n\n  if (element.dataset) {\n    return element.dataset[name];\n  }\n\n  return element.getAttribute(\"data-\".concat(toParamCase(name)));\n}\n/**\n * Set data to the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to set.\n * @param {string} data - The data value.\n */\n\nfunction setData(element, name, data) {\n  if (isObject(data)) {\n    element[name] = data;\n  } else if (element.dataset) {\n    element.dataset[name] = data;\n  } else {\n    element.setAttribute(\"data-\".concat(toParamCase(name)), data);\n  }\n}\n/**\n * Remove data from the given element.\n * @param {Element} element - The target element.\n * @param {string} name - The data key to remove.\n */\n\nfunction removeData(element, name) {\n  if (isObject(element[name])) {\n    try {\n      delete element[name];\n    } catch (error) {\n      element[name] = undefined;\n    }\n  } else if (element.dataset) {\n    // #128 Safari not allows to delete dataset property\n    try {\n      delete element.dataset[name];\n    } catch (error) {\n      element.dataset[name] = undefined;\n    }\n  } else {\n    element.removeAttribute(\"data-\".concat(toParamCase(name)));\n  }\n}\nvar REGEXP_SPACES = /\\s\\s*/;\n\nvar onceSupported = function () {\n  var supported = false;\n\n  if (IS_BROWSER) {\n    var once = false;\n\n    var listener = function listener() {};\n\n    var options = Object.defineProperty({}, 'once', {\n      get: function get() {\n        supported = true;\n        return once;\n      },\n\n      /**\n       * This setter can fix a `TypeError` in strict mode\n       * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}\n       * @param {boolean} value - The value to set\n       */\n      set: function set(value) {\n        once = value;\n      }\n    });\n    WINDOW.addEventListener('test', listener, options);\n    WINDOW.removeEventListener('test', listener, options);\n  }\n\n  return supported;\n}();\n/**\n * Remove event listener from the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Function} listener - The event listener.\n * @param {Object} options - The event options.\n */\n\n\nfunction removeListener(element, type, listener) {\n  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n  var handler = listener;\n  type.trim().split(REGEXP_SPACES).forEach(function (event) {\n    if (!onceSupported) {\n      var listeners = element.listeners;\n\n      if (listeners && listeners[event] && listeners[event][listener]) {\n        handler = listeners[event][listener];\n        delete listeners[event][listener];\n\n        if (Object.keys(listeners[event]).length === 0) {\n          delete listeners[event];\n        }\n\n        if (Object.keys(listeners).length === 0) {\n          delete element.listeners;\n        }\n      }\n    }\n\n    element.removeEventListener(event, handler, options);\n  });\n}\n/**\n * Add event listener to the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Function} listener - The event listener.\n * @param {Object} options - The event options.\n */\n\nfunction addListener(element, type, listener) {\n  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n  var _handler = listener;\n  type.trim().split(REGEXP_SPACES).forEach(function (event) {\n    if (options.once && !onceSupported) {\n      var _element$listeners = element.listeners,\n          listeners = _element$listeners === void 0 ? {} : _element$listeners;\n\n      _handler = function handler() {\n        delete listeners[event][listener];\n        element.removeEventListener(event, _handler, options);\n\n        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n          args[_key2] = arguments[_key2];\n        }\n\n        listener.apply(element, args);\n      };\n\n      if (!listeners[event]) {\n        listeners[event] = {};\n      }\n\n      if (listeners[event][listener]) {\n        element.removeEventListener(event, listeners[event][listener], options);\n      }\n\n      listeners[event][listener] = _handler;\n      element.listeners = listeners;\n    }\n\n    element.addEventListener(event, _handler, options);\n  });\n}\n/**\n * Dispatch event on the target element.\n * @param {Element} element - The event target.\n * @param {string} type - The event type(s).\n * @param {Object} data - The additional event data.\n * @returns {boolean} Indicate if the event is default prevented or not.\n */\n\nfunction dispatchEvent(element, type, data) {\n  var event; // Event and CustomEvent on IE9-11 are global objects, not constructors\n\n  if (isFunction(Event) && isFunction(CustomEvent)) {\n    event = new CustomEvent(type, {\n      detail: data,\n      bubbles: true,\n      cancelable: true\n    });\n  } else {\n    event = document.createEvent('CustomEvent');\n    event.initCustomEvent(type, true, true, data);\n  }\n\n  return element.dispatchEvent(event);\n}\n/**\n * Get the offset base on the document.\n * @param {Element} element - The target element.\n * @returns {Object} The offset data.\n */\n\nfunction getOffset(element) {\n  var box = element.getBoundingClientRect();\n  return {\n    left: box.left + (window.pageXOffset - document.documentElement.clientLeft),\n    top: box.top + (window.pageYOffset - document.documentElement.clientTop)\n  };\n}\nvar location = WINDOW.location;\nvar REGEXP_ORIGINS = /^(\\w+:)\\/\\/([^:/?#]*):?(\\d*)/i;\n/**\n * Check if the given URL is a cross origin URL.\n * @param {string} url - The target URL.\n * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.\n */\n\nfunction isCrossOriginURL(url) {\n  var parts = url.match(REGEXP_ORIGINS);\n  return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);\n}\n/**\n * Add timestamp to the given URL.\n * @param {string} url - The target URL.\n * @returns {string} The result URL.\n */\n\nfunction addTimestamp(url) {\n  var timestamp = \"timestamp=\".concat(new Date().getTime());\n  return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;\n}\n/**\n * Get transforms base on the given object.\n * @param {Object} obj - The target object.\n * @returns {string} A string contains transform values.\n */\n\nfunction getTransforms(_ref) {\n  var rotate = _ref.rotate,\n      scaleX = _ref.scaleX,\n      scaleY = _ref.scaleY,\n      translateX = _ref.translateX,\n      translateY = _ref.translateY;\n  var values = [];\n\n  if (isNumber(translateX) && translateX !== 0) {\n    values.push(\"translateX(\".concat(translateX, \"px)\"));\n  }\n\n  if (isNumber(translateY) && translateY !== 0) {\n    values.push(\"translateY(\".concat(translateY, \"px)\"));\n  } // Rotate should come first before scale to match orientation transform\n\n\n  if (isNumber(rotate) && rotate !== 0) {\n    values.push(\"rotate(\".concat(rotate, \"deg)\"));\n  }\n\n  if (isNumber(scaleX) && scaleX !== 1) {\n    values.push(\"scaleX(\".concat(scaleX, \")\"));\n  }\n\n  if (isNumber(scaleY) && scaleY !== 1) {\n    values.push(\"scaleY(\".concat(scaleY, \")\"));\n  }\n\n  var transform = values.length ? values.join(' ') : 'none';\n  return {\n    WebkitTransform: transform,\n    msTransform: transform,\n    transform: transform\n  };\n}\n/**\n * Get the max ratio of a group of pointers.\n * @param {string} pointers - The target pointers.\n * @returns {number} The result ratio.\n */\n\nfunction getMaxZoomRatio(pointers) {\n  var pointers2 = _objectSpread2({}, pointers);\n\n  var ratios = [];\n  forEach(pointers, function (pointer, pointerId) {\n    delete pointers2[pointerId];\n    forEach(pointers2, function (pointer2) {\n      var x1 = Math.abs(pointer.startX - pointer2.startX);\n      var y1 = Math.abs(pointer.startY - pointer2.startY);\n      var x2 = Math.abs(pointer.endX - pointer2.endX);\n      var y2 = Math.abs(pointer.endY - pointer2.endY);\n      var z1 = Math.sqrt(x1 * x1 + y1 * y1);\n      var z2 = Math.sqrt(x2 * x2 + y2 * y2);\n      var ratio = (z2 - z1) / z1;\n      ratios.push(ratio);\n    });\n  });\n  ratios.sort(function (a, b) {\n    return Math.abs(a) < Math.abs(b);\n  });\n  return ratios[0];\n}\n/**\n * Get a pointer from an event object.\n * @param {Object} event - The target event object.\n * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.\n * @returns {Object} The result pointer contains start and/or end point coordinates.\n */\n\nfunction getPointer(_ref2, endOnly) {\n  var pageX = _ref2.pageX,\n      pageY = _ref2.pageY;\n  var end = {\n    endX: pageX,\n    endY: pageY\n  };\n  return endOnly ? end : _objectSpread2({\n    startX: pageX,\n    startY: pageY\n  }, end);\n}\n/**\n * Get the center point coordinate of a group of pointers.\n * @param {Object} pointers - The target pointers.\n * @returns {Object} The center point coordinate.\n */\n\nfunction getPointersCenter(pointers) {\n  var pageX = 0;\n  var pageY = 0;\n  var count = 0;\n  forEach(pointers, function (_ref3) {\n    var startX = _ref3.startX,\n        startY = _ref3.startY;\n    pageX += startX;\n    pageY += startY;\n    count += 1;\n  });\n  pageX /= count;\n  pageY /= count;\n  return {\n    pageX: pageX,\n    pageY: pageY\n  };\n}\n/**\n * Get the max sizes in a rectangle under the given aspect ratio.\n * @param {Object} data - The original sizes.\n * @param {string} [type='contain'] - The adjust type.\n * @returns {Object} The result sizes.\n */\n\nfunction getAdjustedSizes(_ref4) // or 'cover'\n{\n  var aspectRatio = _ref4.aspectRatio,\n      height = _ref4.height,\n      width = _ref4.width;\n  var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';\n  var isValidWidth = isPositiveNumber(width);\n  var isValidHeight = isPositiveNumber(height);\n\n  if (isValidWidth && isValidHeight) {\n    var adjustedWidth = height * aspectRatio;\n\n    if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {\n      height = width / aspectRatio;\n    } else {\n      width = height * aspectRatio;\n    }\n  } else if (isValidWidth) {\n    height = width / aspectRatio;\n  } else if (isValidHeight) {\n    width = height * aspectRatio;\n  }\n\n  return {\n    width: width,\n    height: height\n  };\n}\n/**\n * Get the new sizes of a rectangle after rotated.\n * @param {Object} data - The original sizes.\n * @returns {Object} The result sizes.\n */\n\nfunction getRotatedSizes(_ref5) {\n  var width = _ref5.width,\n      height = _ref5.height,\n      degree = _ref5.degree;\n  degree = Math.abs(degree) % 180;\n\n  if (degree === 90) {\n    return {\n      width: height,\n      height: width\n    };\n  }\n\n  var arc = degree % 90 * Math.PI / 180;\n  var sinArc = Math.sin(arc);\n  var cosArc = Math.cos(arc);\n  var newWidth = width * cosArc + height * sinArc;\n  var newHeight = width * sinArc + height * cosArc;\n  return degree > 90 ? {\n    width: newHeight,\n    height: newWidth\n  } : {\n    width: newWidth,\n    height: newHeight\n  };\n}\n/**\n * Get a canvas which drew the given image.\n * @param {HTMLImageElement} image - The image for drawing.\n * @param {Object} imageData - The image data.\n * @param {Object} canvasData - The canvas data.\n * @param {Object} options - The options.\n * @returns {HTMLCanvasElement} The result canvas.\n */\n\nfunction getSourceCanvas(image, _ref6, _ref7, _ref8) {\n  var imageAspectRatio = _ref6.aspectRatio,\n      imageNaturalWidth = _ref6.naturalWidth,\n      imageNaturalHeight = _ref6.naturalHeight,\n      _ref6$rotate = _ref6.rotate,\n      rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,\n      _ref6$scaleX = _ref6.scaleX,\n      scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,\n      _ref6$scaleY = _ref6.scaleY,\n      scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;\n  var aspectRatio = _ref7.aspectRatio,\n      naturalWidth = _ref7.naturalWidth,\n      naturalHeight = _ref7.naturalHeight;\n  var _ref8$fillColor = _ref8.fillColor,\n      fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor,\n      _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,\n      imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,\n      _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,\n      imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ,\n      _ref8$maxWidth = _ref8.maxWidth,\n      maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,\n      _ref8$maxHeight = _ref8.maxHeight,\n      maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,\n      _ref8$minWidth = _ref8.minWidth,\n      minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,\n      _ref8$minHeight = _ref8.minHeight,\n      minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;\n  var canvas = document.createElement('canvas');\n  var context = canvas.getContext('2d');\n  var maxSizes = getAdjustedSizes({\n    aspectRatio: aspectRatio,\n    width: maxWidth,\n    height: maxHeight\n  });\n  var minSizes = getAdjustedSizes({\n    aspectRatio: aspectRatio,\n    width: minWidth,\n    height: minHeight\n  }, 'cover');\n  var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));\n  var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as\n  // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90\n\n  var destMaxSizes = getAdjustedSizes({\n    aspectRatio: imageAspectRatio,\n    width: maxWidth,\n    height: maxHeight\n  });\n  var destMinSizes = getAdjustedSizes({\n    aspectRatio: imageAspectRatio,\n    width: minWidth,\n    height: minHeight\n  }, 'cover');\n  var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));\n  var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));\n  var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];\n  canvas.width = normalizeDecimalNumber(width);\n  canvas.height = normalizeDecimalNumber(height);\n  context.fillStyle = fillColor;\n  context.fillRect(0, 0, width, height);\n  context.save();\n  context.translate(width / 2, height / 2);\n  context.rotate(rotate * Math.PI / 180);\n  context.scale(scaleX, scaleY);\n  context.imageSmoothingEnabled = imageSmoothingEnabled;\n  context.imageSmoothingQuality = imageSmoothingQuality;\n  context.drawImage.apply(context, [image].concat(_toConsumableArray(params.map(function (param) {\n    return Math.floor(normalizeDecimalNumber(param));\n  }))));\n  context.restore();\n  return canvas;\n}\nvar fromCharCode = String.fromCharCode;\n/**\n * Get string from char code in data view.\n * @param {DataView} dataView - The data view for read.\n * @param {number} start - The start index.\n * @param {number} length - The read length.\n * @returns {string} The read result.\n */\n\nfunction getStringFromCharCode(dataView, start, length) {\n  var str = '';\n  length += start;\n\n  for (var i = start; i < length; i += 1) {\n    str += fromCharCode(dataView.getUint8(i));\n  }\n\n  return str;\n}\nvar REGEXP_DATA_URL_HEAD = /^data:.*,/;\n/**\n * Transform Data URL to array buffer.\n * @param {string} dataURL - The Data URL to transform.\n * @returns {ArrayBuffer} The result array buffer.\n */\n\nfunction dataURLToArrayBuffer(dataURL) {\n  var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n  var binary = atob(base64);\n  var arrayBuffer = new ArrayBuffer(binary.length);\n  var uint8 = new Uint8Array(arrayBuffer);\n  forEach(uint8, function (value, i) {\n    uint8[i] = binary.charCodeAt(i);\n  });\n  return arrayBuffer;\n}\n/**\n * Transform array buffer to Data URL.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.\n * @param {string} mimeType - The mime type of the Data URL.\n * @returns {string} The result Data URL.\n */\n\nfunction arrayBufferToDataURL(arrayBuffer, mimeType) {\n  var chunks = []; // Chunk Typed Array for better performance (#435)\n\n  var chunkSize = 8192;\n  var uint8 = new Uint8Array(arrayBuffer);\n\n  while (uint8.length > 0) {\n    // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9\n    // eslint-disable-next-line prefer-spread\n    chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));\n    uint8 = uint8.subarray(chunkSize);\n  }\n\n  return \"data:\".concat(mimeType, \";base64,\").concat(btoa(chunks.join('')));\n}\n/**\n * Get orientation value from given array buffer.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to read.\n * @returns {number} The read orientation value.\n */\n\nfunction resetAndGetOrientation(arrayBuffer) {\n  var dataView = new DataView(arrayBuffer);\n  var orientation; // Ignores range error when the image does not have correct Exif information\n\n  try {\n    var littleEndian;\n    var app1Start;\n    var ifdStart; // Only handle JPEG image (start by 0xFFD8)\n\n    if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n      var length = dataView.byteLength;\n      var offset = 2;\n\n      while (offset + 1 < length) {\n        if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n          app1Start = offset;\n          break;\n        }\n\n        offset += 1;\n      }\n    }\n\n    if (app1Start) {\n      var exifIDCode = app1Start + 4;\n      var tiffOffset = app1Start + 10;\n\n      if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n        var endianness = dataView.getUint16(tiffOffset);\n        littleEndian = endianness === 0x4949;\n\n        if (littleEndian || endianness === 0x4D4D\n        /* bigEndian */\n        ) {\n            if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n              var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n              if (firstIFDOffset >= 0x00000008) {\n                ifdStart = tiffOffset + firstIFDOffset;\n              }\n            }\n          }\n      }\n    }\n\n    if (ifdStart) {\n      var _length = dataView.getUint16(ifdStart, littleEndian);\n\n      var _offset;\n\n      var i;\n\n      for (i = 0; i < _length; i += 1) {\n        _offset = ifdStart + i * 12 + 2;\n\n        if (dataView.getUint16(_offset, littleEndian) === 0x0112\n        /* Orientation */\n        ) {\n            // 8 is the offset of the current tag's value\n            _offset += 8; // Get the original orientation value\n\n            orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value\n\n            dataView.setUint16(_offset, 1, littleEndian);\n            break;\n          }\n      }\n    }\n  } catch (error) {\n    orientation = 1;\n  }\n\n  return orientation;\n}\n/**\n * Parse Exif Orientation value.\n * @param {number} orientation - The orientation to parse.\n * @returns {Object} The parsed result.\n */\n\nfunction parseOrientation(orientation) {\n  var rotate = 0;\n  var scaleX = 1;\n  var scaleY = 1;\n\n  switch (orientation) {\n    // Flip horizontal\n    case 2:\n      scaleX = -1;\n      break;\n    // Rotate left 180°\n\n    case 3:\n      rotate = -180;\n      break;\n    // Flip vertical\n\n    case 4:\n      scaleY = -1;\n      break;\n    // Flip vertical and rotate right 90°\n\n    case 5:\n      rotate = 90;\n      scaleY = -1;\n      break;\n    // Rotate right 90°\n\n    case 6:\n      rotate = 90;\n      break;\n    // Flip horizontal and rotate right 90°\n\n    case 7:\n      rotate = 90;\n      scaleX = -1;\n      break;\n    // Rotate left 90°\n\n    case 8:\n      rotate = -90;\n      break;\n\n    default:\n  }\n\n  return {\n    rotate: rotate,\n    scaleX: scaleX,\n    scaleY: scaleY\n  };\n}\n\nvar render = {\n  render: function render() {\n    this.initContainer();\n    this.initCanvas();\n    this.initCropBox();\n    this.renderCanvas();\n\n    if (this.cropped) {\n      this.renderCropBox();\n    }\n  },\n  initContainer: function initContainer() {\n    var element = this.element,\n        options = this.options,\n        container = this.container,\n        cropper = this.cropper;\n    addClass(cropper, CLASS_HIDDEN);\n    removeClass(element, CLASS_HIDDEN);\n    var containerData = {\n      width: Math.max(container.offsetWidth, Number(options.minContainerWidth) || 200),\n      height: Math.max(container.offsetHeight, Number(options.minContainerHeight) || 100)\n    };\n    this.containerData = containerData;\n    setStyle(cropper, {\n      width: containerData.width,\n      height: containerData.height\n    });\n    addClass(element, CLASS_HIDDEN);\n    removeClass(cropper, CLASS_HIDDEN);\n  },\n  // Canvas (image wrapper)\n  initCanvas: function initCanvas() {\n    var containerData = this.containerData,\n        imageData = this.imageData;\n    var viewMode = this.options.viewMode;\n    var rotated = Math.abs(imageData.rotate) % 180 === 90;\n    var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;\n    var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;\n    var aspectRatio = naturalWidth / naturalHeight;\n    var canvasWidth = containerData.width;\n    var canvasHeight = containerData.height;\n\n    if (containerData.height * aspectRatio > containerData.width) {\n      if (viewMode === 3) {\n        canvasWidth = containerData.height * aspectRatio;\n      } else {\n        canvasHeight = containerData.width / aspectRatio;\n      }\n    } else if (viewMode === 3) {\n      canvasHeight = containerData.width / aspectRatio;\n    } else {\n      canvasWidth = containerData.height * aspectRatio;\n    }\n\n    var canvasData = {\n      aspectRatio: aspectRatio,\n      naturalWidth: naturalWidth,\n      naturalHeight: naturalHeight,\n      width: canvasWidth,\n      height: canvasHeight\n    };\n    canvasData.left = (containerData.width - canvasWidth) / 2;\n    canvasData.top = (containerData.height - canvasHeight) / 2;\n    canvasData.oldLeft = canvasData.left;\n    canvasData.oldTop = canvasData.top;\n    this.canvasData = canvasData;\n    this.limited = viewMode === 1 || viewMode === 2;\n    this.limitCanvas(true, true);\n    this.initialImageData = assign({}, imageData);\n    this.initialCanvasData = assign({}, canvasData);\n  },\n  limitCanvas: function limitCanvas(sizeLimited, positionLimited) {\n    var options = this.options,\n        containerData = this.containerData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData;\n    var viewMode = options.viewMode;\n    var aspectRatio = canvasData.aspectRatio;\n    var cropped = this.cropped && cropBoxData;\n\n    if (sizeLimited) {\n      var minCanvasWidth = Number(options.minCanvasWidth) || 0;\n      var minCanvasHeight = Number(options.minCanvasHeight) || 0;\n\n      if (viewMode > 1) {\n        minCanvasWidth = Math.max(minCanvasWidth, containerData.width);\n        minCanvasHeight = Math.max(minCanvasHeight, containerData.height);\n\n        if (viewMode === 3) {\n          if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n            minCanvasWidth = minCanvasHeight * aspectRatio;\n          } else {\n            minCanvasHeight = minCanvasWidth / aspectRatio;\n          }\n        }\n      } else if (viewMode > 0) {\n        if (minCanvasWidth) {\n          minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);\n        } else if (minCanvasHeight) {\n          minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);\n        } else if (cropped) {\n          minCanvasWidth = cropBoxData.width;\n          minCanvasHeight = cropBoxData.height;\n\n          if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n            minCanvasWidth = minCanvasHeight * aspectRatio;\n          } else {\n            minCanvasHeight = minCanvasWidth / aspectRatio;\n          }\n        }\n      }\n\n      var _getAdjustedSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: minCanvasWidth,\n        height: minCanvasHeight\n      });\n\n      minCanvasWidth = _getAdjustedSizes.width;\n      minCanvasHeight = _getAdjustedSizes.height;\n      canvasData.minWidth = minCanvasWidth;\n      canvasData.minHeight = minCanvasHeight;\n      canvasData.maxWidth = Infinity;\n      canvasData.maxHeight = Infinity;\n    }\n\n    if (positionLimited) {\n      if (viewMode > (cropped ? 0 : 1)) {\n        var newCanvasLeft = containerData.width - canvasData.width;\n        var newCanvasTop = containerData.height - canvasData.height;\n        canvasData.minLeft = Math.min(0, newCanvasLeft);\n        canvasData.minTop = Math.min(0, newCanvasTop);\n        canvasData.maxLeft = Math.max(0, newCanvasLeft);\n        canvasData.maxTop = Math.max(0, newCanvasTop);\n\n        if (cropped && this.limited) {\n          canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));\n          canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));\n          canvasData.maxLeft = cropBoxData.left;\n          canvasData.maxTop = cropBoxData.top;\n\n          if (viewMode === 2) {\n            if (canvasData.width >= containerData.width) {\n              canvasData.minLeft = Math.min(0, newCanvasLeft);\n              canvasData.maxLeft = Math.max(0, newCanvasLeft);\n            }\n\n            if (canvasData.height >= containerData.height) {\n              canvasData.minTop = Math.min(0, newCanvasTop);\n              canvasData.maxTop = Math.max(0, newCanvasTop);\n            }\n          }\n        }\n      } else {\n        canvasData.minLeft = -canvasData.width;\n        canvasData.minTop = -canvasData.height;\n        canvasData.maxLeft = containerData.width;\n        canvasData.maxTop = containerData.height;\n      }\n    }\n  },\n  renderCanvas: function renderCanvas(changed, transformed) {\n    var canvasData = this.canvasData,\n        imageData = this.imageData;\n\n    if (transformed) {\n      var _getRotatedSizes = getRotatedSizes({\n        width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),\n        height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),\n        degree: imageData.rotate || 0\n      }),\n          naturalWidth = _getRotatedSizes.width,\n          naturalHeight = _getRotatedSizes.height;\n\n      var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);\n      var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);\n      canvasData.left -= (width - canvasData.width) / 2;\n      canvasData.top -= (height - canvasData.height) / 2;\n      canvasData.width = width;\n      canvasData.height = height;\n      canvasData.aspectRatio = naturalWidth / naturalHeight;\n      canvasData.naturalWidth = naturalWidth;\n      canvasData.naturalHeight = naturalHeight;\n      this.limitCanvas(true, false);\n    }\n\n    if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {\n      canvasData.left = canvasData.oldLeft;\n    }\n\n    if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {\n      canvasData.top = canvasData.oldTop;\n    }\n\n    canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);\n    canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);\n    this.limitCanvas(false, true);\n    canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);\n    canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);\n    canvasData.oldLeft = canvasData.left;\n    canvasData.oldTop = canvasData.top;\n    setStyle(this.canvas, assign({\n      width: canvasData.width,\n      height: canvasData.height\n    }, getTransforms({\n      translateX: canvasData.left,\n      translateY: canvasData.top\n    })));\n    this.renderImage(changed);\n\n    if (this.cropped && this.limited) {\n      this.limitCropBox(true, true);\n    }\n  },\n  renderImage: function renderImage(changed) {\n    var canvasData = this.canvasData,\n        imageData = this.imageData;\n    var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);\n    var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);\n    assign(imageData, {\n      width: width,\n      height: height,\n      left: (canvasData.width - width) / 2,\n      top: (canvasData.height - height) / 2\n    });\n    setStyle(this.image, assign({\n      width: imageData.width,\n      height: imageData.height\n    }, getTransforms(assign({\n      translateX: imageData.left,\n      translateY: imageData.top\n    }, imageData))));\n\n    if (changed) {\n      this.output();\n    }\n  },\n  initCropBox: function initCropBox() {\n    var options = this.options,\n        canvasData = this.canvasData;\n    var aspectRatio = options.aspectRatio || options.initialAspectRatio;\n    var autoCropArea = Number(options.autoCropArea) || 0.8;\n    var cropBoxData = {\n      width: canvasData.width,\n      height: canvasData.height\n    };\n\n    if (aspectRatio) {\n      if (canvasData.height * aspectRatio > canvasData.width) {\n        cropBoxData.height = cropBoxData.width / aspectRatio;\n      } else {\n        cropBoxData.width = cropBoxData.height * aspectRatio;\n      }\n    }\n\n    this.cropBoxData = cropBoxData;\n    this.limitCropBox(true, true); // Initialize auto crop area\n\n    cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n    cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than \"minWidth/Height\"\n\n    cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);\n    cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);\n    cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;\n    cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;\n    cropBoxData.oldLeft = cropBoxData.left;\n    cropBoxData.oldTop = cropBoxData.top;\n    this.initialCropBoxData = assign({}, cropBoxData);\n  },\n  limitCropBox: function limitCropBox(sizeLimited, positionLimited) {\n    var options = this.options,\n        containerData = this.containerData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData,\n        limited = this.limited;\n    var aspectRatio = options.aspectRatio;\n\n    if (sizeLimited) {\n      var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;\n      var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;\n      var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;\n      var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height\n\n      minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);\n      minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);\n\n      if (aspectRatio) {\n        if (minCropBoxWidth && minCropBoxHeight) {\n          if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n            minCropBoxHeight = minCropBoxWidth / aspectRatio;\n          } else {\n            minCropBoxWidth = minCropBoxHeight * aspectRatio;\n          }\n        } else if (minCropBoxWidth) {\n          minCropBoxHeight = minCropBoxWidth / aspectRatio;\n        } else if (minCropBoxHeight) {\n          minCropBoxWidth = minCropBoxHeight * aspectRatio;\n        }\n\n        if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n          maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n        } else {\n          maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n        }\n      } // The minWidth/Height must be less than maxWidth/Height\n\n\n      cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);\n      cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);\n      cropBoxData.maxWidth = maxCropBoxWidth;\n      cropBoxData.maxHeight = maxCropBoxHeight;\n    }\n\n    if (positionLimited) {\n      if (limited) {\n        cropBoxData.minLeft = Math.max(0, canvasData.left);\n        cropBoxData.minTop = Math.max(0, canvasData.top);\n        cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;\n        cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;\n      } else {\n        cropBoxData.minLeft = 0;\n        cropBoxData.minTop = 0;\n        cropBoxData.maxLeft = containerData.width - cropBoxData.width;\n        cropBoxData.maxTop = containerData.height - cropBoxData.height;\n      }\n    }\n  },\n  renderCropBox: function renderCropBox() {\n    var options = this.options,\n        containerData = this.containerData,\n        cropBoxData = this.cropBoxData;\n\n    if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {\n      cropBoxData.left = cropBoxData.oldLeft;\n    }\n\n    if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {\n      cropBoxData.top = cropBoxData.oldTop;\n    }\n\n    cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n    cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);\n    this.limitCropBox(false, true);\n    cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);\n    cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);\n    cropBoxData.oldLeft = cropBoxData.left;\n    cropBoxData.oldTop = cropBoxData.top;\n\n    if (options.movable && options.cropBoxMovable) {\n      // Turn to move the canvas when the crop box is equal to the container\n      setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);\n    }\n\n    setStyle(this.cropBox, assign({\n      width: cropBoxData.width,\n      height: cropBoxData.height\n    }, getTransforms({\n      translateX: cropBoxData.left,\n      translateY: cropBoxData.top\n    })));\n\n    if (this.cropped && this.limited) {\n      this.limitCanvas(true, true);\n    }\n\n    if (!this.disabled) {\n      this.output();\n    }\n  },\n  output: function output() {\n    this.preview();\n    dispatchEvent(this.element, EVENT_CROP, this.getData());\n  }\n};\n\nvar preview = {\n  initPreview: function initPreview() {\n    var element = this.element,\n        crossOrigin = this.crossOrigin;\n    var preview = this.options.preview;\n    var url = crossOrigin ? this.crossOriginUrl : this.url;\n    var alt = element.alt || 'The image to preview';\n    var image = document.createElement('img');\n\n    if (crossOrigin) {\n      image.crossOrigin = crossOrigin;\n    }\n\n    image.src = url;\n    image.alt = alt;\n    this.viewBox.appendChild(image);\n    this.viewBoxImage = image;\n\n    if (!preview) {\n      return;\n    }\n\n    var previews = preview;\n\n    if (typeof preview === 'string') {\n      previews = element.ownerDocument.querySelectorAll(preview);\n    } else if (preview.querySelector) {\n      previews = [preview];\n    }\n\n    this.previews = previews;\n    forEach(previews, function (el) {\n      var img = document.createElement('img'); // Save the original size for recover\n\n      setData(el, DATA_PREVIEW, {\n        width: el.offsetWidth,\n        height: el.offsetHeight,\n        html: el.innerHTML\n      });\n\n      if (crossOrigin) {\n        img.crossOrigin = crossOrigin;\n      }\n\n      img.src = url;\n      img.alt = alt;\n      /**\n       * Override img element styles\n       * Add `display:block` to avoid margin top issue\n       * Add `height:auto` to override `height` attribute on IE8\n       * (Occur only when margin-top <= -height)\n       */\n\n      img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;\"';\n      el.innerHTML = '';\n      el.appendChild(img);\n    });\n  },\n  resetPreview: function resetPreview() {\n    forEach(this.previews, function (element) {\n      var data = getData(element, DATA_PREVIEW);\n      setStyle(element, {\n        width: data.width,\n        height: data.height\n      });\n      element.innerHTML = data.html;\n      removeData(element, DATA_PREVIEW);\n    });\n  },\n  preview: function preview() {\n    var imageData = this.imageData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData;\n    var cropBoxWidth = cropBoxData.width,\n        cropBoxHeight = cropBoxData.height;\n    var width = imageData.width,\n        height = imageData.height;\n    var left = cropBoxData.left - canvasData.left - imageData.left;\n    var top = cropBoxData.top - canvasData.top - imageData.top;\n\n    if (!this.cropped || this.disabled) {\n      return;\n    }\n\n    setStyle(this.viewBoxImage, assign({\n      width: width,\n      height: height\n    }, getTransforms(assign({\n      translateX: -left,\n      translateY: -top\n    }, imageData))));\n    forEach(this.previews, function (element) {\n      var data = getData(element, DATA_PREVIEW);\n      var originalWidth = data.width;\n      var originalHeight = data.height;\n      var newWidth = originalWidth;\n      var newHeight = originalHeight;\n      var ratio = 1;\n\n      if (cropBoxWidth) {\n        ratio = originalWidth / cropBoxWidth;\n        newHeight = cropBoxHeight * ratio;\n      }\n\n      if (cropBoxHeight && newHeight > originalHeight) {\n        ratio = originalHeight / cropBoxHeight;\n        newWidth = cropBoxWidth * ratio;\n        newHeight = originalHeight;\n      }\n\n      setStyle(element, {\n        width: newWidth,\n        height: newHeight\n      });\n      setStyle(element.getElementsByTagName('img')[0], assign({\n        width: width * ratio,\n        height: height * ratio\n      }, getTransforms(assign({\n        translateX: -left * ratio,\n        translateY: -top * ratio\n      }, imageData))));\n    });\n  }\n};\n\nvar events = {\n  bind: function bind() {\n    var element = this.element,\n        options = this.options,\n        cropper = this.cropper;\n\n    if (isFunction(options.cropstart)) {\n      addListener(element, EVENT_CROP_START, options.cropstart);\n    }\n\n    if (isFunction(options.cropmove)) {\n      addListener(element, EVENT_CROP_MOVE, options.cropmove);\n    }\n\n    if (isFunction(options.cropend)) {\n      addListener(element, EVENT_CROP_END, options.cropend);\n    }\n\n    if (isFunction(options.crop)) {\n      addListener(element, EVENT_CROP, options.crop);\n    }\n\n    if (isFunction(options.zoom)) {\n      addListener(element, EVENT_ZOOM, options.zoom);\n    }\n\n    addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));\n\n    if (options.zoomable && options.zoomOnWheel) {\n      addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {\n        passive: false,\n        capture: true\n      });\n    }\n\n    if (options.toggleDragModeOnDblclick) {\n      addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));\n    }\n\n    addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));\n    addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));\n\n    if (options.responsive) {\n      addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));\n    }\n  },\n  unbind: function unbind() {\n    var element = this.element,\n        options = this.options,\n        cropper = this.cropper;\n\n    if (isFunction(options.cropstart)) {\n      removeListener(element, EVENT_CROP_START, options.cropstart);\n    }\n\n    if (isFunction(options.cropmove)) {\n      removeListener(element, EVENT_CROP_MOVE, options.cropmove);\n    }\n\n    if (isFunction(options.cropend)) {\n      removeListener(element, EVENT_CROP_END, options.cropend);\n    }\n\n    if (isFunction(options.crop)) {\n      removeListener(element, EVENT_CROP, options.crop);\n    }\n\n    if (isFunction(options.zoom)) {\n      removeListener(element, EVENT_ZOOM, options.zoom);\n    }\n\n    removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);\n\n    if (options.zoomable && options.zoomOnWheel) {\n      removeListener(cropper, EVENT_WHEEL, this.onWheel, {\n        passive: false,\n        capture: true\n      });\n    }\n\n    if (options.toggleDragModeOnDblclick) {\n      removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);\n    }\n\n    removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);\n    removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);\n\n    if (options.responsive) {\n      removeListener(window, EVENT_RESIZE, this.onResize);\n    }\n  }\n};\n\nvar handlers = {\n  resize: function resize() {\n    var options = this.options,\n        container = this.container,\n        containerData = this.containerData;\n    var minContainerWidth = Number(options.minContainerWidth) || MIN_CONTAINER_WIDTH;\n    var minContainerHeight = Number(options.minContainerHeight) || MIN_CONTAINER_HEIGHT;\n\n    if (this.disabled || containerData.width <= minContainerWidth || containerData.height <= minContainerHeight) {\n      return;\n    }\n\n    var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed\n\n    if (ratio !== 1 || container.offsetHeight !== containerData.height) {\n      var canvasData;\n      var cropBoxData;\n\n      if (options.restore) {\n        canvasData = this.getCanvasData();\n        cropBoxData = this.getCropBoxData();\n      }\n\n      this.render();\n\n      if (options.restore) {\n        this.setCanvasData(forEach(canvasData, function (n, i) {\n          canvasData[i] = n * ratio;\n        }));\n        this.setCropBoxData(forEach(cropBoxData, function (n, i) {\n          cropBoxData[i] = n * ratio;\n        }));\n      }\n    }\n  },\n  dblclick: function dblclick() {\n    if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {\n      return;\n    }\n\n    this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);\n  },\n  wheel: function wheel(event) {\n    var _this = this;\n\n    var ratio = Number(this.options.wheelZoomRatio) || 0.1;\n    var delta = 1;\n\n    if (this.disabled) {\n      return;\n    }\n\n    event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)\n\n    if (this.wheeling) {\n      return;\n    }\n\n    this.wheeling = true;\n    setTimeout(function () {\n      _this.wheeling = false;\n    }, 50);\n\n    if (event.deltaY) {\n      delta = event.deltaY > 0 ? 1 : -1;\n    } else if (event.wheelDelta) {\n      delta = -event.wheelDelta / 120;\n    } else if (event.detail) {\n      delta = event.detail > 0 ? 1 : -1;\n    }\n\n    this.zoom(-delta * ratio, event);\n  },\n  cropStart: function cropStart(event) {\n    var buttons = event.buttons,\n        button = event.button;\n\n    if (this.disabled // Handle mouse event and pointer event and ignore touch event\n    || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && ( // No primary button (Usually the left button)\n    isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu\n    || event.ctrlKey)) {\n      return;\n    }\n\n    var options = this.options,\n        pointers = this.pointers;\n    var action;\n\n    if (event.changedTouches) {\n      // Handle touch event\n      forEach(event.changedTouches, function (touch) {\n        pointers[touch.identifier] = getPointer(touch);\n      });\n    } else {\n      // Handle mouse event and pointer event\n      pointers[event.pointerId || 0] = getPointer(event);\n    }\n\n    if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {\n      action = ACTION_ZOOM;\n    } else {\n      action = getData(event.target, DATA_ACTION);\n    }\n\n    if (!REGEXP_ACTIONS.test(action)) {\n      return;\n    }\n\n    if (dispatchEvent(this.element, EVENT_CROP_START, {\n      originalEvent: event,\n      action: action\n    }) === false) {\n      return;\n    } // This line is required for preventing page zooming in iOS browsers\n\n\n    event.preventDefault();\n    this.action = action;\n    this.cropping = false;\n\n    if (action === ACTION_CROP) {\n      this.cropping = true;\n      addClass(this.dragBox, CLASS_MODAL);\n    }\n  },\n  cropMove: function cropMove(event) {\n    var action = this.action;\n\n    if (this.disabled || !action) {\n      return;\n    }\n\n    var pointers = this.pointers;\n    event.preventDefault();\n\n    if (dispatchEvent(this.element, EVENT_CROP_MOVE, {\n      originalEvent: event,\n      action: action\n    }) === false) {\n      return;\n    }\n\n    if (event.changedTouches) {\n      forEach(event.changedTouches, function (touch) {\n        // The first parameter should not be undefined (#432)\n        assign(pointers[touch.identifier] || {}, getPointer(touch, true));\n      });\n    } else {\n      assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));\n    }\n\n    this.change(event);\n  },\n  cropEnd: function cropEnd(event) {\n    if (this.disabled) {\n      return;\n    }\n\n    var action = this.action,\n        pointers = this.pointers;\n\n    if (event.changedTouches) {\n      forEach(event.changedTouches, function (touch) {\n        delete pointers[touch.identifier];\n      });\n    } else {\n      delete pointers[event.pointerId || 0];\n    }\n\n    if (!action) {\n      return;\n    }\n\n    event.preventDefault();\n\n    if (!Object.keys(pointers).length) {\n      this.action = '';\n    }\n\n    if (this.cropping) {\n      this.cropping = false;\n      toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);\n    }\n\n    dispatchEvent(this.element, EVENT_CROP_END, {\n      originalEvent: event,\n      action: action\n    });\n  }\n};\n\nvar change = {\n  change: function change(event) {\n    var options = this.options,\n        canvasData = this.canvasData,\n        containerData = this.containerData,\n        cropBoxData = this.cropBoxData,\n        pointers = this.pointers;\n    var action = this.action;\n    var aspectRatio = options.aspectRatio;\n    var left = cropBoxData.left,\n        top = cropBoxData.top,\n        width = cropBoxData.width,\n        height = cropBoxData.height;\n    var right = left + width;\n    var bottom = top + height;\n    var minLeft = 0;\n    var minTop = 0;\n    var maxWidth = containerData.width;\n    var maxHeight = containerData.height;\n    var renderable = true;\n    var offset; // Locking aspect ratio in \"free mode\" by holding shift key\n\n    if (!aspectRatio && event.shiftKey) {\n      aspectRatio = width && height ? width / height : 1;\n    }\n\n    if (this.limited) {\n      minLeft = cropBoxData.minLeft;\n      minTop = cropBoxData.minTop;\n      maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);\n      maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);\n    }\n\n    var pointer = pointers[Object.keys(pointers)[0]];\n    var range = {\n      x: pointer.endX - pointer.startX,\n      y: pointer.endY - pointer.startY\n    };\n\n    var check = function check(side) {\n      switch (side) {\n        case ACTION_EAST:\n          if (right + range.x > maxWidth) {\n            range.x = maxWidth - right;\n          }\n\n          break;\n\n        case ACTION_WEST:\n          if (left + range.x < minLeft) {\n            range.x = minLeft - left;\n          }\n\n          break;\n\n        case ACTION_NORTH:\n          if (top + range.y < minTop) {\n            range.y = minTop - top;\n          }\n\n          break;\n\n        case ACTION_SOUTH:\n          if (bottom + range.y > maxHeight) {\n            range.y = maxHeight - bottom;\n          }\n\n          break;\n\n        default:\n      }\n    };\n\n    switch (action) {\n      // Move crop box\n      case ACTION_ALL:\n        left += range.x;\n        top += range.y;\n        break;\n      // Resize crop box\n\n      case ACTION_EAST:\n        if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_EAST);\n        width += range.x;\n\n        if (width < 0) {\n          action = ACTION_WEST;\n          width = -width;\n          left -= width;\n        }\n\n        if (aspectRatio) {\n          height = width / aspectRatio;\n          top += (cropBoxData.height - height) / 2;\n        }\n\n        break;\n\n      case ACTION_NORTH:\n        if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_NORTH);\n        height -= range.y;\n        top += range.y;\n\n        if (height < 0) {\n          action = ACTION_SOUTH;\n          height = -height;\n          top -= height;\n        }\n\n        if (aspectRatio) {\n          width = height * aspectRatio;\n          left += (cropBoxData.width - width) / 2;\n        }\n\n        break;\n\n      case ACTION_WEST:\n        if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_WEST);\n        width -= range.x;\n        left += range.x;\n\n        if (width < 0) {\n          action = ACTION_EAST;\n          width = -width;\n          left -= width;\n        }\n\n        if (aspectRatio) {\n          height = width / aspectRatio;\n          top += (cropBoxData.height - height) / 2;\n        }\n\n        break;\n\n      case ACTION_SOUTH:\n        if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_SOUTH);\n        height += range.y;\n\n        if (height < 0) {\n          action = ACTION_NORTH;\n          height = -height;\n          top -= height;\n        }\n\n        if (aspectRatio) {\n          width = height * aspectRatio;\n          left += (cropBoxData.width - width) / 2;\n        }\n\n        break;\n\n      case ACTION_NORTH_EAST:\n        if (aspectRatio) {\n          if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n          width = height * aspectRatio;\n        } else {\n          check(ACTION_NORTH);\n          check(ACTION_EAST);\n\n          if (range.x >= 0) {\n            if (right < maxWidth) {\n              width += range.x;\n            } else if (range.y <= 0 && top <= minTop) {\n              renderable = false;\n            }\n          } else {\n            width += range.x;\n          }\n\n          if (range.y <= 0) {\n            if (top > minTop) {\n              height -= range.y;\n              top += range.y;\n            }\n          } else {\n            height -= range.y;\n            top += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_SOUTH_WEST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_NORTH_WEST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_SOUTH_EAST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n\n      case ACTION_NORTH_WEST:\n        if (aspectRatio) {\n          if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n          width = height * aspectRatio;\n          left += cropBoxData.width - width;\n        } else {\n          check(ACTION_NORTH);\n          check(ACTION_WEST);\n\n          if (range.x <= 0) {\n            if (left > minLeft) {\n              width -= range.x;\n              left += range.x;\n            } else if (range.y <= 0 && top <= minTop) {\n              renderable = false;\n            }\n          } else {\n            width -= range.x;\n            left += range.x;\n          }\n\n          if (range.y <= 0) {\n            if (top > minTop) {\n              height -= range.y;\n              top += range.y;\n            }\n          } else {\n            height -= range.y;\n            top += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_SOUTH_EAST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_NORTH_EAST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_SOUTH_WEST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n\n      case ACTION_SOUTH_WEST:\n        if (aspectRatio) {\n          if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_WEST);\n          width -= range.x;\n          left += range.x;\n          height = width / aspectRatio;\n        } else {\n          check(ACTION_SOUTH);\n          check(ACTION_WEST);\n\n          if (range.x <= 0) {\n            if (left > minLeft) {\n              width -= range.x;\n              left += range.x;\n            } else if (range.y >= 0 && bottom >= maxHeight) {\n              renderable = false;\n            }\n          } else {\n            width -= range.x;\n            left += range.x;\n          }\n\n          if (range.y >= 0) {\n            if (bottom < maxHeight) {\n              height += range.y;\n            }\n          } else {\n            height += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_NORTH_EAST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_SOUTH_EAST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_NORTH_WEST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n\n      case ACTION_SOUTH_EAST:\n        if (aspectRatio) {\n          if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_EAST);\n          width += range.x;\n          height = width / aspectRatio;\n        } else {\n          check(ACTION_SOUTH);\n          check(ACTION_EAST);\n\n          if (range.x >= 0) {\n            if (right < maxWidth) {\n              width += range.x;\n            } else if (range.y >= 0 && bottom >= maxHeight) {\n              renderable = false;\n            }\n          } else {\n            width += range.x;\n          }\n\n          if (range.y >= 0) {\n            if (bottom < maxHeight) {\n              height += range.y;\n            }\n          } else {\n            height += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_NORTH_WEST;\n          height = -height;\n          width = -width;\n          top -= height;\n          left -= width;\n        } else if (width < 0) {\n          action = ACTION_SOUTH_WEST;\n          width = -width;\n          left -= width;\n        } else if (height < 0) {\n          action = ACTION_NORTH_EAST;\n          height = -height;\n          top -= height;\n        }\n\n        break;\n      // Move canvas\n\n      case ACTION_MOVE:\n        this.move(range.x, range.y);\n        renderable = false;\n        break;\n      // Zoom canvas\n\n      case ACTION_ZOOM:\n        this.zoom(getMaxZoomRatio(pointers), event);\n        renderable = false;\n        break;\n      // Create crop box\n\n      case ACTION_CROP:\n        if (!range.x || !range.y) {\n          renderable = false;\n          break;\n        }\n\n        offset = getOffset(this.cropper);\n        left = pointer.startX - offset.left;\n        top = pointer.startY - offset.top;\n        width = cropBoxData.minWidth;\n        height = cropBoxData.minHeight;\n\n        if (range.x > 0) {\n          action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n        } else if (range.x < 0) {\n          left -= width;\n          action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n        }\n\n        if (range.y < 0) {\n          top -= height;\n        } // Show the crop box if is hidden\n\n\n        if (!this.cropped) {\n          removeClass(this.cropBox, CLASS_HIDDEN);\n          this.cropped = true;\n\n          if (this.limited) {\n            this.limitCropBox(true, true);\n          }\n        }\n\n        break;\n\n      default:\n    }\n\n    if (renderable) {\n      cropBoxData.width = width;\n      cropBoxData.height = height;\n      cropBoxData.left = left;\n      cropBoxData.top = top;\n      this.action = action;\n      this.renderCropBox();\n    } // Override\n\n\n    forEach(pointers, function (p) {\n      p.startX = p.endX;\n      p.startY = p.endY;\n    });\n  }\n};\n\nvar methods = {\n  // Show the crop box manually\n  crop: function crop() {\n    if (this.ready && !this.cropped && !this.disabled) {\n      this.cropped = true;\n      this.limitCropBox(true, true);\n\n      if (this.options.modal) {\n        addClass(this.dragBox, CLASS_MODAL);\n      }\n\n      removeClass(this.cropBox, CLASS_HIDDEN);\n      this.setCropBoxData(this.initialCropBoxData);\n    }\n\n    return this;\n  },\n  // Reset the image and crop box to their initial states\n  reset: function reset() {\n    if (this.ready && !this.disabled) {\n      this.imageData = assign({}, this.initialImageData);\n      this.canvasData = assign({}, this.initialCanvasData);\n      this.cropBoxData = assign({}, this.initialCropBoxData);\n      this.renderCanvas();\n\n      if (this.cropped) {\n        this.renderCropBox();\n      }\n    }\n\n    return this;\n  },\n  // Clear the crop box\n  clear: function clear() {\n    if (this.cropped && !this.disabled) {\n      assign(this.cropBoxData, {\n        left: 0,\n        top: 0,\n        width: 0,\n        height: 0\n      });\n      this.cropped = false;\n      this.renderCropBox();\n      this.limitCanvas(true, true); // Render canvas after crop box rendered\n\n      this.renderCanvas();\n      removeClass(this.dragBox, CLASS_MODAL);\n      addClass(this.cropBox, CLASS_HIDDEN);\n    }\n\n    return this;\n  },\n\n  /**\n   * Replace the image's src and rebuild the cropper\n   * @param {string} url - The new URL.\n   * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.\n   * @returns {Cropper} this\n   */\n  replace: function replace(url) {\n    var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n    if (!this.disabled && url) {\n      if (this.isImg) {\n        this.element.src = url;\n      }\n\n      if (hasSameSize) {\n        this.url = url;\n        this.image.src = url;\n\n        if (this.ready) {\n          this.viewBoxImage.src = url;\n          forEach(this.previews, function (element) {\n            element.getElementsByTagName('img')[0].src = url;\n          });\n        }\n      } else {\n        if (this.isImg) {\n          this.replaced = true;\n        }\n\n        this.options.data = null;\n        this.uncreate();\n        this.load(url);\n      }\n    }\n\n    return this;\n  },\n  // Enable (unfreeze) the cropper\n  enable: function enable() {\n    if (this.ready && this.disabled) {\n      this.disabled = false;\n      removeClass(this.cropper, CLASS_DISABLED);\n    }\n\n    return this;\n  },\n  // Disable (freeze) the cropper\n  disable: function disable() {\n    if (this.ready && !this.disabled) {\n      this.disabled = true;\n      addClass(this.cropper, CLASS_DISABLED);\n    }\n\n    return this;\n  },\n\n  /**\n   * Destroy the cropper and remove the instance from the image\n   * @returns {Cropper} this\n   */\n  destroy: function destroy() {\n    var element = this.element;\n\n    if (!element[NAMESPACE]) {\n      return this;\n    }\n\n    element[NAMESPACE] = undefined;\n\n    if (this.isImg && this.replaced) {\n      element.src = this.originalUrl;\n    }\n\n    this.uncreate();\n    return this;\n  },\n\n  /**\n   * Move the canvas with relative offsets\n   * @param {number} offsetX - The relative offset distance on the x-axis.\n   * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.\n   * @returns {Cropper} this\n   */\n  move: function move(offsetX) {\n    var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;\n    var _this$canvasData = this.canvasData,\n        left = _this$canvasData.left,\n        top = _this$canvasData.top;\n    return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));\n  },\n\n  /**\n   * Move the canvas to an absolute point\n   * @param {number} x - The x-axis coordinate.\n   * @param {number} [y=x] - The y-axis coordinate.\n   * @returns {Cropper} this\n   */\n  moveTo: function moveTo(x) {\n    var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;\n    var canvasData = this.canvasData;\n    var changed = false;\n    x = Number(x);\n    y = Number(y);\n\n    if (this.ready && !this.disabled && this.options.movable) {\n      if (isNumber(x)) {\n        canvasData.left = x;\n        changed = true;\n      }\n\n      if (isNumber(y)) {\n        canvasData.top = y;\n        changed = true;\n      }\n\n      if (changed) {\n        this.renderCanvas(true);\n      }\n    }\n\n    return this;\n  },\n\n  /**\n   * Zoom the canvas with a relative ratio\n   * @param {number} ratio - The target ratio.\n   * @param {Event} _originalEvent - The original event if any.\n   * @returns {Cropper} this\n   */\n  zoom: function zoom(ratio, _originalEvent) {\n    var canvasData = this.canvasData;\n    ratio = Number(ratio);\n\n    if (ratio < 0) {\n      ratio = 1 / (1 - ratio);\n    } else {\n      ratio = 1 + ratio;\n    }\n\n    return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);\n  },\n\n  /**\n   * Zoom the canvas to an absolute ratio\n   * @param {number} ratio - The target ratio.\n   * @param {Object} pivot - The zoom pivot point coordinate.\n   * @param {Event} _originalEvent - The original event if any.\n   * @returns {Cropper} this\n   */\n  zoomTo: function zoomTo(ratio, pivot, _originalEvent) {\n    var options = this.options,\n        canvasData = this.canvasData;\n    var width = canvasData.width,\n        height = canvasData.height,\n        naturalWidth = canvasData.naturalWidth,\n        naturalHeight = canvasData.naturalHeight;\n    ratio = Number(ratio);\n\n    if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {\n      var newWidth = naturalWidth * ratio;\n      var newHeight = naturalHeight * ratio;\n\n      if (dispatchEvent(this.element, EVENT_ZOOM, {\n        ratio: ratio,\n        oldRatio: width / naturalWidth,\n        originalEvent: _originalEvent\n      }) === false) {\n        return this;\n      }\n\n      if (_originalEvent) {\n        var pointers = this.pointers;\n        var offset = getOffset(this.cropper);\n        var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {\n          pageX: _originalEvent.pageX,\n          pageY: _originalEvent.pageY\n        }; // Zoom from the triggering point of the event\n\n        canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);\n        canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);\n      } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {\n        canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);\n        canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);\n      } else {\n        // Zoom from the center of the canvas\n        canvasData.left -= (newWidth - width) / 2;\n        canvasData.top -= (newHeight - height) / 2;\n      }\n\n      canvasData.width = newWidth;\n      canvasData.height = newHeight;\n      this.renderCanvas(true);\n    }\n\n    return this;\n  },\n\n  /**\n   * Rotate the canvas with a relative degree\n   * @param {number} degree - The rotate degree.\n   * @returns {Cropper} this\n   */\n  rotate: function rotate(degree) {\n    return this.rotateTo((this.imageData.rotate || 0) + Number(degree));\n  },\n\n  /**\n   * Rotate the canvas to an absolute degree\n   * @param {number} degree - The rotate degree.\n   * @returns {Cropper} this\n   */\n  rotateTo: function rotateTo(degree) {\n    degree = Number(degree);\n\n    if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {\n      this.imageData.rotate = degree % 360;\n      this.renderCanvas(true, true);\n    }\n\n    return this;\n  },\n\n  /**\n   * Scale the image on the x-axis.\n   * @param {number} scaleX - The scale ratio on the x-axis.\n   * @returns {Cropper} this\n   */\n  scaleX: function scaleX(_scaleX) {\n    var scaleY = this.imageData.scaleY;\n    return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);\n  },\n\n  /**\n   * Scale the image on the y-axis.\n   * @param {number} scaleY - The scale ratio on the y-axis.\n   * @returns {Cropper} this\n   */\n  scaleY: function scaleY(_scaleY) {\n    var scaleX = this.imageData.scaleX;\n    return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);\n  },\n\n  /**\n   * Scale the image\n   * @param {number} scaleX - The scale ratio on the x-axis.\n   * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.\n   * @returns {Cropper} this\n   */\n  scale: function scale(scaleX) {\n    var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;\n    var imageData = this.imageData;\n    var transformed = false;\n    scaleX = Number(scaleX);\n    scaleY = Number(scaleY);\n\n    if (this.ready && !this.disabled && this.options.scalable) {\n      if (isNumber(scaleX)) {\n        imageData.scaleX = scaleX;\n        transformed = true;\n      }\n\n      if (isNumber(scaleY)) {\n        imageData.scaleY = scaleY;\n        transformed = true;\n      }\n\n      if (transformed) {\n        this.renderCanvas(true, true);\n      }\n    }\n\n    return this;\n  },\n\n  /**\n   * Get the cropped area position and size data (base on the original image)\n   * @param {boolean} [rounded=false] - Indicate if round the data values or not.\n   * @returns {Object} The result cropped data.\n   */\n  getData: function getData() {\n    var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n    var options = this.options,\n        imageData = this.imageData,\n        canvasData = this.canvasData,\n        cropBoxData = this.cropBoxData;\n    var data;\n\n    if (this.ready && this.cropped) {\n      data = {\n        x: cropBoxData.left - canvasData.left,\n        y: cropBoxData.top - canvasData.top,\n        width: cropBoxData.width,\n        height: cropBoxData.height\n      };\n      var ratio = imageData.width / imageData.naturalWidth;\n      forEach(data, function (n, i) {\n        data[i] = n / ratio;\n      });\n\n      if (rounded) {\n        // In case rounding off leads to extra 1px in right or bottom border\n        // we should round the top-left corner and the dimension (#343).\n        var bottom = Math.round(data.y + data.height);\n        var right = Math.round(data.x + data.width);\n        data.x = Math.round(data.x);\n        data.y = Math.round(data.y);\n        data.width = right - data.x;\n        data.height = bottom - data.y;\n      }\n    } else {\n      data = {\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      };\n    }\n\n    if (options.rotatable) {\n      data.rotate = imageData.rotate || 0;\n    }\n\n    if (options.scalable) {\n      data.scaleX = imageData.scaleX || 1;\n      data.scaleY = imageData.scaleY || 1;\n    }\n\n    return data;\n  },\n\n  /**\n   * Set the cropped area position and size with new data\n   * @param {Object} data - The new data.\n   * @returns {Cropper} this\n   */\n  setData: function setData(data) {\n    var options = this.options,\n        imageData = this.imageData,\n        canvasData = this.canvasData;\n    var cropBoxData = {};\n\n    if (this.ready && !this.disabled && isPlainObject(data)) {\n      var transformed = false;\n\n      if (options.rotatable) {\n        if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {\n          imageData.rotate = data.rotate;\n          transformed = true;\n        }\n      }\n\n      if (options.scalable) {\n        if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {\n          imageData.scaleX = data.scaleX;\n          transformed = true;\n        }\n\n        if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {\n          imageData.scaleY = data.scaleY;\n          transformed = true;\n        }\n      }\n\n      if (transformed) {\n        this.renderCanvas(true, true);\n      }\n\n      var ratio = imageData.width / imageData.naturalWidth;\n\n      if (isNumber(data.x)) {\n        cropBoxData.left = data.x * ratio + canvasData.left;\n      }\n\n      if (isNumber(data.y)) {\n        cropBoxData.top = data.y * ratio + canvasData.top;\n      }\n\n      if (isNumber(data.width)) {\n        cropBoxData.width = data.width * ratio;\n      }\n\n      if (isNumber(data.height)) {\n        cropBoxData.height = data.height * ratio;\n      }\n\n      this.setCropBoxData(cropBoxData);\n    }\n\n    return this;\n  },\n\n  /**\n   * Get the container size data.\n   * @returns {Object} The result container data.\n   */\n  getContainerData: function getContainerData() {\n    return this.ready ? assign({}, this.containerData) : {};\n  },\n\n  /**\n   * Get the image position and size data.\n   * @returns {Object} The result image data.\n   */\n  getImageData: function getImageData() {\n    return this.sized ? assign({}, this.imageData) : {};\n  },\n\n  /**\n   * Get the canvas position and size data.\n   * @returns {Object} The result canvas data.\n   */\n  getCanvasData: function getCanvasData() {\n    var canvasData = this.canvasData;\n    var data = {};\n\n    if (this.ready) {\n      forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {\n        data[n] = canvasData[n];\n      });\n    }\n\n    return data;\n  },\n\n  /**\n   * Set the canvas position and size with new data.\n   * @param {Object} data - The new canvas data.\n   * @returns {Cropper} this\n   */\n  setCanvasData: function setCanvasData(data) {\n    var canvasData = this.canvasData;\n    var aspectRatio = canvasData.aspectRatio;\n\n    if (this.ready && !this.disabled && isPlainObject(data)) {\n      if (isNumber(data.left)) {\n        canvasData.left = data.left;\n      }\n\n      if (isNumber(data.top)) {\n        canvasData.top = data.top;\n      }\n\n      if (isNumber(data.width)) {\n        canvasData.width = data.width;\n        canvasData.height = data.width / aspectRatio;\n      } else if (isNumber(data.height)) {\n        canvasData.height = data.height;\n        canvasData.width = data.height * aspectRatio;\n      }\n\n      this.renderCanvas(true);\n    }\n\n    return this;\n  },\n\n  /**\n   * Get the crop box position and size data.\n   * @returns {Object} The result crop box data.\n   */\n  getCropBoxData: function getCropBoxData() {\n    var cropBoxData = this.cropBoxData;\n    var data;\n\n    if (this.ready && this.cropped) {\n      data = {\n        left: cropBoxData.left,\n        top: cropBoxData.top,\n        width: cropBoxData.width,\n        height: cropBoxData.height\n      };\n    }\n\n    return data || {};\n  },\n\n  /**\n   * Set the crop box position and size with new data.\n   * @param {Object} data - The new crop box data.\n   * @returns {Cropper} this\n   */\n  setCropBoxData: function setCropBoxData(data) {\n    var cropBoxData = this.cropBoxData;\n    var aspectRatio = this.options.aspectRatio;\n    var widthChanged;\n    var heightChanged;\n\n    if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {\n      if (isNumber(data.left)) {\n        cropBoxData.left = data.left;\n      }\n\n      if (isNumber(data.top)) {\n        cropBoxData.top = data.top;\n      }\n\n      if (isNumber(data.width) && data.width !== cropBoxData.width) {\n        widthChanged = true;\n        cropBoxData.width = data.width;\n      }\n\n      if (isNumber(data.height) && data.height !== cropBoxData.height) {\n        heightChanged = true;\n        cropBoxData.height = data.height;\n      }\n\n      if (aspectRatio) {\n        if (widthChanged) {\n          cropBoxData.height = cropBoxData.width / aspectRatio;\n        } else if (heightChanged) {\n          cropBoxData.width = cropBoxData.height * aspectRatio;\n        }\n      }\n\n      this.renderCropBox();\n    }\n\n    return this;\n  },\n\n  /**\n   * Get a canvas drawn the cropped image.\n   * @param {Object} [options={}] - The config options.\n   * @returns {HTMLCanvasElement} - The result canvas.\n   */\n  getCroppedCanvas: function getCroppedCanvas() {\n    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n    if (!this.ready || !window.HTMLCanvasElement) {\n      return null;\n    }\n\n    var canvasData = this.canvasData;\n    var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.\n\n    if (!this.cropped) {\n      return source;\n    }\n\n    var _this$getData = this.getData(),\n        initialX = _this$getData.x,\n        initialY = _this$getData.y,\n        initialWidth = _this$getData.width,\n        initialHeight = _this$getData.height;\n\n    var ratio = source.width / Math.floor(canvasData.naturalWidth);\n\n    if (ratio !== 1) {\n      initialX *= ratio;\n      initialY *= ratio;\n      initialWidth *= ratio;\n      initialHeight *= ratio;\n    }\n\n    var aspectRatio = initialWidth / initialHeight;\n    var maxSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.maxWidth || Infinity,\n      height: options.maxHeight || Infinity\n    });\n    var minSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.minWidth || 0,\n      height: options.minHeight || 0\n    }, 'cover');\n\n    var _getAdjustedSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.width || (ratio !== 1 ? source.width : initialWidth),\n      height: options.height || (ratio !== 1 ? source.height : initialHeight)\n    }),\n        width = _getAdjustedSizes.width,\n        height = _getAdjustedSizes.height;\n\n    width = Math.min(maxSizes.width, Math.max(minSizes.width, width));\n    height = Math.min(maxSizes.height, Math.max(minSizes.height, height));\n    var canvas = document.createElement('canvas');\n    var context = canvas.getContext('2d');\n    canvas.width = normalizeDecimalNumber(width);\n    canvas.height = normalizeDecimalNumber(height);\n    context.fillStyle = options.fillColor || 'transparent';\n    context.fillRect(0, 0, width, height);\n    var _options$imageSmoothi = options.imageSmoothingEnabled,\n        imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,\n        imageSmoothingQuality = options.imageSmoothingQuality;\n    context.imageSmoothingEnabled = imageSmoothingEnabled;\n\n    if (imageSmoothingQuality) {\n      context.imageSmoothingQuality = imageSmoothingQuality;\n    } // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n\n\n    var sourceWidth = source.width;\n    var sourceHeight = source.height; // Source canvas parameters\n\n    var srcX = initialX;\n    var srcY = initialY;\n    var srcWidth;\n    var srcHeight; // Destination canvas parameters\n\n    var dstX;\n    var dstY;\n    var dstWidth;\n    var dstHeight;\n\n    if (srcX <= -initialWidth || srcX > sourceWidth) {\n      srcX = 0;\n      srcWidth = 0;\n      dstX = 0;\n      dstWidth = 0;\n    } else if (srcX <= 0) {\n      dstX = -srcX;\n      srcX = 0;\n      srcWidth = Math.min(sourceWidth, initialWidth + srcX);\n      dstWidth = srcWidth;\n    } else if (srcX <= sourceWidth) {\n      dstX = 0;\n      srcWidth = Math.min(initialWidth, sourceWidth - srcX);\n      dstWidth = srcWidth;\n    }\n\n    if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {\n      srcY = 0;\n      srcHeight = 0;\n      dstY = 0;\n      dstHeight = 0;\n    } else if (srcY <= 0) {\n      dstY = -srcY;\n      srcY = 0;\n      srcHeight = Math.min(sourceHeight, initialHeight + srcY);\n      dstHeight = srcHeight;\n    } else if (srcY <= sourceHeight) {\n      dstY = 0;\n      srcHeight = Math.min(initialHeight, sourceHeight - srcY);\n      dstHeight = srcHeight;\n    }\n\n    var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid \"IndexSizeError\"\n\n    if (dstWidth > 0 && dstHeight > 0) {\n      var scale = width / initialWidth;\n      params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);\n    } // All the numerical parameters should be integer for `drawImage`\n    // https://github.com/fengyuanchen/cropper/issues/476\n\n\n    context.drawImage.apply(context, [source].concat(_toConsumableArray(params.map(function (param) {\n      return Math.floor(normalizeDecimalNumber(param));\n    }))));\n    return canvas;\n  },\n\n  /**\n   * Change the aspect ratio of the crop box.\n   * @param {number} aspectRatio - The new aspect ratio.\n   * @returns {Cropper} this\n   */\n  setAspectRatio: function setAspectRatio(aspectRatio) {\n    var options = this.options;\n\n    if (!this.disabled && !isUndefined(aspectRatio)) {\n      // 0 -> NaN\n      options.aspectRatio = Math.max(0, aspectRatio) || NaN;\n\n      if (this.ready) {\n        this.initCropBox();\n\n        if (this.cropped) {\n          this.renderCropBox();\n        }\n      }\n    }\n\n    return this;\n  },\n\n  /**\n   * Change the drag mode.\n   * @param {string} mode - The new drag mode.\n   * @returns {Cropper} this\n   */\n  setDragMode: function setDragMode(mode) {\n    var options = this.options,\n        dragBox = this.dragBox,\n        face = this.face;\n\n    if (this.ready && !this.disabled) {\n      var croppable = mode === DRAG_MODE_CROP;\n      var movable = options.movable && mode === DRAG_MODE_MOVE;\n      mode = croppable || movable ? mode : DRAG_MODE_NONE;\n      options.dragMode = mode;\n      setData(dragBox, DATA_ACTION, mode);\n      toggleClass(dragBox, CLASS_CROP, croppable);\n      toggleClass(dragBox, CLASS_MOVE, movable);\n\n      if (!options.cropBoxMovable) {\n        // Sync drag mode to crop box when it is not movable\n        setData(face, DATA_ACTION, mode);\n        toggleClass(face, CLASS_CROP, croppable);\n        toggleClass(face, CLASS_MOVE, movable);\n      }\n    }\n\n    return this;\n  }\n};\n\nvar AnotherCropper = WINDOW.Cropper;\n\nvar Cropper =\n/*#__PURE__*/\nfunction () {\n  /**\n   * Create a new Cropper.\n   * @param {Element} element - The target element for cropping.\n   * @param {Object} [options={}] - The configuration options.\n   */\n  function Cropper(element) {\n    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n    _classCallCheck(this, Cropper);\n\n    if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {\n      throw new Error('The first argument is required and must be an <img> or <canvas> element.');\n    }\n\n    this.element = element;\n    this.options = assign({}, DEFAULTS, isPlainObject(options) && options);\n    this.cropped = false;\n    this.disabled = false;\n    this.pointers = {};\n    this.ready = false;\n    this.reloading = false;\n    this.replaced = false;\n    this.sized = false;\n    this.sizing = false;\n    this.init();\n  }\n\n  _createClass(Cropper, [{\n    key: \"init\",\n    value: function init() {\n      var element = this.element;\n      var tagName = element.tagName.toLowerCase();\n      var url;\n\n      if (element[NAMESPACE]) {\n        return;\n      }\n\n      element[NAMESPACE] = this;\n\n      if (tagName === 'img') {\n        this.isImg = true; // e.g.: \"img/picture.jpg\"\n\n        url = element.getAttribute('src') || '';\n        this.originalUrl = url; // Stop when it's a blank image\n\n        if (!url) {\n          return;\n        } // e.g.: \"http://example.com/img/picture.jpg\"\n\n\n        url = element.src;\n      } else if (tagName === 'canvas' && window.HTMLCanvasElement) {\n        url = element.toDataURL();\n      }\n\n      this.load(url);\n    }\n  }, {\n    key: \"load\",\n    value: function load(url) {\n      var _this = this;\n\n      if (!url) {\n        return;\n      }\n\n      this.url = url;\n      this.imageData = {};\n      var element = this.element,\n          options = this.options;\n\n      if (!options.rotatable && !options.scalable) {\n        options.checkOrientation = false;\n      } // Only IE10+ supports Typed Arrays\n\n\n      if (!options.checkOrientation || !window.ArrayBuffer) {\n        this.clone();\n        return;\n      } // Detect the mime type of the image directly if it is a Data URL\n\n\n      if (REGEXP_DATA_URL.test(url)) {\n        // Read ArrayBuffer from Data URL of JPEG images directly for better performance\n        if (REGEXP_DATA_URL_JPEG.test(url)) {\n          this.read(dataURLToArrayBuffer(url));\n        } else {\n          // Only a JPEG image may contains Exif Orientation information,\n          // the rest types of Data URLs are not necessary to check orientation at all.\n          this.clone();\n        }\n\n        return;\n      } // 1. Detect the mime type of the image by a XMLHttpRequest.\n      // 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.\n\n\n      var xhr = new XMLHttpRequest();\n      var clone = this.clone.bind(this);\n      this.reloading = true;\n      this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:\n      // http, https, data, chrome, chrome-extension.\n      // 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy\n      // in some browsers as IE11 and Safari.\n\n      xhr.onabort = clone;\n      xhr.onerror = clone;\n      xhr.ontimeout = clone;\n\n      xhr.onprogress = function () {\n        // Abort the request directly if it not a JPEG image for better performance\n        if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {\n          xhr.abort();\n        }\n      };\n\n      xhr.onload = function () {\n        _this.read(xhr.response);\n      };\n\n      xhr.onloadend = function () {\n        _this.reloading = false;\n        _this.xhr = null;\n      }; // Bust cache when there is a \"crossOrigin\" property to avoid browser cache error\n\n\n      if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {\n        url = addTimestamp(url);\n      }\n\n      xhr.open('GET', url);\n      xhr.responseType = 'arraybuffer';\n      xhr.withCredentials = element.crossOrigin === 'use-credentials';\n      xhr.send();\n    }\n  }, {\n    key: \"read\",\n    value: function read(arrayBuffer) {\n      var options = this.options,\n          imageData = this.imageData; // Reset the orientation value to its default value 1\n      // as some iOS browsers will render image with its orientation\n\n      var orientation = resetAndGetOrientation(arrayBuffer);\n      var rotate = 0;\n      var scaleX = 1;\n      var scaleY = 1;\n\n      if (orientation > 1) {\n        // Generate a new URL which has the default orientation value\n        this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);\n\n        var _parseOrientation = parseOrientation(orientation);\n\n        rotate = _parseOrientation.rotate;\n        scaleX = _parseOrientation.scaleX;\n        scaleY = _parseOrientation.scaleY;\n      }\n\n      if (options.rotatable) {\n        imageData.rotate = rotate;\n      }\n\n      if (options.scalable) {\n        imageData.scaleX = scaleX;\n        imageData.scaleY = scaleY;\n      }\n\n      this.clone();\n    }\n  }, {\n    key: \"clone\",\n    value: function clone() {\n      var element = this.element,\n          url = this.url;\n      var crossOrigin = element.crossOrigin;\n      var crossOriginUrl = url;\n\n      if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {\n        if (!crossOrigin) {\n          crossOrigin = 'anonymous';\n        } // Bust cache when there is not a \"crossOrigin\" property (#519)\n\n\n        crossOriginUrl = addTimestamp(url);\n      }\n\n      this.crossOrigin = crossOrigin;\n      this.crossOriginUrl = crossOriginUrl;\n      var image = document.createElement('img');\n\n      if (crossOrigin) {\n        image.crossOrigin = crossOrigin;\n      }\n\n      image.src = crossOriginUrl || url;\n      image.alt = element.alt || 'The image to crop';\n      this.image = image;\n      image.onload = this.start.bind(this);\n      image.onerror = this.stop.bind(this);\n      addClass(image, CLASS_HIDE);\n      element.parentNode.insertBefore(image, element.nextSibling);\n    }\n  }, {\n    key: \"start\",\n    value: function start() {\n      var _this2 = this;\n\n      var image = this.image;\n      image.onload = null;\n      image.onerror = null;\n      this.sizing = true; // Match all browsers that use WebKit as the layout engine in iOS devices,\n      // such as Safari for iOS, Chrome for iOS, and in-app browsers.\n\n      var isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);\n\n      var done = function done(naturalWidth, naturalHeight) {\n        assign(_this2.imageData, {\n          naturalWidth: naturalWidth,\n          naturalHeight: naturalHeight,\n          aspectRatio: naturalWidth / naturalHeight\n        });\n        _this2.sizing = false;\n        _this2.sized = true;\n\n        _this2.build();\n      }; // Most modern browsers (excepts iOS WebKit)\n\n\n      if (image.naturalWidth && !isIOSWebKit) {\n        done(image.naturalWidth, image.naturalHeight);\n        return;\n      }\n\n      var sizingImage = document.createElement('img');\n      var body = document.body || document.documentElement;\n      this.sizingImage = sizingImage;\n\n      sizingImage.onload = function () {\n        done(sizingImage.width, sizingImage.height);\n\n        if (!isIOSWebKit) {\n          body.removeChild(sizingImage);\n        }\n      };\n\n      sizingImage.src = image.src; // iOS WebKit will convert the image automatically\n      // with its orientation once append it into DOM (#279)\n\n      if (!isIOSWebKit) {\n        sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';\n        body.appendChild(sizingImage);\n      }\n    }\n  }, {\n    key: \"stop\",\n    value: function stop() {\n      var image = this.image;\n      image.onload = null;\n      image.onerror = null;\n      image.parentNode.removeChild(image);\n      this.image = null;\n    }\n  }, {\n    key: \"build\",\n    value: function build() {\n      if (!this.sized || this.ready) {\n        return;\n      }\n\n      var element = this.element,\n          options = this.options,\n          image = this.image; // Create cropper elements\n\n      var container = element.parentNode;\n      var template = document.createElement('div');\n      template.innerHTML = TEMPLATE;\n      var cropper = template.querySelector(\".\".concat(NAMESPACE, \"-container\"));\n      var canvas = cropper.querySelector(\".\".concat(NAMESPACE, \"-canvas\"));\n      var dragBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-drag-box\"));\n      var cropBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-crop-box\"));\n      var face = cropBox.querySelector(\".\".concat(NAMESPACE, \"-face\"));\n      this.container = container;\n      this.cropper = cropper;\n      this.canvas = canvas;\n      this.dragBox = dragBox;\n      this.cropBox = cropBox;\n      this.viewBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-view-box\"));\n      this.face = face;\n      canvas.appendChild(image); // Hide the original image\n\n      addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image\n\n      container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden\n\n      if (!this.isImg) {\n        removeClass(image, CLASS_HIDE);\n      }\n\n      this.initPreview();\n      this.bind();\n      options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;\n      options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;\n      options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;\n      addClass(cropBox, CLASS_HIDDEN);\n\n      if (!options.guides) {\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-dashed\")), CLASS_HIDDEN);\n      }\n\n      if (!options.center) {\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-center\")), CLASS_HIDDEN);\n      }\n\n      if (options.background) {\n        addClass(cropper, \"\".concat(NAMESPACE, \"-bg\"));\n      }\n\n      if (!options.highlight) {\n        addClass(face, CLASS_INVISIBLE);\n      }\n\n      if (options.cropBoxMovable) {\n        addClass(face, CLASS_MOVE);\n        setData(face, DATA_ACTION, ACTION_ALL);\n      }\n\n      if (!options.cropBoxResizable) {\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-line\")), CLASS_HIDDEN);\n        addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-point\")), CLASS_HIDDEN);\n      }\n\n      this.render();\n      this.ready = true;\n      this.setDragMode(options.dragMode);\n\n      if (options.autoCrop) {\n        this.crop();\n      }\n\n      this.setData(options.data);\n\n      if (isFunction(options.ready)) {\n        addListener(element, EVENT_READY, options.ready, {\n          once: true\n        });\n      }\n\n      dispatchEvent(element, EVENT_READY);\n    }\n  }, {\n    key: \"unbuild\",\n    value: function unbuild() {\n      if (!this.ready) {\n        return;\n      }\n\n      this.ready = false;\n      this.unbind();\n      this.resetPreview();\n      this.cropper.parentNode.removeChild(this.cropper);\n      removeClass(this.element, CLASS_HIDDEN);\n    }\n  }, {\n    key: \"uncreate\",\n    value: function uncreate() {\n      if (this.ready) {\n        this.unbuild();\n        this.ready = false;\n        this.cropped = false;\n      } else if (this.sizing) {\n        this.sizingImage.onload = null;\n        this.sizing = false;\n        this.sized = false;\n      } else if (this.reloading) {\n        this.xhr.onabort = null;\n        this.xhr.abort();\n      } else if (this.image) {\n        this.stop();\n      }\n    }\n    /**\n     * Get the no conflict cropper class.\n     * @returns {Cropper} The cropper class.\n     */\n\n  }], [{\n    key: \"noConflict\",\n    value: function noConflict() {\n      window.Cropper = AnotherCropper;\n      return Cropper;\n    }\n    /**\n     * Change the default options.\n     * @param {Object} options - The new default options.\n     */\n\n  }, {\n    key: \"setDefaults\",\n    value: function setDefaults(options) {\n      assign(DEFAULTS, isPlainObject(options) && options);\n    }\n  }]);\n\n  return Cropper;\n}();\n\nassign(Cropper.prototype, render, preview, events, handlers, change, methods);\n\nif ($.fn) {\n  var AnotherCropper$1 = $.fn.cropper;\n  var NAMESPACE$1 = 'cropper';\n\n  $.fn.cropper = function jQueryCropper(option) {\n    for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    var result;\n    this.each(function (i, element) {\n      var $element = $(element);\n      var isDestroy = option === 'destroy';\n      var cropper = $element.data(NAMESPACE$1);\n\n      if (!cropper) {\n        if (isDestroy) {\n          return;\n        }\n\n        var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);\n        cropper = new Cropper(element, options);\n        $element.data(NAMESPACE$1, cropper);\n      }\n\n      if (typeof option === 'string') {\n        var fn = cropper[option];\n\n        if ($.isFunction(fn)) {\n          result = fn.apply(cropper, args);\n\n          if (result === cropper) {\n            result = undefined;\n          }\n\n          if (isDestroy) {\n            $element.removeData(NAMESPACE$1);\n          }\n        }\n      }\n    });\n    return result !== undefined ? result : this;\n  };\n\n  $.fn.cropper.Constructor = Cropper;\n  $.fn.cropper.setDefaults = Cropper.setDefaults;\n\n  $.fn.cropper.noConflict = function noConflict() {\n    $.fn.cropper = AnotherCropper$1;\n    return this;\n  };\n}\n"
  },
  {
    "path": "dist/cropper.js",
    "content": "/*!\n * Cropper v4.1.0\n * https://fengyuanchen.github.io/cropper\n *\n * Copyright 2014-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2019-10-12T07:43:51.850Z\n */\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :\n  typeof define === 'function' && define.amd ? define(['jquery'], factory) :\n  (global = global || self, factory(global.jQuery));\n}(this, function ($) { 'use strict';\n\n  $ = $ && $.hasOwnProperty('default') ? $['default'] : $;\n\n  function _typeof(obj) {\n    if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n      _typeof = function (obj) {\n        return typeof obj;\n      };\n    } else {\n      _typeof = function (obj) {\n        return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n      };\n    }\n\n    return _typeof(obj);\n  }\n\n  function _classCallCheck(instance, Constructor) {\n    if (!(instance instanceof Constructor)) {\n      throw new TypeError(\"Cannot call a class as a function\");\n    }\n  }\n\n  function _defineProperties(target, props) {\n    for (var i = 0; i < props.length; i++) {\n      var descriptor = props[i];\n      descriptor.enumerable = descriptor.enumerable || false;\n      descriptor.configurable = true;\n      if (\"value\" in descriptor) descriptor.writable = true;\n      Object.defineProperty(target, descriptor.key, descriptor);\n    }\n  }\n\n  function _createClass(Constructor, protoProps, staticProps) {\n    if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n    if (staticProps) _defineProperties(Constructor, staticProps);\n    return Constructor;\n  }\n\n  function _defineProperty(obj, key, value) {\n    if (key in obj) {\n      Object.defineProperty(obj, key, {\n        value: value,\n        enumerable: true,\n        configurable: true,\n        writable: true\n      });\n    } else {\n      obj[key] = value;\n    }\n\n    return obj;\n  }\n\n  function ownKeys(object, enumerableOnly) {\n    var keys = Object.keys(object);\n\n    if (Object.getOwnPropertySymbols) {\n      var symbols = Object.getOwnPropertySymbols(object);\n      if (enumerableOnly) symbols = symbols.filter(function (sym) {\n        return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n      });\n      keys.push.apply(keys, symbols);\n    }\n\n    return keys;\n  }\n\n  function _objectSpread2(target) {\n    for (var i = 1; i < arguments.length; i++) {\n      var source = arguments[i] != null ? arguments[i] : {};\n\n      if (i % 2) {\n        ownKeys(source, true).forEach(function (key) {\n          _defineProperty(target, key, source[key]);\n        });\n      } else if (Object.getOwnPropertyDescriptors) {\n        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n      } else {\n        ownKeys(source).forEach(function (key) {\n          Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n        });\n      }\n    }\n\n    return target;\n  }\n\n  function _toConsumableArray(arr) {\n    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n  }\n\n  function _arrayWithoutHoles(arr) {\n    if (Array.isArray(arr)) {\n      for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n      return arr2;\n    }\n  }\n\n  function _iterableToArray(iter) {\n    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter);\n  }\n\n  function _nonIterableSpread() {\n    throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n  }\n\n  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';\n  var WINDOW = IS_BROWSER ? window : {};\n  var IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false;\n  var HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;\n  var NAMESPACE = 'cropper'; // Actions\n\n  var ACTION_ALL = 'all';\n  var ACTION_CROP = 'crop';\n  var ACTION_MOVE = 'move';\n  var ACTION_ZOOM = 'zoom';\n  var ACTION_EAST = 'e';\n  var ACTION_WEST = 'w';\n  var ACTION_SOUTH = 's';\n  var ACTION_NORTH = 'n';\n  var ACTION_NORTH_EAST = 'ne';\n  var ACTION_NORTH_WEST = 'nw';\n  var ACTION_SOUTH_EAST = 'se';\n  var ACTION_SOUTH_WEST = 'sw'; // Classes\n\n  var CLASS_CROP = \"\".concat(NAMESPACE, \"-crop\");\n  var CLASS_DISABLED = \"\".concat(NAMESPACE, \"-disabled\");\n  var CLASS_HIDDEN = \"\".concat(NAMESPACE, \"-hidden\");\n  var CLASS_HIDE = \"\".concat(NAMESPACE, \"-hide\");\n  var CLASS_INVISIBLE = \"\".concat(NAMESPACE, \"-invisible\");\n  var CLASS_MODAL = \"\".concat(NAMESPACE, \"-modal\");\n  var CLASS_MOVE = \"\".concat(NAMESPACE, \"-move\"); // Data keys\n\n  var DATA_ACTION = \"\".concat(NAMESPACE, \"Action\");\n  var DATA_PREVIEW = \"\".concat(NAMESPACE, \"Preview\"); // Drag modes\n\n  var DRAG_MODE_CROP = 'crop';\n  var DRAG_MODE_MOVE = 'move';\n  var DRAG_MODE_NONE = 'none'; // Events\n\n  var EVENT_CROP = 'crop';\n  var EVENT_CROP_END = 'cropend';\n  var EVENT_CROP_MOVE = 'cropmove';\n  var EVENT_CROP_START = 'cropstart';\n  var EVENT_DBLCLICK = 'dblclick';\n  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';\n  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';\n  var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';\n  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;\n  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;\n  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;\n  var EVENT_READY = 'ready';\n  var EVENT_RESIZE = 'resize';\n  var EVENT_WHEEL = 'wheel';\n  var EVENT_ZOOM = 'zoom'; // Mime types\n\n  var MIME_TYPE_JPEG = 'image/jpeg'; // RegExps\n\n  var REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;\n  var REGEXP_DATA_URL = /^data:/;\n  var REGEXP_DATA_URL_JPEG = /^data:image\\/jpeg;base64,/;\n  var REGEXP_TAG_NAME = /^img|canvas$/i; // Misc\n  // Inspired by the default width and height of a canvas element.\n\n  var MIN_CONTAINER_WIDTH = 200;\n  var MIN_CONTAINER_HEIGHT = 100;\n\n  var DEFAULTS = {\n    // Define the view mode of the cropper\n    viewMode: 0,\n    // 0, 1, 2, 3\n    // Define the dragging mode of the cropper\n    dragMode: DRAG_MODE_CROP,\n    // 'crop', 'move' or 'none'\n    // Define the initial aspect ratio of the crop box\n    initialAspectRatio: NaN,\n    // Define the aspect ratio of the crop box\n    aspectRatio: NaN,\n    // An object with the previous cropping result data\n    data: null,\n    // A selector for adding extra containers to preview\n    preview: '',\n    // Re-render the cropper when resize the window\n    responsive: true,\n    // Restore the cropped area after resize the window\n    restore: true,\n    // Check if the current image is a cross-origin image\n    checkCrossOrigin: true,\n    // Check the current image's Exif Orientation information\n    checkOrientation: true,\n    // Show the black modal\n    modal: true,\n    // Show the dashed lines for guiding\n    guides: true,\n    // Show the center indicator for guiding\n    center: true,\n    // Show the white modal to highlight the crop box\n    highlight: true,\n    // Show the grid background\n    background: true,\n    // Enable to crop the image automatically when initialize\n    autoCrop: true,\n    // Define the percentage of automatic cropping area when initializes\n    autoCropArea: 0.8,\n    // Enable to move the image\n    movable: true,\n    // Enable to rotate the image\n    rotatable: true,\n    // Enable to scale the image\n    scalable: true,\n    // Enable to zoom the image\n    zoomable: true,\n    // Enable to zoom the image by dragging touch\n    zoomOnTouch: true,\n    // Enable to zoom the image by wheeling mouse\n    zoomOnWheel: true,\n    // Define zoom ratio when zoom the image by wheeling mouse\n    wheelZoomRatio: 0.1,\n    // Enable to move the crop box\n    cropBoxMovable: true,\n    // Enable to resize the crop box\n    cropBoxResizable: true,\n    // Toggle drag mode between \"crop\" and \"move\" when click twice on the cropper\n    toggleDragModeOnDblclick: true,\n    // Size limitation\n    minCanvasWidth: 0,\n    minCanvasHeight: 0,\n    minCropBoxWidth: 0,\n    minCropBoxHeight: 0,\n    minContainerWidth: 200,\n    minContainerHeight: 100,\n    // Shortcuts of events\n    ready: null,\n    cropstart: null,\n    cropmove: null,\n    cropend: null,\n    crop: null,\n    zoom: null\n  };\n\n  var TEMPLATE = '<div class=\"cropper-container\" touch-action=\"none\">' + '<div class=\"cropper-wrap-box\">' + '<div class=\"cropper-canvas\"></div>' + '</div>' + '<div class=\"cropper-drag-box\"></div>' + '<div class=\"cropper-crop-box\">' + '<span class=\"cropper-view-box\"></span>' + '<span class=\"cropper-dashed dashed-h\"></span>' + '<span class=\"cropper-dashed dashed-v\"></span>' + '<span class=\"cropper-center\"></span>' + '<span class=\"cropper-face\"></span>' + '<span class=\"cropper-line line-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-line line-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-line line-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-line line-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-point point-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-point point-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-point point-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-ne\" data-cropper-action=\"ne\"></span>' + '<span class=\"cropper-point point-nw\" data-cropper-action=\"nw\"></span>' + '<span class=\"cropper-point point-sw\" data-cropper-action=\"sw\"></span>' + '<span class=\"cropper-point point-se\" data-cropper-action=\"se\"></span>' + '</div>' + '</div>';\n\n  /**\n   * Check if the given value is not a number.\n   */\n\n  var isNaN = Number.isNaN || WINDOW.isNaN;\n  /**\n   * Check if the given value is a number.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a number, else `false`.\n   */\n\n  function isNumber(value) {\n    return typeof value === 'number' && !isNaN(value);\n  }\n  /**\n   * Check if the given value is a positive number.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a positive number, else `false`.\n   */\n\n  var isPositiveNumber = function isPositiveNumber(value) {\n    return value > 0 && value < Infinity;\n  };\n  /**\n   * Check if the given value is undefined.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is undefined, else `false`.\n   */\n\n  function isUndefined(value) {\n    return typeof value === 'undefined';\n  }\n  /**\n   * Check if the given value is an object.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is an object, else `false`.\n   */\n\n  function isObject(value) {\n    return _typeof(value) === 'object' && value !== null;\n  }\n  var hasOwnProperty = Object.prototype.hasOwnProperty;\n  /**\n   * Check if the given value is a plain object.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.\n   */\n\n  function isPlainObject(value) {\n    if (!isObject(value)) {\n      return false;\n    }\n\n    try {\n      var _constructor = value.constructor;\n      var prototype = _constructor.prototype;\n      return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');\n    } catch (error) {\n      return false;\n    }\n  }\n  /**\n   * Check if the given value is a function.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a function, else `false`.\n   */\n\n  function isFunction(value) {\n    return typeof value === 'function';\n  }\n  var slice = Array.prototype.slice;\n  /**\n   * Convert array-like or iterable object to an array.\n   * @param {*} value - The value to convert.\n   * @returns {Array} Returns a new array.\n   */\n\n  function toArray(value) {\n    return Array.from ? Array.from(value) : slice.call(value);\n  }\n  /**\n   * Iterate the given data.\n   * @param {*} data - The data to iterate.\n   * @param {Function} callback - The process function for each element.\n   * @returns {*} The original data.\n   */\n\n  function forEach(data, callback) {\n    if (data && isFunction(callback)) {\n      if (Array.isArray(data) || isNumber(data.length)\n      /* array-like */\n      ) {\n          toArray(data).forEach(function (value, key) {\n            callback.call(data, value, key, data);\n          });\n        } else if (isObject(data)) {\n        Object.keys(data).forEach(function (key) {\n          callback.call(data, data[key], key, data);\n        });\n      }\n    }\n\n    return data;\n  }\n  /**\n   * Extend the given object.\n   * @param {*} target - The target object to extend.\n   * @param {*} args - The rest objects for merging to the target object.\n   * @returns {Object} The extended object.\n   */\n\n  var assign = Object.assign || function assign(target) {\n    for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    if (isObject(target) && args.length > 0) {\n      args.forEach(function (arg) {\n        if (isObject(arg)) {\n          Object.keys(arg).forEach(function (key) {\n            target[key] = arg[key];\n          });\n        }\n      });\n    }\n\n    return target;\n  };\n  var REGEXP_DECIMALS = /\\.\\d*(?:0|9){12}\\d*$/;\n  /**\n   * Normalize decimal number.\n   * Check out {@link http://0.30000000000000004.com/}\n   * @param {number} value - The value to normalize.\n   * @param {number} [times=100000000000] - The times for normalizing.\n   * @returns {number} Returns the normalized number.\n   */\n\n  function normalizeDecimalNumber(value) {\n    var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;\n    return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;\n  }\n  var REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;\n  /**\n   * Apply styles to the given element.\n   * @param {Element} element - The target element.\n   * @param {Object} styles - The styles for applying.\n   */\n\n  function setStyle(element, styles) {\n    var style = element.style;\n    forEach(styles, function (value, property) {\n      if (REGEXP_SUFFIX.test(property) && isNumber(value)) {\n        value = \"\".concat(value, \"px\");\n      }\n\n      style[property] = value;\n    });\n  }\n  /**\n   * Check if the given element has a special class.\n   * @param {Element} element - The element to check.\n   * @param {string} value - The class to search.\n   * @returns {boolean} Returns `true` if the special class was found.\n   */\n\n  function hasClass(element, value) {\n    return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;\n  }\n  /**\n   * Add classes to the given element.\n   * @param {Element} element - The target element.\n   * @param {string} value - The classes to be added.\n   */\n\n  function addClass(element, value) {\n    if (!value) {\n      return;\n    }\n\n    if (isNumber(element.length)) {\n      forEach(element, function (elem) {\n        addClass(elem, value);\n      });\n      return;\n    }\n\n    if (element.classList) {\n      element.classList.add(value);\n      return;\n    }\n\n    var className = element.className.trim();\n\n    if (!className) {\n      element.className = value;\n    } else if (className.indexOf(value) < 0) {\n      element.className = \"\".concat(className, \" \").concat(value);\n    }\n  }\n  /**\n   * Remove classes from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} value - The classes to be removed.\n   */\n\n  function removeClass(element, value) {\n    if (!value) {\n      return;\n    }\n\n    if (isNumber(element.length)) {\n      forEach(element, function (elem) {\n        removeClass(elem, value);\n      });\n      return;\n    }\n\n    if (element.classList) {\n      element.classList.remove(value);\n      return;\n    }\n\n    if (element.className.indexOf(value) >= 0) {\n      element.className = element.className.replace(value, '');\n    }\n  }\n  /**\n   * Add or remove classes from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} value - The classes to be toggled.\n   * @param {boolean} added - Add only.\n   */\n\n  function toggleClass(element, value, added) {\n    if (!value) {\n      return;\n    }\n\n    if (isNumber(element.length)) {\n      forEach(element, function (elem) {\n        toggleClass(elem, value, added);\n      });\n      return;\n    } // IE10-11 doesn't support the second parameter of `classList.toggle`\n\n\n    if (added) {\n      addClass(element, value);\n    } else {\n      removeClass(element, value);\n    }\n  }\n  var REGEXP_CAMEL_CASE = /([a-z\\d])([A-Z])/g;\n  /**\n   * Transform the given string from camelCase to kebab-case\n   * @param {string} value - The value to transform.\n   * @returns {string} The transformed value.\n   */\n\n  function toParamCase(value) {\n    return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();\n  }\n  /**\n   * Get data from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} name - The data key to get.\n   * @returns {string} The data value.\n   */\n\n  function getData(element, name) {\n    if (isObject(element[name])) {\n      return element[name];\n    }\n\n    if (element.dataset) {\n      return element.dataset[name];\n    }\n\n    return element.getAttribute(\"data-\".concat(toParamCase(name)));\n  }\n  /**\n   * Set data to the given element.\n   * @param {Element} element - The target element.\n   * @param {string} name - The data key to set.\n   * @param {string} data - The data value.\n   */\n\n  function setData(element, name, data) {\n    if (isObject(data)) {\n      element[name] = data;\n    } else if (element.dataset) {\n      element.dataset[name] = data;\n    } else {\n      element.setAttribute(\"data-\".concat(toParamCase(name)), data);\n    }\n  }\n  /**\n   * Remove data from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} name - The data key to remove.\n   */\n\n  function removeData(element, name) {\n    if (isObject(element[name])) {\n      try {\n        delete element[name];\n      } catch (error) {\n        element[name] = undefined;\n      }\n    } else if (element.dataset) {\n      // #128 Safari not allows to delete dataset property\n      try {\n        delete element.dataset[name];\n      } catch (error) {\n        element.dataset[name] = undefined;\n      }\n    } else {\n      element.removeAttribute(\"data-\".concat(toParamCase(name)));\n    }\n  }\n  var REGEXP_SPACES = /\\s\\s*/;\n\n  var onceSupported = function () {\n    var supported = false;\n\n    if (IS_BROWSER) {\n      var once = false;\n\n      var listener = function listener() {};\n\n      var options = Object.defineProperty({}, 'once', {\n        get: function get() {\n          supported = true;\n          return once;\n        },\n\n        /**\n         * This setter can fix a `TypeError` in strict mode\n         * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}\n         * @param {boolean} value - The value to set\n         */\n        set: function set(value) {\n          once = value;\n        }\n      });\n      WINDOW.addEventListener('test', listener, options);\n      WINDOW.removeEventListener('test', listener, options);\n    }\n\n    return supported;\n  }();\n  /**\n   * Remove event listener from the target element.\n   * @param {Element} element - The event target.\n   * @param {string} type - The event type(s).\n   * @param {Function} listener - The event listener.\n   * @param {Object} options - The event options.\n   */\n\n\n  function removeListener(element, type, listener) {\n    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n    var handler = listener;\n    type.trim().split(REGEXP_SPACES).forEach(function (event) {\n      if (!onceSupported) {\n        var listeners = element.listeners;\n\n        if (listeners && listeners[event] && listeners[event][listener]) {\n          handler = listeners[event][listener];\n          delete listeners[event][listener];\n\n          if (Object.keys(listeners[event]).length === 0) {\n            delete listeners[event];\n          }\n\n          if (Object.keys(listeners).length === 0) {\n            delete element.listeners;\n          }\n        }\n      }\n\n      element.removeEventListener(event, handler, options);\n    });\n  }\n  /**\n   * Add event listener to the target element.\n   * @param {Element} element - The event target.\n   * @param {string} type - The event type(s).\n   * @param {Function} listener - The event listener.\n   * @param {Object} options - The event options.\n   */\n\n  function addListener(element, type, listener) {\n    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n    var _handler = listener;\n    type.trim().split(REGEXP_SPACES).forEach(function (event) {\n      if (options.once && !onceSupported) {\n        var _element$listeners = element.listeners,\n            listeners = _element$listeners === void 0 ? {} : _element$listeners;\n\n        _handler = function handler() {\n          delete listeners[event][listener];\n          element.removeEventListener(event, _handler, options);\n\n          for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n            args[_key2] = arguments[_key2];\n          }\n\n          listener.apply(element, args);\n        };\n\n        if (!listeners[event]) {\n          listeners[event] = {};\n        }\n\n        if (listeners[event][listener]) {\n          element.removeEventListener(event, listeners[event][listener], options);\n        }\n\n        listeners[event][listener] = _handler;\n        element.listeners = listeners;\n      }\n\n      element.addEventListener(event, _handler, options);\n    });\n  }\n  /**\n   * Dispatch event on the target element.\n   * @param {Element} element - The event target.\n   * @param {string} type - The event type(s).\n   * @param {Object} data - The additional event data.\n   * @returns {boolean} Indicate if the event is default prevented or not.\n   */\n\n  function dispatchEvent(element, type, data) {\n    var event; // Event and CustomEvent on IE9-11 are global objects, not constructors\n\n    if (isFunction(Event) && isFunction(CustomEvent)) {\n      event = new CustomEvent(type, {\n        detail: data,\n        bubbles: true,\n        cancelable: true\n      });\n    } else {\n      event = document.createEvent('CustomEvent');\n      event.initCustomEvent(type, true, true, data);\n    }\n\n    return element.dispatchEvent(event);\n  }\n  /**\n   * Get the offset base on the document.\n   * @param {Element} element - The target element.\n   * @returns {Object} The offset data.\n   */\n\n  function getOffset(element) {\n    var box = element.getBoundingClientRect();\n    return {\n      left: box.left + (window.pageXOffset - document.documentElement.clientLeft),\n      top: box.top + (window.pageYOffset - document.documentElement.clientTop)\n    };\n  }\n  var location = WINDOW.location;\n  var REGEXP_ORIGINS = /^(\\w+:)\\/\\/([^:/?#]*):?(\\d*)/i;\n  /**\n   * Check if the given URL is a cross origin URL.\n   * @param {string} url - The target URL.\n   * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.\n   */\n\n  function isCrossOriginURL(url) {\n    var parts = url.match(REGEXP_ORIGINS);\n    return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);\n  }\n  /**\n   * Add timestamp to the given URL.\n   * @param {string} url - The target URL.\n   * @returns {string} The result URL.\n   */\n\n  function addTimestamp(url) {\n    var timestamp = \"timestamp=\".concat(new Date().getTime());\n    return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;\n  }\n  /**\n   * Get transforms base on the given object.\n   * @param {Object} obj - The target object.\n   * @returns {string} A string contains transform values.\n   */\n\n  function getTransforms(_ref) {\n    var rotate = _ref.rotate,\n        scaleX = _ref.scaleX,\n        scaleY = _ref.scaleY,\n        translateX = _ref.translateX,\n        translateY = _ref.translateY;\n    var values = [];\n\n    if (isNumber(translateX) && translateX !== 0) {\n      values.push(\"translateX(\".concat(translateX, \"px)\"));\n    }\n\n    if (isNumber(translateY) && translateY !== 0) {\n      values.push(\"translateY(\".concat(translateY, \"px)\"));\n    } // Rotate should come first before scale to match orientation transform\n\n\n    if (isNumber(rotate) && rotate !== 0) {\n      values.push(\"rotate(\".concat(rotate, \"deg)\"));\n    }\n\n    if (isNumber(scaleX) && scaleX !== 1) {\n      values.push(\"scaleX(\".concat(scaleX, \")\"));\n    }\n\n    if (isNumber(scaleY) && scaleY !== 1) {\n      values.push(\"scaleY(\".concat(scaleY, \")\"));\n    }\n\n    var transform = values.length ? values.join(' ') : 'none';\n    return {\n      WebkitTransform: transform,\n      msTransform: transform,\n      transform: transform\n    };\n  }\n  /**\n   * Get the max ratio of a group of pointers.\n   * @param {string} pointers - The target pointers.\n   * @returns {number} The result ratio.\n   */\n\n  function getMaxZoomRatio(pointers) {\n    var pointers2 = _objectSpread2({}, pointers);\n\n    var ratios = [];\n    forEach(pointers, function (pointer, pointerId) {\n      delete pointers2[pointerId];\n      forEach(pointers2, function (pointer2) {\n        var x1 = Math.abs(pointer.startX - pointer2.startX);\n        var y1 = Math.abs(pointer.startY - pointer2.startY);\n        var x2 = Math.abs(pointer.endX - pointer2.endX);\n        var y2 = Math.abs(pointer.endY - pointer2.endY);\n        var z1 = Math.sqrt(x1 * x1 + y1 * y1);\n        var z2 = Math.sqrt(x2 * x2 + y2 * y2);\n        var ratio = (z2 - z1) / z1;\n        ratios.push(ratio);\n      });\n    });\n    ratios.sort(function (a, b) {\n      return Math.abs(a) < Math.abs(b);\n    });\n    return ratios[0];\n  }\n  /**\n   * Get a pointer from an event object.\n   * @param {Object} event - The target event object.\n   * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.\n   * @returns {Object} The result pointer contains start and/or end point coordinates.\n   */\n\n  function getPointer(_ref2, endOnly) {\n    var pageX = _ref2.pageX,\n        pageY = _ref2.pageY;\n    var end = {\n      endX: pageX,\n      endY: pageY\n    };\n    return endOnly ? end : _objectSpread2({\n      startX: pageX,\n      startY: pageY\n    }, end);\n  }\n  /**\n   * Get the center point coordinate of a group of pointers.\n   * @param {Object} pointers - The target pointers.\n   * @returns {Object} The center point coordinate.\n   */\n\n  function getPointersCenter(pointers) {\n    var pageX = 0;\n    var pageY = 0;\n    var count = 0;\n    forEach(pointers, function (_ref3) {\n      var startX = _ref3.startX,\n          startY = _ref3.startY;\n      pageX += startX;\n      pageY += startY;\n      count += 1;\n    });\n    pageX /= count;\n    pageY /= count;\n    return {\n      pageX: pageX,\n      pageY: pageY\n    };\n  }\n  /**\n   * Get the max sizes in a rectangle under the given aspect ratio.\n   * @param {Object} data - The original sizes.\n   * @param {string} [type='contain'] - The adjust type.\n   * @returns {Object} The result sizes.\n   */\n\n  function getAdjustedSizes(_ref4) // or 'cover'\n  {\n    var aspectRatio = _ref4.aspectRatio,\n        height = _ref4.height,\n        width = _ref4.width;\n    var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';\n    var isValidWidth = isPositiveNumber(width);\n    var isValidHeight = isPositiveNumber(height);\n\n    if (isValidWidth && isValidHeight) {\n      var adjustedWidth = height * aspectRatio;\n\n      if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {\n        height = width / aspectRatio;\n      } else {\n        width = height * aspectRatio;\n      }\n    } else if (isValidWidth) {\n      height = width / aspectRatio;\n    } else if (isValidHeight) {\n      width = height * aspectRatio;\n    }\n\n    return {\n      width: width,\n      height: height\n    };\n  }\n  /**\n   * Get the new sizes of a rectangle after rotated.\n   * @param {Object} data - The original sizes.\n   * @returns {Object} The result sizes.\n   */\n\n  function getRotatedSizes(_ref5) {\n    var width = _ref5.width,\n        height = _ref5.height,\n        degree = _ref5.degree;\n    degree = Math.abs(degree) % 180;\n\n    if (degree === 90) {\n      return {\n        width: height,\n        height: width\n      };\n    }\n\n    var arc = degree % 90 * Math.PI / 180;\n    var sinArc = Math.sin(arc);\n    var cosArc = Math.cos(arc);\n    var newWidth = width * cosArc + height * sinArc;\n    var newHeight = width * sinArc + height * cosArc;\n    return degree > 90 ? {\n      width: newHeight,\n      height: newWidth\n    } : {\n      width: newWidth,\n      height: newHeight\n    };\n  }\n  /**\n   * Get a canvas which drew the given image.\n   * @param {HTMLImageElement} image - The image for drawing.\n   * @param {Object} imageData - The image data.\n   * @param {Object} canvasData - The canvas data.\n   * @param {Object} options - The options.\n   * @returns {HTMLCanvasElement} The result canvas.\n   */\n\n  function getSourceCanvas(image, _ref6, _ref7, _ref8) {\n    var imageAspectRatio = _ref6.aspectRatio,\n        imageNaturalWidth = _ref6.naturalWidth,\n        imageNaturalHeight = _ref6.naturalHeight,\n        _ref6$rotate = _ref6.rotate,\n        rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,\n        _ref6$scaleX = _ref6.scaleX,\n        scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,\n        _ref6$scaleY = _ref6.scaleY,\n        scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;\n    var aspectRatio = _ref7.aspectRatio,\n        naturalWidth = _ref7.naturalWidth,\n        naturalHeight = _ref7.naturalHeight;\n    var _ref8$fillColor = _ref8.fillColor,\n        fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor,\n        _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,\n        imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,\n        _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,\n        imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ,\n        _ref8$maxWidth = _ref8.maxWidth,\n        maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,\n        _ref8$maxHeight = _ref8.maxHeight,\n        maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,\n        _ref8$minWidth = _ref8.minWidth,\n        minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,\n        _ref8$minHeight = _ref8.minHeight,\n        minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;\n    var canvas = document.createElement('canvas');\n    var context = canvas.getContext('2d');\n    var maxSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: maxWidth,\n      height: maxHeight\n    });\n    var minSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: minWidth,\n      height: minHeight\n    }, 'cover');\n    var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));\n    var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as\n    // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90\n\n    var destMaxSizes = getAdjustedSizes({\n      aspectRatio: imageAspectRatio,\n      width: maxWidth,\n      height: maxHeight\n    });\n    var destMinSizes = getAdjustedSizes({\n      aspectRatio: imageAspectRatio,\n      width: minWidth,\n      height: minHeight\n    }, 'cover');\n    var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));\n    var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));\n    var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];\n    canvas.width = normalizeDecimalNumber(width);\n    canvas.height = normalizeDecimalNumber(height);\n    context.fillStyle = fillColor;\n    context.fillRect(0, 0, width, height);\n    context.save();\n    context.translate(width / 2, height / 2);\n    context.rotate(rotate * Math.PI / 180);\n    context.scale(scaleX, scaleY);\n    context.imageSmoothingEnabled = imageSmoothingEnabled;\n    context.imageSmoothingQuality = imageSmoothingQuality;\n    context.drawImage.apply(context, [image].concat(_toConsumableArray(params.map(function (param) {\n      return Math.floor(normalizeDecimalNumber(param));\n    }))));\n    context.restore();\n    return canvas;\n  }\n  var fromCharCode = String.fromCharCode;\n  /**\n   * Get string from char code in data view.\n   * @param {DataView} dataView - The data view for read.\n   * @param {number} start - The start index.\n   * @param {number} length - The read length.\n   * @returns {string} The read result.\n   */\n\n  function getStringFromCharCode(dataView, start, length) {\n    var str = '';\n    length += start;\n\n    for (var i = start; i < length; i += 1) {\n      str += fromCharCode(dataView.getUint8(i));\n    }\n\n    return str;\n  }\n  var REGEXP_DATA_URL_HEAD = /^data:.*,/;\n  /**\n   * Transform Data URL to array buffer.\n   * @param {string} dataURL - The Data URL to transform.\n   * @returns {ArrayBuffer} The result array buffer.\n   */\n\n  function dataURLToArrayBuffer(dataURL) {\n    var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n    var binary = atob(base64);\n    var arrayBuffer = new ArrayBuffer(binary.length);\n    var uint8 = new Uint8Array(arrayBuffer);\n    forEach(uint8, function (value, i) {\n      uint8[i] = binary.charCodeAt(i);\n    });\n    return arrayBuffer;\n  }\n  /**\n   * Transform array buffer to Data URL.\n   * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.\n   * @param {string} mimeType - The mime type of the Data URL.\n   * @returns {string} The result Data URL.\n   */\n\n  function arrayBufferToDataURL(arrayBuffer, mimeType) {\n    var chunks = []; // Chunk Typed Array for better performance (#435)\n\n    var chunkSize = 8192;\n    var uint8 = new Uint8Array(arrayBuffer);\n\n    while (uint8.length > 0) {\n      // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9\n      // eslint-disable-next-line prefer-spread\n      chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));\n      uint8 = uint8.subarray(chunkSize);\n    }\n\n    return \"data:\".concat(mimeType, \";base64,\").concat(btoa(chunks.join('')));\n  }\n  /**\n   * Get orientation value from given array buffer.\n   * @param {ArrayBuffer} arrayBuffer - The array buffer to read.\n   * @returns {number} The read orientation value.\n   */\n\n  function resetAndGetOrientation(arrayBuffer) {\n    var dataView = new DataView(arrayBuffer);\n    var orientation; // Ignores range error when the image does not have correct Exif information\n\n    try {\n      var littleEndian;\n      var app1Start;\n      var ifdStart; // Only handle JPEG image (start by 0xFFD8)\n\n      if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n        var length = dataView.byteLength;\n        var offset = 2;\n\n        while (offset + 1 < length) {\n          if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n            app1Start = offset;\n            break;\n          }\n\n          offset += 1;\n        }\n      }\n\n      if (app1Start) {\n        var exifIDCode = app1Start + 4;\n        var tiffOffset = app1Start + 10;\n\n        if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n          var endianness = dataView.getUint16(tiffOffset);\n          littleEndian = endianness === 0x4949;\n\n          if (littleEndian || endianness === 0x4D4D\n          /* bigEndian */\n          ) {\n              if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n                var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n                if (firstIFDOffset >= 0x00000008) {\n                  ifdStart = tiffOffset + firstIFDOffset;\n                }\n              }\n            }\n        }\n      }\n\n      if (ifdStart) {\n        var _length = dataView.getUint16(ifdStart, littleEndian);\n\n        var _offset;\n\n        var i;\n\n        for (i = 0; i < _length; i += 1) {\n          _offset = ifdStart + i * 12 + 2;\n\n          if (dataView.getUint16(_offset, littleEndian) === 0x0112\n          /* Orientation */\n          ) {\n              // 8 is the offset of the current tag's value\n              _offset += 8; // Get the original orientation value\n\n              orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value\n\n              dataView.setUint16(_offset, 1, littleEndian);\n              break;\n            }\n        }\n      }\n    } catch (error) {\n      orientation = 1;\n    }\n\n    return orientation;\n  }\n  /**\n   * Parse Exif Orientation value.\n   * @param {number} orientation - The orientation to parse.\n   * @returns {Object} The parsed result.\n   */\n\n  function parseOrientation(orientation) {\n    var rotate = 0;\n    var scaleX = 1;\n    var scaleY = 1;\n\n    switch (orientation) {\n      // Flip horizontal\n      case 2:\n        scaleX = -1;\n        break;\n      // Rotate left 180°\n\n      case 3:\n        rotate = -180;\n        break;\n      // Flip vertical\n\n      case 4:\n        scaleY = -1;\n        break;\n      // Flip vertical and rotate right 90°\n\n      case 5:\n        rotate = 90;\n        scaleY = -1;\n        break;\n      // Rotate right 90°\n\n      case 6:\n        rotate = 90;\n        break;\n      // Flip horizontal and rotate right 90°\n\n      case 7:\n        rotate = 90;\n        scaleX = -1;\n        break;\n      // Rotate left 90°\n\n      case 8:\n        rotate = -90;\n        break;\n\n      default:\n    }\n\n    return {\n      rotate: rotate,\n      scaleX: scaleX,\n      scaleY: scaleY\n    };\n  }\n\n  var render = {\n    render: function render() {\n      this.initContainer();\n      this.initCanvas();\n      this.initCropBox();\n      this.renderCanvas();\n\n      if (this.cropped) {\n        this.renderCropBox();\n      }\n    },\n    initContainer: function initContainer() {\n      var element = this.element,\n          options = this.options,\n          container = this.container,\n          cropper = this.cropper;\n      addClass(cropper, CLASS_HIDDEN);\n      removeClass(element, CLASS_HIDDEN);\n      var containerData = {\n        width: Math.max(container.offsetWidth, Number(options.minContainerWidth) || 200),\n        height: Math.max(container.offsetHeight, Number(options.minContainerHeight) || 100)\n      };\n      this.containerData = containerData;\n      setStyle(cropper, {\n        width: containerData.width,\n        height: containerData.height\n      });\n      addClass(element, CLASS_HIDDEN);\n      removeClass(cropper, CLASS_HIDDEN);\n    },\n    // Canvas (image wrapper)\n    initCanvas: function initCanvas() {\n      var containerData = this.containerData,\n          imageData = this.imageData;\n      var viewMode = this.options.viewMode;\n      var rotated = Math.abs(imageData.rotate) % 180 === 90;\n      var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;\n      var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;\n      var aspectRatio = naturalWidth / naturalHeight;\n      var canvasWidth = containerData.width;\n      var canvasHeight = containerData.height;\n\n      if (containerData.height * aspectRatio > containerData.width) {\n        if (viewMode === 3) {\n          canvasWidth = containerData.height * aspectRatio;\n        } else {\n          canvasHeight = containerData.width / aspectRatio;\n        }\n      } else if (viewMode === 3) {\n        canvasHeight = containerData.width / aspectRatio;\n      } else {\n        canvasWidth = containerData.height * aspectRatio;\n      }\n\n      var canvasData = {\n        aspectRatio: aspectRatio,\n        naturalWidth: naturalWidth,\n        naturalHeight: naturalHeight,\n        width: canvasWidth,\n        height: canvasHeight\n      };\n      canvasData.left = (containerData.width - canvasWidth) / 2;\n      canvasData.top = (containerData.height - canvasHeight) / 2;\n      canvasData.oldLeft = canvasData.left;\n      canvasData.oldTop = canvasData.top;\n      this.canvasData = canvasData;\n      this.limited = viewMode === 1 || viewMode === 2;\n      this.limitCanvas(true, true);\n      this.initialImageData = assign({}, imageData);\n      this.initialCanvasData = assign({}, canvasData);\n    },\n    limitCanvas: function limitCanvas(sizeLimited, positionLimited) {\n      var options = this.options,\n          containerData = this.containerData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData;\n      var viewMode = options.viewMode;\n      var aspectRatio = canvasData.aspectRatio;\n      var cropped = this.cropped && cropBoxData;\n\n      if (sizeLimited) {\n        var minCanvasWidth = Number(options.minCanvasWidth) || 0;\n        var minCanvasHeight = Number(options.minCanvasHeight) || 0;\n\n        if (viewMode > 1) {\n          minCanvasWidth = Math.max(minCanvasWidth, containerData.width);\n          minCanvasHeight = Math.max(minCanvasHeight, containerData.height);\n\n          if (viewMode === 3) {\n            if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n              minCanvasWidth = minCanvasHeight * aspectRatio;\n            } else {\n              minCanvasHeight = minCanvasWidth / aspectRatio;\n            }\n          }\n        } else if (viewMode > 0) {\n          if (minCanvasWidth) {\n            minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);\n          } else if (minCanvasHeight) {\n            minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);\n          } else if (cropped) {\n            minCanvasWidth = cropBoxData.width;\n            minCanvasHeight = cropBoxData.height;\n\n            if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n              minCanvasWidth = minCanvasHeight * aspectRatio;\n            } else {\n              minCanvasHeight = minCanvasWidth / aspectRatio;\n            }\n          }\n        }\n\n        var _getAdjustedSizes = getAdjustedSizes({\n          aspectRatio: aspectRatio,\n          width: minCanvasWidth,\n          height: minCanvasHeight\n        });\n\n        minCanvasWidth = _getAdjustedSizes.width;\n        minCanvasHeight = _getAdjustedSizes.height;\n        canvasData.minWidth = minCanvasWidth;\n        canvasData.minHeight = minCanvasHeight;\n        canvasData.maxWidth = Infinity;\n        canvasData.maxHeight = Infinity;\n      }\n\n      if (positionLimited) {\n        if (viewMode > (cropped ? 0 : 1)) {\n          var newCanvasLeft = containerData.width - canvasData.width;\n          var newCanvasTop = containerData.height - canvasData.height;\n          canvasData.minLeft = Math.min(0, newCanvasLeft);\n          canvasData.minTop = Math.min(0, newCanvasTop);\n          canvasData.maxLeft = Math.max(0, newCanvasLeft);\n          canvasData.maxTop = Math.max(0, newCanvasTop);\n\n          if (cropped && this.limited) {\n            canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));\n            canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));\n            canvasData.maxLeft = cropBoxData.left;\n            canvasData.maxTop = cropBoxData.top;\n\n            if (viewMode === 2) {\n              if (canvasData.width >= containerData.width) {\n                canvasData.minLeft = Math.min(0, newCanvasLeft);\n                canvasData.maxLeft = Math.max(0, newCanvasLeft);\n              }\n\n              if (canvasData.height >= containerData.height) {\n                canvasData.minTop = Math.min(0, newCanvasTop);\n                canvasData.maxTop = Math.max(0, newCanvasTop);\n              }\n            }\n          }\n        } else {\n          canvasData.minLeft = -canvasData.width;\n          canvasData.minTop = -canvasData.height;\n          canvasData.maxLeft = containerData.width;\n          canvasData.maxTop = containerData.height;\n        }\n      }\n    },\n    renderCanvas: function renderCanvas(changed, transformed) {\n      var canvasData = this.canvasData,\n          imageData = this.imageData;\n\n      if (transformed) {\n        var _getRotatedSizes = getRotatedSizes({\n          width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),\n          height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),\n          degree: imageData.rotate || 0\n        }),\n            naturalWidth = _getRotatedSizes.width,\n            naturalHeight = _getRotatedSizes.height;\n\n        var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);\n        var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);\n        canvasData.left -= (width - canvasData.width) / 2;\n        canvasData.top -= (height - canvasData.height) / 2;\n        canvasData.width = width;\n        canvasData.height = height;\n        canvasData.aspectRatio = naturalWidth / naturalHeight;\n        canvasData.naturalWidth = naturalWidth;\n        canvasData.naturalHeight = naturalHeight;\n        this.limitCanvas(true, false);\n      }\n\n      if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {\n        canvasData.left = canvasData.oldLeft;\n      }\n\n      if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {\n        canvasData.top = canvasData.oldTop;\n      }\n\n      canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);\n      canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);\n      this.limitCanvas(false, true);\n      canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);\n      canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);\n      canvasData.oldLeft = canvasData.left;\n      canvasData.oldTop = canvasData.top;\n      setStyle(this.canvas, assign({\n        width: canvasData.width,\n        height: canvasData.height\n      }, getTransforms({\n        translateX: canvasData.left,\n        translateY: canvasData.top\n      })));\n      this.renderImage(changed);\n\n      if (this.cropped && this.limited) {\n        this.limitCropBox(true, true);\n      }\n    },\n    renderImage: function renderImage(changed) {\n      var canvasData = this.canvasData,\n          imageData = this.imageData;\n      var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);\n      var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);\n      assign(imageData, {\n        width: width,\n        height: height,\n        left: (canvasData.width - width) / 2,\n        top: (canvasData.height - height) / 2\n      });\n      setStyle(this.image, assign({\n        width: imageData.width,\n        height: imageData.height\n      }, getTransforms(assign({\n        translateX: imageData.left,\n        translateY: imageData.top\n      }, imageData))));\n\n      if (changed) {\n        this.output();\n      }\n    },\n    initCropBox: function initCropBox() {\n      var options = this.options,\n          canvasData = this.canvasData;\n      var aspectRatio = options.aspectRatio || options.initialAspectRatio;\n      var autoCropArea = Number(options.autoCropArea) || 0.8;\n      var cropBoxData = {\n        width: canvasData.width,\n        height: canvasData.height\n      };\n\n      if (aspectRatio) {\n        if (canvasData.height * aspectRatio > canvasData.width) {\n          cropBoxData.height = cropBoxData.width / aspectRatio;\n        } else {\n          cropBoxData.width = cropBoxData.height * aspectRatio;\n        }\n      }\n\n      this.cropBoxData = cropBoxData;\n      this.limitCropBox(true, true); // Initialize auto crop area\n\n      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than \"minWidth/Height\"\n\n      cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);\n      cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);\n      cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;\n      cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;\n      cropBoxData.oldLeft = cropBoxData.left;\n      cropBoxData.oldTop = cropBoxData.top;\n      this.initialCropBoxData = assign({}, cropBoxData);\n    },\n    limitCropBox: function limitCropBox(sizeLimited, positionLimited) {\n      var options = this.options,\n          containerData = this.containerData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData,\n          limited = this.limited;\n      var aspectRatio = options.aspectRatio;\n\n      if (sizeLimited) {\n        var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;\n        var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;\n        var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;\n        var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height\n\n        minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);\n        minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);\n\n        if (aspectRatio) {\n          if (minCropBoxWidth && minCropBoxHeight) {\n            if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n              minCropBoxHeight = minCropBoxWidth / aspectRatio;\n            } else {\n              minCropBoxWidth = minCropBoxHeight * aspectRatio;\n            }\n          } else if (minCropBoxWidth) {\n            minCropBoxHeight = minCropBoxWidth / aspectRatio;\n          } else if (minCropBoxHeight) {\n            minCropBoxWidth = minCropBoxHeight * aspectRatio;\n          }\n\n          if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n            maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n          } else {\n            maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n          }\n        } // The minWidth/Height must be less than maxWidth/Height\n\n\n        cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);\n        cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);\n        cropBoxData.maxWidth = maxCropBoxWidth;\n        cropBoxData.maxHeight = maxCropBoxHeight;\n      }\n\n      if (positionLimited) {\n        if (limited) {\n          cropBoxData.minLeft = Math.max(0, canvasData.left);\n          cropBoxData.minTop = Math.max(0, canvasData.top);\n          cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;\n          cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;\n        } else {\n          cropBoxData.minLeft = 0;\n          cropBoxData.minTop = 0;\n          cropBoxData.maxLeft = containerData.width - cropBoxData.width;\n          cropBoxData.maxTop = containerData.height - cropBoxData.height;\n        }\n      }\n    },\n    renderCropBox: function renderCropBox() {\n      var options = this.options,\n          containerData = this.containerData,\n          cropBoxData = this.cropBoxData;\n\n      if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {\n        cropBoxData.left = cropBoxData.oldLeft;\n      }\n\n      if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {\n        cropBoxData.top = cropBoxData.oldTop;\n      }\n\n      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);\n      this.limitCropBox(false, true);\n      cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);\n      cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);\n      cropBoxData.oldLeft = cropBoxData.left;\n      cropBoxData.oldTop = cropBoxData.top;\n\n      if (options.movable && options.cropBoxMovable) {\n        // Turn to move the canvas when the crop box is equal to the container\n        setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);\n      }\n\n      setStyle(this.cropBox, assign({\n        width: cropBoxData.width,\n        height: cropBoxData.height\n      }, getTransforms({\n        translateX: cropBoxData.left,\n        translateY: cropBoxData.top\n      })));\n\n      if (this.cropped && this.limited) {\n        this.limitCanvas(true, true);\n      }\n\n      if (!this.disabled) {\n        this.output();\n      }\n    },\n    output: function output() {\n      this.preview();\n      dispatchEvent(this.element, EVENT_CROP, this.getData());\n    }\n  };\n\n  var preview = {\n    initPreview: function initPreview() {\n      var element = this.element,\n          crossOrigin = this.crossOrigin;\n      var preview = this.options.preview;\n      var url = crossOrigin ? this.crossOriginUrl : this.url;\n      var alt = element.alt || 'The image to preview';\n      var image = document.createElement('img');\n\n      if (crossOrigin) {\n        image.crossOrigin = crossOrigin;\n      }\n\n      image.src = url;\n      image.alt = alt;\n      this.viewBox.appendChild(image);\n      this.viewBoxImage = image;\n\n      if (!preview) {\n        return;\n      }\n\n      var previews = preview;\n\n      if (typeof preview === 'string') {\n        previews = element.ownerDocument.querySelectorAll(preview);\n      } else if (preview.querySelector) {\n        previews = [preview];\n      }\n\n      this.previews = previews;\n      forEach(previews, function (el) {\n        var img = document.createElement('img'); // Save the original size for recover\n\n        setData(el, DATA_PREVIEW, {\n          width: el.offsetWidth,\n          height: el.offsetHeight,\n          html: el.innerHTML\n        });\n\n        if (crossOrigin) {\n          img.crossOrigin = crossOrigin;\n        }\n\n        img.src = url;\n        img.alt = alt;\n        /**\n         * Override img element styles\n         * Add `display:block` to avoid margin top issue\n         * Add `height:auto` to override `height` attribute on IE8\n         * (Occur only when margin-top <= -height)\n         */\n\n        img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;\"';\n        el.innerHTML = '';\n        el.appendChild(img);\n      });\n    },\n    resetPreview: function resetPreview() {\n      forEach(this.previews, function (element) {\n        var data = getData(element, DATA_PREVIEW);\n        setStyle(element, {\n          width: data.width,\n          height: data.height\n        });\n        element.innerHTML = data.html;\n        removeData(element, DATA_PREVIEW);\n      });\n    },\n    preview: function preview() {\n      var imageData = this.imageData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData;\n      var cropBoxWidth = cropBoxData.width,\n          cropBoxHeight = cropBoxData.height;\n      var width = imageData.width,\n          height = imageData.height;\n      var left = cropBoxData.left - canvasData.left - imageData.left;\n      var top = cropBoxData.top - canvasData.top - imageData.top;\n\n      if (!this.cropped || this.disabled) {\n        return;\n      }\n\n      setStyle(this.viewBoxImage, assign({\n        width: width,\n        height: height\n      }, getTransforms(assign({\n        translateX: -left,\n        translateY: -top\n      }, imageData))));\n      forEach(this.previews, function (element) {\n        var data = getData(element, DATA_PREVIEW);\n        var originalWidth = data.width;\n        var originalHeight = data.height;\n        var newWidth = originalWidth;\n        var newHeight = originalHeight;\n        var ratio = 1;\n\n        if (cropBoxWidth) {\n          ratio = originalWidth / cropBoxWidth;\n          newHeight = cropBoxHeight * ratio;\n        }\n\n        if (cropBoxHeight && newHeight > originalHeight) {\n          ratio = originalHeight / cropBoxHeight;\n          newWidth = cropBoxWidth * ratio;\n          newHeight = originalHeight;\n        }\n\n        setStyle(element, {\n          width: newWidth,\n          height: newHeight\n        });\n        setStyle(element.getElementsByTagName('img')[0], assign({\n          width: width * ratio,\n          height: height * ratio\n        }, getTransforms(assign({\n          translateX: -left * ratio,\n          translateY: -top * ratio\n        }, imageData))));\n      });\n    }\n  };\n\n  var events = {\n    bind: function bind() {\n      var element = this.element,\n          options = this.options,\n          cropper = this.cropper;\n\n      if (isFunction(options.cropstart)) {\n        addListener(element, EVENT_CROP_START, options.cropstart);\n      }\n\n      if (isFunction(options.cropmove)) {\n        addListener(element, EVENT_CROP_MOVE, options.cropmove);\n      }\n\n      if (isFunction(options.cropend)) {\n        addListener(element, EVENT_CROP_END, options.cropend);\n      }\n\n      if (isFunction(options.crop)) {\n        addListener(element, EVENT_CROP, options.crop);\n      }\n\n      if (isFunction(options.zoom)) {\n        addListener(element, EVENT_ZOOM, options.zoom);\n      }\n\n      addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));\n\n      if (options.zoomable && options.zoomOnWheel) {\n        addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {\n          passive: false,\n          capture: true\n        });\n      }\n\n      if (options.toggleDragModeOnDblclick) {\n        addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));\n      }\n\n      addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));\n      addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));\n\n      if (options.responsive) {\n        addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));\n      }\n    },\n    unbind: function unbind() {\n      var element = this.element,\n          options = this.options,\n          cropper = this.cropper;\n\n      if (isFunction(options.cropstart)) {\n        removeListener(element, EVENT_CROP_START, options.cropstart);\n      }\n\n      if (isFunction(options.cropmove)) {\n        removeListener(element, EVENT_CROP_MOVE, options.cropmove);\n      }\n\n      if (isFunction(options.cropend)) {\n        removeListener(element, EVENT_CROP_END, options.cropend);\n      }\n\n      if (isFunction(options.crop)) {\n        removeListener(element, EVENT_CROP, options.crop);\n      }\n\n      if (isFunction(options.zoom)) {\n        removeListener(element, EVENT_ZOOM, options.zoom);\n      }\n\n      removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);\n\n      if (options.zoomable && options.zoomOnWheel) {\n        removeListener(cropper, EVENT_WHEEL, this.onWheel, {\n          passive: false,\n          capture: true\n        });\n      }\n\n      if (options.toggleDragModeOnDblclick) {\n        removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);\n      }\n\n      removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);\n      removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);\n\n      if (options.responsive) {\n        removeListener(window, EVENT_RESIZE, this.onResize);\n      }\n    }\n  };\n\n  var handlers = {\n    resize: function resize() {\n      var options = this.options,\n          container = this.container,\n          containerData = this.containerData;\n      var minContainerWidth = Number(options.minContainerWidth) || MIN_CONTAINER_WIDTH;\n      var minContainerHeight = Number(options.minContainerHeight) || MIN_CONTAINER_HEIGHT;\n\n      if (this.disabled || containerData.width <= minContainerWidth || containerData.height <= minContainerHeight) {\n        return;\n      }\n\n      var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed\n\n      if (ratio !== 1 || container.offsetHeight !== containerData.height) {\n        var canvasData;\n        var cropBoxData;\n\n        if (options.restore) {\n          canvasData = this.getCanvasData();\n          cropBoxData = this.getCropBoxData();\n        }\n\n        this.render();\n\n        if (options.restore) {\n          this.setCanvasData(forEach(canvasData, function (n, i) {\n            canvasData[i] = n * ratio;\n          }));\n          this.setCropBoxData(forEach(cropBoxData, function (n, i) {\n            cropBoxData[i] = n * ratio;\n          }));\n        }\n      }\n    },\n    dblclick: function dblclick() {\n      if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {\n        return;\n      }\n\n      this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);\n    },\n    wheel: function wheel(event) {\n      var _this = this;\n\n      var ratio = Number(this.options.wheelZoomRatio) || 0.1;\n      var delta = 1;\n\n      if (this.disabled) {\n        return;\n      }\n\n      event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)\n\n      if (this.wheeling) {\n        return;\n      }\n\n      this.wheeling = true;\n      setTimeout(function () {\n        _this.wheeling = false;\n      }, 50);\n\n      if (event.deltaY) {\n        delta = event.deltaY > 0 ? 1 : -1;\n      } else if (event.wheelDelta) {\n        delta = -event.wheelDelta / 120;\n      } else if (event.detail) {\n        delta = event.detail > 0 ? 1 : -1;\n      }\n\n      this.zoom(-delta * ratio, event);\n    },\n    cropStart: function cropStart(event) {\n      var buttons = event.buttons,\n          button = event.button;\n\n      if (this.disabled // Handle mouse event and pointer event and ignore touch event\n      || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && ( // No primary button (Usually the left button)\n      isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu\n      || event.ctrlKey)) {\n        return;\n      }\n\n      var options = this.options,\n          pointers = this.pointers;\n      var action;\n\n      if (event.changedTouches) {\n        // Handle touch event\n        forEach(event.changedTouches, function (touch) {\n          pointers[touch.identifier] = getPointer(touch);\n        });\n      } else {\n        // Handle mouse event and pointer event\n        pointers[event.pointerId || 0] = getPointer(event);\n      }\n\n      if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {\n        action = ACTION_ZOOM;\n      } else {\n        action = getData(event.target, DATA_ACTION);\n      }\n\n      if (!REGEXP_ACTIONS.test(action)) {\n        return;\n      }\n\n      if (dispatchEvent(this.element, EVENT_CROP_START, {\n        originalEvent: event,\n        action: action\n      }) === false) {\n        return;\n      } // This line is required for preventing page zooming in iOS browsers\n\n\n      event.preventDefault();\n      this.action = action;\n      this.cropping = false;\n\n      if (action === ACTION_CROP) {\n        this.cropping = true;\n        addClass(this.dragBox, CLASS_MODAL);\n      }\n    },\n    cropMove: function cropMove(event) {\n      var action = this.action;\n\n      if (this.disabled || !action) {\n        return;\n      }\n\n      var pointers = this.pointers;\n      event.preventDefault();\n\n      if (dispatchEvent(this.element, EVENT_CROP_MOVE, {\n        originalEvent: event,\n        action: action\n      }) === false) {\n        return;\n      }\n\n      if (event.changedTouches) {\n        forEach(event.changedTouches, function (touch) {\n          // The first parameter should not be undefined (#432)\n          assign(pointers[touch.identifier] || {}, getPointer(touch, true));\n        });\n      } else {\n        assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));\n      }\n\n      this.change(event);\n    },\n    cropEnd: function cropEnd(event) {\n      if (this.disabled) {\n        return;\n      }\n\n      var action = this.action,\n          pointers = this.pointers;\n\n      if (event.changedTouches) {\n        forEach(event.changedTouches, function (touch) {\n          delete pointers[touch.identifier];\n        });\n      } else {\n        delete pointers[event.pointerId || 0];\n      }\n\n      if (!action) {\n        return;\n      }\n\n      event.preventDefault();\n\n      if (!Object.keys(pointers).length) {\n        this.action = '';\n      }\n\n      if (this.cropping) {\n        this.cropping = false;\n        toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);\n      }\n\n      dispatchEvent(this.element, EVENT_CROP_END, {\n        originalEvent: event,\n        action: action\n      });\n    }\n  };\n\n  var change = {\n    change: function change(event) {\n      var options = this.options,\n          canvasData = this.canvasData,\n          containerData = this.containerData,\n          cropBoxData = this.cropBoxData,\n          pointers = this.pointers;\n      var action = this.action;\n      var aspectRatio = options.aspectRatio;\n      var left = cropBoxData.left,\n          top = cropBoxData.top,\n          width = cropBoxData.width,\n          height = cropBoxData.height;\n      var right = left + width;\n      var bottom = top + height;\n      var minLeft = 0;\n      var minTop = 0;\n      var maxWidth = containerData.width;\n      var maxHeight = containerData.height;\n      var renderable = true;\n      var offset; // Locking aspect ratio in \"free mode\" by holding shift key\n\n      if (!aspectRatio && event.shiftKey) {\n        aspectRatio = width && height ? width / height : 1;\n      }\n\n      if (this.limited) {\n        minLeft = cropBoxData.minLeft;\n        minTop = cropBoxData.minTop;\n        maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);\n        maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);\n      }\n\n      var pointer = pointers[Object.keys(pointers)[0]];\n      var range = {\n        x: pointer.endX - pointer.startX,\n        y: pointer.endY - pointer.startY\n      };\n\n      var check = function check(side) {\n        switch (side) {\n          case ACTION_EAST:\n            if (right + range.x > maxWidth) {\n              range.x = maxWidth - right;\n            }\n\n            break;\n\n          case ACTION_WEST:\n            if (left + range.x < minLeft) {\n              range.x = minLeft - left;\n            }\n\n            break;\n\n          case ACTION_NORTH:\n            if (top + range.y < minTop) {\n              range.y = minTop - top;\n            }\n\n            break;\n\n          case ACTION_SOUTH:\n            if (bottom + range.y > maxHeight) {\n              range.y = maxHeight - bottom;\n            }\n\n            break;\n\n          default:\n        }\n      };\n\n      switch (action) {\n        // Move crop box\n        case ACTION_ALL:\n          left += range.x;\n          top += range.y;\n          break;\n        // Resize crop box\n\n        case ACTION_EAST:\n          if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_EAST);\n          width += range.x;\n\n          if (width < 0) {\n            action = ACTION_WEST;\n            width = -width;\n            left -= width;\n          }\n\n          if (aspectRatio) {\n            height = width / aspectRatio;\n            top += (cropBoxData.height - height) / 2;\n          }\n\n          break;\n\n        case ACTION_NORTH:\n          if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n\n          if (height < 0) {\n            action = ACTION_SOUTH;\n            height = -height;\n            top -= height;\n          }\n\n          if (aspectRatio) {\n            width = height * aspectRatio;\n            left += (cropBoxData.width - width) / 2;\n          }\n\n          break;\n\n        case ACTION_WEST:\n          if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_WEST);\n          width -= range.x;\n          left += range.x;\n\n          if (width < 0) {\n            action = ACTION_EAST;\n            width = -width;\n            left -= width;\n          }\n\n          if (aspectRatio) {\n            height = width / aspectRatio;\n            top += (cropBoxData.height - height) / 2;\n          }\n\n          break;\n\n        case ACTION_SOUTH:\n          if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_SOUTH);\n          height += range.y;\n\n          if (height < 0) {\n            action = ACTION_NORTH;\n            height = -height;\n            top -= height;\n          }\n\n          if (aspectRatio) {\n            width = height * aspectRatio;\n            left += (cropBoxData.width - width) / 2;\n          }\n\n          break;\n\n        case ACTION_NORTH_EAST:\n          if (aspectRatio) {\n            if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_NORTH);\n            height -= range.y;\n            top += range.y;\n            width = height * aspectRatio;\n          } else {\n            check(ACTION_NORTH);\n            check(ACTION_EAST);\n\n            if (range.x >= 0) {\n              if (right < maxWidth) {\n                width += range.x;\n              } else if (range.y <= 0 && top <= minTop) {\n                renderable = false;\n              }\n            } else {\n              width += range.x;\n            }\n\n            if (range.y <= 0) {\n              if (top > minTop) {\n                height -= range.y;\n                top += range.y;\n              }\n            } else {\n              height -= range.y;\n              top += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_SOUTH_WEST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_NORTH_WEST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_SOUTH_EAST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n\n        case ACTION_NORTH_WEST:\n          if (aspectRatio) {\n            if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_NORTH);\n            height -= range.y;\n            top += range.y;\n            width = height * aspectRatio;\n            left += cropBoxData.width - width;\n          } else {\n            check(ACTION_NORTH);\n            check(ACTION_WEST);\n\n            if (range.x <= 0) {\n              if (left > minLeft) {\n                width -= range.x;\n                left += range.x;\n              } else if (range.y <= 0 && top <= minTop) {\n                renderable = false;\n              }\n            } else {\n              width -= range.x;\n              left += range.x;\n            }\n\n            if (range.y <= 0) {\n              if (top > minTop) {\n                height -= range.y;\n                top += range.y;\n              }\n            } else {\n              height -= range.y;\n              top += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_SOUTH_EAST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_NORTH_EAST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_SOUTH_WEST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n\n        case ACTION_SOUTH_WEST:\n          if (aspectRatio) {\n            if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_WEST);\n            width -= range.x;\n            left += range.x;\n            height = width / aspectRatio;\n          } else {\n            check(ACTION_SOUTH);\n            check(ACTION_WEST);\n\n            if (range.x <= 0) {\n              if (left > minLeft) {\n                width -= range.x;\n                left += range.x;\n              } else if (range.y >= 0 && bottom >= maxHeight) {\n                renderable = false;\n              }\n            } else {\n              width -= range.x;\n              left += range.x;\n            }\n\n            if (range.y >= 0) {\n              if (bottom < maxHeight) {\n                height += range.y;\n              }\n            } else {\n              height += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_NORTH_EAST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_SOUTH_EAST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_NORTH_WEST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n\n        case ACTION_SOUTH_EAST:\n          if (aspectRatio) {\n            if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_EAST);\n            width += range.x;\n            height = width / aspectRatio;\n          } else {\n            check(ACTION_SOUTH);\n            check(ACTION_EAST);\n\n            if (range.x >= 0) {\n              if (right < maxWidth) {\n                width += range.x;\n              } else if (range.y >= 0 && bottom >= maxHeight) {\n                renderable = false;\n              }\n            } else {\n              width += range.x;\n            }\n\n            if (range.y >= 0) {\n              if (bottom < maxHeight) {\n                height += range.y;\n              }\n            } else {\n              height += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_NORTH_WEST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_SOUTH_WEST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_NORTH_EAST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n        // Move canvas\n\n        case ACTION_MOVE:\n          this.move(range.x, range.y);\n          renderable = false;\n          break;\n        // Zoom canvas\n\n        case ACTION_ZOOM:\n          this.zoom(getMaxZoomRatio(pointers), event);\n          renderable = false;\n          break;\n        // Create crop box\n\n        case ACTION_CROP:\n          if (!range.x || !range.y) {\n            renderable = false;\n            break;\n          }\n\n          offset = getOffset(this.cropper);\n          left = pointer.startX - offset.left;\n          top = pointer.startY - offset.top;\n          width = cropBoxData.minWidth;\n          height = cropBoxData.minHeight;\n\n          if (range.x > 0) {\n            action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n          } else if (range.x < 0) {\n            left -= width;\n            action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n          }\n\n          if (range.y < 0) {\n            top -= height;\n          } // Show the crop box if is hidden\n\n\n          if (!this.cropped) {\n            removeClass(this.cropBox, CLASS_HIDDEN);\n            this.cropped = true;\n\n            if (this.limited) {\n              this.limitCropBox(true, true);\n            }\n          }\n\n          break;\n\n        default:\n      }\n\n      if (renderable) {\n        cropBoxData.width = width;\n        cropBoxData.height = height;\n        cropBoxData.left = left;\n        cropBoxData.top = top;\n        this.action = action;\n        this.renderCropBox();\n      } // Override\n\n\n      forEach(pointers, function (p) {\n        p.startX = p.endX;\n        p.startY = p.endY;\n      });\n    }\n  };\n\n  var methods = {\n    // Show the crop box manually\n    crop: function crop() {\n      if (this.ready && !this.cropped && !this.disabled) {\n        this.cropped = true;\n        this.limitCropBox(true, true);\n\n        if (this.options.modal) {\n          addClass(this.dragBox, CLASS_MODAL);\n        }\n\n        removeClass(this.cropBox, CLASS_HIDDEN);\n        this.setCropBoxData(this.initialCropBoxData);\n      }\n\n      return this;\n    },\n    // Reset the image and crop box to their initial states\n    reset: function reset() {\n      if (this.ready && !this.disabled) {\n        this.imageData = assign({}, this.initialImageData);\n        this.canvasData = assign({}, this.initialCanvasData);\n        this.cropBoxData = assign({}, this.initialCropBoxData);\n        this.renderCanvas();\n\n        if (this.cropped) {\n          this.renderCropBox();\n        }\n      }\n\n      return this;\n    },\n    // Clear the crop box\n    clear: function clear() {\n      if (this.cropped && !this.disabled) {\n        assign(this.cropBoxData, {\n          left: 0,\n          top: 0,\n          width: 0,\n          height: 0\n        });\n        this.cropped = false;\n        this.renderCropBox();\n        this.limitCanvas(true, true); // Render canvas after crop box rendered\n\n        this.renderCanvas();\n        removeClass(this.dragBox, CLASS_MODAL);\n        addClass(this.cropBox, CLASS_HIDDEN);\n      }\n\n      return this;\n    },\n\n    /**\n     * Replace the image's src and rebuild the cropper\n     * @param {string} url - The new URL.\n     * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.\n     * @returns {Cropper} this\n     */\n    replace: function replace(url) {\n      var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      if (!this.disabled && url) {\n        if (this.isImg) {\n          this.element.src = url;\n        }\n\n        if (hasSameSize) {\n          this.url = url;\n          this.image.src = url;\n\n          if (this.ready) {\n            this.viewBoxImage.src = url;\n            forEach(this.previews, function (element) {\n              element.getElementsByTagName('img')[0].src = url;\n            });\n          }\n        } else {\n          if (this.isImg) {\n            this.replaced = true;\n          }\n\n          this.options.data = null;\n          this.uncreate();\n          this.load(url);\n        }\n      }\n\n      return this;\n    },\n    // Enable (unfreeze) the cropper\n    enable: function enable() {\n      if (this.ready && this.disabled) {\n        this.disabled = false;\n        removeClass(this.cropper, CLASS_DISABLED);\n      }\n\n      return this;\n    },\n    // Disable (freeze) the cropper\n    disable: function disable() {\n      if (this.ready && !this.disabled) {\n        this.disabled = true;\n        addClass(this.cropper, CLASS_DISABLED);\n      }\n\n      return this;\n    },\n\n    /**\n     * Destroy the cropper and remove the instance from the image\n     * @returns {Cropper} this\n     */\n    destroy: function destroy() {\n      var element = this.element;\n\n      if (!element[NAMESPACE]) {\n        return this;\n      }\n\n      element[NAMESPACE] = undefined;\n\n      if (this.isImg && this.replaced) {\n        element.src = this.originalUrl;\n      }\n\n      this.uncreate();\n      return this;\n    },\n\n    /**\n     * Move the canvas with relative offsets\n     * @param {number} offsetX - The relative offset distance on the x-axis.\n     * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.\n     * @returns {Cropper} this\n     */\n    move: function move(offsetX) {\n      var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;\n      var _this$canvasData = this.canvasData,\n          left = _this$canvasData.left,\n          top = _this$canvasData.top;\n      return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));\n    },\n\n    /**\n     * Move the canvas to an absolute point\n     * @param {number} x - The x-axis coordinate.\n     * @param {number} [y=x] - The y-axis coordinate.\n     * @returns {Cropper} this\n     */\n    moveTo: function moveTo(x) {\n      var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;\n      var canvasData = this.canvasData;\n      var changed = false;\n      x = Number(x);\n      y = Number(y);\n\n      if (this.ready && !this.disabled && this.options.movable) {\n        if (isNumber(x)) {\n          canvasData.left = x;\n          changed = true;\n        }\n\n        if (isNumber(y)) {\n          canvasData.top = y;\n          changed = true;\n        }\n\n        if (changed) {\n          this.renderCanvas(true);\n        }\n      }\n\n      return this;\n    },\n\n    /**\n     * Zoom the canvas with a relative ratio\n     * @param {number} ratio - The target ratio.\n     * @param {Event} _originalEvent - The original event if any.\n     * @returns {Cropper} this\n     */\n    zoom: function zoom(ratio, _originalEvent) {\n      var canvasData = this.canvasData;\n      ratio = Number(ratio);\n\n      if (ratio < 0) {\n        ratio = 1 / (1 - ratio);\n      } else {\n        ratio = 1 + ratio;\n      }\n\n      return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);\n    },\n\n    /**\n     * Zoom the canvas to an absolute ratio\n     * @param {number} ratio - The target ratio.\n     * @param {Object} pivot - The zoom pivot point coordinate.\n     * @param {Event} _originalEvent - The original event if any.\n     * @returns {Cropper} this\n     */\n    zoomTo: function zoomTo(ratio, pivot, _originalEvent) {\n      var options = this.options,\n          canvasData = this.canvasData;\n      var width = canvasData.width,\n          height = canvasData.height,\n          naturalWidth = canvasData.naturalWidth,\n          naturalHeight = canvasData.naturalHeight;\n      ratio = Number(ratio);\n\n      if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {\n        var newWidth = naturalWidth * ratio;\n        var newHeight = naturalHeight * ratio;\n\n        if (dispatchEvent(this.element, EVENT_ZOOM, {\n          ratio: ratio,\n          oldRatio: width / naturalWidth,\n          originalEvent: _originalEvent\n        }) === false) {\n          return this;\n        }\n\n        if (_originalEvent) {\n          var pointers = this.pointers;\n          var offset = getOffset(this.cropper);\n          var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {\n            pageX: _originalEvent.pageX,\n            pageY: _originalEvent.pageY\n          }; // Zoom from the triggering point of the event\n\n          canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);\n          canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);\n        } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {\n          canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);\n          canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);\n        } else {\n          // Zoom from the center of the canvas\n          canvasData.left -= (newWidth - width) / 2;\n          canvasData.top -= (newHeight - height) / 2;\n        }\n\n        canvasData.width = newWidth;\n        canvasData.height = newHeight;\n        this.renderCanvas(true);\n      }\n\n      return this;\n    },\n\n    /**\n     * Rotate the canvas with a relative degree\n     * @param {number} degree - The rotate degree.\n     * @returns {Cropper} this\n     */\n    rotate: function rotate(degree) {\n      return this.rotateTo((this.imageData.rotate || 0) + Number(degree));\n    },\n\n    /**\n     * Rotate the canvas to an absolute degree\n     * @param {number} degree - The rotate degree.\n     * @returns {Cropper} this\n     */\n    rotateTo: function rotateTo(degree) {\n      degree = Number(degree);\n\n      if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {\n        this.imageData.rotate = degree % 360;\n        this.renderCanvas(true, true);\n      }\n\n      return this;\n    },\n\n    /**\n     * Scale the image on the x-axis.\n     * @param {number} scaleX - The scale ratio on the x-axis.\n     * @returns {Cropper} this\n     */\n    scaleX: function scaleX(_scaleX) {\n      var scaleY = this.imageData.scaleY;\n      return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);\n    },\n\n    /**\n     * Scale the image on the y-axis.\n     * @param {number} scaleY - The scale ratio on the y-axis.\n     * @returns {Cropper} this\n     */\n    scaleY: function scaleY(_scaleY) {\n      var scaleX = this.imageData.scaleX;\n      return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);\n    },\n\n    /**\n     * Scale the image\n     * @param {number} scaleX - The scale ratio on the x-axis.\n     * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.\n     * @returns {Cropper} this\n     */\n    scale: function scale(scaleX) {\n      var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;\n      var imageData = this.imageData;\n      var transformed = false;\n      scaleX = Number(scaleX);\n      scaleY = Number(scaleY);\n\n      if (this.ready && !this.disabled && this.options.scalable) {\n        if (isNumber(scaleX)) {\n          imageData.scaleX = scaleX;\n          transformed = true;\n        }\n\n        if (isNumber(scaleY)) {\n          imageData.scaleY = scaleY;\n          transformed = true;\n        }\n\n        if (transformed) {\n          this.renderCanvas(true, true);\n        }\n      }\n\n      return this;\n    },\n\n    /**\n     * Get the cropped area position and size data (base on the original image)\n     * @param {boolean} [rounded=false] - Indicate if round the data values or not.\n     * @returns {Object} The result cropped data.\n     */\n    getData: function getData() {\n      var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n      var options = this.options,\n          imageData = this.imageData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData;\n      var data;\n\n      if (this.ready && this.cropped) {\n        data = {\n          x: cropBoxData.left - canvasData.left,\n          y: cropBoxData.top - canvasData.top,\n          width: cropBoxData.width,\n          height: cropBoxData.height\n        };\n        var ratio = imageData.width / imageData.naturalWidth;\n        forEach(data, function (n, i) {\n          data[i] = n / ratio;\n        });\n\n        if (rounded) {\n          // In case rounding off leads to extra 1px in right or bottom border\n          // we should round the top-left corner and the dimension (#343).\n          var bottom = Math.round(data.y + data.height);\n          var right = Math.round(data.x + data.width);\n          data.x = Math.round(data.x);\n          data.y = Math.round(data.y);\n          data.width = right - data.x;\n          data.height = bottom - data.y;\n        }\n      } else {\n        data = {\n          x: 0,\n          y: 0,\n          width: 0,\n          height: 0\n        };\n      }\n\n      if (options.rotatable) {\n        data.rotate = imageData.rotate || 0;\n      }\n\n      if (options.scalable) {\n        data.scaleX = imageData.scaleX || 1;\n        data.scaleY = imageData.scaleY || 1;\n      }\n\n      return data;\n    },\n\n    /**\n     * Set the cropped area position and size with new data\n     * @param {Object} data - The new data.\n     * @returns {Cropper} this\n     */\n    setData: function setData(data) {\n      var options = this.options,\n          imageData = this.imageData,\n          canvasData = this.canvasData;\n      var cropBoxData = {};\n\n      if (this.ready && !this.disabled && isPlainObject(data)) {\n        var transformed = false;\n\n        if (options.rotatable) {\n          if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {\n            imageData.rotate = data.rotate;\n            transformed = true;\n          }\n        }\n\n        if (options.scalable) {\n          if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {\n            imageData.scaleX = data.scaleX;\n            transformed = true;\n          }\n\n          if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {\n            imageData.scaleY = data.scaleY;\n            transformed = true;\n          }\n        }\n\n        if (transformed) {\n          this.renderCanvas(true, true);\n        }\n\n        var ratio = imageData.width / imageData.naturalWidth;\n\n        if (isNumber(data.x)) {\n          cropBoxData.left = data.x * ratio + canvasData.left;\n        }\n\n        if (isNumber(data.y)) {\n          cropBoxData.top = data.y * ratio + canvasData.top;\n        }\n\n        if (isNumber(data.width)) {\n          cropBoxData.width = data.width * ratio;\n        }\n\n        if (isNumber(data.height)) {\n          cropBoxData.height = data.height * ratio;\n        }\n\n        this.setCropBoxData(cropBoxData);\n      }\n\n      return this;\n    },\n\n    /**\n     * Get the container size data.\n     * @returns {Object} The result container data.\n     */\n    getContainerData: function getContainerData() {\n      return this.ready ? assign({}, this.containerData) : {};\n    },\n\n    /**\n     * Get the image position and size data.\n     * @returns {Object} The result image data.\n     */\n    getImageData: function getImageData() {\n      return this.sized ? assign({}, this.imageData) : {};\n    },\n\n    /**\n     * Get the canvas position and size data.\n     * @returns {Object} The result canvas data.\n     */\n    getCanvasData: function getCanvasData() {\n      var canvasData = this.canvasData;\n      var data = {};\n\n      if (this.ready) {\n        forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {\n          data[n] = canvasData[n];\n        });\n      }\n\n      return data;\n    },\n\n    /**\n     * Set the canvas position and size with new data.\n     * @param {Object} data - The new canvas data.\n     * @returns {Cropper} this\n     */\n    setCanvasData: function setCanvasData(data) {\n      var canvasData = this.canvasData;\n      var aspectRatio = canvasData.aspectRatio;\n\n      if (this.ready && !this.disabled && isPlainObject(data)) {\n        if (isNumber(data.left)) {\n          canvasData.left = data.left;\n        }\n\n        if (isNumber(data.top)) {\n          canvasData.top = data.top;\n        }\n\n        if (isNumber(data.width)) {\n          canvasData.width = data.width;\n          canvasData.height = data.width / aspectRatio;\n        } else if (isNumber(data.height)) {\n          canvasData.height = data.height;\n          canvasData.width = data.height * aspectRatio;\n        }\n\n        this.renderCanvas(true);\n      }\n\n      return this;\n    },\n\n    /**\n     * Get the crop box position and size data.\n     * @returns {Object} The result crop box data.\n     */\n    getCropBoxData: function getCropBoxData() {\n      var cropBoxData = this.cropBoxData;\n      var data;\n\n      if (this.ready && this.cropped) {\n        data = {\n          left: cropBoxData.left,\n          top: cropBoxData.top,\n          width: cropBoxData.width,\n          height: cropBoxData.height\n        };\n      }\n\n      return data || {};\n    },\n\n    /**\n     * Set the crop box position and size with new data.\n     * @param {Object} data - The new crop box data.\n     * @returns {Cropper} this\n     */\n    setCropBoxData: function setCropBoxData(data) {\n      var cropBoxData = this.cropBoxData;\n      var aspectRatio = this.options.aspectRatio;\n      var widthChanged;\n      var heightChanged;\n\n      if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {\n        if (isNumber(data.left)) {\n          cropBoxData.left = data.left;\n        }\n\n        if (isNumber(data.top)) {\n          cropBoxData.top = data.top;\n        }\n\n        if (isNumber(data.width) && data.width !== cropBoxData.width) {\n          widthChanged = true;\n          cropBoxData.width = data.width;\n        }\n\n        if (isNumber(data.height) && data.height !== cropBoxData.height) {\n          heightChanged = true;\n          cropBoxData.height = data.height;\n        }\n\n        if (aspectRatio) {\n          if (widthChanged) {\n            cropBoxData.height = cropBoxData.width / aspectRatio;\n          } else if (heightChanged) {\n            cropBoxData.width = cropBoxData.height * aspectRatio;\n          }\n        }\n\n        this.renderCropBox();\n      }\n\n      return this;\n    },\n\n    /**\n     * Get a canvas drawn the cropped image.\n     * @param {Object} [options={}] - The config options.\n     * @returns {HTMLCanvasElement} - The result canvas.\n     */\n    getCroppedCanvas: function getCroppedCanvas() {\n      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n      if (!this.ready || !window.HTMLCanvasElement) {\n        return null;\n      }\n\n      var canvasData = this.canvasData;\n      var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.\n\n      if (!this.cropped) {\n        return source;\n      }\n\n      var _this$getData = this.getData(),\n          initialX = _this$getData.x,\n          initialY = _this$getData.y,\n          initialWidth = _this$getData.width,\n          initialHeight = _this$getData.height;\n\n      var ratio = source.width / Math.floor(canvasData.naturalWidth);\n\n      if (ratio !== 1) {\n        initialX *= ratio;\n        initialY *= ratio;\n        initialWidth *= ratio;\n        initialHeight *= ratio;\n      }\n\n      var aspectRatio = initialWidth / initialHeight;\n      var maxSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: options.maxWidth || Infinity,\n        height: options.maxHeight || Infinity\n      });\n      var minSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: options.minWidth || 0,\n        height: options.minHeight || 0\n      }, 'cover');\n\n      var _getAdjustedSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: options.width || (ratio !== 1 ? source.width : initialWidth),\n        height: options.height || (ratio !== 1 ? source.height : initialHeight)\n      }),\n          width = _getAdjustedSizes.width,\n          height = _getAdjustedSizes.height;\n\n      width = Math.min(maxSizes.width, Math.max(minSizes.width, width));\n      height = Math.min(maxSizes.height, Math.max(minSizes.height, height));\n      var canvas = document.createElement('canvas');\n      var context = canvas.getContext('2d');\n      canvas.width = normalizeDecimalNumber(width);\n      canvas.height = normalizeDecimalNumber(height);\n      context.fillStyle = options.fillColor || 'transparent';\n      context.fillRect(0, 0, width, height);\n      var _options$imageSmoothi = options.imageSmoothingEnabled,\n          imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,\n          imageSmoothingQuality = options.imageSmoothingQuality;\n      context.imageSmoothingEnabled = imageSmoothingEnabled;\n\n      if (imageSmoothingQuality) {\n        context.imageSmoothingQuality = imageSmoothingQuality;\n      } // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n\n\n      var sourceWidth = source.width;\n      var sourceHeight = source.height; // Source canvas parameters\n\n      var srcX = initialX;\n      var srcY = initialY;\n      var srcWidth;\n      var srcHeight; // Destination canvas parameters\n\n      var dstX;\n      var dstY;\n      var dstWidth;\n      var dstHeight;\n\n      if (srcX <= -initialWidth || srcX > sourceWidth) {\n        srcX = 0;\n        srcWidth = 0;\n        dstX = 0;\n        dstWidth = 0;\n      } else if (srcX <= 0) {\n        dstX = -srcX;\n        srcX = 0;\n        srcWidth = Math.min(sourceWidth, initialWidth + srcX);\n        dstWidth = srcWidth;\n      } else if (srcX <= sourceWidth) {\n        dstX = 0;\n        srcWidth = Math.min(initialWidth, sourceWidth - srcX);\n        dstWidth = srcWidth;\n      }\n\n      if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {\n        srcY = 0;\n        srcHeight = 0;\n        dstY = 0;\n        dstHeight = 0;\n      } else if (srcY <= 0) {\n        dstY = -srcY;\n        srcY = 0;\n        srcHeight = Math.min(sourceHeight, initialHeight + srcY);\n        dstHeight = srcHeight;\n      } else if (srcY <= sourceHeight) {\n        dstY = 0;\n        srcHeight = Math.min(initialHeight, sourceHeight - srcY);\n        dstHeight = srcHeight;\n      }\n\n      var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid \"IndexSizeError\"\n\n      if (dstWidth > 0 && dstHeight > 0) {\n        var scale = width / initialWidth;\n        params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);\n      } // All the numerical parameters should be integer for `drawImage`\n      // https://github.com/fengyuanchen/cropper/issues/476\n\n\n      context.drawImage.apply(context, [source].concat(_toConsumableArray(params.map(function (param) {\n        return Math.floor(normalizeDecimalNumber(param));\n      }))));\n      return canvas;\n    },\n\n    /**\n     * Change the aspect ratio of the crop box.\n     * @param {number} aspectRatio - The new aspect ratio.\n     * @returns {Cropper} this\n     */\n    setAspectRatio: function setAspectRatio(aspectRatio) {\n      var options = this.options;\n\n      if (!this.disabled && !isUndefined(aspectRatio)) {\n        // 0 -> NaN\n        options.aspectRatio = Math.max(0, aspectRatio) || NaN;\n\n        if (this.ready) {\n          this.initCropBox();\n\n          if (this.cropped) {\n            this.renderCropBox();\n          }\n        }\n      }\n\n      return this;\n    },\n\n    /**\n     * Change the drag mode.\n     * @param {string} mode - The new drag mode.\n     * @returns {Cropper} this\n     */\n    setDragMode: function setDragMode(mode) {\n      var options = this.options,\n          dragBox = this.dragBox,\n          face = this.face;\n\n      if (this.ready && !this.disabled) {\n        var croppable = mode === DRAG_MODE_CROP;\n        var movable = options.movable && mode === DRAG_MODE_MOVE;\n        mode = croppable || movable ? mode : DRAG_MODE_NONE;\n        options.dragMode = mode;\n        setData(dragBox, DATA_ACTION, mode);\n        toggleClass(dragBox, CLASS_CROP, croppable);\n        toggleClass(dragBox, CLASS_MOVE, movable);\n\n        if (!options.cropBoxMovable) {\n          // Sync drag mode to crop box when it is not movable\n          setData(face, DATA_ACTION, mode);\n          toggleClass(face, CLASS_CROP, croppable);\n          toggleClass(face, CLASS_MOVE, movable);\n        }\n      }\n\n      return this;\n    }\n  };\n\n  var AnotherCropper = WINDOW.Cropper;\n\n  var Cropper =\n  /*#__PURE__*/\n  function () {\n    /**\n     * Create a new Cropper.\n     * @param {Element} element - The target element for cropping.\n     * @param {Object} [options={}] - The configuration options.\n     */\n    function Cropper(element) {\n      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n      _classCallCheck(this, Cropper);\n\n      if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {\n        throw new Error('The first argument is required and must be an <img> or <canvas> element.');\n      }\n\n      this.element = element;\n      this.options = assign({}, DEFAULTS, isPlainObject(options) && options);\n      this.cropped = false;\n      this.disabled = false;\n      this.pointers = {};\n      this.ready = false;\n      this.reloading = false;\n      this.replaced = false;\n      this.sized = false;\n      this.sizing = false;\n      this.init();\n    }\n\n    _createClass(Cropper, [{\n      key: \"init\",\n      value: function init() {\n        var element = this.element;\n        var tagName = element.tagName.toLowerCase();\n        var url;\n\n        if (element[NAMESPACE]) {\n          return;\n        }\n\n        element[NAMESPACE] = this;\n\n        if (tagName === 'img') {\n          this.isImg = true; // e.g.: \"img/picture.jpg\"\n\n          url = element.getAttribute('src') || '';\n          this.originalUrl = url; // Stop when it's a blank image\n\n          if (!url) {\n            return;\n          } // e.g.: \"http://example.com/img/picture.jpg\"\n\n\n          url = element.src;\n        } else if (tagName === 'canvas' && window.HTMLCanvasElement) {\n          url = element.toDataURL();\n        }\n\n        this.load(url);\n      }\n    }, {\n      key: \"load\",\n      value: function load(url) {\n        var _this = this;\n\n        if (!url) {\n          return;\n        }\n\n        this.url = url;\n        this.imageData = {};\n        var element = this.element,\n            options = this.options;\n\n        if (!options.rotatable && !options.scalable) {\n          options.checkOrientation = false;\n        } // Only IE10+ supports Typed Arrays\n\n\n        if (!options.checkOrientation || !window.ArrayBuffer) {\n          this.clone();\n          return;\n        } // Detect the mime type of the image directly if it is a Data URL\n\n\n        if (REGEXP_DATA_URL.test(url)) {\n          // Read ArrayBuffer from Data URL of JPEG images directly for better performance\n          if (REGEXP_DATA_URL_JPEG.test(url)) {\n            this.read(dataURLToArrayBuffer(url));\n          } else {\n            // Only a JPEG image may contains Exif Orientation information,\n            // the rest types of Data URLs are not necessary to check orientation at all.\n            this.clone();\n          }\n\n          return;\n        } // 1. Detect the mime type of the image by a XMLHttpRequest.\n        // 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.\n\n\n        var xhr = new XMLHttpRequest();\n        var clone = this.clone.bind(this);\n        this.reloading = true;\n        this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:\n        // http, https, data, chrome, chrome-extension.\n        // 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy\n        // in some browsers as IE11 and Safari.\n\n        xhr.onabort = clone;\n        xhr.onerror = clone;\n        xhr.ontimeout = clone;\n\n        xhr.onprogress = function () {\n          // Abort the request directly if it not a JPEG image for better performance\n          if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {\n            xhr.abort();\n          }\n        };\n\n        xhr.onload = function () {\n          _this.read(xhr.response);\n        };\n\n        xhr.onloadend = function () {\n          _this.reloading = false;\n          _this.xhr = null;\n        }; // Bust cache when there is a \"crossOrigin\" property to avoid browser cache error\n\n\n        if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {\n          url = addTimestamp(url);\n        }\n\n        xhr.open('GET', url);\n        xhr.responseType = 'arraybuffer';\n        xhr.withCredentials = element.crossOrigin === 'use-credentials';\n        xhr.send();\n      }\n    }, {\n      key: \"read\",\n      value: function read(arrayBuffer) {\n        var options = this.options,\n            imageData = this.imageData; // Reset the orientation value to its default value 1\n        // as some iOS browsers will render image with its orientation\n\n        var orientation = resetAndGetOrientation(arrayBuffer);\n        var rotate = 0;\n        var scaleX = 1;\n        var scaleY = 1;\n\n        if (orientation > 1) {\n          // Generate a new URL which has the default orientation value\n          this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);\n\n          var _parseOrientation = parseOrientation(orientation);\n\n          rotate = _parseOrientation.rotate;\n          scaleX = _parseOrientation.scaleX;\n          scaleY = _parseOrientation.scaleY;\n        }\n\n        if (options.rotatable) {\n          imageData.rotate = rotate;\n        }\n\n        if (options.scalable) {\n          imageData.scaleX = scaleX;\n          imageData.scaleY = scaleY;\n        }\n\n        this.clone();\n      }\n    }, {\n      key: \"clone\",\n      value: function clone() {\n        var element = this.element,\n            url = this.url;\n        var crossOrigin = element.crossOrigin;\n        var crossOriginUrl = url;\n\n        if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {\n          if (!crossOrigin) {\n            crossOrigin = 'anonymous';\n          } // Bust cache when there is not a \"crossOrigin\" property (#519)\n\n\n          crossOriginUrl = addTimestamp(url);\n        }\n\n        this.crossOrigin = crossOrigin;\n        this.crossOriginUrl = crossOriginUrl;\n        var image = document.createElement('img');\n\n        if (crossOrigin) {\n          image.crossOrigin = crossOrigin;\n        }\n\n        image.src = crossOriginUrl || url;\n        image.alt = element.alt || 'The image to crop';\n        this.image = image;\n        image.onload = this.start.bind(this);\n        image.onerror = this.stop.bind(this);\n        addClass(image, CLASS_HIDE);\n        element.parentNode.insertBefore(image, element.nextSibling);\n      }\n    }, {\n      key: \"start\",\n      value: function start() {\n        var _this2 = this;\n\n        var image = this.image;\n        image.onload = null;\n        image.onerror = null;\n        this.sizing = true; // Match all browsers that use WebKit as the layout engine in iOS devices,\n        // such as Safari for iOS, Chrome for iOS, and in-app browsers.\n\n        var isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);\n\n        var done = function done(naturalWidth, naturalHeight) {\n          assign(_this2.imageData, {\n            naturalWidth: naturalWidth,\n            naturalHeight: naturalHeight,\n            aspectRatio: naturalWidth / naturalHeight\n          });\n          _this2.sizing = false;\n          _this2.sized = true;\n\n          _this2.build();\n        }; // Most modern browsers (excepts iOS WebKit)\n\n\n        if (image.naturalWidth && !isIOSWebKit) {\n          done(image.naturalWidth, image.naturalHeight);\n          return;\n        }\n\n        var sizingImage = document.createElement('img');\n        var body = document.body || document.documentElement;\n        this.sizingImage = sizingImage;\n\n        sizingImage.onload = function () {\n          done(sizingImage.width, sizingImage.height);\n\n          if (!isIOSWebKit) {\n            body.removeChild(sizingImage);\n          }\n        };\n\n        sizingImage.src = image.src; // iOS WebKit will convert the image automatically\n        // with its orientation once append it into DOM (#279)\n\n        if (!isIOSWebKit) {\n          sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';\n          body.appendChild(sizingImage);\n        }\n      }\n    }, {\n      key: \"stop\",\n      value: function stop() {\n        var image = this.image;\n        image.onload = null;\n        image.onerror = null;\n        image.parentNode.removeChild(image);\n        this.image = null;\n      }\n    }, {\n      key: \"build\",\n      value: function build() {\n        if (!this.sized || this.ready) {\n          return;\n        }\n\n        var element = this.element,\n            options = this.options,\n            image = this.image; // Create cropper elements\n\n        var container = element.parentNode;\n        var template = document.createElement('div');\n        template.innerHTML = TEMPLATE;\n        var cropper = template.querySelector(\".\".concat(NAMESPACE, \"-container\"));\n        var canvas = cropper.querySelector(\".\".concat(NAMESPACE, \"-canvas\"));\n        var dragBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-drag-box\"));\n        var cropBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-crop-box\"));\n        var face = cropBox.querySelector(\".\".concat(NAMESPACE, \"-face\"));\n        this.container = container;\n        this.cropper = cropper;\n        this.canvas = canvas;\n        this.dragBox = dragBox;\n        this.cropBox = cropBox;\n        this.viewBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-view-box\"));\n        this.face = face;\n        canvas.appendChild(image); // Hide the original image\n\n        addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image\n\n        container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden\n\n        if (!this.isImg) {\n          removeClass(image, CLASS_HIDE);\n        }\n\n        this.initPreview();\n        this.bind();\n        options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;\n        options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;\n        options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;\n        addClass(cropBox, CLASS_HIDDEN);\n\n        if (!options.guides) {\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-dashed\")), CLASS_HIDDEN);\n        }\n\n        if (!options.center) {\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-center\")), CLASS_HIDDEN);\n        }\n\n        if (options.background) {\n          addClass(cropper, \"\".concat(NAMESPACE, \"-bg\"));\n        }\n\n        if (!options.highlight) {\n          addClass(face, CLASS_INVISIBLE);\n        }\n\n        if (options.cropBoxMovable) {\n          addClass(face, CLASS_MOVE);\n          setData(face, DATA_ACTION, ACTION_ALL);\n        }\n\n        if (!options.cropBoxResizable) {\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-line\")), CLASS_HIDDEN);\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-point\")), CLASS_HIDDEN);\n        }\n\n        this.render();\n        this.ready = true;\n        this.setDragMode(options.dragMode);\n\n        if (options.autoCrop) {\n          this.crop();\n        }\n\n        this.setData(options.data);\n\n        if (isFunction(options.ready)) {\n          addListener(element, EVENT_READY, options.ready, {\n            once: true\n          });\n        }\n\n        dispatchEvent(element, EVENT_READY);\n      }\n    }, {\n      key: \"unbuild\",\n      value: function unbuild() {\n        if (!this.ready) {\n          return;\n        }\n\n        this.ready = false;\n        this.unbind();\n        this.resetPreview();\n        this.cropper.parentNode.removeChild(this.cropper);\n        removeClass(this.element, CLASS_HIDDEN);\n      }\n    }, {\n      key: \"uncreate\",\n      value: function uncreate() {\n        if (this.ready) {\n          this.unbuild();\n          this.ready = false;\n          this.cropped = false;\n        } else if (this.sizing) {\n          this.sizingImage.onload = null;\n          this.sizing = false;\n          this.sized = false;\n        } else if (this.reloading) {\n          this.xhr.onabort = null;\n          this.xhr.abort();\n        } else if (this.image) {\n          this.stop();\n        }\n      }\n      /**\n       * Get the no conflict cropper class.\n       * @returns {Cropper} The cropper class.\n       */\n\n    }], [{\n      key: \"noConflict\",\n      value: function noConflict() {\n        window.Cropper = AnotherCropper;\n        return Cropper;\n      }\n      /**\n       * Change the default options.\n       * @param {Object} options - The new default options.\n       */\n\n    }, {\n      key: \"setDefaults\",\n      value: function setDefaults(options) {\n        assign(DEFAULTS, isPlainObject(options) && options);\n      }\n    }]);\n\n    return Cropper;\n  }();\n\n  assign(Cropper.prototype, render, preview, events, handlers, change, methods);\n\n  if ($.fn) {\n    var AnotherCropper$1 = $.fn.cropper;\n    var NAMESPACE$1 = 'cropper';\n\n    $.fn.cropper = function jQueryCropper(option) {\n      for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n        args[_key - 1] = arguments[_key];\n      }\n\n      var result;\n      this.each(function (i, element) {\n        var $element = $(element);\n        var isDestroy = option === 'destroy';\n        var cropper = $element.data(NAMESPACE$1);\n\n        if (!cropper) {\n          if (isDestroy) {\n            return;\n          }\n\n          var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);\n          cropper = new Cropper(element, options);\n          $element.data(NAMESPACE$1, cropper);\n        }\n\n        if (typeof option === 'string') {\n          var fn = cropper[option];\n\n          if ($.isFunction(fn)) {\n            result = fn.apply(cropper, args);\n\n            if (result === cropper) {\n              result = undefined;\n            }\n\n            if (isDestroy) {\n              $element.removeData(NAMESPACE$1);\n            }\n          }\n        }\n      });\n      return result !== undefined ? result : this;\n    };\n\n    $.fn.cropper.Constructor = Cropper;\n    $.fn.cropper.setDefaults = Cropper.setDefaults;\n\n    $.fn.cropper.noConflict = function noConflict() {\n      $.fn.cropper = AnotherCropper$1;\n      return this;\n    };\n  }\n\n}));\n"
  },
  {
    "path": "docs/css/cropper.css",
    "content": "/*!\n * Cropper v4.1.0\n * https://fengyuanchen.github.io/cropper\n *\n * Copyright 2014-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2019-10-12T07:43:47.783Z\n */\n\n.cropper-container {\n  direction: ltr;\n  font-size: 0;\n  line-height: 0;\n  position: relative;\n  -ms-touch-action: none;\n  touch-action: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.cropper-container img {\n  display: block;\n  height: 100%;\n  image-orientation: 0deg;\n  max-height: none !important;\n  max-width: none !important;\n  min-height: 0 !important;\n  min-width: 0 !important;\n  width: 100%;\n}\n\n.cropper-wrap-box,\n.cropper-canvas,\n.cropper-drag-box,\n.cropper-crop-box,\n.cropper-modal {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n\n.cropper-wrap-box,\n.cropper-canvas {\n  overflow: hidden;\n}\n\n.cropper-drag-box {\n  background-color: #fff;\n  opacity: 0;\n}\n\n.cropper-modal {\n  background-color: #000;\n  opacity: 0.5;\n}\n\n.cropper-view-box {\n  display: block;\n  height: 100%;\n  outline: 1px solid #39f;\n  outline-color: rgba(51, 153, 255, 0.75);\n  overflow: hidden;\n  width: 100%;\n}\n\n.cropper-dashed {\n  border: 0 dashed #eee;\n  display: block;\n  opacity: 0.5;\n  position: absolute;\n}\n\n.cropper-dashed.dashed-h {\n  border-bottom-width: 1px;\n  border-top-width: 1px;\n  height: calc(100% / 3);\n  left: 0;\n  top: calc(100% / 3);\n  width: 100%;\n}\n\n.cropper-dashed.dashed-v {\n  border-left-width: 1px;\n  border-right-width: 1px;\n  height: 100%;\n  left: calc(100% / 3);\n  top: 0;\n  width: calc(100% / 3);\n}\n\n.cropper-center {\n  display: block;\n  height: 0;\n  left: 50%;\n  opacity: 0.75;\n  position: absolute;\n  top: 50%;\n  width: 0;\n}\n\n.cropper-center::before,\n.cropper-center::after {\n  background-color: #eee;\n  content: ' ';\n  display: block;\n  position: absolute;\n}\n\n.cropper-center::before {\n  height: 1px;\n  left: -3px;\n  top: 0;\n  width: 7px;\n}\n\n.cropper-center::after {\n  height: 7px;\n  left: 0;\n  top: -3px;\n  width: 1px;\n}\n\n.cropper-face,\n.cropper-line,\n.cropper-point {\n  display: block;\n  height: 100%;\n  opacity: 0.1;\n  position: absolute;\n  width: 100%;\n}\n\n.cropper-face {\n  background-color: #fff;\n  left: 0;\n  top: 0;\n}\n\n.cropper-line {\n  background-color: #39f;\n}\n\n.cropper-line.line-e {\n  cursor: ew-resize;\n  right: -3px;\n  top: 0;\n  width: 5px;\n}\n\n.cropper-line.line-n {\n  cursor: ns-resize;\n  height: 5px;\n  left: 0;\n  top: -3px;\n}\n\n.cropper-line.line-w {\n  cursor: ew-resize;\n  left: -3px;\n  top: 0;\n  width: 5px;\n}\n\n.cropper-line.line-s {\n  bottom: -3px;\n  cursor: ns-resize;\n  height: 5px;\n  left: 0;\n}\n\n.cropper-point {\n  background-color: #39f;\n  height: 5px;\n  opacity: 0.75;\n  width: 5px;\n}\n\n.cropper-point.point-e {\n  cursor: ew-resize;\n  margin-top: -3px;\n  right: -3px;\n  top: 50%;\n}\n\n.cropper-point.point-n {\n  cursor: ns-resize;\n  left: 50%;\n  margin-left: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-w {\n  cursor: ew-resize;\n  left: -3px;\n  margin-top: -3px;\n  top: 50%;\n}\n\n.cropper-point.point-s {\n  bottom: -3px;\n  cursor: s-resize;\n  left: 50%;\n  margin-left: -3px;\n}\n\n.cropper-point.point-ne {\n  cursor: nesw-resize;\n  right: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-nw {\n  cursor: nwse-resize;\n  left: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-sw {\n  bottom: -3px;\n  cursor: nesw-resize;\n  left: -3px;\n}\n\n.cropper-point.point-se {\n  bottom: -3px;\n  cursor: nwse-resize;\n  height: 20px;\n  opacity: 1;\n  right: -3px;\n  width: 20px;\n}\n\n@media (min-width: 768px) {\n  .cropper-point.point-se {\n    height: 15px;\n    width: 15px;\n  }\n}\n\n@media (min-width: 992px) {\n  .cropper-point.point-se {\n    height: 10px;\n    width: 10px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .cropper-point.point-se {\n    height: 5px;\n    opacity: 0.75;\n    width: 5px;\n  }\n}\n\n.cropper-point.point-se::before {\n  background-color: #39f;\n  bottom: -50%;\n  content: ' ';\n  display: block;\n  height: 200%;\n  opacity: 0;\n  position: absolute;\n  right: -50%;\n  width: 200%;\n}\n\n.cropper-invisible {\n  opacity: 0;\n}\n\n.cropper-bg {\n  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');\n}\n\n.cropper-hide {\n  display: block;\n  height: 0;\n  position: absolute;\n  width: 0;\n}\n\n.cropper-hidden {\n  display: none !important;\n}\n\n.cropper-move {\n  cursor: move;\n}\n\n.cropper-crop {\n  cursor: crosshair;\n}\n\n.cropper-disabled .cropper-drag-box,\n.cropper-disabled .cropper-face,\n.cropper-disabled .cropper-line,\n.cropper-disabled .cropper-point {\n  cursor: not-allowed;\n}\n"
  },
  {
    "path": "docs/css/main.css",
    "content": ".btn {\n  padding-left: 0.75rem;\n  padding-right: 0.75rem;\n}\n\nlabel.btn {\n  margin-bottom: 0;\n}\n\n.d-flex > .btn {\n  flex: 1;\n}\n\n.carbonads {\n  border: 1px solid #ccc;\n  border-radius: 0.25rem;\n  font-size: 0.875rem;\n  overflow: hidden;\n  padding: 1rem;\n}\n\n.carbon-wrap {\n  overflow: hidden;\n}\n\n.carbon-img {\n  clear: left;\n  display: block;\n  float: left;\n}\n\n.carbon-text,\n.carbon-poweredby {\n  display: block;\n  margin-left: 140px;\n}\n\n.carbon-text,\n.carbon-text:hover,\n.carbon-text:focus {\n  color: #fff;\n  text-decoration: none;\n}\n\n.carbon-poweredby,\n.carbon-poweredby:hover,\n.carbon-poweredby:focus {\n  color: #ddd;\n  text-decoration: none;\n}\n\n@media (min-width: 768px) {\n  .carbonads {\n    float: right;\n    margin-bottom: -1rem;\n    margin-top: -1rem;\n    max-width: 360px;\n  }\n}\n\n.footer {\n  font-size: 0.875rem;\n}\n\n.heart {\n  color: #ddd;\n  display: block;\n  height: 2rem;\n  line-height: 2rem;\n  margin-bottom: 0;\n  margin-top: 1rem;\n  position: relative;\n  text-align: center;\n  width: 100%;\n}\n\n.heart:hover {\n  color: #ff4136;\n}\n\n.heart::before {\n  border-top: 1px solid #eee;\n  content: \" \";\n  display: block;\n  height: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 50%;\n}\n\n.heart::after {\n  background-color: #fff;\n  content: \"♥\";\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n  position: relative;\n  z-index: 1;\n}\n\n.img-container,\n.img-preview {\n  background-color: #f7f7f7;\n  text-align: center;\n  width: 100%;\n}\n\n.img-container {\n  margin-bottom: 1rem;\n  max-height: 497px;\n  min-height: 200px;\n}\n\n@media (min-width: 768px) {\n  .img-container {\n    min-height: 497px;\n  }\n}\n\n.img-container > img {\n  max-width: 100%;\n}\n\n.docs-preview {\n  margin-right: -1rem;\n}\n\n.img-preview {\n  float: left;\n  margin-bottom: 0.5rem;\n  margin-right: 0.5rem;\n  overflow: hidden;\n}\n\n.img-preview > img {\n  max-width: 100%;\n}\n\n.preview-lg {\n  height: 9rem;\n  width: 16rem;\n}\n\n.preview-md {\n  height: 4.5rem;\n  width: 8rem;\n}\n\n.preview-sm {\n  height: 2.25rem;\n  width: 4rem;\n}\n\n.preview-xs {\n  height: 1.125rem;\n  margin-right: 0;\n  width: 2rem;\n}\n\n.docs-data > .input-group {\n  margin-bottom: 0.5rem;\n}\n\n.docs-data .input-group-prepend .input-group-text {\n  min-width: 4rem;\n}\n\n.docs-data .input-group-append .input-group-text {\n  min-width: 3rem;\n}\n\n.docs-buttons > .btn,\n.docs-buttons > .btn-group,\n.docs-buttons > .form-control {\n  margin-bottom: 0.5rem;\n  margin-right: 0.25rem;\n}\n\n.docs-toggles > .btn,\n.docs-toggles > .btn-group,\n.docs-toggles > .dropdown {\n  margin-bottom: 0.5rem;\n}\n\n.docs-tooltip {\n  display: block;\n  margin: -0.5rem -0.75rem;\n  padding: 0.5rem 0.75rem;\n}\n\n.docs-tooltip > .icon {\n  margin: 0 -0.25rem;\n  vertical-align: top;\n}\n\n.tooltip-inner {\n  white-space: normal;\n}\n\n.btn-upload .tooltip-inner,\n.btn-toggle .tooltip-inner {\n  white-space: nowrap;\n}\n\n.btn-toggle {\n  padding: 0.5rem;\n}\n\n.btn-toggle > .docs-tooltip {\n  margin: -0.5rem;\n  padding: 0.5rem;\n}\n\n@media (max-width: 400px) {\n  .btn-group-crop {\n    margin-right: -1rem !important;\n  }\n\n  .btn-group-crop > .btn {\n    padding-left: 0.5rem;\n    padding-right: 0.5rem;\n  }\n\n  .btn-group-crop .docs-tooltip {\n    margin-left: -0.5rem;\n    margin-right: -0.5rem;\n    padding-left: 0.5rem;\n    padding-right: 0.5rem;\n  }\n}\n\n.docs-options .dropdown-menu {\n  width: 100%;\n}\n\n.docs-options .dropdown-menu > li {\n  font-size: 0.875rem;\n  padding: 0.125rem 1rem;\n}\n\n.docs-options .dropdown-menu .form-check-label {\n  display: block;\n}\n\n.docs-cropped .modal-body {\n  text-align: center;\n}\n\n.docs-cropped .modal-body > img,\n.docs-cropped .modal-body > canvas {\n  max-width: 100%;\n}\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n  <meta name=\"description\" content=\"A simple jQuery image cropping plugin.\">\n  <meta name=\"author\" content=\"Chen Fengyuan\">\n  <title>Cropper</title>\n  <link rel=\"shortcut icon\" href=\"favicon.ico\">\n  <link rel=\"icon\" href=\"favicon.ico\">\n  <link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.11.2/css/all.css\" crossorigin=\"anonymous\">\n  <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\" crossorigin=\"anonymous\">\n  <link rel=\"stylesheet\" href=\"css/cropper.css\">\n  <link rel=\"stylesheet\" href=\"css/main.css\">\n</head>\n<body>\n  <!--[if lt IE 9]>\n  <div class=\"alert alert-warning alert-dismissible fade show m-0 rounded-0\" role=\"alert\">\n    You are using an <strong>outdated</strong> browser. Please <a href=\"https://browsehappy.com/\">upgrade your browser</a> to improve your experience.\n    <button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\">\n      <span aria-hidden=\"true\">&times;</span>\n    </button>\n  </div>\n  <![endif]-->\n\n  <!-- Header -->\n  <header class=\"navbar navbar-light navbar-expand-md\">\n    <div class=\"container\">\n      <a class=\"navbar-brand\" href=\"./\">Cropper</a>\n      <button class=\"navbar-toggler\" type=\"button\" data-toggle=\"collapse\" data-target=\"#navbar-collapse\" aria-controls=\"navbar-collapse\" aria-expanded=\"false\" aria-label=\"Toggle navigation\">\n        <span class=\"navbar-toggler-icon\"></span>\n      </button>\n      <div class=\"collapse navbar-collapse justify-content-end\" id=\"navbar-collapse\" role=\"navigation\">\n        <nav class=\"nav navbar-nav\">\n          <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper/blob/master/README.md\" data-toggle=\"tooltip\" title=\"View the documentation\">Docs</a>\n          <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper\" data-toggle=\"tooltip\" title=\"View the GitHub project\">GitHub</a>\n          <a class=\"nav-link\" href=\"https://fengyuanchen.github.io/cropperjs\" data-toggle=\"tooltip\" title=\"the non-jQuery version of Cropper (recommended)\">Cropper.js</a>\n          <a class=\"nav-link\" href=\"https://fengyuanchen.github.io/\" data-toggle=\"tooltip\" title=\"Explore more projects\">Explore</a>\n          <a class=\"nav-link\" href=\"https://chenfengyuan.com/\" data-toggle=\"tooltip\" title=\"About the author\">About</a>\n        </nav>\n      </div>\n    </div>\n  </header>\n\n  <!-- Jumbotron -->\n  <div class=\"jumbotron bg-primary text-white rounded-0\">\n    <div class=\"container\">\n      <div class=\"row\">\n        <div class=\"col-md\">\n          <h1>Cropper <small class=\"h6\">v4.1.0</small></h1>\n          <p class=\"lead\">A simple jQuery image cropping plugin.</p>\n        </div>\n        <div class=\"col-md\">\n          <div class=\"carbonads\">\n            <script id=\"_carbonads_js\" src=\"https://cdn.carbonads.com/carbon.js?serve=CKYI55Q7&placement=fengyuanchengithubio\" async></script>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <!-- Content -->\n  <div class=\"container\">\n    <div class=\"alert alert-warning alert-dismissible fade show\" role=\"alert\">\n        As of v4.0.0, the core code of Cropper is replaced with <a class=\"alert-link\" href=\"https://github.com/fengyuanchen/cropperjs\">Cropper.js</a>. I recommend you to use the <a class=\"alert-link\" href=\"https://github.com/fengyuanchen/jquery-cropper\">jquery-cropper</a> instead of Cropper.\n      <button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\">\n        <span aria-hidden=\"true\">&times;</span>\n      </button>\n    </div>\n    <div class=\"row\">\n      <div class=\"col-md-9\">\n        <!-- <h3>Demo:</h3> -->\n        <div class=\"img-container\">\n          <img id=\"image\" src=\"images/picture.jpg\" alt=\"Picture\">\n        </div>\n      </div>\n      <div class=\"col-md-3\">\n        <!-- <h3>Preview:</h3> -->\n        <div class=\"docs-preview clearfix\">\n          <div class=\"img-preview preview-lg\"></div>\n          <div class=\"img-preview preview-md\"></div>\n          <div class=\"img-preview preview-sm\"></div>\n          <div class=\"img-preview preview-xs\"></div>\n        </div>\n\n        <!-- <h3>Data:</h3> -->\n        <div class=\"docs-data\">\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataX\">X</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataX\" placeholder=\"x\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataY\">Y</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataY\" placeholder=\"y\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataWidth\">Width</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataWidth\" placeholder=\"width\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataHeight\">Height</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataHeight\" placeholder=\"height\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataRotate\">Rotate</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataRotate\" placeholder=\"rotate\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">deg</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataScaleX\">ScaleX</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataScaleX\" placeholder=\"scaleX\">\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataScaleY\">ScaleY</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataScaleY\" placeholder=\"scaleY\">\n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"row\">\n      <div class=\"col-md-9 docs-buttons\">\n        <!-- <h3>Toolbar:</h3> -->\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"setDragMode\" data-option=\"move\" title=\"Move\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setDragMode&quot;, &quot;move&quot;)\">\n              <span class=\"fa fa-arrows-alt\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"setDragMode\" data-option=\"crop\" title=\"Crop\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setDragMode&quot;, &quot;crop&quot;)\">\n              <span class=\"fa fa-crop-alt\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoom\" data-option=\"0.1\" title=\"Zoom In\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;zoom&quot;, 0.1)\">\n              <span class=\"fa fa-search-plus\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoom\" data-option=\"-0.1\" title=\"Zoom Out\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;zoom&quot;, -0.1)\">\n              <span class=\"fa fa-search-minus\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"-10\" data-second-option=\"0\" title=\"Move Left\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, -10, 0)\">\n              <span class=\"fa fa-arrow-left\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"10\" data-second-option=\"0\" title=\"Move Right\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, 10, 0)\">\n              <span class=\"fa fa-arrow-right\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"0\" data-second-option=\"-10\" title=\"Move Up\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, 0, -10)\">\n              <span class=\"fa fa-arrow-up\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"0\" data-second-option=\"10\" title=\"Move Down\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, 0, 10)\">\n              <span class=\"fa fa-arrow-down\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotate\" data-option=\"-45\" title=\"Rotate Left\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;rotate&quot;, -45)\">\n              <span class=\"fa fa-undo-alt\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotate\" data-option=\"45\" title=\"Rotate Right\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;rotate&quot;, 45)\">\n              <span class=\"fa fa-redo-alt\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"scaleX\" data-option=\"-1\" title=\"Flip Horizontal\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;scaleX&quot;, -1)\">\n              <span class=\"fa fa-arrows-alt-h\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"scaleY\" data-option=\"-1\" title=\"Flip Vertical\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;scaleY&quot;, -1)\">\n              <span class=\"fa fa-arrows-alt-v\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"crop\" title=\"Crop\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;crop&quot;)\">\n              <span class=\"fa fa-check\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"clear\" title=\"Clear\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;clear&quot;)\">\n              <span class=\"fa fa-times\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"disable\" title=\"Disable\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;disable&quot;)\">\n              <span class=\"fa fa-lock\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"enable\" title=\"Enable\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;enable&quot;)\">\n              <span class=\"fa fa-unlock\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"reset\" title=\"Reset\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;reset&quot;)\">\n              <span class=\"fa fa-sync-alt\"></span>\n            </span>\n          </button>\n          <label class=\"btn btn-primary btn-upload\" for=\"inputImage\" title=\"Upload image file\">\n            <input type=\"file\" class=\"sr-only\" id=\"inputImage\" name=\"file\" accept=\".jpg,.jpeg,.png,.gif,.bmp,.tiff\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"Import image with Blob URLs\">\n              <span class=\"fa fa-upload\"></span>\n            </span>\n          </label>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"destroy\" title=\"Destroy\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;destroy&quot;)\">\n              <span class=\"fa fa-power-off\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group btn-group-crop\">\n          <button type=\"button\" class=\"btn btn-success\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;maxWidth&quot;: 4096, &quot;maxHeight&quot;: 4096 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { maxWidth: 4096, maxHeight: 4096 })\">\n              Get Cropped Canvas\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-success\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;width&quot;: 160, &quot;height&quot;: 90 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { width: 160, height: 90 })\">\n              160&times;90\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-success\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;width&quot;: 320, &quot;height&quot;: 180 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { width: 320, height: 180 })\">\n              320&times;180\n            </span>\n          </button>\n        </div>\n\n        <!-- Show the cropped image in modal -->\n        <div class=\"modal fade docs-cropped\" id=\"getCroppedCanvasModal\" aria-hidden=\"true\" aria-labelledby=\"getCroppedCanvasTitle\" role=\"dialog\" tabindex=\"-1\">\n          <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n              <div class=\"modal-header\">\n                <h5 class=\"modal-title\" id=\"getCroppedCanvasTitle\">Cropped</h5>\n                <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n                  <span aria-hidden=\"true\">&times;</span>\n                </button>\n              </div>\n              <div class=\"modal-body\"></div>\n              <div class=\"modal-footer\">\n                <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button>\n                <a class=\"btn btn-primary\" id=\"download\" href=\"javascript:void(0);\" download=\"cropped.jpg\">Download</a>\n              </div>\n            </div>\n          </div>\n        </div><!-- /.modal -->\n\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getData&quot;)\">\n            Get Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"setData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setData&quot;, data)\">\n            Set Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getContainerData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getContainerData&quot;)\">\n            Get Container Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getImageData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getImageData&quot;)\">\n            Get Image Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getCanvasData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCanvasData&quot;)\">\n            Get Canvas Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"setCanvasData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setCanvasData&quot;, data)\">\n            Set Canvas Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getCropBoxData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCropBoxData&quot;)\">\n            Get Crop Box Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"setCropBoxData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setCropBoxData&quot;, data)\">\n            Set Crop Box Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"moveTo\" data-option=\"0\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"cropper.moveTo(0)\">\n            Move to [0,0]\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"zoomTo\" data-option=\"1\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"cropper.zoomTo(1)\">\n            Zoom to 100%\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"rotateTo\" data-option=\"180\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"cropper.rotateTo(180)\">\n            Rotate 180°\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"scale\" data-option=\"-2\" data-second-option=\"-1\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"cropper.scale(-2, -1)\">\n            Scale (-2, -1)\n          </span>\n        </button>\n        <textarea type=\"text\" class=\"form-control\" id=\"putData\" placeholder=\"Get data to here or set data with this value\"></textarea>\n      </div><!-- /.docs-buttons -->\n\n      <div class=\"col-md-3 docs-toggles\">\n        <!-- <h3>Toggles:</h3> -->\n        <div class=\"btn-group d-flex flex-nowrap\" data-toggle=\"buttons\">\n          <label class=\"btn btn-primary active\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio0\" name=\"aspectRatio\" value=\"1.7777777777777777\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 16 / 9\">\n              16:9\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio1\" name=\"aspectRatio\" value=\"1.3333333333333333\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 4 / 3\">\n              4:3\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio2\" name=\"aspectRatio\" value=\"1\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 1 / 1\">\n              1:1\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio3\" name=\"aspectRatio\" value=\"0.6666666666666666\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 2 / 3\">\n              2:3\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio4\" name=\"aspectRatio\" value=\"NaN\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: NaN\">\n              Free\n            </span>\n          </label>\n        </div>\n\n        <div class=\"btn-group d-flex flex-nowrap\" data-toggle=\"buttons\">\n          <label class=\"btn btn-primary active\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode0\" name=\"viewMode\" value=\"0\" checked>\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 0\">\n              VM0\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode1\" name=\"viewMode\" value=\"1\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 1\">\n              VM1\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode2\" name=\"viewMode\" value=\"2\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 2\">\n              VM2\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode3\" name=\"viewMode\" value=\"3\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 3\">\n              VM3\n            </span>\n          </label>\n        </div>\n\n        <div class=\"dropdown dropup docs-options\">\n          <button type=\"button\" class=\"btn btn-primary btn-block dropdown-toggle\" id=\"toggleOptions\" data-toggle=\"dropdown\" aria-expanded=\"true\">\n            Toggle Options\n            <span class=\"caret\"></span>\n          </button>\n          <ul class=\"dropdown-menu\" aria-labelledby=\"toggleOptions\" role=\"menu\">\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"responsive\" type=\"checkbox\" name=\"responsive\" checked>\n                <label class=\"form-check-label\" for=\"responsive\">responsive</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"restore\" type=\"checkbox\" name=\"restore\" checked>\n                <label class=\"form-check-label\" for=\"restore\">restore</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"checkCrossOrigin\" type=\"checkbox\" name=\"checkCrossOrigin\" checked>\n                <label class=\"form-check-label\" for=\"checkCrossOrigin\">checkCrossOrigin</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"checkOrientation\" type=\"checkbox\" name=\"checkOrientation\" checked>\n                <label class=\"form-check-label\" for=\"checkOrientation\">checkOrientation</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"modal\" type=\"checkbox\" name=\"modal\" checked>\n                <label class=\"form-check-label\" for=\"modal\">modal</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"guides\" type=\"checkbox\" name=\"guides\" checked>\n                <label class=\"form-check-label\" for=\"guides\">guides</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"center\" type=\"checkbox\" name=\"center\" checked>\n                <label class=\"form-check-label\" for=\"center\">center</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"highlight\" type=\"checkbox\" name=\"highlight\" checked>\n                <label class=\"form-check-label\" for=\"highlight\">highlight</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"background\" type=\"checkbox\" name=\"background\" checked>\n                <label class=\"form-check-label\" for=\"background\">background</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"autoCrop\" type=\"checkbox\" name=\"autoCrop\" checked>\n                <label class=\"form-check-label\" for=\"autoCrop\">autoCrop</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"movable\" type=\"checkbox\" name=\"movable\" checked>\n                <label class=\"form-check-label\" for=\"movable\">movable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"rotatable\" type=\"checkbox\" name=\"rotatable\" checked>\n                <label class=\"form-check-label\" for=\"rotatable\">rotatable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"scalable\" type=\"checkbox\" name=\"scalable\" checked>\n                <label class=\"form-check-label\" for=\"scalable\">scalable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"zoomable\" type=\"checkbox\" name=\"zoomable\" checked>\n                <label class=\"form-check-label\" for=\"zoomable\">zoomable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"zoomOnTouch\" type=\"checkbox\" name=\"zoomOnTouch\" checked>\n                <label class=\"form-check-label\" for=\"zoomOnTouch\">zoomOnTouch</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"zoomOnWheel\" type=\"checkbox\" name=\"zoomOnWheel\" checked>\n                <label class=\"form-check-label\" for=\"zoomOnWheel\">zoomOnWheel</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"cropBoxMovable\" type=\"checkbox\" name=\"cropBoxMovable\" checked>\n                <label class=\"form-check-label\" for=\"cropBoxMovable\">cropBoxMovable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"cropBoxResizable\" type=\"checkbox\" name=\"cropBoxResizable\" checked>\n                <label class=\"form-check-label\" for=\"cropBoxResizable\">cropBoxResizable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"toggleDragModeOnDblclick\" type=\"checkbox\" name=\"toggleDragModeOnDblclick\" checked>\n                <label class=\"form-check-label\" for=\"toggleDragModeOnDblclick\">toggleDragModeOnDblclick</label>\n              </div>\n            </li>\n          </ul>\n        </div><!-- /.dropdown -->\n\n        <a class=\"btn btn-success btn-block\" data-toggle=\"tooltip\" data-animation=\"false\" href=\"https://fengyuanchen.github.io/cropperjs\" title=\"The non-jQuery version of Cropper (recommended)\">Cropper.js</a>\n\n      </div><!-- /.docs-toggles -->\n    </div>\n  </div>\n\n  <!-- Footer -->\n  <footer class=\"footer\">\n    <div class=\"container\">\n      <p class=\"heart\"></p>\n      <nav class=\"nav flex-wrap justify-content-center mb-3\">\n        <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper\">GitHub</a>\n        <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper/blob/master/LICENSE\">License</a>\n        <a class=\"nav-link\" href=\"https://chenfengyuan.com/\">About</a>\n        <a class=\"nav-link\" href=\"v2.3.4\">v2.3.4</a>\n        <a class=\"nav-link\" href=\"v3.1.6\">v3.1.6</a>\n      </nav>\n    </div>\n  </footer>\n\n  <!-- Scripts -->\n  <script src=\"https://code.jquery.com/jquery-3.4.1.slim.min.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"https://fengyuanchen.github.io/shared/google-analytics.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"js/cropper.js\"></script>\n  <script src=\"js/main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "docs/js/cropper.js",
    "content": "/*!\n * Cropper v4.1.0\n * https://fengyuanchen.github.io/cropper\n *\n * Copyright 2014-present Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2019-10-12T07:43:51.850Z\n */\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :\n  typeof define === 'function' && define.amd ? define(['jquery'], factory) :\n  (global = global || self, factory(global.jQuery));\n}(this, function ($) { 'use strict';\n\n  $ = $ && $.hasOwnProperty('default') ? $['default'] : $;\n\n  function _typeof(obj) {\n    if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n      _typeof = function (obj) {\n        return typeof obj;\n      };\n    } else {\n      _typeof = function (obj) {\n        return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n      };\n    }\n\n    return _typeof(obj);\n  }\n\n  function _classCallCheck(instance, Constructor) {\n    if (!(instance instanceof Constructor)) {\n      throw new TypeError(\"Cannot call a class as a function\");\n    }\n  }\n\n  function _defineProperties(target, props) {\n    for (var i = 0; i < props.length; i++) {\n      var descriptor = props[i];\n      descriptor.enumerable = descriptor.enumerable || false;\n      descriptor.configurable = true;\n      if (\"value\" in descriptor) descriptor.writable = true;\n      Object.defineProperty(target, descriptor.key, descriptor);\n    }\n  }\n\n  function _createClass(Constructor, protoProps, staticProps) {\n    if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n    if (staticProps) _defineProperties(Constructor, staticProps);\n    return Constructor;\n  }\n\n  function _defineProperty(obj, key, value) {\n    if (key in obj) {\n      Object.defineProperty(obj, key, {\n        value: value,\n        enumerable: true,\n        configurable: true,\n        writable: true\n      });\n    } else {\n      obj[key] = value;\n    }\n\n    return obj;\n  }\n\n  function ownKeys(object, enumerableOnly) {\n    var keys = Object.keys(object);\n\n    if (Object.getOwnPropertySymbols) {\n      var symbols = Object.getOwnPropertySymbols(object);\n      if (enumerableOnly) symbols = symbols.filter(function (sym) {\n        return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n      });\n      keys.push.apply(keys, symbols);\n    }\n\n    return keys;\n  }\n\n  function _objectSpread2(target) {\n    for (var i = 1; i < arguments.length; i++) {\n      var source = arguments[i] != null ? arguments[i] : {};\n\n      if (i % 2) {\n        ownKeys(source, true).forEach(function (key) {\n          _defineProperty(target, key, source[key]);\n        });\n      } else if (Object.getOwnPropertyDescriptors) {\n        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n      } else {\n        ownKeys(source).forEach(function (key) {\n          Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));\n        });\n      }\n    }\n\n    return target;\n  }\n\n  function _toConsumableArray(arr) {\n    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n  }\n\n  function _arrayWithoutHoles(arr) {\n    if (Array.isArray(arr)) {\n      for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n      return arr2;\n    }\n  }\n\n  function _iterableToArray(iter) {\n    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter);\n  }\n\n  function _nonIterableSpread() {\n    throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n  }\n\n  var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';\n  var WINDOW = IS_BROWSER ? window : {};\n  var IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false;\n  var HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;\n  var NAMESPACE = 'cropper'; // Actions\n\n  var ACTION_ALL = 'all';\n  var ACTION_CROP = 'crop';\n  var ACTION_MOVE = 'move';\n  var ACTION_ZOOM = 'zoom';\n  var ACTION_EAST = 'e';\n  var ACTION_WEST = 'w';\n  var ACTION_SOUTH = 's';\n  var ACTION_NORTH = 'n';\n  var ACTION_NORTH_EAST = 'ne';\n  var ACTION_NORTH_WEST = 'nw';\n  var ACTION_SOUTH_EAST = 'se';\n  var ACTION_SOUTH_WEST = 'sw'; // Classes\n\n  var CLASS_CROP = \"\".concat(NAMESPACE, \"-crop\");\n  var CLASS_DISABLED = \"\".concat(NAMESPACE, \"-disabled\");\n  var CLASS_HIDDEN = \"\".concat(NAMESPACE, \"-hidden\");\n  var CLASS_HIDE = \"\".concat(NAMESPACE, \"-hide\");\n  var CLASS_INVISIBLE = \"\".concat(NAMESPACE, \"-invisible\");\n  var CLASS_MODAL = \"\".concat(NAMESPACE, \"-modal\");\n  var CLASS_MOVE = \"\".concat(NAMESPACE, \"-move\"); // Data keys\n\n  var DATA_ACTION = \"\".concat(NAMESPACE, \"Action\");\n  var DATA_PREVIEW = \"\".concat(NAMESPACE, \"Preview\"); // Drag modes\n\n  var DRAG_MODE_CROP = 'crop';\n  var DRAG_MODE_MOVE = 'move';\n  var DRAG_MODE_NONE = 'none'; // Events\n\n  var EVENT_CROP = 'crop';\n  var EVENT_CROP_END = 'cropend';\n  var EVENT_CROP_MOVE = 'cropmove';\n  var EVENT_CROP_START = 'cropstart';\n  var EVENT_DBLCLICK = 'dblclick';\n  var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';\n  var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';\n  var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';\n  var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;\n  var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;\n  var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;\n  var EVENT_READY = 'ready';\n  var EVENT_RESIZE = 'resize';\n  var EVENT_WHEEL = 'wheel';\n  var EVENT_ZOOM = 'zoom'; // Mime types\n\n  var MIME_TYPE_JPEG = 'image/jpeg'; // RegExps\n\n  var REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;\n  var REGEXP_DATA_URL = /^data:/;\n  var REGEXP_DATA_URL_JPEG = /^data:image\\/jpeg;base64,/;\n  var REGEXP_TAG_NAME = /^img|canvas$/i; // Misc\n  // Inspired by the default width and height of a canvas element.\n\n  var MIN_CONTAINER_WIDTH = 200;\n  var MIN_CONTAINER_HEIGHT = 100;\n\n  var DEFAULTS = {\n    // Define the view mode of the cropper\n    viewMode: 0,\n    // 0, 1, 2, 3\n    // Define the dragging mode of the cropper\n    dragMode: DRAG_MODE_CROP,\n    // 'crop', 'move' or 'none'\n    // Define the initial aspect ratio of the crop box\n    initialAspectRatio: NaN,\n    // Define the aspect ratio of the crop box\n    aspectRatio: NaN,\n    // An object with the previous cropping result data\n    data: null,\n    // A selector for adding extra containers to preview\n    preview: '',\n    // Re-render the cropper when resize the window\n    responsive: true,\n    // Restore the cropped area after resize the window\n    restore: true,\n    // Check if the current image is a cross-origin image\n    checkCrossOrigin: true,\n    // Check the current image's Exif Orientation information\n    checkOrientation: true,\n    // Show the black modal\n    modal: true,\n    // Show the dashed lines for guiding\n    guides: true,\n    // Show the center indicator for guiding\n    center: true,\n    // Show the white modal to highlight the crop box\n    highlight: true,\n    // Show the grid background\n    background: true,\n    // Enable to crop the image automatically when initialize\n    autoCrop: true,\n    // Define the percentage of automatic cropping area when initializes\n    autoCropArea: 0.8,\n    // Enable to move the image\n    movable: true,\n    // Enable to rotate the image\n    rotatable: true,\n    // Enable to scale the image\n    scalable: true,\n    // Enable to zoom the image\n    zoomable: true,\n    // Enable to zoom the image by dragging touch\n    zoomOnTouch: true,\n    // Enable to zoom the image by wheeling mouse\n    zoomOnWheel: true,\n    // Define zoom ratio when zoom the image by wheeling mouse\n    wheelZoomRatio: 0.1,\n    // Enable to move the crop box\n    cropBoxMovable: true,\n    // Enable to resize the crop box\n    cropBoxResizable: true,\n    // Toggle drag mode between \"crop\" and \"move\" when click twice on the cropper\n    toggleDragModeOnDblclick: true,\n    // Size limitation\n    minCanvasWidth: 0,\n    minCanvasHeight: 0,\n    minCropBoxWidth: 0,\n    minCropBoxHeight: 0,\n    minContainerWidth: 200,\n    minContainerHeight: 100,\n    // Shortcuts of events\n    ready: null,\n    cropstart: null,\n    cropmove: null,\n    cropend: null,\n    crop: null,\n    zoom: null\n  };\n\n  var TEMPLATE = '<div class=\"cropper-container\" touch-action=\"none\">' + '<div class=\"cropper-wrap-box\">' + '<div class=\"cropper-canvas\"></div>' + '</div>' + '<div class=\"cropper-drag-box\"></div>' + '<div class=\"cropper-crop-box\">' + '<span class=\"cropper-view-box\"></span>' + '<span class=\"cropper-dashed dashed-h\"></span>' + '<span class=\"cropper-dashed dashed-v\"></span>' + '<span class=\"cropper-center\"></span>' + '<span class=\"cropper-face\"></span>' + '<span class=\"cropper-line line-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-line line-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-line line-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-line line-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-e\" data-cropper-action=\"e\"></span>' + '<span class=\"cropper-point point-n\" data-cropper-action=\"n\"></span>' + '<span class=\"cropper-point point-w\" data-cropper-action=\"w\"></span>' + '<span class=\"cropper-point point-s\" data-cropper-action=\"s\"></span>' + '<span class=\"cropper-point point-ne\" data-cropper-action=\"ne\"></span>' + '<span class=\"cropper-point point-nw\" data-cropper-action=\"nw\"></span>' + '<span class=\"cropper-point point-sw\" data-cropper-action=\"sw\"></span>' + '<span class=\"cropper-point point-se\" data-cropper-action=\"se\"></span>' + '</div>' + '</div>';\n\n  /**\n   * Check if the given value is not a number.\n   */\n\n  var isNaN = Number.isNaN || WINDOW.isNaN;\n  /**\n   * Check if the given value is a number.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a number, else `false`.\n   */\n\n  function isNumber(value) {\n    return typeof value === 'number' && !isNaN(value);\n  }\n  /**\n   * Check if the given value is a positive number.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a positive number, else `false`.\n   */\n\n  var isPositiveNumber = function isPositiveNumber(value) {\n    return value > 0 && value < Infinity;\n  };\n  /**\n   * Check if the given value is undefined.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is undefined, else `false`.\n   */\n\n  function isUndefined(value) {\n    return typeof value === 'undefined';\n  }\n  /**\n   * Check if the given value is an object.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is an object, else `false`.\n   */\n\n  function isObject(value) {\n    return _typeof(value) === 'object' && value !== null;\n  }\n  var hasOwnProperty = Object.prototype.hasOwnProperty;\n  /**\n   * Check if the given value is a plain object.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.\n   */\n\n  function isPlainObject(value) {\n    if (!isObject(value)) {\n      return false;\n    }\n\n    try {\n      var _constructor = value.constructor;\n      var prototype = _constructor.prototype;\n      return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');\n    } catch (error) {\n      return false;\n    }\n  }\n  /**\n   * Check if the given value is a function.\n   * @param {*} value - The value to check.\n   * @returns {boolean} Returns `true` if the given value is a function, else `false`.\n   */\n\n  function isFunction(value) {\n    return typeof value === 'function';\n  }\n  var slice = Array.prototype.slice;\n  /**\n   * Convert array-like or iterable object to an array.\n   * @param {*} value - The value to convert.\n   * @returns {Array} Returns a new array.\n   */\n\n  function toArray(value) {\n    return Array.from ? Array.from(value) : slice.call(value);\n  }\n  /**\n   * Iterate the given data.\n   * @param {*} data - The data to iterate.\n   * @param {Function} callback - The process function for each element.\n   * @returns {*} The original data.\n   */\n\n  function forEach(data, callback) {\n    if (data && isFunction(callback)) {\n      if (Array.isArray(data) || isNumber(data.length)\n      /* array-like */\n      ) {\n          toArray(data).forEach(function (value, key) {\n            callback.call(data, value, key, data);\n          });\n        } else if (isObject(data)) {\n        Object.keys(data).forEach(function (key) {\n          callback.call(data, data[key], key, data);\n        });\n      }\n    }\n\n    return data;\n  }\n  /**\n   * Extend the given object.\n   * @param {*} target - The target object to extend.\n   * @param {*} args - The rest objects for merging to the target object.\n   * @returns {Object} The extended object.\n   */\n\n  var assign = Object.assign || function assign(target) {\n    for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    if (isObject(target) && args.length > 0) {\n      args.forEach(function (arg) {\n        if (isObject(arg)) {\n          Object.keys(arg).forEach(function (key) {\n            target[key] = arg[key];\n          });\n        }\n      });\n    }\n\n    return target;\n  };\n  var REGEXP_DECIMALS = /\\.\\d*(?:0|9){12}\\d*$/;\n  /**\n   * Normalize decimal number.\n   * Check out {@link https://0.30000000000000004.com/}\n   * @param {number} value - The value to normalize.\n   * @param {number} [times=100000000000] - The times for normalizing.\n   * @returns {number} Returns the normalized number.\n   */\n\n  function normalizeDecimalNumber(value) {\n    var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;\n    return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;\n  }\n  var REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;\n  /**\n   * Apply styles to the given element.\n   * @param {Element} element - The target element.\n   * @param {Object} styles - The styles for applying.\n   */\n\n  function setStyle(element, styles) {\n    var style = element.style;\n    forEach(styles, function (value, property) {\n      if (REGEXP_SUFFIX.test(property) && isNumber(value)) {\n        value = \"\".concat(value, \"px\");\n      }\n\n      style[property] = value;\n    });\n  }\n  /**\n   * Check if the given element has a special class.\n   * @param {Element} element - The element to check.\n   * @param {string} value - The class to search.\n   * @returns {boolean} Returns `true` if the special class was found.\n   */\n\n  function hasClass(element, value) {\n    return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;\n  }\n  /**\n   * Add classes to the given element.\n   * @param {Element} element - The target element.\n   * @param {string} value - The classes to be added.\n   */\n\n  function addClass(element, value) {\n    if (!value) {\n      return;\n    }\n\n    if (isNumber(element.length)) {\n      forEach(element, function (elem) {\n        addClass(elem, value);\n      });\n      return;\n    }\n\n    if (element.classList) {\n      element.classList.add(value);\n      return;\n    }\n\n    var className = element.className.trim();\n\n    if (!className) {\n      element.className = value;\n    } else if (className.indexOf(value) < 0) {\n      element.className = \"\".concat(className, \" \").concat(value);\n    }\n  }\n  /**\n   * Remove classes from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} value - The classes to be removed.\n   */\n\n  function removeClass(element, value) {\n    if (!value) {\n      return;\n    }\n\n    if (isNumber(element.length)) {\n      forEach(element, function (elem) {\n        removeClass(elem, value);\n      });\n      return;\n    }\n\n    if (element.classList) {\n      element.classList.remove(value);\n      return;\n    }\n\n    if (element.className.indexOf(value) >= 0) {\n      element.className = element.className.replace(value, '');\n    }\n  }\n  /**\n   * Add or remove classes from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} value - The classes to be toggled.\n   * @param {boolean} added - Add only.\n   */\n\n  function toggleClass(element, value, added) {\n    if (!value) {\n      return;\n    }\n\n    if (isNumber(element.length)) {\n      forEach(element, function (elem) {\n        toggleClass(elem, value, added);\n      });\n      return;\n    } // IE10-11 doesn't support the second parameter of `classList.toggle`\n\n\n    if (added) {\n      addClass(element, value);\n    } else {\n      removeClass(element, value);\n    }\n  }\n  var REGEXP_CAMEL_CASE = /([a-z\\d])([A-Z])/g;\n  /**\n   * Transform the given string from camelCase to kebab-case\n   * @param {string} value - The value to transform.\n   * @returns {string} The transformed value.\n   */\n\n  function toParamCase(value) {\n    return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();\n  }\n  /**\n   * Get data from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} name - The data key to get.\n   * @returns {string} The data value.\n   */\n\n  function getData(element, name) {\n    if (isObject(element[name])) {\n      return element[name];\n    }\n\n    if (element.dataset) {\n      return element.dataset[name];\n    }\n\n    return element.getAttribute(\"data-\".concat(toParamCase(name)));\n  }\n  /**\n   * Set data to the given element.\n   * @param {Element} element - The target element.\n   * @param {string} name - The data key to set.\n   * @param {string} data - The data value.\n   */\n\n  function setData(element, name, data) {\n    if (isObject(data)) {\n      element[name] = data;\n    } else if (element.dataset) {\n      element.dataset[name] = data;\n    } else {\n      element.setAttribute(\"data-\".concat(toParamCase(name)), data);\n    }\n  }\n  /**\n   * Remove data from the given element.\n   * @param {Element} element - The target element.\n   * @param {string} name - The data key to remove.\n   */\n\n  function removeData(element, name) {\n    if (isObject(element[name])) {\n      try {\n        delete element[name];\n      } catch (error) {\n        element[name] = undefined;\n      }\n    } else if (element.dataset) {\n      // #128 Safari not allows to delete dataset property\n      try {\n        delete element.dataset[name];\n      } catch (error) {\n        element.dataset[name] = undefined;\n      }\n    } else {\n      element.removeAttribute(\"data-\".concat(toParamCase(name)));\n    }\n  }\n  var REGEXP_SPACES = /\\s\\s*/;\n\n  var onceSupported = function () {\n    var supported = false;\n\n    if (IS_BROWSER) {\n      var once = false;\n\n      var listener = function listener() {};\n\n      var options = Object.defineProperty({}, 'once', {\n        get: function get() {\n          supported = true;\n          return once;\n        },\n\n        /**\n         * This setter can fix a `TypeError` in strict mode\n         * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}\n         * @param {boolean} value - The value to set\n         */\n        set: function set(value) {\n          once = value;\n        }\n      });\n      WINDOW.addEventListener('test', listener, options);\n      WINDOW.removeEventListener('test', listener, options);\n    }\n\n    return supported;\n  }();\n  /**\n   * Remove event listener from the target element.\n   * @param {Element} element - The event target.\n   * @param {string} type - The event type(s).\n   * @param {Function} listener - The event listener.\n   * @param {Object} options - The event options.\n   */\n\n\n  function removeListener(element, type, listener) {\n    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n    var handler = listener;\n    type.trim().split(REGEXP_SPACES).forEach(function (event) {\n      if (!onceSupported) {\n        var listeners = element.listeners;\n\n        if (listeners && listeners[event] && listeners[event][listener]) {\n          handler = listeners[event][listener];\n          delete listeners[event][listener];\n\n          if (Object.keys(listeners[event]).length === 0) {\n            delete listeners[event];\n          }\n\n          if (Object.keys(listeners).length === 0) {\n            delete element.listeners;\n          }\n        }\n      }\n\n      element.removeEventListener(event, handler, options);\n    });\n  }\n  /**\n   * Add event listener to the target element.\n   * @param {Element} element - The event target.\n   * @param {string} type - The event type(s).\n   * @param {Function} listener - The event listener.\n   * @param {Object} options - The event options.\n   */\n\n  function addListener(element, type, listener) {\n    var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n    var _handler = listener;\n    type.trim().split(REGEXP_SPACES).forEach(function (event) {\n      if (options.once && !onceSupported) {\n        var _element$listeners = element.listeners,\n            listeners = _element$listeners === void 0 ? {} : _element$listeners;\n\n        _handler = function handler() {\n          delete listeners[event][listener];\n          element.removeEventListener(event, _handler, options);\n\n          for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n            args[_key2] = arguments[_key2];\n          }\n\n          listener.apply(element, args);\n        };\n\n        if (!listeners[event]) {\n          listeners[event] = {};\n        }\n\n        if (listeners[event][listener]) {\n          element.removeEventListener(event, listeners[event][listener], options);\n        }\n\n        listeners[event][listener] = _handler;\n        element.listeners = listeners;\n      }\n\n      element.addEventListener(event, _handler, options);\n    });\n  }\n  /**\n   * Dispatch event on the target element.\n   * @param {Element} element - The event target.\n   * @param {string} type - The event type(s).\n   * @param {Object} data - The additional event data.\n   * @returns {boolean} Indicate if the event is default prevented or not.\n   */\n\n  function dispatchEvent(element, type, data) {\n    var event; // Event and CustomEvent on IE9-11 are global objects, not constructors\n\n    if (isFunction(Event) && isFunction(CustomEvent)) {\n      event = new CustomEvent(type, {\n        detail: data,\n        bubbles: true,\n        cancelable: true\n      });\n    } else {\n      event = document.createEvent('CustomEvent');\n      event.initCustomEvent(type, true, true, data);\n    }\n\n    return element.dispatchEvent(event);\n  }\n  /**\n   * Get the offset base on the document.\n   * @param {Element} element - The target element.\n   * @returns {Object} The offset data.\n   */\n\n  function getOffset(element) {\n    var box = element.getBoundingClientRect();\n    return {\n      left: box.left + (window.pageXOffset - document.documentElement.clientLeft),\n      top: box.top + (window.pageYOffset - document.documentElement.clientTop)\n    };\n  }\n  var location = WINDOW.location;\n  var REGEXP_ORIGINS = /^(\\w+:)\\/\\/([^:/?#]*):?(\\d*)/i;\n  /**\n   * Check if the given URL is a cross origin URL.\n   * @param {string} url - The target URL.\n   * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.\n   */\n\n  function isCrossOriginURL(url) {\n    var parts = url.match(REGEXP_ORIGINS);\n    return parts !== null && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);\n  }\n  /**\n   * Add timestamp to the given URL.\n   * @param {string} url - The target URL.\n   * @returns {string} The result URL.\n   */\n\n  function addTimestamp(url) {\n    var timestamp = \"timestamp=\".concat(new Date().getTime());\n    return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;\n  }\n  /**\n   * Get transforms base on the given object.\n   * @param {Object} obj - The target object.\n   * @returns {string} A string contains transform values.\n   */\n\n  function getTransforms(_ref) {\n    var rotate = _ref.rotate,\n        scaleX = _ref.scaleX,\n        scaleY = _ref.scaleY,\n        translateX = _ref.translateX,\n        translateY = _ref.translateY;\n    var values = [];\n\n    if (isNumber(translateX) && translateX !== 0) {\n      values.push(\"translateX(\".concat(translateX, \"px)\"));\n    }\n\n    if (isNumber(translateY) && translateY !== 0) {\n      values.push(\"translateY(\".concat(translateY, \"px)\"));\n    } // Rotate should come first before scale to match orientation transform\n\n\n    if (isNumber(rotate) && rotate !== 0) {\n      values.push(\"rotate(\".concat(rotate, \"deg)\"));\n    }\n\n    if (isNumber(scaleX) && scaleX !== 1) {\n      values.push(\"scaleX(\".concat(scaleX, \")\"));\n    }\n\n    if (isNumber(scaleY) && scaleY !== 1) {\n      values.push(\"scaleY(\".concat(scaleY, \")\"));\n    }\n\n    var transform = values.length ? values.join(' ') : 'none';\n    return {\n      WebkitTransform: transform,\n      msTransform: transform,\n      transform: transform\n    };\n  }\n  /**\n   * Get the max ratio of a group of pointers.\n   * @param {string} pointers - The target pointers.\n   * @returns {number} The result ratio.\n   */\n\n  function getMaxZoomRatio(pointers) {\n    var pointers2 = _objectSpread2({}, pointers);\n\n    var ratios = [];\n    forEach(pointers, function (pointer, pointerId) {\n      delete pointers2[pointerId];\n      forEach(pointers2, function (pointer2) {\n        var x1 = Math.abs(pointer.startX - pointer2.startX);\n        var y1 = Math.abs(pointer.startY - pointer2.startY);\n        var x2 = Math.abs(pointer.endX - pointer2.endX);\n        var y2 = Math.abs(pointer.endY - pointer2.endY);\n        var z1 = Math.sqrt(x1 * x1 + y1 * y1);\n        var z2 = Math.sqrt(x2 * x2 + y2 * y2);\n        var ratio = (z2 - z1) / z1;\n        ratios.push(ratio);\n      });\n    });\n    ratios.sort(function (a, b) {\n      return Math.abs(a) < Math.abs(b);\n    });\n    return ratios[0];\n  }\n  /**\n   * Get a pointer from an event object.\n   * @param {Object} event - The target event object.\n   * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.\n   * @returns {Object} The result pointer contains start and/or end point coordinates.\n   */\n\n  function getPointer(_ref2, endOnly) {\n    var pageX = _ref2.pageX,\n        pageY = _ref2.pageY;\n    var end = {\n      endX: pageX,\n      endY: pageY\n    };\n    return endOnly ? end : _objectSpread2({\n      startX: pageX,\n      startY: pageY\n    }, end);\n  }\n  /**\n   * Get the center point coordinate of a group of pointers.\n   * @param {Object} pointers - The target pointers.\n   * @returns {Object} The center point coordinate.\n   */\n\n  function getPointersCenter(pointers) {\n    var pageX = 0;\n    var pageY = 0;\n    var count = 0;\n    forEach(pointers, function (_ref3) {\n      var startX = _ref3.startX,\n          startY = _ref3.startY;\n      pageX += startX;\n      pageY += startY;\n      count += 1;\n    });\n    pageX /= count;\n    pageY /= count;\n    return {\n      pageX: pageX,\n      pageY: pageY\n    };\n  }\n  /**\n   * Get the max sizes in a rectangle under the given aspect ratio.\n   * @param {Object} data - The original sizes.\n   * @param {string} [type='contain'] - The adjust type.\n   * @returns {Object} The result sizes.\n   */\n\n  function getAdjustedSizes(_ref4) // or 'cover'\n  {\n    var aspectRatio = _ref4.aspectRatio,\n        height = _ref4.height,\n        width = _ref4.width;\n    var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';\n    var isValidWidth = isPositiveNumber(width);\n    var isValidHeight = isPositiveNumber(height);\n\n    if (isValidWidth && isValidHeight) {\n      var adjustedWidth = height * aspectRatio;\n\n      if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {\n        height = width / aspectRatio;\n      } else {\n        width = height * aspectRatio;\n      }\n    } else if (isValidWidth) {\n      height = width / aspectRatio;\n    } else if (isValidHeight) {\n      width = height * aspectRatio;\n    }\n\n    return {\n      width: width,\n      height: height\n    };\n  }\n  /**\n   * Get the new sizes of a rectangle after rotated.\n   * @param {Object} data - The original sizes.\n   * @returns {Object} The result sizes.\n   */\n\n  function getRotatedSizes(_ref5) {\n    var width = _ref5.width,\n        height = _ref5.height,\n        degree = _ref5.degree;\n    degree = Math.abs(degree) % 180;\n\n    if (degree === 90) {\n      return {\n        width: height,\n        height: width\n      };\n    }\n\n    var arc = degree % 90 * Math.PI / 180;\n    var sinArc = Math.sin(arc);\n    var cosArc = Math.cos(arc);\n    var newWidth = width * cosArc + height * sinArc;\n    var newHeight = width * sinArc + height * cosArc;\n    return degree > 90 ? {\n      width: newHeight,\n      height: newWidth\n    } : {\n      width: newWidth,\n      height: newHeight\n    };\n  }\n  /**\n   * Get a canvas which drew the given image.\n   * @param {HTMLImageElement} image - The image for drawing.\n   * @param {Object} imageData - The image data.\n   * @param {Object} canvasData - The canvas data.\n   * @param {Object} options - The options.\n   * @returns {HTMLCanvasElement} The result canvas.\n   */\n\n  function getSourceCanvas(image, _ref6, _ref7, _ref8) {\n    var imageAspectRatio = _ref6.aspectRatio,\n        imageNaturalWidth = _ref6.naturalWidth,\n        imageNaturalHeight = _ref6.naturalHeight,\n        _ref6$rotate = _ref6.rotate,\n        rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate,\n        _ref6$scaleX = _ref6.scaleX,\n        scaleX = _ref6$scaleX === void 0 ? 1 : _ref6$scaleX,\n        _ref6$scaleY = _ref6.scaleY,\n        scaleY = _ref6$scaleY === void 0 ? 1 : _ref6$scaleY;\n    var aspectRatio = _ref7.aspectRatio,\n        naturalWidth = _ref7.naturalWidth,\n        naturalHeight = _ref7.naturalHeight;\n    var _ref8$fillColor = _ref8.fillColor,\n        fillColor = _ref8$fillColor === void 0 ? 'transparent' : _ref8$fillColor,\n        _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,\n        imageSmoothingEnabled = _ref8$imageSmoothingE === void 0 ? true : _ref8$imageSmoothingE,\n        _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,\n        imageSmoothingQuality = _ref8$imageSmoothingQ === void 0 ? 'low' : _ref8$imageSmoothingQ,\n        _ref8$maxWidth = _ref8.maxWidth,\n        maxWidth = _ref8$maxWidth === void 0 ? Infinity : _ref8$maxWidth,\n        _ref8$maxHeight = _ref8.maxHeight,\n        maxHeight = _ref8$maxHeight === void 0 ? Infinity : _ref8$maxHeight,\n        _ref8$minWidth = _ref8.minWidth,\n        minWidth = _ref8$minWidth === void 0 ? 0 : _ref8$minWidth,\n        _ref8$minHeight = _ref8.minHeight,\n        minHeight = _ref8$minHeight === void 0 ? 0 : _ref8$minHeight;\n    var canvas = document.createElement('canvas');\n    var context = canvas.getContext('2d');\n    var maxSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: maxWidth,\n      height: maxHeight\n    });\n    var minSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: minWidth,\n      height: minHeight\n    }, 'cover');\n    var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));\n    var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight)); // Note: should always use image's natural sizes for drawing as\n    // imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90\n\n    var destMaxSizes = getAdjustedSizes({\n      aspectRatio: imageAspectRatio,\n      width: maxWidth,\n      height: maxHeight\n    });\n    var destMinSizes = getAdjustedSizes({\n      aspectRatio: imageAspectRatio,\n      width: minWidth,\n      height: minHeight\n    }, 'cover');\n    var destWidth = Math.min(destMaxSizes.width, Math.max(destMinSizes.width, imageNaturalWidth));\n    var destHeight = Math.min(destMaxSizes.height, Math.max(destMinSizes.height, imageNaturalHeight));\n    var params = [-destWidth / 2, -destHeight / 2, destWidth, destHeight];\n    canvas.width = normalizeDecimalNumber(width);\n    canvas.height = normalizeDecimalNumber(height);\n    context.fillStyle = fillColor;\n    context.fillRect(0, 0, width, height);\n    context.save();\n    context.translate(width / 2, height / 2);\n    context.rotate(rotate * Math.PI / 180);\n    context.scale(scaleX, scaleY);\n    context.imageSmoothingEnabled = imageSmoothingEnabled;\n    context.imageSmoothingQuality = imageSmoothingQuality;\n    context.drawImage.apply(context, [image].concat(_toConsumableArray(params.map(function (param) {\n      return Math.floor(normalizeDecimalNumber(param));\n    }))));\n    context.restore();\n    return canvas;\n  }\n  var fromCharCode = String.fromCharCode;\n  /**\n   * Get string from char code in data view.\n   * @param {DataView} dataView - The data view for read.\n   * @param {number} start - The start index.\n   * @param {number} length - The read length.\n   * @returns {string} The read result.\n   */\n\n  function getStringFromCharCode(dataView, start, length) {\n    var str = '';\n    length += start;\n\n    for (var i = start; i < length; i += 1) {\n      str += fromCharCode(dataView.getUint8(i));\n    }\n\n    return str;\n  }\n  var REGEXP_DATA_URL_HEAD = /^data:.*,/;\n  /**\n   * Transform Data URL to array buffer.\n   * @param {string} dataURL - The Data URL to transform.\n   * @returns {ArrayBuffer} The result array buffer.\n   */\n\n  function dataURLToArrayBuffer(dataURL) {\n    var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n    var binary = atob(base64);\n    var arrayBuffer = new ArrayBuffer(binary.length);\n    var uint8 = new Uint8Array(arrayBuffer);\n    forEach(uint8, function (value, i) {\n      uint8[i] = binary.charCodeAt(i);\n    });\n    return arrayBuffer;\n  }\n  /**\n   * Transform array buffer to Data URL.\n   * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.\n   * @param {string} mimeType - The mime type of the Data URL.\n   * @returns {string} The result Data URL.\n   */\n\n  function arrayBufferToDataURL(arrayBuffer, mimeType) {\n    var chunks = []; // Chunk Typed Array for better performance (#435)\n\n    var chunkSize = 8192;\n    var uint8 = new Uint8Array(arrayBuffer);\n\n    while (uint8.length > 0) {\n      // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9\n      // eslint-disable-next-line prefer-spread\n      chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));\n      uint8 = uint8.subarray(chunkSize);\n    }\n\n    return \"data:\".concat(mimeType, \";base64,\").concat(btoa(chunks.join('')));\n  }\n  /**\n   * Get orientation value from given array buffer.\n   * @param {ArrayBuffer} arrayBuffer - The array buffer to read.\n   * @returns {number} The read orientation value.\n   */\n\n  function resetAndGetOrientation(arrayBuffer) {\n    var dataView = new DataView(arrayBuffer);\n    var orientation; // Ignores range error when the image does not have correct Exif information\n\n    try {\n      var littleEndian;\n      var app1Start;\n      var ifdStart; // Only handle JPEG image (start by 0xFFD8)\n\n      if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n        var length = dataView.byteLength;\n        var offset = 2;\n\n        while (offset + 1 < length) {\n          if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n            app1Start = offset;\n            break;\n          }\n\n          offset += 1;\n        }\n      }\n\n      if (app1Start) {\n        var exifIDCode = app1Start + 4;\n        var tiffOffset = app1Start + 10;\n\n        if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n          var endianness = dataView.getUint16(tiffOffset);\n          littleEndian = endianness === 0x4949;\n\n          if (littleEndian || endianness === 0x4D4D\n          /* bigEndian */\n          ) {\n              if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n                var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n                if (firstIFDOffset >= 0x00000008) {\n                  ifdStart = tiffOffset + firstIFDOffset;\n                }\n              }\n            }\n        }\n      }\n\n      if (ifdStart) {\n        var _length = dataView.getUint16(ifdStart, littleEndian);\n\n        var _offset;\n\n        var i;\n\n        for (i = 0; i < _length; i += 1) {\n          _offset = ifdStart + i * 12 + 2;\n\n          if (dataView.getUint16(_offset, littleEndian) === 0x0112\n          /* Orientation */\n          ) {\n              // 8 is the offset of the current tag's value\n              _offset += 8; // Get the original orientation value\n\n              orientation = dataView.getUint16(_offset, littleEndian); // Override the orientation with its default value\n\n              dataView.setUint16(_offset, 1, littleEndian);\n              break;\n            }\n        }\n      }\n    } catch (error) {\n      orientation = 1;\n    }\n\n    return orientation;\n  }\n  /**\n   * Parse Exif Orientation value.\n   * @param {number} orientation - The orientation to parse.\n   * @returns {Object} The parsed result.\n   */\n\n  function parseOrientation(orientation) {\n    var rotate = 0;\n    var scaleX = 1;\n    var scaleY = 1;\n\n    switch (orientation) {\n      // Flip horizontal\n      case 2:\n        scaleX = -1;\n        break;\n      // Rotate left 180°\n\n      case 3:\n        rotate = -180;\n        break;\n      // Flip vertical\n\n      case 4:\n        scaleY = -1;\n        break;\n      // Flip vertical and rotate right 90°\n\n      case 5:\n        rotate = 90;\n        scaleY = -1;\n        break;\n      // Rotate right 90°\n\n      case 6:\n        rotate = 90;\n        break;\n      // Flip horizontal and rotate right 90°\n\n      case 7:\n        rotate = 90;\n        scaleX = -1;\n        break;\n      // Rotate left 90°\n\n      case 8:\n        rotate = -90;\n        break;\n\n      default:\n    }\n\n    return {\n      rotate: rotate,\n      scaleX: scaleX,\n      scaleY: scaleY\n    };\n  }\n\n  var render = {\n    render: function render() {\n      this.initContainer();\n      this.initCanvas();\n      this.initCropBox();\n      this.renderCanvas();\n\n      if (this.cropped) {\n        this.renderCropBox();\n      }\n    },\n    initContainer: function initContainer() {\n      var element = this.element,\n          options = this.options,\n          container = this.container,\n          cropper = this.cropper;\n      addClass(cropper, CLASS_HIDDEN);\n      removeClass(element, CLASS_HIDDEN);\n      var containerData = {\n        width: Math.max(container.offsetWidth, Number(options.minContainerWidth) || 200),\n        height: Math.max(container.offsetHeight, Number(options.minContainerHeight) || 100)\n      };\n      this.containerData = containerData;\n      setStyle(cropper, {\n        width: containerData.width,\n        height: containerData.height\n      });\n      addClass(element, CLASS_HIDDEN);\n      removeClass(cropper, CLASS_HIDDEN);\n    },\n    // Canvas (image wrapper)\n    initCanvas: function initCanvas() {\n      var containerData = this.containerData,\n          imageData = this.imageData;\n      var viewMode = this.options.viewMode;\n      var rotated = Math.abs(imageData.rotate) % 180 === 90;\n      var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;\n      var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;\n      var aspectRatio = naturalWidth / naturalHeight;\n      var canvasWidth = containerData.width;\n      var canvasHeight = containerData.height;\n\n      if (containerData.height * aspectRatio > containerData.width) {\n        if (viewMode === 3) {\n          canvasWidth = containerData.height * aspectRatio;\n        } else {\n          canvasHeight = containerData.width / aspectRatio;\n        }\n      } else if (viewMode === 3) {\n        canvasHeight = containerData.width / aspectRatio;\n      } else {\n        canvasWidth = containerData.height * aspectRatio;\n      }\n\n      var canvasData = {\n        aspectRatio: aspectRatio,\n        naturalWidth: naturalWidth,\n        naturalHeight: naturalHeight,\n        width: canvasWidth,\n        height: canvasHeight\n      };\n      canvasData.left = (containerData.width - canvasWidth) / 2;\n      canvasData.top = (containerData.height - canvasHeight) / 2;\n      canvasData.oldLeft = canvasData.left;\n      canvasData.oldTop = canvasData.top;\n      this.canvasData = canvasData;\n      this.limited = viewMode === 1 || viewMode === 2;\n      this.limitCanvas(true, true);\n      this.initialImageData = assign({}, imageData);\n      this.initialCanvasData = assign({}, canvasData);\n    },\n    limitCanvas: function limitCanvas(sizeLimited, positionLimited) {\n      var options = this.options,\n          containerData = this.containerData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData;\n      var viewMode = options.viewMode;\n      var aspectRatio = canvasData.aspectRatio;\n      var cropped = this.cropped && cropBoxData;\n\n      if (sizeLimited) {\n        var minCanvasWidth = Number(options.minCanvasWidth) || 0;\n        var minCanvasHeight = Number(options.minCanvasHeight) || 0;\n\n        if (viewMode > 1) {\n          minCanvasWidth = Math.max(minCanvasWidth, containerData.width);\n          minCanvasHeight = Math.max(minCanvasHeight, containerData.height);\n\n          if (viewMode === 3) {\n            if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n              minCanvasWidth = minCanvasHeight * aspectRatio;\n            } else {\n              minCanvasHeight = minCanvasWidth / aspectRatio;\n            }\n          }\n        } else if (viewMode > 0) {\n          if (minCanvasWidth) {\n            minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBoxData.width : 0);\n          } else if (minCanvasHeight) {\n            minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBoxData.height : 0);\n          } else if (cropped) {\n            minCanvasWidth = cropBoxData.width;\n            minCanvasHeight = cropBoxData.height;\n\n            if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n              minCanvasWidth = minCanvasHeight * aspectRatio;\n            } else {\n              minCanvasHeight = minCanvasWidth / aspectRatio;\n            }\n          }\n        }\n\n        var _getAdjustedSizes = getAdjustedSizes({\n          aspectRatio: aspectRatio,\n          width: minCanvasWidth,\n          height: minCanvasHeight\n        });\n\n        minCanvasWidth = _getAdjustedSizes.width;\n        minCanvasHeight = _getAdjustedSizes.height;\n        canvasData.minWidth = minCanvasWidth;\n        canvasData.minHeight = minCanvasHeight;\n        canvasData.maxWidth = Infinity;\n        canvasData.maxHeight = Infinity;\n      }\n\n      if (positionLimited) {\n        if (viewMode > (cropped ? 0 : 1)) {\n          var newCanvasLeft = containerData.width - canvasData.width;\n          var newCanvasTop = containerData.height - canvasData.height;\n          canvasData.minLeft = Math.min(0, newCanvasLeft);\n          canvasData.minTop = Math.min(0, newCanvasTop);\n          canvasData.maxLeft = Math.max(0, newCanvasLeft);\n          canvasData.maxTop = Math.max(0, newCanvasTop);\n\n          if (cropped && this.limited) {\n            canvasData.minLeft = Math.min(cropBoxData.left, cropBoxData.left + (cropBoxData.width - canvasData.width));\n            canvasData.minTop = Math.min(cropBoxData.top, cropBoxData.top + (cropBoxData.height - canvasData.height));\n            canvasData.maxLeft = cropBoxData.left;\n            canvasData.maxTop = cropBoxData.top;\n\n            if (viewMode === 2) {\n              if (canvasData.width >= containerData.width) {\n                canvasData.minLeft = Math.min(0, newCanvasLeft);\n                canvasData.maxLeft = Math.max(0, newCanvasLeft);\n              }\n\n              if (canvasData.height >= containerData.height) {\n                canvasData.minTop = Math.min(0, newCanvasTop);\n                canvasData.maxTop = Math.max(0, newCanvasTop);\n              }\n            }\n          }\n        } else {\n          canvasData.minLeft = -canvasData.width;\n          canvasData.minTop = -canvasData.height;\n          canvasData.maxLeft = containerData.width;\n          canvasData.maxTop = containerData.height;\n        }\n      }\n    },\n    renderCanvas: function renderCanvas(changed, transformed) {\n      var canvasData = this.canvasData,\n          imageData = this.imageData;\n\n      if (transformed) {\n        var _getRotatedSizes = getRotatedSizes({\n          width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),\n          height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),\n          degree: imageData.rotate || 0\n        }),\n            naturalWidth = _getRotatedSizes.width,\n            naturalHeight = _getRotatedSizes.height;\n\n        var width = canvasData.width * (naturalWidth / canvasData.naturalWidth);\n        var height = canvasData.height * (naturalHeight / canvasData.naturalHeight);\n        canvasData.left -= (width - canvasData.width) / 2;\n        canvasData.top -= (height - canvasData.height) / 2;\n        canvasData.width = width;\n        canvasData.height = height;\n        canvasData.aspectRatio = naturalWidth / naturalHeight;\n        canvasData.naturalWidth = naturalWidth;\n        canvasData.naturalHeight = naturalHeight;\n        this.limitCanvas(true, false);\n      }\n\n      if (canvasData.width > canvasData.maxWidth || canvasData.width < canvasData.minWidth) {\n        canvasData.left = canvasData.oldLeft;\n      }\n\n      if (canvasData.height > canvasData.maxHeight || canvasData.height < canvasData.minHeight) {\n        canvasData.top = canvasData.oldTop;\n      }\n\n      canvasData.width = Math.min(Math.max(canvasData.width, canvasData.minWidth), canvasData.maxWidth);\n      canvasData.height = Math.min(Math.max(canvasData.height, canvasData.minHeight), canvasData.maxHeight);\n      this.limitCanvas(false, true);\n      canvasData.left = Math.min(Math.max(canvasData.left, canvasData.minLeft), canvasData.maxLeft);\n      canvasData.top = Math.min(Math.max(canvasData.top, canvasData.minTop), canvasData.maxTop);\n      canvasData.oldLeft = canvasData.left;\n      canvasData.oldTop = canvasData.top;\n      setStyle(this.canvas, assign({\n        width: canvasData.width,\n        height: canvasData.height\n      }, getTransforms({\n        translateX: canvasData.left,\n        translateY: canvasData.top\n      })));\n      this.renderImage(changed);\n\n      if (this.cropped && this.limited) {\n        this.limitCropBox(true, true);\n      }\n    },\n    renderImage: function renderImage(changed) {\n      var canvasData = this.canvasData,\n          imageData = this.imageData;\n      var width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);\n      var height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);\n      assign(imageData, {\n        width: width,\n        height: height,\n        left: (canvasData.width - width) / 2,\n        top: (canvasData.height - height) / 2\n      });\n      setStyle(this.image, assign({\n        width: imageData.width,\n        height: imageData.height\n      }, getTransforms(assign({\n        translateX: imageData.left,\n        translateY: imageData.top\n      }, imageData))));\n\n      if (changed) {\n        this.output();\n      }\n    },\n    initCropBox: function initCropBox() {\n      var options = this.options,\n          canvasData = this.canvasData;\n      var aspectRatio = options.aspectRatio || options.initialAspectRatio;\n      var autoCropArea = Number(options.autoCropArea) || 0.8;\n      var cropBoxData = {\n        width: canvasData.width,\n        height: canvasData.height\n      };\n\n      if (aspectRatio) {\n        if (canvasData.height * aspectRatio > canvasData.width) {\n          cropBoxData.height = cropBoxData.width / aspectRatio;\n        } else {\n          cropBoxData.width = cropBoxData.height * aspectRatio;\n        }\n      }\n\n      this.cropBoxData = cropBoxData;\n      this.limitCropBox(true, true); // Initialize auto crop area\n\n      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight); // The width/height of auto crop area must large than \"minWidth/Height\"\n\n      cropBoxData.width = Math.max(cropBoxData.minWidth, cropBoxData.width * autoCropArea);\n      cropBoxData.height = Math.max(cropBoxData.minHeight, cropBoxData.height * autoCropArea);\n      cropBoxData.left = canvasData.left + (canvasData.width - cropBoxData.width) / 2;\n      cropBoxData.top = canvasData.top + (canvasData.height - cropBoxData.height) / 2;\n      cropBoxData.oldLeft = cropBoxData.left;\n      cropBoxData.oldTop = cropBoxData.top;\n      this.initialCropBoxData = assign({}, cropBoxData);\n    },\n    limitCropBox: function limitCropBox(sizeLimited, positionLimited) {\n      var options = this.options,\n          containerData = this.containerData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData,\n          limited = this.limited;\n      var aspectRatio = options.aspectRatio;\n\n      if (sizeLimited) {\n        var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;\n        var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;\n        var maxCropBoxWidth = limited ? Math.min(containerData.width, canvasData.width, canvasData.width + canvasData.left, containerData.width - canvasData.left) : containerData.width;\n        var maxCropBoxHeight = limited ? Math.min(containerData.height, canvasData.height, canvasData.height + canvasData.top, containerData.height - canvasData.top) : containerData.height; // The min/maxCropBoxWidth/Height must be less than container's width/height\n\n        minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);\n        minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);\n\n        if (aspectRatio) {\n          if (minCropBoxWidth && minCropBoxHeight) {\n            if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n              minCropBoxHeight = minCropBoxWidth / aspectRatio;\n            } else {\n              minCropBoxWidth = minCropBoxHeight * aspectRatio;\n            }\n          } else if (minCropBoxWidth) {\n            minCropBoxHeight = minCropBoxWidth / aspectRatio;\n          } else if (minCropBoxHeight) {\n            minCropBoxWidth = minCropBoxHeight * aspectRatio;\n          }\n\n          if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n            maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n          } else {\n            maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n          }\n        } // The minWidth/Height must be less than maxWidth/Height\n\n\n        cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);\n        cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);\n        cropBoxData.maxWidth = maxCropBoxWidth;\n        cropBoxData.maxHeight = maxCropBoxHeight;\n      }\n\n      if (positionLimited) {\n        if (limited) {\n          cropBoxData.minLeft = Math.max(0, canvasData.left);\n          cropBoxData.minTop = Math.max(0, canvasData.top);\n          cropBoxData.maxLeft = Math.min(containerData.width, canvasData.left + canvasData.width) - cropBoxData.width;\n          cropBoxData.maxTop = Math.min(containerData.height, canvasData.top + canvasData.height) - cropBoxData.height;\n        } else {\n          cropBoxData.minLeft = 0;\n          cropBoxData.minTop = 0;\n          cropBoxData.maxLeft = containerData.width - cropBoxData.width;\n          cropBoxData.maxTop = containerData.height - cropBoxData.height;\n        }\n      }\n    },\n    renderCropBox: function renderCropBox() {\n      var options = this.options,\n          containerData = this.containerData,\n          cropBoxData = this.cropBoxData;\n\n      if (cropBoxData.width > cropBoxData.maxWidth || cropBoxData.width < cropBoxData.minWidth) {\n        cropBoxData.left = cropBoxData.oldLeft;\n      }\n\n      if (cropBoxData.height > cropBoxData.maxHeight || cropBoxData.height < cropBoxData.minHeight) {\n        cropBoxData.top = cropBoxData.oldTop;\n      }\n\n      cropBoxData.width = Math.min(Math.max(cropBoxData.width, cropBoxData.minWidth), cropBoxData.maxWidth);\n      cropBoxData.height = Math.min(Math.max(cropBoxData.height, cropBoxData.minHeight), cropBoxData.maxHeight);\n      this.limitCropBox(false, true);\n      cropBoxData.left = Math.min(Math.max(cropBoxData.left, cropBoxData.minLeft), cropBoxData.maxLeft);\n      cropBoxData.top = Math.min(Math.max(cropBoxData.top, cropBoxData.minTop), cropBoxData.maxTop);\n      cropBoxData.oldLeft = cropBoxData.left;\n      cropBoxData.oldTop = cropBoxData.top;\n\n      if (options.movable && options.cropBoxMovable) {\n        // Turn to move the canvas when the crop box is equal to the container\n        setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width && cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);\n      }\n\n      setStyle(this.cropBox, assign({\n        width: cropBoxData.width,\n        height: cropBoxData.height\n      }, getTransforms({\n        translateX: cropBoxData.left,\n        translateY: cropBoxData.top\n      })));\n\n      if (this.cropped && this.limited) {\n        this.limitCanvas(true, true);\n      }\n\n      if (!this.disabled) {\n        this.output();\n      }\n    },\n    output: function output() {\n      this.preview();\n      dispatchEvent(this.element, EVENT_CROP, this.getData());\n    }\n  };\n\n  var preview = {\n    initPreview: function initPreview() {\n      var element = this.element,\n          crossOrigin = this.crossOrigin;\n      var preview = this.options.preview;\n      var url = crossOrigin ? this.crossOriginUrl : this.url;\n      var alt = element.alt || 'The image to preview';\n      var image = document.createElement('img');\n\n      if (crossOrigin) {\n        image.crossOrigin = crossOrigin;\n      }\n\n      image.src = url;\n      image.alt = alt;\n      this.viewBox.appendChild(image);\n      this.viewBoxImage = image;\n\n      if (!preview) {\n        return;\n      }\n\n      var previews = preview;\n\n      if (typeof preview === 'string') {\n        previews = element.ownerDocument.querySelectorAll(preview);\n      } else if (preview.querySelector) {\n        previews = [preview];\n      }\n\n      this.previews = previews;\n      forEach(previews, function (el) {\n        var img = document.createElement('img'); // Save the original size for recover\n\n        setData(el, DATA_PREVIEW, {\n          width: el.offsetWidth,\n          height: el.offsetHeight,\n          html: el.innerHTML\n        });\n\n        if (crossOrigin) {\n          img.crossOrigin = crossOrigin;\n        }\n\n        img.src = url;\n        img.alt = alt;\n        /**\n         * Override img element styles\n         * Add `display:block` to avoid margin top issue\n         * Add `height:auto` to override `height` attribute on IE8\n         * (Occur only when margin-top <= -height)\n         */\n\n        img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;\"';\n        el.innerHTML = '';\n        el.appendChild(img);\n      });\n    },\n    resetPreview: function resetPreview() {\n      forEach(this.previews, function (element) {\n        var data = getData(element, DATA_PREVIEW);\n        setStyle(element, {\n          width: data.width,\n          height: data.height\n        });\n        element.innerHTML = data.html;\n        removeData(element, DATA_PREVIEW);\n      });\n    },\n    preview: function preview() {\n      var imageData = this.imageData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData;\n      var cropBoxWidth = cropBoxData.width,\n          cropBoxHeight = cropBoxData.height;\n      var width = imageData.width,\n          height = imageData.height;\n      var left = cropBoxData.left - canvasData.left - imageData.left;\n      var top = cropBoxData.top - canvasData.top - imageData.top;\n\n      if (!this.cropped || this.disabled) {\n        return;\n      }\n\n      setStyle(this.viewBoxImage, assign({\n        width: width,\n        height: height\n      }, getTransforms(assign({\n        translateX: -left,\n        translateY: -top\n      }, imageData))));\n      forEach(this.previews, function (element) {\n        var data = getData(element, DATA_PREVIEW);\n        var originalWidth = data.width;\n        var originalHeight = data.height;\n        var newWidth = originalWidth;\n        var newHeight = originalHeight;\n        var ratio = 1;\n\n        if (cropBoxWidth) {\n          ratio = originalWidth / cropBoxWidth;\n          newHeight = cropBoxHeight * ratio;\n        }\n\n        if (cropBoxHeight && newHeight > originalHeight) {\n          ratio = originalHeight / cropBoxHeight;\n          newWidth = cropBoxWidth * ratio;\n          newHeight = originalHeight;\n        }\n\n        setStyle(element, {\n          width: newWidth,\n          height: newHeight\n        });\n        setStyle(element.getElementsByTagName('img')[0], assign({\n          width: width * ratio,\n          height: height * ratio\n        }, getTransforms(assign({\n          translateX: -left * ratio,\n          translateY: -top * ratio\n        }, imageData))));\n      });\n    }\n  };\n\n  var events = {\n    bind: function bind() {\n      var element = this.element,\n          options = this.options,\n          cropper = this.cropper;\n\n      if (isFunction(options.cropstart)) {\n        addListener(element, EVENT_CROP_START, options.cropstart);\n      }\n\n      if (isFunction(options.cropmove)) {\n        addListener(element, EVENT_CROP_MOVE, options.cropmove);\n      }\n\n      if (isFunction(options.cropend)) {\n        addListener(element, EVENT_CROP_END, options.cropend);\n      }\n\n      if (isFunction(options.crop)) {\n        addListener(element, EVENT_CROP, options.crop);\n      }\n\n      if (isFunction(options.zoom)) {\n        addListener(element, EVENT_ZOOM, options.zoom);\n      }\n\n      addListener(cropper, EVENT_POINTER_DOWN, this.onCropStart = this.cropStart.bind(this));\n\n      if (options.zoomable && options.zoomOnWheel) {\n        addListener(cropper, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {\n          passive: false,\n          capture: true\n        });\n      }\n\n      if (options.toggleDragModeOnDblclick) {\n        addListener(cropper, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));\n      }\n\n      addListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove = this.cropMove.bind(this));\n      addListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd = this.cropEnd.bind(this));\n\n      if (options.responsive) {\n        addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));\n      }\n    },\n    unbind: function unbind() {\n      var element = this.element,\n          options = this.options,\n          cropper = this.cropper;\n\n      if (isFunction(options.cropstart)) {\n        removeListener(element, EVENT_CROP_START, options.cropstart);\n      }\n\n      if (isFunction(options.cropmove)) {\n        removeListener(element, EVENT_CROP_MOVE, options.cropmove);\n      }\n\n      if (isFunction(options.cropend)) {\n        removeListener(element, EVENT_CROP_END, options.cropend);\n      }\n\n      if (isFunction(options.crop)) {\n        removeListener(element, EVENT_CROP, options.crop);\n      }\n\n      if (isFunction(options.zoom)) {\n        removeListener(element, EVENT_ZOOM, options.zoom);\n      }\n\n      removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);\n\n      if (options.zoomable && options.zoomOnWheel) {\n        removeListener(cropper, EVENT_WHEEL, this.onWheel, {\n          passive: false,\n          capture: true\n        });\n      }\n\n      if (options.toggleDragModeOnDblclick) {\n        removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);\n      }\n\n      removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);\n      removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);\n\n      if (options.responsive) {\n        removeListener(window, EVENT_RESIZE, this.onResize);\n      }\n    }\n  };\n\n  var handlers = {\n    resize: function resize() {\n      var options = this.options,\n          container = this.container,\n          containerData = this.containerData;\n      var minContainerWidth = Number(options.minContainerWidth) || MIN_CONTAINER_WIDTH;\n      var minContainerHeight = Number(options.minContainerHeight) || MIN_CONTAINER_HEIGHT;\n\n      if (this.disabled || containerData.width <= minContainerWidth || containerData.height <= minContainerHeight) {\n        return;\n      }\n\n      var ratio = container.offsetWidth / containerData.width; // Resize when width changed or height changed\n\n      if (ratio !== 1 || container.offsetHeight !== containerData.height) {\n        var canvasData;\n        var cropBoxData;\n\n        if (options.restore) {\n          canvasData = this.getCanvasData();\n          cropBoxData = this.getCropBoxData();\n        }\n\n        this.render();\n\n        if (options.restore) {\n          this.setCanvasData(forEach(canvasData, function (n, i) {\n            canvasData[i] = n * ratio;\n          }));\n          this.setCropBoxData(forEach(cropBoxData, function (n, i) {\n            cropBoxData[i] = n * ratio;\n          }));\n        }\n      }\n    },\n    dblclick: function dblclick() {\n      if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {\n        return;\n      }\n\n      this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);\n    },\n    wheel: function wheel(event) {\n      var _this = this;\n\n      var ratio = Number(this.options.wheelZoomRatio) || 0.1;\n      var delta = 1;\n\n      if (this.disabled) {\n        return;\n      }\n\n      event.preventDefault(); // Limit wheel speed to prevent zoom too fast (#21)\n\n      if (this.wheeling) {\n        return;\n      }\n\n      this.wheeling = true;\n      setTimeout(function () {\n        _this.wheeling = false;\n      }, 50);\n\n      if (event.deltaY) {\n        delta = event.deltaY > 0 ? 1 : -1;\n      } else if (event.wheelDelta) {\n        delta = -event.wheelDelta / 120;\n      } else if (event.detail) {\n        delta = event.detail > 0 ? 1 : -1;\n      }\n\n      this.zoom(-delta * ratio, event);\n    },\n    cropStart: function cropStart(event) {\n      var buttons = event.buttons,\n          button = event.button;\n\n      if (this.disabled // Handle mouse event and pointer event and ignore touch event\n      || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && ( // No primary button (Usually the left button)\n      isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu\n      || event.ctrlKey)) {\n        return;\n      }\n\n      var options = this.options,\n          pointers = this.pointers;\n      var action;\n\n      if (event.changedTouches) {\n        // Handle touch event\n        forEach(event.changedTouches, function (touch) {\n          pointers[touch.identifier] = getPointer(touch);\n        });\n      } else {\n        // Handle mouse event and pointer event\n        pointers[event.pointerId || 0] = getPointer(event);\n      }\n\n      if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {\n        action = ACTION_ZOOM;\n      } else {\n        action = getData(event.target, DATA_ACTION);\n      }\n\n      if (!REGEXP_ACTIONS.test(action)) {\n        return;\n      }\n\n      if (dispatchEvent(this.element, EVENT_CROP_START, {\n        originalEvent: event,\n        action: action\n      }) === false) {\n        return;\n      } // This line is required for preventing page zooming in iOS browsers\n\n\n      event.preventDefault();\n      this.action = action;\n      this.cropping = false;\n\n      if (action === ACTION_CROP) {\n        this.cropping = true;\n        addClass(this.dragBox, CLASS_MODAL);\n      }\n    },\n    cropMove: function cropMove(event) {\n      var action = this.action;\n\n      if (this.disabled || !action) {\n        return;\n      }\n\n      var pointers = this.pointers;\n      event.preventDefault();\n\n      if (dispatchEvent(this.element, EVENT_CROP_MOVE, {\n        originalEvent: event,\n        action: action\n      }) === false) {\n        return;\n      }\n\n      if (event.changedTouches) {\n        forEach(event.changedTouches, function (touch) {\n          // The first parameter should not be undefined (#432)\n          assign(pointers[touch.identifier] || {}, getPointer(touch, true));\n        });\n      } else {\n        assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));\n      }\n\n      this.change(event);\n    },\n    cropEnd: function cropEnd(event) {\n      if (this.disabled) {\n        return;\n      }\n\n      var action = this.action,\n          pointers = this.pointers;\n\n      if (event.changedTouches) {\n        forEach(event.changedTouches, function (touch) {\n          delete pointers[touch.identifier];\n        });\n      } else {\n        delete pointers[event.pointerId || 0];\n      }\n\n      if (!action) {\n        return;\n      }\n\n      event.preventDefault();\n\n      if (!Object.keys(pointers).length) {\n        this.action = '';\n      }\n\n      if (this.cropping) {\n        this.cropping = false;\n        toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);\n      }\n\n      dispatchEvent(this.element, EVENT_CROP_END, {\n        originalEvent: event,\n        action: action\n      });\n    }\n  };\n\n  var change = {\n    change: function change(event) {\n      var options = this.options,\n          canvasData = this.canvasData,\n          containerData = this.containerData,\n          cropBoxData = this.cropBoxData,\n          pointers = this.pointers;\n      var action = this.action;\n      var aspectRatio = options.aspectRatio;\n      var left = cropBoxData.left,\n          top = cropBoxData.top,\n          width = cropBoxData.width,\n          height = cropBoxData.height;\n      var right = left + width;\n      var bottom = top + height;\n      var minLeft = 0;\n      var minTop = 0;\n      var maxWidth = containerData.width;\n      var maxHeight = containerData.height;\n      var renderable = true;\n      var offset; // Locking aspect ratio in \"free mode\" by holding shift key\n\n      if (!aspectRatio && event.shiftKey) {\n        aspectRatio = width && height ? width / height : 1;\n      }\n\n      if (this.limited) {\n        minLeft = cropBoxData.minLeft;\n        minTop = cropBoxData.minTop;\n        maxWidth = minLeft + Math.min(containerData.width, canvasData.width, canvasData.left + canvasData.width);\n        maxHeight = minTop + Math.min(containerData.height, canvasData.height, canvasData.top + canvasData.height);\n      }\n\n      var pointer = pointers[Object.keys(pointers)[0]];\n      var range = {\n        x: pointer.endX - pointer.startX,\n        y: pointer.endY - pointer.startY\n      };\n\n      var check = function check(side) {\n        switch (side) {\n          case ACTION_EAST:\n            if (right + range.x > maxWidth) {\n              range.x = maxWidth - right;\n            }\n\n            break;\n\n          case ACTION_WEST:\n            if (left + range.x < minLeft) {\n              range.x = minLeft - left;\n            }\n\n            break;\n\n          case ACTION_NORTH:\n            if (top + range.y < minTop) {\n              range.y = minTop - top;\n            }\n\n            break;\n\n          case ACTION_SOUTH:\n            if (bottom + range.y > maxHeight) {\n              range.y = maxHeight - bottom;\n            }\n\n            break;\n\n          default:\n        }\n      };\n\n      switch (action) {\n        // Move crop box\n        case ACTION_ALL:\n          left += range.x;\n          top += range.y;\n          break;\n        // Resize crop box\n\n        case ACTION_EAST:\n          if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_EAST);\n          width += range.x;\n\n          if (width < 0) {\n            action = ACTION_WEST;\n            width = -width;\n            left -= width;\n          }\n\n          if (aspectRatio) {\n            height = width / aspectRatio;\n            top += (cropBoxData.height - height) / 2;\n          }\n\n          break;\n\n        case ACTION_NORTH:\n          if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n\n          if (height < 0) {\n            action = ACTION_SOUTH;\n            height = -height;\n            top -= height;\n          }\n\n          if (aspectRatio) {\n            width = height * aspectRatio;\n            left += (cropBoxData.width - width) / 2;\n          }\n\n          break;\n\n        case ACTION_WEST:\n          if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_WEST);\n          width -= range.x;\n          left += range.x;\n\n          if (width < 0) {\n            action = ACTION_EAST;\n            width = -width;\n            left -= width;\n          }\n\n          if (aspectRatio) {\n            height = width / aspectRatio;\n            top += (cropBoxData.height - height) / 2;\n          }\n\n          break;\n\n        case ACTION_SOUTH:\n          if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_SOUTH);\n          height += range.y;\n\n          if (height < 0) {\n            action = ACTION_NORTH;\n            height = -height;\n            top -= height;\n          }\n\n          if (aspectRatio) {\n            width = height * aspectRatio;\n            left += (cropBoxData.width - width) / 2;\n          }\n\n          break;\n\n        case ACTION_NORTH_EAST:\n          if (aspectRatio) {\n            if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_NORTH);\n            height -= range.y;\n            top += range.y;\n            width = height * aspectRatio;\n          } else {\n            check(ACTION_NORTH);\n            check(ACTION_EAST);\n\n            if (range.x >= 0) {\n              if (right < maxWidth) {\n                width += range.x;\n              } else if (range.y <= 0 && top <= minTop) {\n                renderable = false;\n              }\n            } else {\n              width += range.x;\n            }\n\n            if (range.y <= 0) {\n              if (top > minTop) {\n                height -= range.y;\n                top += range.y;\n              }\n            } else {\n              height -= range.y;\n              top += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_SOUTH_WEST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_NORTH_WEST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_SOUTH_EAST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n\n        case ACTION_NORTH_WEST:\n          if (aspectRatio) {\n            if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_NORTH);\n            height -= range.y;\n            top += range.y;\n            width = height * aspectRatio;\n            left += cropBoxData.width - width;\n          } else {\n            check(ACTION_NORTH);\n            check(ACTION_WEST);\n\n            if (range.x <= 0) {\n              if (left > minLeft) {\n                width -= range.x;\n                left += range.x;\n              } else if (range.y <= 0 && top <= minTop) {\n                renderable = false;\n              }\n            } else {\n              width -= range.x;\n              left += range.x;\n            }\n\n            if (range.y <= 0) {\n              if (top > minTop) {\n                height -= range.y;\n                top += range.y;\n              }\n            } else {\n              height -= range.y;\n              top += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_SOUTH_EAST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_NORTH_EAST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_SOUTH_WEST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n\n        case ACTION_SOUTH_WEST:\n          if (aspectRatio) {\n            if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_WEST);\n            width -= range.x;\n            left += range.x;\n            height = width / aspectRatio;\n          } else {\n            check(ACTION_SOUTH);\n            check(ACTION_WEST);\n\n            if (range.x <= 0) {\n              if (left > minLeft) {\n                width -= range.x;\n                left += range.x;\n              } else if (range.y >= 0 && bottom >= maxHeight) {\n                renderable = false;\n              }\n            } else {\n              width -= range.x;\n              left += range.x;\n            }\n\n            if (range.y >= 0) {\n              if (bottom < maxHeight) {\n                height += range.y;\n              }\n            } else {\n              height += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_NORTH_EAST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_SOUTH_EAST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_NORTH_WEST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n\n        case ACTION_SOUTH_EAST:\n          if (aspectRatio) {\n            if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n              renderable = false;\n              break;\n            }\n\n            check(ACTION_EAST);\n            width += range.x;\n            height = width / aspectRatio;\n          } else {\n            check(ACTION_SOUTH);\n            check(ACTION_EAST);\n\n            if (range.x >= 0) {\n              if (right < maxWidth) {\n                width += range.x;\n              } else if (range.y >= 0 && bottom >= maxHeight) {\n                renderable = false;\n              }\n            } else {\n              width += range.x;\n            }\n\n            if (range.y >= 0) {\n              if (bottom < maxHeight) {\n                height += range.y;\n              }\n            } else {\n              height += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_NORTH_WEST;\n            height = -height;\n            width = -width;\n            top -= height;\n            left -= width;\n          } else if (width < 0) {\n            action = ACTION_SOUTH_WEST;\n            width = -width;\n            left -= width;\n          } else if (height < 0) {\n            action = ACTION_NORTH_EAST;\n            height = -height;\n            top -= height;\n          }\n\n          break;\n        // Move canvas\n\n        case ACTION_MOVE:\n          this.move(range.x, range.y);\n          renderable = false;\n          break;\n        // Zoom canvas\n\n        case ACTION_ZOOM:\n          this.zoom(getMaxZoomRatio(pointers), event);\n          renderable = false;\n          break;\n        // Create crop box\n\n        case ACTION_CROP:\n          if (!range.x || !range.y) {\n            renderable = false;\n            break;\n          }\n\n          offset = getOffset(this.cropper);\n          left = pointer.startX - offset.left;\n          top = pointer.startY - offset.top;\n          width = cropBoxData.minWidth;\n          height = cropBoxData.minHeight;\n\n          if (range.x > 0) {\n            action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n          } else if (range.x < 0) {\n            left -= width;\n            action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n          }\n\n          if (range.y < 0) {\n            top -= height;\n          } // Show the crop box if is hidden\n\n\n          if (!this.cropped) {\n            removeClass(this.cropBox, CLASS_HIDDEN);\n            this.cropped = true;\n\n            if (this.limited) {\n              this.limitCropBox(true, true);\n            }\n          }\n\n          break;\n\n        default:\n      }\n\n      if (renderable) {\n        cropBoxData.width = width;\n        cropBoxData.height = height;\n        cropBoxData.left = left;\n        cropBoxData.top = top;\n        this.action = action;\n        this.renderCropBox();\n      } // Override\n\n\n      forEach(pointers, function (p) {\n        p.startX = p.endX;\n        p.startY = p.endY;\n      });\n    }\n  };\n\n  var methods = {\n    // Show the crop box manually\n    crop: function crop() {\n      if (this.ready && !this.cropped && !this.disabled) {\n        this.cropped = true;\n        this.limitCropBox(true, true);\n\n        if (this.options.modal) {\n          addClass(this.dragBox, CLASS_MODAL);\n        }\n\n        removeClass(this.cropBox, CLASS_HIDDEN);\n        this.setCropBoxData(this.initialCropBoxData);\n      }\n\n      return this;\n    },\n    // Reset the image and crop box to their initial states\n    reset: function reset() {\n      if (this.ready && !this.disabled) {\n        this.imageData = assign({}, this.initialImageData);\n        this.canvasData = assign({}, this.initialCanvasData);\n        this.cropBoxData = assign({}, this.initialCropBoxData);\n        this.renderCanvas();\n\n        if (this.cropped) {\n          this.renderCropBox();\n        }\n      }\n\n      return this;\n    },\n    // Clear the crop box\n    clear: function clear() {\n      if (this.cropped && !this.disabled) {\n        assign(this.cropBoxData, {\n          left: 0,\n          top: 0,\n          width: 0,\n          height: 0\n        });\n        this.cropped = false;\n        this.renderCropBox();\n        this.limitCanvas(true, true); // Render canvas after crop box rendered\n\n        this.renderCanvas();\n        removeClass(this.dragBox, CLASS_MODAL);\n        addClass(this.cropBox, CLASS_HIDDEN);\n      }\n\n      return this;\n    },\n\n    /**\n     * Replace the image's src and rebuild the cropper\n     * @param {string} url - The new URL.\n     * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.\n     * @returns {Cropper} this\n     */\n    replace: function replace(url) {\n      var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n      if (!this.disabled && url) {\n        if (this.isImg) {\n          this.element.src = url;\n        }\n\n        if (hasSameSize) {\n          this.url = url;\n          this.image.src = url;\n\n          if (this.ready) {\n            this.viewBoxImage.src = url;\n            forEach(this.previews, function (element) {\n              element.getElementsByTagName('img')[0].src = url;\n            });\n          }\n        } else {\n          if (this.isImg) {\n            this.replaced = true;\n          }\n\n          this.options.data = null;\n          this.uncreate();\n          this.load(url);\n        }\n      }\n\n      return this;\n    },\n    // Enable (unfreeze) the cropper\n    enable: function enable() {\n      if (this.ready && this.disabled) {\n        this.disabled = false;\n        removeClass(this.cropper, CLASS_DISABLED);\n      }\n\n      return this;\n    },\n    // Disable (freeze) the cropper\n    disable: function disable() {\n      if (this.ready && !this.disabled) {\n        this.disabled = true;\n        addClass(this.cropper, CLASS_DISABLED);\n      }\n\n      return this;\n    },\n\n    /**\n     * Destroy the cropper and remove the instance from the image\n     * @returns {Cropper} this\n     */\n    destroy: function destroy() {\n      var element = this.element;\n\n      if (!element[NAMESPACE]) {\n        return this;\n      }\n\n      element[NAMESPACE] = undefined;\n\n      if (this.isImg && this.replaced) {\n        element.src = this.originalUrl;\n      }\n\n      this.uncreate();\n      return this;\n    },\n\n    /**\n     * Move the canvas with relative offsets\n     * @param {number} offsetX - The relative offset distance on the x-axis.\n     * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.\n     * @returns {Cropper} this\n     */\n    move: function move(offsetX) {\n      var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;\n      var _this$canvasData = this.canvasData,\n          left = _this$canvasData.left,\n          top = _this$canvasData.top;\n      return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));\n    },\n\n    /**\n     * Move the canvas to an absolute point\n     * @param {number} x - The x-axis coordinate.\n     * @param {number} [y=x] - The y-axis coordinate.\n     * @returns {Cropper} this\n     */\n    moveTo: function moveTo(x) {\n      var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;\n      var canvasData = this.canvasData;\n      var changed = false;\n      x = Number(x);\n      y = Number(y);\n\n      if (this.ready && !this.disabled && this.options.movable) {\n        if (isNumber(x)) {\n          canvasData.left = x;\n          changed = true;\n        }\n\n        if (isNumber(y)) {\n          canvasData.top = y;\n          changed = true;\n        }\n\n        if (changed) {\n          this.renderCanvas(true);\n        }\n      }\n\n      return this;\n    },\n\n    /**\n     * Zoom the canvas with a relative ratio\n     * @param {number} ratio - The target ratio.\n     * @param {Event} _originalEvent - The original event if any.\n     * @returns {Cropper} this\n     */\n    zoom: function zoom(ratio, _originalEvent) {\n      var canvasData = this.canvasData;\n      ratio = Number(ratio);\n\n      if (ratio < 0) {\n        ratio = 1 / (1 - ratio);\n      } else {\n        ratio = 1 + ratio;\n      }\n\n      return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);\n    },\n\n    /**\n     * Zoom the canvas to an absolute ratio\n     * @param {number} ratio - The target ratio.\n     * @param {Object} pivot - The zoom pivot point coordinate.\n     * @param {Event} _originalEvent - The original event if any.\n     * @returns {Cropper} this\n     */\n    zoomTo: function zoomTo(ratio, pivot, _originalEvent) {\n      var options = this.options,\n          canvasData = this.canvasData;\n      var width = canvasData.width,\n          height = canvasData.height,\n          naturalWidth = canvasData.naturalWidth,\n          naturalHeight = canvasData.naturalHeight;\n      ratio = Number(ratio);\n\n      if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {\n        var newWidth = naturalWidth * ratio;\n        var newHeight = naturalHeight * ratio;\n\n        if (dispatchEvent(this.element, EVENT_ZOOM, {\n          ratio: ratio,\n          oldRatio: width / naturalWidth,\n          originalEvent: _originalEvent\n        }) === false) {\n          return this;\n        }\n\n        if (_originalEvent) {\n          var pointers = this.pointers;\n          var offset = getOffset(this.cropper);\n          var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {\n            pageX: _originalEvent.pageX,\n            pageY: _originalEvent.pageY\n          }; // Zoom from the triggering point of the event\n\n          canvasData.left -= (newWidth - width) * ((center.pageX - offset.left - canvasData.left) / width);\n          canvasData.top -= (newHeight - height) * ((center.pageY - offset.top - canvasData.top) / height);\n        } else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {\n          canvasData.left -= (newWidth - width) * ((pivot.x - canvasData.left) / width);\n          canvasData.top -= (newHeight - height) * ((pivot.y - canvasData.top) / height);\n        } else {\n          // Zoom from the center of the canvas\n          canvasData.left -= (newWidth - width) / 2;\n          canvasData.top -= (newHeight - height) / 2;\n        }\n\n        canvasData.width = newWidth;\n        canvasData.height = newHeight;\n        this.renderCanvas(true);\n      }\n\n      return this;\n    },\n\n    /**\n     * Rotate the canvas with a relative degree\n     * @param {number} degree - The rotate degree.\n     * @returns {Cropper} this\n     */\n    rotate: function rotate(degree) {\n      return this.rotateTo((this.imageData.rotate || 0) + Number(degree));\n    },\n\n    /**\n     * Rotate the canvas to an absolute degree\n     * @param {number} degree - The rotate degree.\n     * @returns {Cropper} this\n     */\n    rotateTo: function rotateTo(degree) {\n      degree = Number(degree);\n\n      if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {\n        this.imageData.rotate = degree % 360;\n        this.renderCanvas(true, true);\n      }\n\n      return this;\n    },\n\n    /**\n     * Scale the image on the x-axis.\n     * @param {number} scaleX - The scale ratio on the x-axis.\n     * @returns {Cropper} this\n     */\n    scaleX: function scaleX(_scaleX) {\n      var scaleY = this.imageData.scaleY;\n      return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);\n    },\n\n    /**\n     * Scale the image on the y-axis.\n     * @param {number} scaleY - The scale ratio on the y-axis.\n     * @returns {Cropper} this\n     */\n    scaleY: function scaleY(_scaleY) {\n      var scaleX = this.imageData.scaleX;\n      return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);\n    },\n\n    /**\n     * Scale the image\n     * @param {number} scaleX - The scale ratio on the x-axis.\n     * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.\n     * @returns {Cropper} this\n     */\n    scale: function scale(scaleX) {\n      var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;\n      var imageData = this.imageData;\n      var transformed = false;\n      scaleX = Number(scaleX);\n      scaleY = Number(scaleY);\n\n      if (this.ready && !this.disabled && this.options.scalable) {\n        if (isNumber(scaleX)) {\n          imageData.scaleX = scaleX;\n          transformed = true;\n        }\n\n        if (isNumber(scaleY)) {\n          imageData.scaleY = scaleY;\n          transformed = true;\n        }\n\n        if (transformed) {\n          this.renderCanvas(true, true);\n        }\n      }\n\n      return this;\n    },\n\n    /**\n     * Get the cropped area position and size data (base on the original image)\n     * @param {boolean} [rounded=false] - Indicate if round the data values or not.\n     * @returns {Object} The result cropped data.\n     */\n    getData: function getData() {\n      var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n      var options = this.options,\n          imageData = this.imageData,\n          canvasData = this.canvasData,\n          cropBoxData = this.cropBoxData;\n      var data;\n\n      if (this.ready && this.cropped) {\n        data = {\n          x: cropBoxData.left - canvasData.left,\n          y: cropBoxData.top - canvasData.top,\n          width: cropBoxData.width,\n          height: cropBoxData.height\n        };\n        var ratio = imageData.width / imageData.naturalWidth;\n        forEach(data, function (n, i) {\n          data[i] = n / ratio;\n        });\n\n        if (rounded) {\n          // In case rounding off leads to extra 1px in right or bottom border\n          // we should round the top-left corner and the dimension (#343).\n          var bottom = Math.round(data.y + data.height);\n          var right = Math.round(data.x + data.width);\n          data.x = Math.round(data.x);\n          data.y = Math.round(data.y);\n          data.width = right - data.x;\n          data.height = bottom - data.y;\n        }\n      } else {\n        data = {\n          x: 0,\n          y: 0,\n          width: 0,\n          height: 0\n        };\n      }\n\n      if (options.rotatable) {\n        data.rotate = imageData.rotate || 0;\n      }\n\n      if (options.scalable) {\n        data.scaleX = imageData.scaleX || 1;\n        data.scaleY = imageData.scaleY || 1;\n      }\n\n      return data;\n    },\n\n    /**\n     * Set the cropped area position and size with new data\n     * @param {Object} data - The new data.\n     * @returns {Cropper} this\n     */\n    setData: function setData(data) {\n      var options = this.options,\n          imageData = this.imageData,\n          canvasData = this.canvasData;\n      var cropBoxData = {};\n\n      if (this.ready && !this.disabled && isPlainObject(data)) {\n        var transformed = false;\n\n        if (options.rotatable) {\n          if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {\n            imageData.rotate = data.rotate;\n            transformed = true;\n          }\n        }\n\n        if (options.scalable) {\n          if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {\n            imageData.scaleX = data.scaleX;\n            transformed = true;\n          }\n\n          if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {\n            imageData.scaleY = data.scaleY;\n            transformed = true;\n          }\n        }\n\n        if (transformed) {\n          this.renderCanvas(true, true);\n        }\n\n        var ratio = imageData.width / imageData.naturalWidth;\n\n        if (isNumber(data.x)) {\n          cropBoxData.left = data.x * ratio + canvasData.left;\n        }\n\n        if (isNumber(data.y)) {\n          cropBoxData.top = data.y * ratio + canvasData.top;\n        }\n\n        if (isNumber(data.width)) {\n          cropBoxData.width = data.width * ratio;\n        }\n\n        if (isNumber(data.height)) {\n          cropBoxData.height = data.height * ratio;\n        }\n\n        this.setCropBoxData(cropBoxData);\n      }\n\n      return this;\n    },\n\n    /**\n     * Get the container size data.\n     * @returns {Object} The result container data.\n     */\n    getContainerData: function getContainerData() {\n      return this.ready ? assign({}, this.containerData) : {};\n    },\n\n    /**\n     * Get the image position and size data.\n     * @returns {Object} The result image data.\n     */\n    getImageData: function getImageData() {\n      return this.sized ? assign({}, this.imageData) : {};\n    },\n\n    /**\n     * Get the canvas position and size data.\n     * @returns {Object} The result canvas data.\n     */\n    getCanvasData: function getCanvasData() {\n      var canvasData = this.canvasData;\n      var data = {};\n\n      if (this.ready) {\n        forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {\n          data[n] = canvasData[n];\n        });\n      }\n\n      return data;\n    },\n\n    /**\n     * Set the canvas position and size with new data.\n     * @param {Object} data - The new canvas data.\n     * @returns {Cropper} this\n     */\n    setCanvasData: function setCanvasData(data) {\n      var canvasData = this.canvasData;\n      var aspectRatio = canvasData.aspectRatio;\n\n      if (this.ready && !this.disabled && isPlainObject(data)) {\n        if (isNumber(data.left)) {\n          canvasData.left = data.left;\n        }\n\n        if (isNumber(data.top)) {\n          canvasData.top = data.top;\n        }\n\n        if (isNumber(data.width)) {\n          canvasData.width = data.width;\n          canvasData.height = data.width / aspectRatio;\n        } else if (isNumber(data.height)) {\n          canvasData.height = data.height;\n          canvasData.width = data.height * aspectRatio;\n        }\n\n        this.renderCanvas(true);\n      }\n\n      return this;\n    },\n\n    /**\n     * Get the crop box position and size data.\n     * @returns {Object} The result crop box data.\n     */\n    getCropBoxData: function getCropBoxData() {\n      var cropBoxData = this.cropBoxData;\n      var data;\n\n      if (this.ready && this.cropped) {\n        data = {\n          left: cropBoxData.left,\n          top: cropBoxData.top,\n          width: cropBoxData.width,\n          height: cropBoxData.height\n        };\n      }\n\n      return data || {};\n    },\n\n    /**\n     * Set the crop box position and size with new data.\n     * @param {Object} data - The new crop box data.\n     * @returns {Cropper} this\n     */\n    setCropBoxData: function setCropBoxData(data) {\n      var cropBoxData = this.cropBoxData;\n      var aspectRatio = this.options.aspectRatio;\n      var widthChanged;\n      var heightChanged;\n\n      if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {\n        if (isNumber(data.left)) {\n          cropBoxData.left = data.left;\n        }\n\n        if (isNumber(data.top)) {\n          cropBoxData.top = data.top;\n        }\n\n        if (isNumber(data.width) && data.width !== cropBoxData.width) {\n          widthChanged = true;\n          cropBoxData.width = data.width;\n        }\n\n        if (isNumber(data.height) && data.height !== cropBoxData.height) {\n          heightChanged = true;\n          cropBoxData.height = data.height;\n        }\n\n        if (aspectRatio) {\n          if (widthChanged) {\n            cropBoxData.height = cropBoxData.width / aspectRatio;\n          } else if (heightChanged) {\n            cropBoxData.width = cropBoxData.height * aspectRatio;\n          }\n        }\n\n        this.renderCropBox();\n      }\n\n      return this;\n    },\n\n    /**\n     * Get a canvas drawn the cropped image.\n     * @param {Object} [options={}] - The config options.\n     * @returns {HTMLCanvasElement} - The result canvas.\n     */\n    getCroppedCanvas: function getCroppedCanvas() {\n      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n      if (!this.ready || !window.HTMLCanvasElement) {\n        return null;\n      }\n\n      var canvasData = this.canvasData;\n      var source = getSourceCanvas(this.image, this.imageData, canvasData, options); // Returns the source canvas if it is not cropped.\n\n      if (!this.cropped) {\n        return source;\n      }\n\n      var _this$getData = this.getData(),\n          initialX = _this$getData.x,\n          initialY = _this$getData.y,\n          initialWidth = _this$getData.width,\n          initialHeight = _this$getData.height;\n\n      var ratio = source.width / Math.floor(canvasData.naturalWidth);\n\n      if (ratio !== 1) {\n        initialX *= ratio;\n        initialY *= ratio;\n        initialWidth *= ratio;\n        initialHeight *= ratio;\n      }\n\n      var aspectRatio = initialWidth / initialHeight;\n      var maxSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: options.maxWidth || Infinity,\n        height: options.maxHeight || Infinity\n      });\n      var minSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: options.minWidth || 0,\n        height: options.minHeight || 0\n      }, 'cover');\n\n      var _getAdjustedSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: options.width || (ratio !== 1 ? source.width : initialWidth),\n        height: options.height || (ratio !== 1 ? source.height : initialHeight)\n      }),\n          width = _getAdjustedSizes.width,\n          height = _getAdjustedSizes.height;\n\n      width = Math.min(maxSizes.width, Math.max(minSizes.width, width));\n      height = Math.min(maxSizes.height, Math.max(minSizes.height, height));\n      var canvas = document.createElement('canvas');\n      var context = canvas.getContext('2d');\n      canvas.width = normalizeDecimalNumber(width);\n      canvas.height = normalizeDecimalNumber(height);\n      context.fillStyle = options.fillColor || 'transparent';\n      context.fillRect(0, 0, width, height);\n      var _options$imageSmoothi = options.imageSmoothingEnabled,\n          imageSmoothingEnabled = _options$imageSmoothi === void 0 ? true : _options$imageSmoothi,\n          imageSmoothingQuality = options.imageSmoothingQuality;\n      context.imageSmoothingEnabled = imageSmoothingEnabled;\n\n      if (imageSmoothingQuality) {\n        context.imageSmoothingQuality = imageSmoothingQuality;\n      } // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n\n\n      var sourceWidth = source.width;\n      var sourceHeight = source.height; // Source canvas parameters\n\n      var srcX = initialX;\n      var srcY = initialY;\n      var srcWidth;\n      var srcHeight; // Destination canvas parameters\n\n      var dstX;\n      var dstY;\n      var dstWidth;\n      var dstHeight;\n\n      if (srcX <= -initialWidth || srcX > sourceWidth) {\n        srcX = 0;\n        srcWidth = 0;\n        dstX = 0;\n        dstWidth = 0;\n      } else if (srcX <= 0) {\n        dstX = -srcX;\n        srcX = 0;\n        srcWidth = Math.min(sourceWidth, initialWidth + srcX);\n        dstWidth = srcWidth;\n      } else if (srcX <= sourceWidth) {\n        dstX = 0;\n        srcWidth = Math.min(initialWidth, sourceWidth - srcX);\n        dstWidth = srcWidth;\n      }\n\n      if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {\n        srcY = 0;\n        srcHeight = 0;\n        dstY = 0;\n        dstHeight = 0;\n      } else if (srcY <= 0) {\n        dstY = -srcY;\n        srcY = 0;\n        srcHeight = Math.min(sourceHeight, initialHeight + srcY);\n        dstHeight = srcHeight;\n      } else if (srcY <= sourceHeight) {\n        dstY = 0;\n        srcHeight = Math.min(initialHeight, sourceHeight - srcY);\n        dstHeight = srcHeight;\n      }\n\n      var params = [srcX, srcY, srcWidth, srcHeight]; // Avoid \"IndexSizeError\"\n\n      if (dstWidth > 0 && dstHeight > 0) {\n        var scale = width / initialWidth;\n        params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);\n      } // All the numerical parameters should be integer for `drawImage`\n      // https://github.com/fengyuanchen/cropper/issues/476\n\n\n      context.drawImage.apply(context, [source].concat(_toConsumableArray(params.map(function (param) {\n        return Math.floor(normalizeDecimalNumber(param));\n      }))));\n      return canvas;\n    },\n\n    /**\n     * Change the aspect ratio of the crop box.\n     * @param {number} aspectRatio - The new aspect ratio.\n     * @returns {Cropper} this\n     */\n    setAspectRatio: function setAspectRatio(aspectRatio) {\n      var options = this.options;\n\n      if (!this.disabled && !isUndefined(aspectRatio)) {\n        // 0 -> NaN\n        options.aspectRatio = Math.max(0, aspectRatio) || NaN;\n\n        if (this.ready) {\n          this.initCropBox();\n\n          if (this.cropped) {\n            this.renderCropBox();\n          }\n        }\n      }\n\n      return this;\n    },\n\n    /**\n     * Change the drag mode.\n     * @param {string} mode - The new drag mode.\n     * @returns {Cropper} this\n     */\n    setDragMode: function setDragMode(mode) {\n      var options = this.options,\n          dragBox = this.dragBox,\n          face = this.face;\n\n      if (this.ready && !this.disabled) {\n        var croppable = mode === DRAG_MODE_CROP;\n        var movable = options.movable && mode === DRAG_MODE_MOVE;\n        mode = croppable || movable ? mode : DRAG_MODE_NONE;\n        options.dragMode = mode;\n        setData(dragBox, DATA_ACTION, mode);\n        toggleClass(dragBox, CLASS_CROP, croppable);\n        toggleClass(dragBox, CLASS_MOVE, movable);\n\n        if (!options.cropBoxMovable) {\n          // Sync drag mode to crop box when it is not movable\n          setData(face, DATA_ACTION, mode);\n          toggleClass(face, CLASS_CROP, croppable);\n          toggleClass(face, CLASS_MOVE, movable);\n        }\n      }\n\n      return this;\n    }\n  };\n\n  var AnotherCropper = WINDOW.Cropper;\n\n  var Cropper =\n  /*#__PURE__*/\n  function () {\n    /**\n     * Create a new Cropper.\n     * @param {Element} element - The target element for cropping.\n     * @param {Object} [options={}] - The configuration options.\n     */\n    function Cropper(element) {\n      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n      _classCallCheck(this, Cropper);\n\n      if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {\n        throw new Error('The first argument is required and must be an <img> or <canvas> element.');\n      }\n\n      this.element = element;\n      this.options = assign({}, DEFAULTS, isPlainObject(options) && options);\n      this.cropped = false;\n      this.disabled = false;\n      this.pointers = {};\n      this.ready = false;\n      this.reloading = false;\n      this.replaced = false;\n      this.sized = false;\n      this.sizing = false;\n      this.init();\n    }\n\n    _createClass(Cropper, [{\n      key: \"init\",\n      value: function init() {\n        var element = this.element;\n        var tagName = element.tagName.toLowerCase();\n        var url;\n\n        if (element[NAMESPACE]) {\n          return;\n        }\n\n        element[NAMESPACE] = this;\n\n        if (tagName === 'img') {\n          this.isImg = true; // e.g.: \"img/picture.jpg\"\n\n          url = element.getAttribute('src') || '';\n          this.originalUrl = url; // Stop when it's a blank image\n\n          if (!url) {\n            return;\n          } // e.g.: \"https://example.com/img/picture.jpg\"\n\n\n          url = element.src;\n        } else if (tagName === 'canvas' && window.HTMLCanvasElement) {\n          url = element.toDataURL();\n        }\n\n        this.load(url);\n      }\n    }, {\n      key: \"load\",\n      value: function load(url) {\n        var _this = this;\n\n        if (!url) {\n          return;\n        }\n\n        this.url = url;\n        this.imageData = {};\n        var element = this.element,\n            options = this.options;\n\n        if (!options.rotatable && !options.scalable) {\n          options.checkOrientation = false;\n        } // Only IE10+ supports Typed Arrays\n\n\n        if (!options.checkOrientation || !window.ArrayBuffer) {\n          this.clone();\n          return;\n        } // Detect the mime type of the image directly if it is a Data URL\n\n\n        if (REGEXP_DATA_URL.test(url)) {\n          // Read ArrayBuffer from Data URL of JPEG images directly for better performance\n          if (REGEXP_DATA_URL_JPEG.test(url)) {\n            this.read(dataURLToArrayBuffer(url));\n          } else {\n            // Only a JPEG image may contains Exif Orientation information,\n            // the rest types of Data URLs are not necessary to check orientation at all.\n            this.clone();\n          }\n\n          return;\n        } // 1. Detect the mime type of the image by a XMLHttpRequest.\n        // 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.\n\n\n        var xhr = new XMLHttpRequest();\n        var clone = this.clone.bind(this);\n        this.reloading = true;\n        this.xhr = xhr; // 1. Cross origin requests are only supported for protocol schemes:\n        // http, https, data, chrome, chrome-extension.\n        // 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy\n        // in some browsers as IE11 and Safari.\n\n        xhr.onabort = clone;\n        xhr.onerror = clone;\n        xhr.ontimeout = clone;\n\n        xhr.onprogress = function () {\n          // Abort the request directly if it not a JPEG image for better performance\n          if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {\n            xhr.abort();\n          }\n        };\n\n        xhr.onload = function () {\n          _this.read(xhr.response);\n        };\n\n        xhr.onloadend = function () {\n          _this.reloading = false;\n          _this.xhr = null;\n        }; // Bust cache when there is a \"crossOrigin\" property to avoid browser cache error\n\n\n        if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {\n          url = addTimestamp(url);\n        }\n\n        xhr.open('GET', url);\n        xhr.responseType = 'arraybuffer';\n        xhr.withCredentials = element.crossOrigin === 'use-credentials';\n        xhr.send();\n      }\n    }, {\n      key: \"read\",\n      value: function read(arrayBuffer) {\n        var options = this.options,\n            imageData = this.imageData; // Reset the orientation value to its default value 1\n        // as some iOS browsers will render image with its orientation\n\n        var orientation = resetAndGetOrientation(arrayBuffer);\n        var rotate = 0;\n        var scaleX = 1;\n        var scaleY = 1;\n\n        if (orientation > 1) {\n          // Generate a new URL which has the default orientation value\n          this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);\n\n          var _parseOrientation = parseOrientation(orientation);\n\n          rotate = _parseOrientation.rotate;\n          scaleX = _parseOrientation.scaleX;\n          scaleY = _parseOrientation.scaleY;\n        }\n\n        if (options.rotatable) {\n          imageData.rotate = rotate;\n        }\n\n        if (options.scalable) {\n          imageData.scaleX = scaleX;\n          imageData.scaleY = scaleY;\n        }\n\n        this.clone();\n      }\n    }, {\n      key: \"clone\",\n      value: function clone() {\n        var element = this.element,\n            url = this.url;\n        var crossOrigin = element.crossOrigin;\n        var crossOriginUrl = url;\n\n        if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {\n          if (!crossOrigin) {\n            crossOrigin = 'anonymous';\n          } // Bust cache when there is not a \"crossOrigin\" property (#519)\n\n\n          crossOriginUrl = addTimestamp(url);\n        }\n\n        this.crossOrigin = crossOrigin;\n        this.crossOriginUrl = crossOriginUrl;\n        var image = document.createElement('img');\n\n        if (crossOrigin) {\n          image.crossOrigin = crossOrigin;\n        }\n\n        image.src = crossOriginUrl || url;\n        image.alt = element.alt || 'The image to crop';\n        this.image = image;\n        image.onload = this.start.bind(this);\n        image.onerror = this.stop.bind(this);\n        addClass(image, CLASS_HIDE);\n        element.parentNode.insertBefore(image, element.nextSibling);\n      }\n    }, {\n      key: \"start\",\n      value: function start() {\n        var _this2 = this;\n\n        var image = this.image;\n        image.onload = null;\n        image.onerror = null;\n        this.sizing = true; // Match all browsers that use WebKit as the layout engine in iOS devices,\n        // such as Safari for iOS, Chrome for iOS, and in-app browsers.\n\n        var isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);\n\n        var done = function done(naturalWidth, naturalHeight) {\n          assign(_this2.imageData, {\n            naturalWidth: naturalWidth,\n            naturalHeight: naturalHeight,\n            aspectRatio: naturalWidth / naturalHeight\n          });\n          _this2.sizing = false;\n          _this2.sized = true;\n\n          _this2.build();\n        }; // Most modern browsers (excepts iOS WebKit)\n\n\n        if (image.naturalWidth && !isIOSWebKit) {\n          done(image.naturalWidth, image.naturalHeight);\n          return;\n        }\n\n        var sizingImage = document.createElement('img');\n        var body = document.body || document.documentElement;\n        this.sizingImage = sizingImage;\n\n        sizingImage.onload = function () {\n          done(sizingImage.width, sizingImage.height);\n\n          if (!isIOSWebKit) {\n            body.removeChild(sizingImage);\n          }\n        };\n\n        sizingImage.src = image.src; // iOS WebKit will convert the image automatically\n        // with its orientation once append it into DOM (#279)\n\n        if (!isIOSWebKit) {\n          sizingImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';\n          body.appendChild(sizingImage);\n        }\n      }\n    }, {\n      key: \"stop\",\n      value: function stop() {\n        var image = this.image;\n        image.onload = null;\n        image.onerror = null;\n        image.parentNode.removeChild(image);\n        this.image = null;\n      }\n    }, {\n      key: \"build\",\n      value: function build() {\n        if (!this.sized || this.ready) {\n          return;\n        }\n\n        var element = this.element,\n            options = this.options,\n            image = this.image; // Create cropper elements\n\n        var container = element.parentNode;\n        var template = document.createElement('div');\n        template.innerHTML = TEMPLATE;\n        var cropper = template.querySelector(\".\".concat(NAMESPACE, \"-container\"));\n        var canvas = cropper.querySelector(\".\".concat(NAMESPACE, \"-canvas\"));\n        var dragBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-drag-box\"));\n        var cropBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-crop-box\"));\n        var face = cropBox.querySelector(\".\".concat(NAMESPACE, \"-face\"));\n        this.container = container;\n        this.cropper = cropper;\n        this.canvas = canvas;\n        this.dragBox = dragBox;\n        this.cropBox = cropBox;\n        this.viewBox = cropper.querySelector(\".\".concat(NAMESPACE, \"-view-box\"));\n        this.face = face;\n        canvas.appendChild(image); // Hide the original image\n\n        addClass(element, CLASS_HIDDEN); // Inserts the cropper after to the current image\n\n        container.insertBefore(cropper, element.nextSibling); // Show the image if is hidden\n\n        if (!this.isImg) {\n          removeClass(image, CLASS_HIDE);\n        }\n\n        this.initPreview();\n        this.bind();\n        options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;\n        options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;\n        options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;\n        addClass(cropBox, CLASS_HIDDEN);\n\n        if (!options.guides) {\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-dashed\")), CLASS_HIDDEN);\n        }\n\n        if (!options.center) {\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-center\")), CLASS_HIDDEN);\n        }\n\n        if (options.background) {\n          addClass(cropper, \"\".concat(NAMESPACE, \"-bg\"));\n        }\n\n        if (!options.highlight) {\n          addClass(face, CLASS_INVISIBLE);\n        }\n\n        if (options.cropBoxMovable) {\n          addClass(face, CLASS_MOVE);\n          setData(face, DATA_ACTION, ACTION_ALL);\n        }\n\n        if (!options.cropBoxResizable) {\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-line\")), CLASS_HIDDEN);\n          addClass(cropBox.getElementsByClassName(\"\".concat(NAMESPACE, \"-point\")), CLASS_HIDDEN);\n        }\n\n        this.render();\n        this.ready = true;\n        this.setDragMode(options.dragMode);\n\n        if (options.autoCrop) {\n          this.crop();\n        }\n\n        this.setData(options.data);\n\n        if (isFunction(options.ready)) {\n          addListener(element, EVENT_READY, options.ready, {\n            once: true\n          });\n        }\n\n        dispatchEvent(element, EVENT_READY);\n      }\n    }, {\n      key: \"unbuild\",\n      value: function unbuild() {\n        if (!this.ready) {\n          return;\n        }\n\n        this.ready = false;\n        this.unbind();\n        this.resetPreview();\n        this.cropper.parentNode.removeChild(this.cropper);\n        removeClass(this.element, CLASS_HIDDEN);\n      }\n    }, {\n      key: \"uncreate\",\n      value: function uncreate() {\n        if (this.ready) {\n          this.unbuild();\n          this.ready = false;\n          this.cropped = false;\n        } else if (this.sizing) {\n          this.sizingImage.onload = null;\n          this.sizing = false;\n          this.sized = false;\n        } else if (this.reloading) {\n          this.xhr.onabort = null;\n          this.xhr.abort();\n        } else if (this.image) {\n          this.stop();\n        }\n      }\n      /**\n       * Get the no conflict cropper class.\n       * @returns {Cropper} The cropper class.\n       */\n\n    }], [{\n      key: \"noConflict\",\n      value: function noConflict() {\n        window.Cropper = AnotherCropper;\n        return Cropper;\n      }\n      /**\n       * Change the default options.\n       * @param {Object} options - The new default options.\n       */\n\n    }, {\n      key: \"setDefaults\",\n      value: function setDefaults(options) {\n        assign(DEFAULTS, isPlainObject(options) && options);\n      }\n    }]);\n\n    return Cropper;\n  }();\n\n  assign(Cropper.prototype, render, preview, events, handlers, change, methods);\n\n  if ($.fn) {\n    var AnotherCropper$1 = $.fn.cropper;\n    var NAMESPACE$1 = 'cropper';\n\n    $.fn.cropper = function jQueryCropper(option) {\n      for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n        args[_key - 1] = arguments[_key];\n      }\n\n      var result;\n      this.each(function (i, element) {\n        var $element = $(element);\n        var isDestroy = option === 'destroy';\n        var cropper = $element.data(NAMESPACE$1);\n\n        if (!cropper) {\n          if (isDestroy) {\n            return;\n          }\n\n          var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);\n          cropper = new Cropper(element, options);\n          $element.data(NAMESPACE$1, cropper);\n        }\n\n        if (typeof option === 'string') {\n          var fn = cropper[option];\n\n          if ($.isFunction(fn)) {\n            result = fn.apply(cropper, args);\n\n            if (result === cropper) {\n              result = undefined;\n            }\n\n            if (isDestroy) {\n              $element.removeData(NAMESPACE$1);\n            }\n          }\n        }\n      });\n      return result !== undefined ? result : this;\n    };\n\n    $.fn.cropper.Constructor = Cropper;\n    $.fn.cropper.setDefaults = Cropper.setDefaults;\n\n    $.fn.cropper.noConflict = function noConflict() {\n      $.fn.cropper = AnotherCropper$1;\n      return this;\n    };\n  }\n\n}));\n"
  },
  {
    "path": "docs/js/main.js",
    "content": "$(function () {\n  'use strict';\n\n  var console = window.console || { log: function () {} };\n  var URL = window.URL || window.webkitURL;\n  var $image = $('#image');\n  var $download = $('#download');\n  var $dataX = $('#dataX');\n  var $dataY = $('#dataY');\n  var $dataHeight = $('#dataHeight');\n  var $dataWidth = $('#dataWidth');\n  var $dataRotate = $('#dataRotate');\n  var $dataScaleX = $('#dataScaleX');\n  var $dataScaleY = $('#dataScaleY');\n  var options = {\n    aspectRatio: 16 / 9,\n    preview: '.img-preview',\n    crop: function (e) {\n      $dataX.val(Math.round(e.detail.x));\n      $dataY.val(Math.round(e.detail.y));\n      $dataHeight.val(Math.round(e.detail.height));\n      $dataWidth.val(Math.round(e.detail.width));\n      $dataRotate.val(e.detail.rotate);\n      $dataScaleX.val(e.detail.scaleX);\n      $dataScaleY.val(e.detail.scaleY);\n    }\n  };\n  var originalImageURL = $image.attr('src');\n  var uploadedImageName = 'cropped.jpg';\n  var uploadedImageType = 'image/jpeg';\n  var uploadedImageURL;\n\n  // Tooltip\n  $('[data-toggle=\"tooltip\"]').tooltip();\n\n  // Cropper\n  $image.on({\n    ready: function (e) {\n      console.log(e.type);\n    },\n    cropstart: function (e) {\n      console.log(e.type, e.detail.action);\n    },\n    cropmove: function (e) {\n      console.log(e.type, e.detail.action);\n    },\n    cropend: function (e) {\n      console.log(e.type, e.detail.action);\n    },\n    crop: function (e) {\n      console.log(e.type);\n    },\n    zoom: function (e) {\n      console.log(e.type, e.detail.ratio);\n    }\n  }).cropper(options);\n\n  // Buttons\n  if (!$.isFunction(document.createElement('canvas').getContext)) {\n    $('button[data-method=\"getCroppedCanvas\"]').prop('disabled', true);\n  }\n\n  if (typeof document.createElement('cropper').style.transition === 'undefined') {\n    $('button[data-method=\"rotate\"]').prop('disabled', true);\n    $('button[data-method=\"scale\"]').prop('disabled', true);\n  }\n\n  // Download\n  if (typeof $download[0].download === 'undefined') {\n    $download.addClass('disabled');\n  }\n\n  // Options\n  $('.docs-toggles').on('change', 'input', function () {\n    var $this = $(this);\n    var name = $this.attr('name');\n    var type = $this.prop('type');\n    var cropBoxData;\n    var canvasData;\n\n    if (!$image.data('cropper')) {\n      return;\n    }\n\n    if (type === 'checkbox') {\n      options[name] = $this.prop('checked');\n      cropBoxData = $image.cropper('getCropBoxData');\n      canvasData = $image.cropper('getCanvasData');\n\n      options.ready = function () {\n        $image.cropper('setCropBoxData', cropBoxData);\n        $image.cropper('setCanvasData', canvasData);\n      };\n    } else if (type === 'radio') {\n      options[name] = $this.val();\n    }\n\n    $image.cropper('destroy').cropper(options);\n  });\n\n  // Methods\n  $('.docs-buttons').on('click', '[data-method]', function () {\n    var $this = $(this);\n    var data = $this.data();\n    var cropper = $image.data('cropper');\n    var cropped;\n    var $target;\n    var result;\n\n    if ($this.prop('disabled') || $this.hasClass('disabled')) {\n      return;\n    }\n\n    if (cropper && data.method) {\n      data = $.extend({}, data); // Clone a new one\n\n      if (typeof data.target !== 'undefined') {\n        $target = $(data.target);\n\n        if (typeof data.option === 'undefined') {\n          try {\n            data.option = JSON.parse($target.val());\n          } catch (e) {\n            console.log(e.message);\n          }\n        }\n      }\n\n      cropped = cropper.cropped;\n\n      switch (data.method) {\n        case 'rotate':\n          if (cropped && options.viewMode > 0) {\n            $image.cropper('clear');\n          }\n\n          break;\n\n        case 'getCroppedCanvas':\n          if (uploadedImageType === 'image/jpeg') {\n            if (!data.option) {\n              data.option = {};\n            }\n\n            data.option.fillColor = '#fff';\n          }\n\n          break;\n      }\n\n      result = $image.cropper(data.method, data.option, data.secondOption);\n\n      switch (data.method) {\n        case 'rotate':\n          if (cropped && options.viewMode > 0) {\n            $image.cropper('crop');\n          }\n\n          break;\n\n        case 'scaleX':\n        case 'scaleY':\n          $(this).data('option', -data.option);\n          break;\n\n        case 'getCroppedCanvas':\n          if (result) {\n            // Bootstrap's Modal\n            $('#getCroppedCanvasModal').modal().find('.modal-body').html(result);\n\n            if (!$download.hasClass('disabled')) {\n              download.download = uploadedImageName;\n              $download.attr('href', result.toDataURL(uploadedImageType));\n            }\n          }\n\n          break;\n\n        case 'destroy':\n          if (uploadedImageURL) {\n            URL.revokeObjectURL(uploadedImageURL);\n            uploadedImageURL = '';\n            $image.attr('src', originalImageURL);\n          }\n\n          break;\n      }\n\n      if ($.isPlainObject(result) && $target) {\n        try {\n          $target.val(JSON.stringify(result));\n        } catch (e) {\n          console.log(e.message);\n        }\n      }\n    }\n  });\n\n  // Keyboard\n  $(document.body).on('keydown', function (e) {\n    if (e.target !== this || !$image.data('cropper') || this.scrollTop > 300) {\n      return;\n    }\n\n    switch (e.which) {\n      case 37:\n        e.preventDefault();\n        $image.cropper('move', -1, 0);\n        break;\n\n      case 38:\n        e.preventDefault();\n        $image.cropper('move', 0, -1);\n        break;\n\n      case 39:\n        e.preventDefault();\n        $image.cropper('move', 1, 0);\n        break;\n\n      case 40:\n        e.preventDefault();\n        $image.cropper('move', 0, 1);\n        break;\n    }\n  });\n\n  // Import image\n  var $inputImage = $('#inputImage');\n\n  if (URL) {\n    $inputImage.change(function () {\n      var files = this.files;\n      var file;\n\n      if (!$image.data('cropper')) {\n        return;\n      }\n\n      if (files && files.length) {\n        file = files[0];\n\n        if (/^image\\/\\w+$/.test(file.type)) {\n          uploadedImageName = file.name;\n          uploadedImageType = file.type;\n\n          if (uploadedImageURL) {\n            URL.revokeObjectURL(uploadedImageURL);\n          }\n\n          uploadedImageURL = URL.createObjectURL(file);\n          $image.cropper('destroy').attr('src', uploadedImageURL).cropper(options);\n          $inputImage.val('');\n        } else {\n          window.alert('Please choose an image file.');\n        }\n      }\n    });\n  } else {\n    $inputImage.prop('disabled', true).parent().addClass('disabled');\n  }\n});\n"
  },
  {
    "path": "docs/v2.3.4/css/cropper.css",
    "content": "/*!\n * Cropper v2.3.4\n * https://github.com/fengyuanchen/cropper\n *\n * Copyright (c) 2014-2016 Chen Fengyuan and contributors\n * Released under the MIT license\n *\n * Date: 2016-09-03T05:50:45.412Z\n */\n.cropper-container {\n  font-size: 0;\n  line-height: 0;\n\n  position: relative;\n\n  -webkit-user-select: none;\n     -moz-user-select: none;\n      -ms-user-select: none;\n          user-select: none;\n\n  direction: ltr !important;\n}\n\n.cropper-container img {\n  display: block;\n\n  width: 100%;\n  min-width: 0 !important;\n  max-width: none !important;\n  height: 100%;\n  min-height: 0 !important;\n  max-height: none !important;\n\n  image-orientation: 0deg !important;\n}\n\n.cropper-wrap-box,\n.cropper-canvas,\n.cropper-drag-box,\n.cropper-crop-box,\n.cropper-modal {\n  position: absolute;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  left: 0;\n}\n\n.cropper-wrap-box {\n  overflow: hidden;\n}\n\n.cropper-drag-box {\n  opacity: 0;\n  background-color: #fff;\n\n  filter: alpha(opacity=0);\n}\n\n.cropper-modal {\n  opacity: .5;\n  background-color: #000;\n\n  filter: alpha(opacity=50);\n}\n\n.cropper-view-box {\n  display: block;\n  overflow: hidden;\n\n  width: 100%;\n  height: 100%;\n\n  outline: 1px solid #39f;\n  outline-color: rgba(51, 153, 255, .75);\n}\n\n.cropper-dashed {\n  position: absolute;\n\n  display: block;\n\n  opacity: .5;\n  border: 0 dashed #eee;\n\n  filter: alpha(opacity=50);\n}\n\n.cropper-dashed.dashed-h {\n  top: 33.33333%;\n  left: 0;\n\n  width: 100%;\n  height: 33.33333%;\n\n  border-top-width: 1px;\n  border-bottom-width: 1px;\n}\n\n.cropper-dashed.dashed-v {\n  top: 0;\n  left: 33.33333%;\n\n  width: 33.33333%;\n  height: 100%;\n\n  border-right-width: 1px;\n  border-left-width: 1px;\n}\n\n.cropper-center {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n\n  display: block;\n\n  width: 0;\n  height: 0;\n\n  opacity: .75;\n\n  filter: alpha(opacity=75);\n}\n\n.cropper-center:before,\n.cropper-center:after {\n  position: absolute;\n\n  display: block;\n\n  content: ' ';\n\n  background-color: #eee;\n}\n\n.cropper-center:before {\n  top: 0;\n  left: -3px;\n\n  width: 7px;\n  height: 1px;\n}\n\n.cropper-center:after {\n  top: -3px;\n  left: 0;\n\n  width: 1px;\n  height: 7px;\n}\n\n.cropper-face,\n.cropper-line,\n.cropper-point {\n  position: absolute;\n\n  display: block;\n\n  width: 100%;\n  height: 100%;\n\n  opacity: .1;\n\n  filter: alpha(opacity=10);\n}\n\n.cropper-face {\n  top: 0;\n  left: 0;\n\n  background-color: #fff;\n}\n\n.cropper-line {\n  background-color: #39f;\n}\n\n.cropper-line.line-e {\n  top: 0;\n  right: -3px;\n\n  width: 5px;\n\n  cursor: e-resize;\n}\n\n.cropper-line.line-n {\n  top: -3px;\n  left: 0;\n\n  height: 5px;\n\n  cursor: n-resize;\n}\n\n.cropper-line.line-w {\n  top: 0;\n  left: -3px;\n\n  width: 5px;\n\n  cursor: w-resize;\n}\n\n.cropper-line.line-s {\n  bottom: -3px;\n  left: 0;\n\n  height: 5px;\n\n  cursor: s-resize;\n}\n\n.cropper-point {\n  width: 5px;\n  height: 5px;\n\n  opacity: .75;\n  background-color: #39f;\n\n  filter: alpha(opacity=75);\n}\n\n.cropper-point.point-e {\n  top: 50%;\n  right: -3px;\n\n  margin-top: -3px;\n\n  cursor: e-resize;\n}\n\n.cropper-point.point-n {\n  top: -3px;\n  left: 50%;\n\n  margin-left: -3px;\n\n  cursor: n-resize;\n}\n\n.cropper-point.point-w {\n  top: 50%;\n  left: -3px;\n\n  margin-top: -3px;\n\n  cursor: w-resize;\n}\n\n.cropper-point.point-s {\n  bottom: -3px;\n  left: 50%;\n\n  margin-left: -3px;\n\n  cursor: s-resize;\n}\n\n.cropper-point.point-ne {\n  top: -3px;\n  right: -3px;\n\n  cursor: ne-resize;\n}\n\n.cropper-point.point-nw {\n  top: -3px;\n  left: -3px;\n\n  cursor: nw-resize;\n}\n\n.cropper-point.point-sw {\n  bottom: -3px;\n  left: -3px;\n\n  cursor: sw-resize;\n}\n\n.cropper-point.point-se {\n  right: -3px;\n  bottom: -3px;\n\n  width: 20px;\n  height: 20px;\n\n  cursor: se-resize;\n\n  opacity: 1;\n\n  filter: alpha(opacity=100);\n}\n\n.cropper-point.point-se:before {\n  position: absolute;\n  right: -50%;\n  bottom: -50%;\n\n  display: block;\n\n  width: 200%;\n  height: 200%;\n\n  content: ' ';\n\n  opacity: 0;\n  background-color: #39f;\n\n  filter: alpha(opacity=0);\n}\n\n@media (min-width: 768px) {\n  .cropper-point.point-se {\n    width: 15px;\n    height: 15px;\n  }\n}\n\n@media (min-width: 992px) {\n  .cropper-point.point-se {\n    width: 10px;\n    height: 10px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .cropper-point.point-se {\n    width: 5px;\n    height: 5px;\n\n    opacity: .75;\n\n    filter: alpha(opacity=75);\n  }\n}\n\n.cropper-invisible {\n  opacity: 0;\n\n  filter: alpha(opacity=0);\n}\n\n.cropper-bg {\n  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');\n}\n\n.cropper-hide {\n  position: absolute;\n\n  display: block;\n\n  width: 0;\n  height: 0;\n}\n\n.cropper-hidden {\n  display: none !important;\n}\n\n.cropper-move {\n  cursor: move;\n}\n\n.cropper-crop {\n  cursor: crosshair;\n}\n\n.cropper-disabled .cropper-drag-box,\n.cropper-disabled .cropper-face,\n.cropper-disabled .cropper-line,\n.cropper-disabled .cropper-point {\n  cursor: not-allowed;\n}\n"
  },
  {
    "path": "docs/v2.3.4/css/main.css",
    "content": "/* Basic */\n\nbody {\n  margin: 0;\n  overflow-x: hidden;\n}\n\n.browserupgrade {\n  margin: 0;\n  padding: .5em 1em;\n  background-color: #fcfcfc;\n  text-align: center;\n}\n\n\n/* Header */\n\n.docs-header {\n  margin-bottom: 0;\n}\n\n.navbar-toggle:hover,\n.navbar-toggle:focus {\n  border-color: #0074d9;\n}\n\n.navbar-toggle .icon-bar {\n  background-color: #0074d9;\n}\n\n\n/* Jumbotron */\n\n.docs-jumbotron {\n  background-color: #0074d9;\n  color: #fff;\n}\n\n.docs-jumbotron .version {\n  font-size: 14px;\n  color: #fff;\n  filter: alpha(opacity=50);\n  opacity: 0.5;\n}\n\n\n/* Content */\n\n.img-container,\n.img-preview {\n  background-color: #f7f7f7;\n  width: 100%;\n  text-align: center;\n}\n\n.img-container {\n  min-height: 200px;\n  max-height: 516px;\n  margin-bottom: 20px;\n}\n\n@media (min-width: 768px) {\n  .img-container {\n    min-height: 516px;\n  }\n}\n\n.img-container > img {\n  max-width: 100%;\n}\n\n.docs-preview {\n  margin-right: -15px;\n}\n\n.img-preview {\n  float: left;\n  margin-right: 10px;\n  margin-bottom: 10px;\n  overflow: hidden;\n}\n\n.img-preview > img {\n  max-width: 100%;\n}\n\n.preview-lg {\n  width: 263px;\n  height: 148px;\n}\n\n.preview-md {\n  width: 139px;\n  height: 78px;\n}\n\n.preview-sm {\n  width: 69px;\n  height: 39px;\n}\n\n.preview-xs {\n  width: 35px;\n  height: 20px;\n  margin-right: 0;\n}\n\n.docs-data > .input-group {\n  margin-bottom: 10px;\n}\n\n.docs-data > .input-group > label {\n  min-width: 80px;\n}\n\n.docs-data > .input-group > span {\n  min-width: 50px;\n}\n\n.docs-buttons > .btn,\n.docs-buttons > .btn-group,\n.docs-buttons > .form-control {\n  margin-right: 5px;\n  margin-bottom: 10px;\n}\n\n.docs-toggles > .btn,\n.docs-toggles > .btn-group,\n.docs-toggles > .dropdown {\n  margin-bottom: 10px;\n}\n\n.docs-tooltip {\n  display: block;\n  margin: -6px -12px;\n  padding: 6px 12px;\n}\n\n.docs-tooltip > .icon {\n  margin: 0 -3px;\n  vertical-align: top;\n}\n\n.tooltip-inner {\n  white-space: normal;\n}\n\n.btn-upload .tooltip-inner,\n.btn-toggle .tooltip-inner {\n  white-space: nowrap;\n}\n\n.btn-toggle {\n  padding: 6px;\n}\n\n.btn-toggle > .docs-tooltip {\n  margin: -6px;\n  padding: 6px;\n}\n\n@media (max-width: 400px) {\n  .btn-group-crop {\n    margin-right: -15px!important;\n  }\n\n  .btn-group-crop > .btn {\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n\n  .btn-group-crop .docs-tooltip {\n    margin-left: -5px;\n    margin-right: -5px;\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n.docs-options .dropdown-menu {\n  width: 100%;\n}\n\n.docs-options .dropdown-menu > li {\n  padding: 3px 20px;\n}\n\n.docs-options .dropdown-menu > li:hover {\n  background-color: #f7f7f7;\n}\n\n.docs-options .dropdown-menu > li > label {\n  display: block;\n}\n\n.docs-cropped .modal-body {\n  text-align: center;\n}\n\n.docs-cropped .modal-body > img,\n.docs-cropped .modal-body > canvas {\n  max-width: 100%;\n}\n\n.docs-diagram .modal-dialog {\n  max-width: 352px;\n}\n\n\n/* Footer */\n\n.docs-footer {\n  overflow: hidden;\n}\n\n.links {\n  text-align: center;\n  margin-bottom: 30px;\n}\n\n.heart {\n  position: relative;\n  display: block;\n  width: 100%;\n  height: 30px;\n  margin-top: 20px;\n  margin-bottom: 20px;\n  color: #ddd;\n  font-size: 18px;\n  line-height: 30px;\n  text-align: center;\n}\n\n.heart:hover {\n  color: #ff4136;\n}\n\n.heart:before {\n  position: absolute;\n  top: 50%;\n  right: 0;\n  left: 0;\n  display: block;\n  height: 0;\n  border-top: 1px solid #eee;\n  content: \" \";\n}\n\n.heart:after {\n  position: relative;\n  z-index: 1;\n  padding-left: 8px;\n  padding-right: 8px;\n  background-color: #fff;\n  content: \"♥\";\n}\n"
  },
  {
    "path": "docs/v2.3.4/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <meta name=\"description\" content=\"A simple jQuery image cropping plugin.\">\n  <meta name=\"author\" content=\"Chen Fengyuan\">\n  <title>Cropper</title>\n  <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css\" crossorigin=\"anonymous\">\n  <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" crossorigin=\"anonymous\">\n  <link rel=\"stylesheet\" href=\"css/cropper.css\">\n  <link rel=\"stylesheet\" href=\"css/main.css\">\n\n  <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->\n  <!--[if lt IE 9]>\n    <script src=\"https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js\"></script>\n    <script src=\"https://oss.maxcdn.com/respond/1.4.2/respond.min.js\"></script>\n  <![endif]-->\n</head>\n<body>\n  <!--[if lt IE 8]>\n    <p class=\"browserupgrade\">You are using an <strong>outdated</strong> browser. Please <a href=\"https://browsehappy.com/\">upgrade your browser</a> to improve your experience.</p>\n  <![endif]-->\n\n  <!-- Header -->\n  <header class=\"navbar navbar-static-top docs-header\" id=\"top\">\n    <div class=\"container\">\n      <div class=\"navbar-header\">\n        <button type=\"button\" class=\"navbar-toggle\" data-target=\"#navbar-collapse\" data-toggle=\"collapse\">\n          <span class=\"sr-only\">Toggle navigation</span>\n          <span class=\"icon-bar\"></span>\n          <span class=\"icon-bar\"></span>\n          <span class=\"icon-bar\"></span>\n        </button>\n        <a class=\"navbar-brand\" href=\"./\">Cropper</a>\n      </div>\n      <nav class=\"collapse navbar-collapse\" id=\"navbar-collapse\" role=\"navigation\">\n        <ul class=\"nav navbar-nav navbar-right\">\n          <li><a href=\"https://github.com/fengyuanchen/cropper/tree/v2.3.4/README.md\">Docs</a></li>\n          <li><a href=\"https://github.com/fengyuanchen/cropper\">GitHub</a></li>\n          <li><a href=\"https://fengyuanchen.github.io/\">Explore</a></li>\n          <li><a href=\"https://chenfengyuan.com/\">About</a></li>\n        </ul>\n      </nav>\n    </div>\n  </header>\n\n  <!-- Jumbotron -->\n  <div class=\"jumbotron docs-jumbotron\">\n    <div class=\"container\">\n      <h1>Cropper <small class=\"version\">v2.3.4</small></h1>\n      <p class=\"lead\">A simple jQuery image cropping plugin.</p>\n    </div>\n  </div>\n\n  <!-- Content -->\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"col-md-9\">\n        <!-- <h3>Demo:</h3> -->\n        <div class=\"img-container\">\n          <img id=\"image\" src=\"images/picture.jpg\" alt=\"Picture\">\n        </div>\n      </div>\n      <div class=\"col-md-3\">\n        <!-- <h3>Preview:</h3> -->\n        <div class=\"docs-preview clearfix\">\n          <div class=\"img-preview preview-lg\"></div>\n          <div class=\"img-preview preview-md\"></div>\n          <div class=\"img-preview preview-sm\"></div>\n          <div class=\"img-preview preview-xs\"></div>\n        </div>\n\n        <!-- <h3>Data:</h3> -->\n        <div class=\"docs-data\">\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataX\">X</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataX\" placeholder=\"x\">\n            <span class=\"input-group-addon\">px</span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataY\">Y</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataY\" placeholder=\"y\">\n            <span class=\"input-group-addon\">px</span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataWidth\">Width</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataWidth\" placeholder=\"width\">\n            <span class=\"input-group-addon\">px</span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataHeight\">Height</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataHeight\" placeholder=\"height\">\n            <span class=\"input-group-addon\">px</span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataRotate\">Rotate</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataRotate\" placeholder=\"rotate\">\n            <span class=\"input-group-addon\">deg</span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataScaleX\">ScaleX</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataScaleX\" placeholder=\"scaleX\">\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <label class=\"input-group-addon\" for=\"dataScaleY\">ScaleY</label>\n            <input type=\"text\" class=\"form-control\" id=\"dataScaleY\" placeholder=\"scaleY\">\n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"row\">\n      <div class=\"col-md-9 docs-buttons\">\n        <!-- <h3>Toolbar:</h3> -->\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"setDragMode\" data-option=\"move\" title=\"Move\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;setDragMode&quot;, &quot;move&quot;)\">\n              <span class=\"fa fa-arrows\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"setDragMode\" data-option=\"crop\" title=\"Crop\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;setDragMode&quot;, &quot;crop&quot;)\">\n              <span class=\"fa fa-crop\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoom\" data-option=\"0.1\" title=\"Zoom In\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;zoom&quot;, 0.1)\">\n              <span class=\"fa fa-search-plus\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoom\" data-option=\"-0.1\" title=\"Zoom Out\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;zoom&quot;, -0.1)\">\n              <span class=\"fa fa-search-minus\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"-10\" data-second-option=\"0\" title=\"Move Left\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;move&quot;, -10, 0)\">\n              <span class=\"fa fa-arrow-left\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"10\" data-second-option=\"0\" title=\"Move Right\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;move&quot;, 10, 0)\">\n              <span class=\"fa fa-arrow-right\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"0\" data-second-option=\"-10\" title=\"Move Up\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;move&quot;, 0, -10)\">\n              <span class=\"fa fa-arrow-up\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"0\" data-second-option=\"10\" title=\"Move Down\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;move&quot;, 0, 10)\">\n              <span class=\"fa fa-arrow-down\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotate\" data-option=\"-45\" title=\"Rotate Left\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;rotate&quot;, -45)\">\n              <span class=\"fa fa-rotate-left\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotate\" data-option=\"45\" title=\"Rotate Right\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;rotate&quot;, 45)\">\n              <span class=\"fa fa-rotate-right\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"scaleX\" data-option=\"-1\" title=\"Flip Horizontal\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;scaleX&quot;, -1)\">\n              <span class=\"fa fa-arrows-h\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"scaleY\" data-option=\"-1\" title=\"Flip Vertical\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;scaleY&quot;, -1)\">\n              <span class=\"fa fa-arrows-v\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"crop\" title=\"Crop\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;crop&quot;)\">\n              <span class=\"fa fa-check\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"clear\" title=\"Clear\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;clear&quot;)\">\n              <span class=\"fa fa-remove\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"disable\" title=\"Disable\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;disable&quot;)\">\n              <span class=\"fa fa-lock\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"enable\" title=\"Enable\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;enable&quot;)\">\n              <span class=\"fa fa-unlock\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"reset\" title=\"Reset\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;reset&quot;)\">\n              <span class=\"fa fa-refresh\"></span>\n            </span>\n          </button>\n          <label class=\"btn btn-primary btn-upload\" for=\"inputImage\" title=\"Upload image file\">\n            <input type=\"file\" class=\"sr-only\" id=\"inputImage\" name=\"file\" accept=\".jpg,.jpeg,.png,.gif,.bmp,.tiff\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"Import image with Blob URLs\">\n              <span class=\"fa fa-upload\"></span>\n            </span>\n          </label>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"destroy\" title=\"Destroy\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;destroy&quot;)\">\n              <span class=\"fa fa-power-off\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group btn-group-crop\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"getCroppedCanvas\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getCroppedCanvas&quot;)\">\n              Get Cropped Canvas\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;width&quot;: 160, &quot;height&quot;: 90 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { width: 160, height: 90 })\">\n              160&times;90\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;width&quot;: 320, &quot;height&quot;: 180 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { width: 320, height: 180 })\">\n              320&times;180\n            </span>\n          </button>\n        </div>\n\n        <!-- Show the cropped image in modal -->\n        <div class=\"modal fade docs-cropped\" id=\"getCroppedCanvasModal\" aria-hidden=\"true\" aria-labelledby=\"getCroppedCanvasTitle\" role=\"dialog\" tabindex=\"-1\">\n          <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n              <div class=\"modal-header\">\n                <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-hidden=\"true\">&times;</button>\n                <h4 class=\"modal-title\" id=\"getCroppedCanvasTitle\">Cropped</h4>\n              </div>\n              <div class=\"modal-body\"></div>\n              <div class=\"modal-footer\">\n                <button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>\n                <a class=\"btn btn-primary\" id=\"download\" href=\"javascript:void(0);\" download=\"cropped.jpg\">Download</a>\n              </div>\n            </div>\n          </div>\n        </div><!-- /.modal -->\n\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"getData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getData&quot;)\">\n            Get Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"setData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;setData&quot;, data)\">\n            Set Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"getContainerData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getContainerData&quot;)\">\n            Get Container Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"getImageData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getImageData&quot;)\">\n            Get Image Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"getCanvasData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getCanvasData&quot;)\">\n            Get Canvas Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"setCanvasData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;setCanvasData&quot;, data)\">\n            Set Canvas Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"getCropBoxData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;getCropBoxData&quot;)\">\n            Get Crop Box Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"setCropBoxData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"$().cropper(&quot;setCropBoxData&quot;, data)\">\n            Set Crop Box Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"moveTo\" data-option=\"0\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"cropper.moveTo(0)\">\n            0,0\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoomTo\" data-option=\"1\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"cropper.zoomTo(1)\">\n            100%\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotateTo\" data-option=\"180\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"cropper.rotateTo(180)\">\n            180°\n          </span>\n        </button>\n        <input type=\"text\" class=\"form-control\" id=\"putData\" placeholder=\"Get data to here or set data with this value\">\n      </div><!-- /.docs-buttons -->\n\n      <div class=\"col-md-3 docs-toggles\">\n        <!-- <h3>Toggles:</h3> -->\n        <div class=\"btn-group btn-group-justified\" data-toggle=\"buttons\">\n          <label class=\"btn btn-primary active\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio0\" name=\"aspectRatio\" value=\"1.7777777777777777\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"aspectRatio: 16 / 9\">\n              16:9\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio1\" name=\"aspectRatio\" value=\"1.3333333333333333\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"aspectRatio: 4 / 3\">\n              4:3\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio2\" name=\"aspectRatio\" value=\"1\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"aspectRatio: 1 / 1\">\n              1:1\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio3\" name=\"aspectRatio\" value=\"0.6666666666666666\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"aspectRatio: 2 / 3\">\n              2:3\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio4\" name=\"aspectRatio\" value=\"NaN\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"aspectRatio: NaN\">\n              Free\n            </span>\n          </label>\n        </div>\n\n        <div class=\"btn-group btn-group-justified\" data-toggle=\"buttons\">\n          <label class=\"btn btn-primary active\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode0\" name=\"viewMode\" value=\"0\" checked>\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"View Mode 0\">\n              VM0\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode1\" name=\"viewMode\" value=\"1\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"View Mode 1\">\n              VM1\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode2\" name=\"viewMode\" value=\"2\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"View Mode 2\">\n              VM2\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode3\" name=\"viewMode\" value=\"3\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"View Mode 3\">\n              VM3\n            </span>\n          </label>\n        </div>\n\n        <div class=\"dropdown dropup docs-options\">\n          <button type=\"button\" class=\"btn btn-primary btn-block dropdown-toggle\" id=\"toggleOptions\" data-toggle=\"dropdown\" aria-expanded=\"true\">\n            Toggle Options\n            <span class=\"caret\"></span>\n          </button>\n          <ul class=\"dropdown-menu\" aria-labelledby=\"toggleOptions\" role=\"menu\">\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"responsive\" checked>\n                responsive\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"restore\" checked>\n                restore\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"checkCrossOrigin\" checked>\n                checkCrossOrigin\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"checkOrientation\" checked>\n                checkOrientation\n              </label>\n            </li>\n\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"modal\" checked>\n                modal\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"guides\" checked>\n                guides\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"center\" checked>\n                center\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"highlight\" checked>\n                highlight\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"background\" checked>\n                background\n              </label>\n            </li>\n\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"autoCrop\" checked>\n                autoCrop\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"movable\" checked>\n                movable\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"rotatable\" checked>\n                rotatable\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"scalable\" checked>\n                scalable\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"zoomable\" checked>\n                zoomable\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"zoomOnTouch\" checked>\n                zoomOnTouch\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"zoomOnWheel\" checked>\n                zoomOnWheel\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"cropBoxMovable\" checked>\n                cropBoxMovable\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"cropBoxResizable\" checked>\n                cropBoxResizable\n              </label>\n            </li>\n            <li role=\"presentation\">\n              <label class=\"checkbox-inline\">\n                <input type=\"checkbox\" name=\"toggleDragModeOnDblclick\" checked>\n                toggleDragModeOnDblclick\n              </label>\n            </li>\n          </ul>\n        </div><!-- /.dropdown -->\n\n        <a class=\"btn btn-default btn-block\" data-toggle=\"tooltip\" href=\"https://fengyuanchen.github.io/cropperjs\" title=\"Cropper without jQuery\">Cropper.js</a>\n\n      </div><!-- /.docs-toggles -->\n    </div>\n  </div>\n\n  <!-- Footer -->\n  <footer class=\"docs-footer\">\n    <div class=\"container\">\n      <p class=\"heart\"></p>\n      <ul class=\"list-inline links\">\n        <li><a href=\"https://github.com/fengyuanchen/cropper\">GitHub</a></li>\n        <li><a href=\"https://github.com/fengyuanchen/cropper/tree/v2.3.4/examples\">Examples</a></li>\n        <li><a href=\"https://github.com/fengyuanchen/cropper/tree/v2.3.4/LICENSE\">License</a></li>\n        <li><a href=\"https://chenfengyuan.com/\">About</a></li>\n      </ul>\n    </div>\n  </footer>\n\n  <!-- Scripts -->\n  <script src=\"https://code.jquery.com/jquery-1.12.4.min.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"https://stackpath.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"https://fengyuanchen.github.io/shared/google-analytics.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"js/cropper.js\"></script>\n  <script src=\"js/main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "docs/v2.3.4/js/cropper.js",
    "content": "/*!\n * Cropper v2.3.4\n * https://github.com/fengyuanchen/cropper\n *\n * Copyright (c) 2014-2016 Chen Fengyuan and contributors\n * Released under the MIT license\n *\n * Date: 2016-09-03T05:50:45.412Z\n */\n\n(function (factory) {\n  if (typeof define === 'function' && define.amd) {\n    // AMD. Register as anonymous module.\n    define(['jquery'], factory);\n  } else if (typeof exports === 'object') {\n    // Node / CommonJS\n    factory(require('jquery'));\n  } else {\n    // Browser globals.\n    factory(jQuery);\n  }\n})(function ($) {\n\n  'use strict';\n\n  // Globals\n  var $window = $(window);\n  var $document = $(document);\n  var location = window.location;\n  var navigator = window.navigator;\n  var ArrayBuffer = window.ArrayBuffer;\n  var Uint8Array = window.Uint8Array;\n  var DataView = window.DataView;\n  var btoa = window.btoa;\n\n  // Constants\n  var NAMESPACE = 'cropper';\n\n  // Classes\n  var CLASS_MODAL = 'cropper-modal';\n  var CLASS_HIDE = 'cropper-hide';\n  var CLASS_HIDDEN = 'cropper-hidden';\n  var CLASS_INVISIBLE = 'cropper-invisible';\n  var CLASS_MOVE = 'cropper-move';\n  var CLASS_CROP = 'cropper-crop';\n  var CLASS_DISABLED = 'cropper-disabled';\n  var CLASS_BG = 'cropper-bg';\n\n  // Events\n  var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown';\n  var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove';\n  var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel';\n  var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';\n  var EVENT_DBLCLICK = 'dblclick';\n  var EVENT_LOAD = 'load.' + NAMESPACE;\n  var EVENT_ERROR = 'error.' + NAMESPACE;\n  var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace\n  var EVENT_BUILD = 'build.' + NAMESPACE;\n  var EVENT_BUILT = 'built.' + NAMESPACE;\n  var EVENT_CROP_START = 'cropstart.' + NAMESPACE;\n  var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE;\n  var EVENT_CROP_END = 'cropend.' + NAMESPACE;\n  var EVENT_CROP = 'crop.' + NAMESPACE;\n  var EVENT_ZOOM = 'zoom.' + NAMESPACE;\n\n  // RegExps\n  var REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;\n  var REGEXP_DATA_URL = /^data:/;\n  var REGEXP_DATA_URL_HEAD = /^data:([^;]+);base64,/;\n  var REGEXP_DATA_URL_JPEG = /^data:image\\/jpeg.*;base64,/;\n\n  // Data keys\n  var DATA_PREVIEW = 'preview';\n  var DATA_ACTION = 'action';\n\n  // Actions\n  var ACTION_EAST = 'e';\n  var ACTION_WEST = 'w';\n  var ACTION_SOUTH = 's';\n  var ACTION_NORTH = 'n';\n  var ACTION_SOUTH_EAST = 'se';\n  var ACTION_SOUTH_WEST = 'sw';\n  var ACTION_NORTH_EAST = 'ne';\n  var ACTION_NORTH_WEST = 'nw';\n  var ACTION_ALL = 'all';\n  var ACTION_CROP = 'crop';\n  var ACTION_MOVE = 'move';\n  var ACTION_ZOOM = 'zoom';\n  var ACTION_NONE = 'none';\n\n  // Supports\n  var SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext);\n  var IS_SAFARI_OR_UIWEBVIEW = navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(navigator.userAgent);\n\n  // Maths\n  var num = Number;\n  var min = Math.min;\n  var max = Math.max;\n  var abs = Math.abs;\n  var sin = Math.sin;\n  var cos = Math.cos;\n  var sqrt = Math.sqrt;\n  var round = Math.round;\n  var floor = Math.floor;\n\n  // Utilities\n  var fromCharCode = String.fromCharCode;\n\n  function isNumber(n) {\n    return typeof n === 'number' && !isNaN(n);\n  }\n\n  function isUndefined(n) {\n    return typeof n === 'undefined';\n  }\n\n  function toArray(obj, offset) {\n    var args = [];\n\n    // This is necessary for IE8\n    if (isNumber(offset)) {\n      args.push(offset);\n    }\n\n    return args.slice.apply(obj, args);\n  }\n\n  // Custom proxy to avoid jQuery's guid\n  function proxy(fn, context) {\n    var args = toArray(arguments, 2);\n\n    return function () {\n      return fn.apply(context, args.concat(toArray(arguments)));\n    };\n  }\n\n  function isCrossOriginURL(url) {\n    var parts = url.match(/^(https?:)\\/\\/([^\\:\\/\\?#]+):?(\\d*)/i);\n\n    return parts && (\n      parts[1] !== location.protocol ||\n      parts[2] !== location.hostname ||\n      parts[3] !== location.port\n    );\n  }\n\n  function addTimestamp(url) {\n    var timestamp = 'timestamp=' + (new Date()).getTime();\n\n    return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);\n  }\n\n  function getCrossOrigin(crossOrigin) {\n    return crossOrigin ? ' crossOrigin=\"' + crossOrigin + '\"' : '';\n  }\n\n  function getImageSize(image, callback) {\n    var newImage;\n\n    // Modern browsers (ignore Safari, #120 & #509)\n    if (image.naturalWidth && !IS_SAFARI_OR_UIWEBVIEW) {\n      return callback(image.naturalWidth, image.naturalHeight);\n    }\n\n    // IE8: Don't use `new Image()` here (#319)\n    newImage = document.createElement('img');\n\n    newImage.onload = function () {\n      callback(this.width, this.height);\n    };\n\n    newImage.src = image.src;\n  }\n\n  function getTransform(options) {\n    var transforms = [];\n    var rotate = options.rotate;\n    var scaleX = options.scaleX;\n    var scaleY = options.scaleY;\n\n    // Rotate should come first before scale to match orientation transform\n    if (isNumber(rotate) && rotate !== 0) {\n      transforms.push('rotate(' + rotate + 'deg)');\n    }\n\n    if (isNumber(scaleX) && scaleX !== 1) {\n      transforms.push('scaleX(' + scaleX + ')');\n    }\n\n    if (isNumber(scaleY) && scaleY !== 1) {\n      transforms.push('scaleY(' + scaleY + ')');\n    }\n\n    return transforms.length ? transforms.join(' ') : 'none';\n  }\n\n  function getRotatedSizes(data, isReversed) {\n    var deg = abs(data.degree) % 180;\n    var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180;\n    var sinArc = sin(arc);\n    var cosArc = cos(arc);\n    var width = data.width;\n    var height = data.height;\n    var aspectRatio = data.aspectRatio;\n    var newWidth;\n    var newHeight;\n\n    if (!isReversed) {\n      newWidth = width * cosArc + height * sinArc;\n      newHeight = width * sinArc + height * cosArc;\n    } else {\n      newWidth = width / (cosArc + sinArc / aspectRatio);\n      newHeight = newWidth / aspectRatio;\n    }\n\n    return {\n      width: newWidth,\n      height: newHeight\n    };\n  }\n\n  function getSourceCanvas(image, data) {\n    var canvas = $('<canvas>')[0];\n    var context = canvas.getContext('2d');\n    var dstX = 0;\n    var dstY = 0;\n    var dstWidth = data.naturalWidth;\n    var dstHeight = data.naturalHeight;\n    var rotate = data.rotate;\n    var scaleX = data.scaleX;\n    var scaleY = data.scaleY;\n    var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1);\n    var rotatable = isNumber(rotate) && rotate !== 0;\n    var advanced = rotatable || scalable;\n    var canvasWidth = dstWidth * abs(scaleX || 1);\n    var canvasHeight = dstHeight * abs(scaleY || 1);\n    var translateX;\n    var translateY;\n    var rotated;\n\n    if (scalable) {\n      translateX = canvasWidth / 2;\n      translateY = canvasHeight / 2;\n    }\n\n    if (rotatable) {\n      rotated = getRotatedSizes({\n        width: canvasWidth,\n        height: canvasHeight,\n        degree: rotate\n      });\n\n      canvasWidth = rotated.width;\n      canvasHeight = rotated.height;\n      translateX = canvasWidth / 2;\n      translateY = canvasHeight / 2;\n    }\n\n    canvas.width = canvasWidth;\n    canvas.height = canvasHeight;\n\n    if (advanced) {\n      dstX = -dstWidth / 2;\n      dstY = -dstHeight / 2;\n\n      context.save();\n      context.translate(translateX, translateY);\n    }\n\n    // Rotate should come first before scale as in the \"getTransform\" function\n    if (rotatable) {\n      context.rotate(rotate * Math.PI / 180);\n    }\n\n    if (scalable) {\n      context.scale(scaleX, scaleY);\n    }\n\n    context.drawImage(image, floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));\n\n    if (advanced) {\n      context.restore();\n    }\n\n    return canvas;\n  }\n\n  function getTouchesCenter(touches) {\n    var length = touches.length;\n    var pageX = 0;\n    var pageY = 0;\n\n    if (length) {\n      $.each(touches, function (i, touch) {\n        pageX += touch.pageX;\n        pageY += touch.pageY;\n      });\n\n      pageX /= length;\n      pageY /= length;\n    }\n\n    return {\n      pageX: pageX,\n      pageY: pageY\n    };\n  }\n\n  function getStringFromCharCode(dataView, start, length) {\n    var str = '';\n    var i;\n\n    for (i = start, length += start; i < length; i++) {\n      str += fromCharCode(dataView.getUint8(i));\n    }\n\n    return str;\n  }\n\n  function getOrientation(arrayBuffer) {\n    var dataView = new DataView(arrayBuffer);\n    var length = dataView.byteLength;\n    var orientation;\n    var exifIDCode;\n    var tiffOffset;\n    var firstIFDOffset;\n    var littleEndian;\n    var endianness;\n    var app1Start;\n    var ifdStart;\n    var offset;\n    var i;\n\n    // Only handle JPEG image (start by 0xFFD8)\n    if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n      offset = 2;\n\n      while (offset < length) {\n        if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n          app1Start = offset;\n          break;\n        }\n\n        offset++;\n      }\n    }\n\n    if (app1Start) {\n      exifIDCode = app1Start + 4;\n      tiffOffset = app1Start + 10;\n\n      if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n        endianness = dataView.getUint16(tiffOffset);\n        littleEndian = endianness === 0x4949;\n\n        if (littleEndian || endianness === 0x4D4D /* bigEndian */) {\n          if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n            firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n            if (firstIFDOffset >= 0x00000008) {\n              ifdStart = tiffOffset + firstIFDOffset;\n            }\n          }\n        }\n      }\n    }\n\n    if (ifdStart) {\n      length = dataView.getUint16(ifdStart, littleEndian);\n\n      for (i = 0; i < length; i++) {\n        offset = ifdStart + i * 12 + 2;\n\n        if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {\n\n          // 8 is the offset of the current tag's value\n          offset += 8;\n\n          // Get the original orientation value\n          orientation = dataView.getUint16(offset, littleEndian);\n\n          // Override the orientation with its default value for Safari (#120)\n          if (IS_SAFARI_OR_UIWEBVIEW) {\n            dataView.setUint16(offset, 1, littleEndian);\n          }\n\n          break;\n        }\n      }\n    }\n\n    return orientation;\n  }\n\n  function dataURLToArrayBuffer(dataURL) {\n    var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n    var binary = atob(base64);\n    var length = binary.length;\n    var arrayBuffer = new ArrayBuffer(length);\n    var dataView = new Uint8Array(arrayBuffer);\n    var i;\n\n    for (i = 0; i < length; i++) {\n      dataView[i] = binary.charCodeAt(i);\n    }\n\n    return arrayBuffer;\n  }\n\n  // Only available for JPEG image\n  function arrayBufferToDataURL(arrayBuffer) {\n    var dataView = new Uint8Array(arrayBuffer);\n    var length = dataView.length;\n    var base64 = '';\n    var i;\n\n    for (i = 0; i < length; i++) {\n      base64 += fromCharCode(dataView[i]);\n    }\n\n    return 'data:image/jpeg;base64,' + btoa(base64);\n  }\n\n  function Cropper(element, options) {\n    this.$element = $(element);\n    this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options);\n    this.isLoaded = false;\n    this.isBuilt = false;\n    this.isCompleted = false;\n    this.isRotated = false;\n    this.isCropped = false;\n    this.isDisabled = false;\n    this.isReplaced = false;\n    this.isLimited = false;\n    this.wheeling = false;\n    this.isImg = false;\n    this.originalUrl = '';\n    this.canvas = null;\n    this.cropBox = null;\n    this.init();\n  }\n\n  Cropper.prototype = {\n    constructor: Cropper,\n\n    init: function () {\n      var $this = this.$element;\n      var url;\n\n      if ($this.is('img')) {\n        this.isImg = true;\n\n        // Should use `$.fn.attr` here. e.g.: \"img/picture.jpg\"\n        this.originalUrl = url = $this.attr('src');\n\n        // Stop when it's a blank image\n        if (!url) {\n          return;\n        }\n\n        // Should use `$.fn.prop` here. e.g.: \"https://example.com/img/picture.jpg\"\n        url = $this.prop('src');\n      } else if ($this.is('canvas') && SUPPORT_CANVAS) {\n        url = $this[0].toDataURL();\n      }\n\n      this.load(url);\n    },\n\n    // A shortcut for triggering custom events\n    trigger: function (type, data) {\n      var e = $.Event(type, data);\n\n      this.$element.trigger(e);\n\n      return e;\n    },\n\n    load: function (url) {\n      var options = this.options;\n      var $this = this.$element;\n      var read;\n      var xhr;\n\n      if (!url) {\n        return;\n      }\n\n      // Trigger build event first\n      $this.one(EVENT_BUILD, options.build);\n\n      if (this.trigger(EVENT_BUILD).isDefaultPrevented()) {\n        return;\n      }\n\n      this.url = url;\n      this.image = {};\n\n      if (!options.checkOrientation || !ArrayBuffer) {\n        return this.clone();\n      }\n\n      read = $.proxy(this.read, this);\n\n      // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari\n      if (REGEXP_DATA_URL.test(url)) {\n        return REGEXP_DATA_URL_JPEG.test(url) ?\n          read(dataURLToArrayBuffer(url)) :\n          this.clone();\n      }\n\n      xhr = new XMLHttpRequest();\n\n      xhr.onerror = xhr.onabort = $.proxy(function () {\n        this.clone();\n      }, this);\n\n      xhr.onload = function () {\n        read(this.response);\n      };\n\n      if (options.checkCrossOrigin && isCrossOriginURL(url) && $this.prop('crossOrigin')) {\n        url = addTimestamp(url);\n      }\n\n      xhr.open('get', url);\n      xhr.responseType = 'arraybuffer';\n      xhr.send();\n    },\n\n    read: function (arrayBuffer) {\n      var options = this.options;\n      var orientation = getOrientation(arrayBuffer);\n      var image = this.image;\n      var rotate = 0;\n      var scaleX = 1;\n      var scaleY = 1;\n\n      if (orientation > 1) {\n        this.url = arrayBufferToDataURL(arrayBuffer);\n\n        switch (orientation) {\n\n          // flip horizontal\n          case 2:\n            scaleX = -1;\n            break;\n\n          // rotate left 180°\n          case 3:\n            rotate = -180;\n            break;\n\n          // flip vertical\n          case 4:\n            scaleY = -1;\n            break;\n\n          // flip vertical + rotate right 90°\n          case 5:\n            rotate = 90;\n            scaleY = -1;\n            break;\n\n          // rotate right 90°\n          case 6:\n            rotate = 90;\n            break;\n\n          // flip horizontal + rotate right 90°\n          case 7:\n            rotate = 90;\n            scaleX = -1;\n            break;\n\n          // rotate left 90°\n          case 8:\n            rotate = -90;\n            break;\n        }\n      }\n\n      if (options.rotatable) {\n        image.rotate = rotate;\n      }\n\n      if (options.scalable) {\n        image.scaleX = scaleX;\n        image.scaleY = scaleY;\n      }\n\n      this.clone();\n    },\n\n    clone: function () {\n      var options = this.options;\n      var $this = this.$element;\n      var url = this.url;\n      var crossOrigin = '';\n      var crossOriginUrl;\n      var $clone;\n\n      if (options.checkCrossOrigin && isCrossOriginURL(url)) {\n        crossOrigin = $this.prop('crossOrigin');\n\n        if (crossOrigin) {\n          crossOriginUrl = url;\n        } else {\n          crossOrigin = 'anonymous';\n\n          // Bust cache (#148) when there is not a \"crossOrigin\" property\n          crossOriginUrl = addTimestamp(url);\n        }\n      }\n\n      this.crossOrigin = crossOrigin;\n      this.crossOriginUrl = crossOriginUrl;\n      this.$clone = $clone = $('<img' + getCrossOrigin(crossOrigin) + ' src=\"' + (crossOriginUrl || url) + '\">');\n\n      if (this.isImg) {\n        if ($this[0].complete) {\n          this.start();\n        } else {\n          $this.one(EVENT_LOAD, $.proxy(this.start, this));\n        }\n      } else {\n        $clone.\n          one(EVENT_LOAD, $.proxy(this.start, this)).\n          one(EVENT_ERROR, $.proxy(this.stop, this)).\n          addClass(CLASS_HIDE).\n          insertAfter($this);\n      }\n    },\n\n    start: function () {\n      var $image = this.$element;\n      var $clone = this.$clone;\n\n      if (!this.isImg) {\n        $clone.off(EVENT_ERROR, this.stop);\n        $image = $clone;\n      }\n\n      getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) {\n        $.extend(this.image, {\n          naturalWidth: naturalWidth,\n          naturalHeight: naturalHeight,\n          aspectRatio: naturalWidth / naturalHeight\n        });\n\n        this.isLoaded = true;\n        this.build();\n      }, this));\n    },\n\n    stop: function () {\n      this.$clone.remove();\n      this.$clone = null;\n    },\n\n    build: function () {\n      var options = this.options;\n      var $this = this.$element;\n      var $clone = this.$clone;\n      var $cropper;\n      var $cropBox;\n      var $face;\n\n      if (!this.isLoaded) {\n        return;\n      }\n\n      // Unbuild first when replace\n      if (this.isBuilt) {\n        this.unbuild();\n      }\n\n      // Create cropper elements\n      this.$container = $this.parent();\n      this.$cropper = $cropper = $(Cropper.TEMPLATE);\n      this.$canvas = $cropper.find('.cropper-canvas').append($clone);\n      this.$dragBox = $cropper.find('.cropper-drag-box');\n      this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');\n      this.$viewBox = $cropper.find('.cropper-view-box');\n      this.$face = $face = $cropBox.find('.cropper-face');\n\n      // Hide the original image\n      $this.addClass(CLASS_HIDDEN).after($cropper);\n\n      // Show the clone image if is hidden\n      if (!this.isImg) {\n        $clone.removeClass(CLASS_HIDE);\n      }\n\n      this.initPreview();\n      this.bind();\n\n      options.aspectRatio = max(0, options.aspectRatio) || NaN;\n      options.viewMode = max(0, min(3, round(options.viewMode))) || 0;\n\n      if (options.autoCrop) {\n        this.isCropped = true;\n\n        if (options.modal) {\n          this.$dragBox.addClass(CLASS_MODAL);\n        }\n      } else {\n        $cropBox.addClass(CLASS_HIDDEN);\n      }\n\n      if (!options.guides) {\n        $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);\n      }\n\n      if (!options.center) {\n        $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);\n      }\n\n      if (options.cropBoxMovable) {\n        $face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL);\n      }\n\n      if (!options.highlight) {\n        $face.addClass(CLASS_INVISIBLE);\n      }\n\n      if (options.background) {\n        $cropper.addClass(CLASS_BG);\n      }\n\n      if (!options.cropBoxResizable) {\n        $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);\n      }\n\n      this.setDragMode(options.dragMode);\n      this.render();\n      this.isBuilt = true;\n      this.setData(options.data);\n      $this.one(EVENT_BUILT, options.built);\n\n      // Trigger the built event asynchronously to keep `data('cropper')` is defined\n      this.completing = setTimeout($.proxy(function () {\n        this.trigger(EVENT_BUILT);\n        this.trigger(EVENT_CROP, this.getData());\n        this.isCompleted = true;\n      }, this), 0);\n    },\n\n    unbuild: function () {\n      if (!this.isBuilt) {\n        return;\n      }\n\n      if (!this.isCompleted) {\n        clearTimeout(this.completing);\n      }\n\n      this.isBuilt = false;\n      this.isCompleted = false;\n      this.initialImage = null;\n\n      // Clear `initialCanvas` is necessary when replace\n      this.initialCanvas = null;\n      this.initialCropBox = null;\n      this.container = null;\n      this.canvas = null;\n\n      // Clear `cropBox` is necessary when replace\n      this.cropBox = null;\n      this.unbind();\n\n      this.resetPreview();\n      this.$preview = null;\n\n      this.$viewBox = null;\n      this.$cropBox = null;\n      this.$dragBox = null;\n      this.$canvas = null;\n      this.$container = null;\n\n      this.$cropper.remove();\n      this.$cropper = null;\n    },\n\n    render: function () {\n      this.initContainer();\n      this.initCanvas();\n      this.initCropBox();\n\n      this.renderCanvas();\n\n      if (this.isCropped) {\n        this.renderCropBox();\n      }\n    },\n\n    initContainer: function () {\n      var options = this.options;\n      var $this = this.$element;\n      var $container = this.$container;\n      var $cropper = this.$cropper;\n\n      $cropper.addClass(CLASS_HIDDEN);\n      $this.removeClass(CLASS_HIDDEN);\n\n      $cropper.css((this.container = {\n        width: max($container.width(), num(options.minContainerWidth) || 200),\n        height: max($container.height(), num(options.minContainerHeight) || 100)\n      }));\n\n      $this.addClass(CLASS_HIDDEN);\n      $cropper.removeClass(CLASS_HIDDEN);\n    },\n\n    // Canvas (image wrapper)\n    initCanvas: function () {\n      var viewMode = this.options.viewMode;\n      var container = this.container;\n      var containerWidth = container.width;\n      var containerHeight = container.height;\n      var image = this.image;\n      var imageNaturalWidth = image.naturalWidth;\n      var imageNaturalHeight = image.naturalHeight;\n      var is90Degree = abs(image.rotate) === 90;\n      var naturalWidth = is90Degree ? imageNaturalHeight : imageNaturalWidth;\n      var naturalHeight = is90Degree ? imageNaturalWidth : imageNaturalHeight;\n      var aspectRatio = naturalWidth / naturalHeight;\n      var canvasWidth = containerWidth;\n      var canvasHeight = containerHeight;\n      var canvas;\n\n      if (containerHeight * aspectRatio > containerWidth) {\n        if (viewMode === 3) {\n          canvasWidth = containerHeight * aspectRatio;\n        } else {\n          canvasHeight = containerWidth / aspectRatio;\n        }\n      } else {\n        if (viewMode === 3) {\n          canvasHeight = containerWidth / aspectRatio;\n        } else {\n          canvasWidth = containerHeight * aspectRatio;\n        }\n      }\n\n      canvas = {\n        naturalWidth: naturalWidth,\n        naturalHeight: naturalHeight,\n        aspectRatio: aspectRatio,\n        width: canvasWidth,\n        height: canvasHeight\n      };\n\n      canvas.oldLeft = canvas.left = (containerWidth - canvasWidth) / 2;\n      canvas.oldTop = canvas.top = (containerHeight - canvasHeight) / 2;\n\n      this.canvas = canvas;\n      this.isLimited = (viewMode === 1 || viewMode === 2);\n      this.limitCanvas(true, true);\n      this.initialImage = $.extend({}, image);\n      this.initialCanvas = $.extend({}, canvas);\n    },\n\n    limitCanvas: function (isSizeLimited, isPositionLimited) {\n      var options = this.options;\n      var viewMode = options.viewMode;\n      var container = this.container;\n      var containerWidth = container.width;\n      var containerHeight = container.height;\n      var canvas = this.canvas;\n      var aspectRatio = canvas.aspectRatio;\n      var cropBox = this.cropBox;\n      var isCropped = this.isCropped && cropBox;\n      var minCanvasWidth;\n      var minCanvasHeight;\n      var newCanvasLeft;\n      var newCanvasTop;\n\n      if (isSizeLimited) {\n        minCanvasWidth = num(options.minCanvasWidth) || 0;\n        minCanvasHeight = num(options.minCanvasHeight) || 0;\n\n        if (viewMode) {\n          if (viewMode > 1) {\n            minCanvasWidth = max(minCanvasWidth, containerWidth);\n            minCanvasHeight = max(minCanvasHeight, containerHeight);\n\n            if (viewMode === 3) {\n              if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n                minCanvasWidth = minCanvasHeight * aspectRatio;\n              } else {\n                minCanvasHeight = minCanvasWidth / aspectRatio;\n              }\n            }\n          } else {\n            if (minCanvasWidth) {\n              minCanvasWidth = max(minCanvasWidth, isCropped ? cropBox.width : 0);\n            } else if (minCanvasHeight) {\n              minCanvasHeight = max(minCanvasHeight, isCropped ? cropBox.height : 0);\n            } else if (isCropped) {\n              minCanvasWidth = cropBox.width;\n              minCanvasHeight = cropBox.height;\n\n              if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n                minCanvasWidth = minCanvasHeight * aspectRatio;\n              } else {\n                minCanvasHeight = minCanvasWidth / aspectRatio;\n              }\n            }\n          }\n        }\n\n        if (minCanvasWidth && minCanvasHeight) {\n          if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n            minCanvasHeight = minCanvasWidth / aspectRatio;\n          } else {\n            minCanvasWidth = minCanvasHeight * aspectRatio;\n          }\n        } else if (minCanvasWidth) {\n          minCanvasHeight = minCanvasWidth / aspectRatio;\n        } else if (minCanvasHeight) {\n          minCanvasWidth = minCanvasHeight * aspectRatio;\n        }\n\n        canvas.minWidth = minCanvasWidth;\n        canvas.minHeight = minCanvasHeight;\n        canvas.maxWidth = Infinity;\n        canvas.maxHeight = Infinity;\n      }\n\n      if (isPositionLimited) {\n        if (viewMode) {\n          newCanvasLeft = containerWidth - canvas.width;\n          newCanvasTop = containerHeight - canvas.height;\n\n          canvas.minLeft = min(0, newCanvasLeft);\n          canvas.minTop = min(0, newCanvasTop);\n          canvas.maxLeft = max(0, newCanvasLeft);\n          canvas.maxTop = max(0, newCanvasTop);\n\n          if (isCropped && this.isLimited) {\n            canvas.minLeft = min(\n              cropBox.left,\n              cropBox.left + cropBox.width - canvas.width\n            );\n            canvas.minTop = min(\n              cropBox.top,\n              cropBox.top + cropBox.height - canvas.height\n            );\n            canvas.maxLeft = cropBox.left;\n            canvas.maxTop = cropBox.top;\n\n            if (viewMode === 2) {\n              if (canvas.width >= containerWidth) {\n                canvas.minLeft = min(0, newCanvasLeft);\n                canvas.maxLeft = max(0, newCanvasLeft);\n              }\n\n              if (canvas.height >= containerHeight) {\n                canvas.minTop = min(0, newCanvasTop);\n                canvas.maxTop = max(0, newCanvasTop);\n              }\n            }\n          }\n        } else {\n          canvas.minLeft = -canvas.width;\n          canvas.minTop = -canvas.height;\n          canvas.maxLeft = containerWidth;\n          canvas.maxTop = containerHeight;\n        }\n      }\n    },\n\n    renderCanvas: function (isChanged) {\n      var canvas = this.canvas;\n      var image = this.image;\n      var rotate = image.rotate;\n      var naturalWidth = image.naturalWidth;\n      var naturalHeight = image.naturalHeight;\n      var aspectRatio;\n      var rotated;\n\n      if (this.isRotated) {\n        this.isRotated = false;\n\n        // Computes rotated sizes with image sizes\n        rotated = getRotatedSizes({\n          width: image.width,\n          height: image.height,\n          degree: rotate\n        });\n\n        aspectRatio = rotated.width / rotated.height;\n\n        if (aspectRatio !== canvas.aspectRatio) {\n          canvas.left -= (rotated.width - canvas.width) / 2;\n          canvas.top -= (rotated.height - canvas.height) / 2;\n          canvas.width = rotated.width;\n          canvas.height = rotated.height;\n          canvas.aspectRatio = aspectRatio;\n          canvas.naturalWidth = naturalWidth;\n          canvas.naturalHeight = naturalHeight;\n\n          // Computes rotated sizes with natural image sizes\n          if (rotate % 180) {\n            rotated = getRotatedSizes({\n              width: naturalWidth,\n              height: naturalHeight,\n              degree: rotate\n            });\n\n            canvas.naturalWidth = rotated.width;\n            canvas.naturalHeight = rotated.height;\n          }\n\n          this.limitCanvas(true, false);\n        }\n      }\n\n      if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {\n        canvas.left = canvas.oldLeft;\n      }\n\n      if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {\n        canvas.top = canvas.oldTop;\n      }\n\n      canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);\n      canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight);\n\n      this.limitCanvas(false, true);\n\n      canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);\n      canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop);\n\n      this.$canvas.css({\n        width: canvas.width,\n        height: canvas.height,\n        left: canvas.left,\n        top: canvas.top\n      });\n\n      this.renderImage();\n\n      if (this.isCropped && this.isLimited) {\n        this.limitCropBox(true, true);\n      }\n\n      if (isChanged) {\n        this.output();\n      }\n    },\n\n    renderImage: function (isChanged) {\n      var canvas = this.canvas;\n      var image = this.image;\n      var reversed;\n\n      if (image.rotate) {\n        reversed = getRotatedSizes({\n          width: canvas.width,\n          height: canvas.height,\n          degree: image.rotate,\n          aspectRatio: image.aspectRatio\n        }, true);\n      }\n\n      $.extend(image, reversed ? {\n        width: reversed.width,\n        height: reversed.height,\n        left: (canvas.width - reversed.width) / 2,\n        top: (canvas.height - reversed.height) / 2\n      } : {\n        width: canvas.width,\n        height: canvas.height,\n        left: 0,\n        top: 0\n      });\n\n      this.$clone.css({\n        width: image.width,\n        height: image.height,\n        marginLeft: image.left,\n        marginTop: image.top,\n        transform: getTransform(image)\n      });\n\n      if (isChanged) {\n        this.output();\n      }\n    },\n\n    initCropBox: function () {\n      var options = this.options;\n      var canvas = this.canvas;\n      var aspectRatio = options.aspectRatio;\n      var autoCropArea = num(options.autoCropArea) || 0.8;\n      var cropBox = {\n            width: canvas.width,\n            height: canvas.height\n          };\n\n      if (aspectRatio) {\n        if (canvas.height * aspectRatio > canvas.width) {\n          cropBox.height = cropBox.width / aspectRatio;\n        } else {\n          cropBox.width = cropBox.height * aspectRatio;\n        }\n      }\n\n      this.cropBox = cropBox;\n      this.limitCropBox(true, true);\n\n      // Initialize auto crop area\n      cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);\n      cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);\n\n      // The width of auto crop area must large than \"minWidth\", and the height too. (#164)\n      cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);\n      cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);\n      cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;\n      cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;\n\n      this.initialCropBox = $.extend({}, cropBox);\n    },\n\n    limitCropBox: function (isSizeLimited, isPositionLimited) {\n      var options = this.options;\n      var aspectRatio = options.aspectRatio;\n      var container = this.container;\n      var containerWidth = container.width;\n      var containerHeight = container.height;\n      var canvas = this.canvas;\n      var cropBox = this.cropBox;\n      var isLimited = this.isLimited;\n      var minCropBoxWidth;\n      var minCropBoxHeight;\n      var maxCropBoxWidth;\n      var maxCropBoxHeight;\n\n      if (isSizeLimited) {\n        minCropBoxWidth = num(options.minCropBoxWidth) || 0;\n        minCropBoxHeight = num(options.minCropBoxHeight) || 0;\n\n        // The min/maxCropBoxWidth/Height must be less than containerWidth/Height\n        minCropBoxWidth = min(minCropBoxWidth, containerWidth);\n        minCropBoxHeight = min(minCropBoxHeight, containerHeight);\n        maxCropBoxWidth = min(containerWidth, isLimited ? canvas.width : containerWidth);\n        maxCropBoxHeight = min(containerHeight, isLimited ? canvas.height : containerHeight);\n\n        if (aspectRatio) {\n          if (minCropBoxWidth && minCropBoxHeight) {\n            if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n              minCropBoxHeight = minCropBoxWidth / aspectRatio;\n            } else {\n              minCropBoxWidth = minCropBoxHeight * aspectRatio;\n            }\n          } else if (minCropBoxWidth) {\n            minCropBoxHeight = minCropBoxWidth / aspectRatio;\n          } else if (minCropBoxHeight) {\n            minCropBoxWidth = minCropBoxHeight * aspectRatio;\n          }\n\n          if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n            maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n          } else {\n            maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n          }\n        }\n\n        // The minWidth/Height must be less than maxWidth/Height\n        cropBox.minWidth = min(minCropBoxWidth, maxCropBoxWidth);\n        cropBox.minHeight = min(minCropBoxHeight, maxCropBoxHeight);\n        cropBox.maxWidth = maxCropBoxWidth;\n        cropBox.maxHeight = maxCropBoxHeight;\n      }\n\n      if (isPositionLimited) {\n        if (isLimited) {\n          cropBox.minLeft = max(0, canvas.left);\n          cropBox.minTop = max(0, canvas.top);\n          cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;\n          cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;\n        } else {\n          cropBox.minLeft = 0;\n          cropBox.minTop = 0;\n          cropBox.maxLeft = containerWidth - cropBox.width;\n          cropBox.maxTop = containerHeight - cropBox.height;\n        }\n      }\n    },\n\n    renderCropBox: function () {\n      var options = this.options;\n      var container = this.container;\n      var containerWidth = container.width;\n      var containerHeight = container.height;\n      var cropBox = this.cropBox;\n\n      if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {\n        cropBox.left = cropBox.oldLeft;\n      }\n\n      if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {\n        cropBox.top = cropBox.oldTop;\n      }\n\n      cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);\n      cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);\n\n      this.limitCropBox(false, true);\n\n      cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);\n      cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop);\n\n      if (options.movable && options.cropBoxMovable) {\n\n        // Turn to move the canvas when the crop box is equal to the container\n        this.$face.data(DATA_ACTION, (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL);\n      }\n\n      this.$cropBox.css({\n        width: cropBox.width,\n        height: cropBox.height,\n        left: cropBox.left,\n        top: cropBox.top\n      });\n\n      if (this.isCropped && this.isLimited) {\n        this.limitCanvas(true, true);\n      }\n\n      if (!this.isDisabled) {\n        this.output();\n      }\n    },\n\n    output: function () {\n      this.preview();\n\n      if (this.isCompleted) {\n        this.trigger(EVENT_CROP, this.getData());\n      }\n    },\n\n    initPreview: function () {\n      var crossOrigin = getCrossOrigin(this.crossOrigin);\n      var url = crossOrigin ? this.crossOriginUrl : this.url;\n      var $clone2;\n\n      this.$preview = $(this.options.preview);\n      this.$clone2 = $clone2 = $('<img' + crossOrigin + ' src=\"' + url + '\">');\n      this.$viewBox.html($clone2);\n      this.$preview.each(function () {\n        var $this = $(this);\n\n        // Save the original size for recover\n        $this.data(DATA_PREVIEW, {\n          width: $this.width(),\n          height: $this.height(),\n          html: $this.html()\n        });\n\n        /**\n         * Override img element styles\n         * Add `display:block` to avoid margin top issue\n         * (Occur only when margin-top <= -height)\n         */\n        $this.html(\n          '<img' + crossOrigin + ' src=\"' + url + '\" style=\"' +\n          'display:block;width:100%;height:auto;' +\n          'min-width:0!important;min-height:0!important;' +\n          'max-width:none!important;max-height:none!important;' +\n          'image-orientation:0deg!important;\">'\n        );\n      });\n    },\n\n    resetPreview: function () {\n      this.$preview.each(function () {\n        var $this = $(this);\n        var data = $this.data(DATA_PREVIEW);\n\n        $this.css({\n          width: data.width,\n          height: data.height\n        }).html(data.html).removeData(DATA_PREVIEW);\n      });\n    },\n\n    preview: function () {\n      var image = this.image;\n      var canvas = this.canvas;\n      var cropBox = this.cropBox;\n      var cropBoxWidth = cropBox.width;\n      var cropBoxHeight = cropBox.height;\n      var width = image.width;\n      var height = image.height;\n      var left = cropBox.left - canvas.left - image.left;\n      var top = cropBox.top - canvas.top - image.top;\n\n      if (!this.isCropped || this.isDisabled) {\n        return;\n      }\n\n      this.$clone2.css({\n        width: width,\n        height: height,\n        marginLeft: -left,\n        marginTop: -top,\n        transform: getTransform(image)\n      });\n\n      this.$preview.each(function () {\n        var $this = $(this);\n        var data = $this.data(DATA_PREVIEW);\n        var originalWidth = data.width;\n        var originalHeight = data.height;\n        var newWidth = originalWidth;\n        var newHeight = originalHeight;\n        var ratio = 1;\n\n        if (cropBoxWidth) {\n          ratio = originalWidth / cropBoxWidth;\n          newHeight = cropBoxHeight * ratio;\n        }\n\n        if (cropBoxHeight && newHeight > originalHeight) {\n          ratio = originalHeight / cropBoxHeight;\n          newWidth = cropBoxWidth * ratio;\n          newHeight = originalHeight;\n        }\n\n        $this.css({\n          width: newWidth,\n          height: newHeight\n        }).find('img').css({\n          width: width * ratio,\n          height: height * ratio,\n          marginLeft: -left * ratio,\n          marginTop: -top * ratio,\n          transform: getTransform(image)\n        });\n      });\n    },\n\n    bind: function () {\n      var options = this.options;\n      var $this = this.$element;\n      var $cropper = this.$cropper;\n\n      if ($.isFunction(options.cropstart)) {\n        $this.on(EVENT_CROP_START, options.cropstart);\n      }\n\n      if ($.isFunction(options.cropmove)) {\n        $this.on(EVENT_CROP_MOVE, options.cropmove);\n      }\n\n      if ($.isFunction(options.cropend)) {\n        $this.on(EVENT_CROP_END, options.cropend);\n      }\n\n      if ($.isFunction(options.crop)) {\n        $this.on(EVENT_CROP, options.crop);\n      }\n\n      if ($.isFunction(options.zoom)) {\n        $this.on(EVENT_ZOOM, options.zoom);\n      }\n\n      $cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this));\n\n      if (options.zoomable && options.zoomOnWheel) {\n        $cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));\n      }\n\n      if (options.toggleDragModeOnDblclick) {\n        $cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));\n      }\n\n      $document.\n        on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))).\n        on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this)));\n\n      if (options.responsive) {\n        $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));\n      }\n    },\n\n    unbind: function () {\n      var options = this.options;\n      var $this = this.$element;\n      var $cropper = this.$cropper;\n\n      if ($.isFunction(options.cropstart)) {\n        $this.off(EVENT_CROP_START, options.cropstart);\n      }\n\n      if ($.isFunction(options.cropmove)) {\n        $this.off(EVENT_CROP_MOVE, options.cropmove);\n      }\n\n      if ($.isFunction(options.cropend)) {\n        $this.off(EVENT_CROP_END, options.cropend);\n      }\n\n      if ($.isFunction(options.crop)) {\n        $this.off(EVENT_CROP, options.crop);\n      }\n\n      if ($.isFunction(options.zoom)) {\n        $this.off(EVENT_ZOOM, options.zoom);\n      }\n\n      $cropper.off(EVENT_MOUSE_DOWN, this.cropStart);\n\n      if (options.zoomable && options.zoomOnWheel) {\n        $cropper.off(EVENT_WHEEL, this.wheel);\n      }\n\n      if (options.toggleDragModeOnDblclick) {\n        $cropper.off(EVENT_DBLCLICK, this.dblclick);\n      }\n\n      $document.\n        off(EVENT_MOUSE_MOVE, this._cropMove).\n        off(EVENT_MOUSE_UP, this._cropEnd);\n\n      if (options.responsive) {\n        $window.off(EVENT_RESIZE, this._resize);\n      }\n    },\n\n    resize: function () {\n      var restore = this.options.restore;\n      var $container = this.$container;\n      var container = this.container;\n      var canvasData;\n      var cropBoxData;\n      var ratio;\n\n      // Check `container` is necessary for IE8\n      if (this.isDisabled || !container) {\n        return;\n      }\n\n      ratio = $container.width() / container.width;\n\n      // Resize when width changed or height changed\n      if (ratio !== 1 || $container.height() !== container.height) {\n        if (restore) {\n          canvasData = this.getCanvasData();\n          cropBoxData = this.getCropBoxData();\n        }\n\n        this.render();\n\n        if (restore) {\n          this.setCanvasData($.each(canvasData, function (i, n) {\n            canvasData[i] = n * ratio;\n          }));\n          this.setCropBoxData($.each(cropBoxData, function (i, n) {\n            cropBoxData[i] = n * ratio;\n          }));\n        }\n      }\n    },\n\n    dblclick: function () {\n      if (this.isDisabled) {\n        return;\n      }\n\n      if (this.$dragBox.hasClass(CLASS_CROP)) {\n        this.setDragMode(ACTION_MOVE);\n      } else {\n        this.setDragMode(ACTION_CROP);\n      }\n    },\n\n    wheel: function (event) {\n      var e = event.originalEvent || event;\n      var ratio = num(this.options.wheelZoomRatio) || 0.1;\n      var delta = 1;\n\n      if (this.isDisabled) {\n        return;\n      }\n\n      event.preventDefault();\n\n      // Limit wheel speed to prevent zoom too fast\n      if (this.wheeling) {\n        return;\n      }\n\n      this.wheeling = true;\n\n      setTimeout($.proxy(function () {\n        this.wheeling = false;\n      }, this), 50);\n\n      if (e.deltaY) {\n        delta = e.deltaY > 0 ? 1 : -1;\n      } else if (e.wheelDelta) {\n        delta = -e.wheelDelta / 120;\n      } else if (e.detail) {\n        delta = e.detail > 0 ? 1 : -1;\n      }\n\n      this.zoom(-delta * ratio, event);\n    },\n\n    cropStart: function (event) {\n      var options = this.options;\n      var originalEvent = event.originalEvent;\n      var touches = originalEvent && originalEvent.touches;\n      var e = event;\n      var touchesLength;\n      var action;\n\n      if (this.isDisabled) {\n        return;\n      }\n\n      if (touches) {\n        touchesLength = touches.length;\n\n        if (touchesLength > 1) {\n          if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {\n            e = touches[1];\n            this.startX2 = e.pageX;\n            this.startY2 = e.pageY;\n            action = ACTION_ZOOM;\n          } else {\n            return;\n          }\n        }\n\n        e = touches[0];\n      }\n\n      action = action || $(e.target).data(DATA_ACTION);\n\n      if (REGEXP_ACTIONS.test(action)) {\n        if (this.trigger(EVENT_CROP_START, {\n          originalEvent: originalEvent,\n          action: action\n        }).isDefaultPrevented()) {\n          return;\n        }\n\n        event.preventDefault();\n\n        this.action = action;\n        this.cropping = false;\n\n        // IE8  has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`\n        // IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`\n        this.startX = e.pageX || originalEvent && originalEvent.pageX;\n        this.startY = e.pageY || originalEvent && originalEvent.pageY;\n\n        if (action === ACTION_CROP) {\n          this.cropping = true;\n          this.$dragBox.addClass(CLASS_MODAL);\n        }\n      }\n    },\n\n    cropMove: function (event) {\n      var options = this.options;\n      var originalEvent = event.originalEvent;\n      var touches = originalEvent && originalEvent.touches;\n      var e = event;\n      var action = this.action;\n      var touchesLength;\n\n      if (this.isDisabled) {\n        return;\n      }\n\n      if (touches) {\n        touchesLength = touches.length;\n\n        if (touchesLength > 1) {\n          if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {\n            e = touches[1];\n            this.endX2 = e.pageX;\n            this.endY2 = e.pageY;\n          } else {\n            return;\n          }\n        }\n\n        e = touches[0];\n      }\n\n      if (action) {\n        if (this.trigger(EVENT_CROP_MOVE, {\n          originalEvent: originalEvent,\n          action: action\n        }).isDefaultPrevented()) {\n          return;\n        }\n\n        event.preventDefault();\n\n        this.endX = e.pageX || originalEvent && originalEvent.pageX;\n        this.endY = e.pageY || originalEvent && originalEvent.pageY;\n\n        this.change(e.shiftKey, action === ACTION_ZOOM ? event : null);\n      }\n    },\n\n    cropEnd: function (event) {\n      var originalEvent = event.originalEvent;\n      var action = this.action;\n\n      if (this.isDisabled) {\n        return;\n      }\n\n      if (action) {\n        event.preventDefault();\n\n        if (this.cropping) {\n          this.cropping = false;\n          this.$dragBox.toggleClass(CLASS_MODAL, this.isCropped && this.options.modal);\n        }\n\n        this.action = '';\n\n        this.trigger(EVENT_CROP_END, {\n          originalEvent: originalEvent,\n          action: action\n        });\n      }\n    },\n\n    change: function (shiftKey, event) {\n      var options = this.options;\n      var aspectRatio = options.aspectRatio;\n      var action = this.action;\n      var container = this.container;\n      var canvas = this.canvas;\n      var cropBox = this.cropBox;\n      var width = cropBox.width;\n      var height = cropBox.height;\n      var left = cropBox.left;\n      var top = cropBox.top;\n      var right = left + width;\n      var bottom = top + height;\n      var minLeft = 0;\n      var minTop = 0;\n      var maxWidth = container.width;\n      var maxHeight = container.height;\n      var renderable = true;\n      var offset;\n      var range;\n\n      // Locking aspect ratio in \"free mode\" by holding shift key (#259)\n      if (!aspectRatio && shiftKey) {\n        aspectRatio = width && height ? width / height : 1;\n      }\n\n      if (this.isLimited) {\n        minLeft = cropBox.minLeft;\n        minTop = cropBox.minTop;\n        maxWidth = minLeft + min(container.width, canvas.width, canvas.left + canvas.width);\n        maxHeight = minTop + min(container.height, canvas.height, canvas.top + canvas.height);\n      }\n\n      range = {\n        x: this.endX - this.startX,\n        y: this.endY - this.startY\n      };\n\n      if (aspectRatio) {\n        range.X = range.y * aspectRatio;\n        range.Y = range.x / aspectRatio;\n      }\n\n      switch (action) {\n        // Move crop box\n        case ACTION_ALL:\n          left += range.x;\n          top += range.y;\n          break;\n\n        // Resize crop box\n        case ACTION_EAST:\n          if (range.x >= 0 && (right >= maxWidth || aspectRatio &&\n            (top <= minTop || bottom >= maxHeight))) {\n\n            renderable = false;\n            break;\n          }\n\n          width += range.x;\n\n          if (aspectRatio) {\n            height = width / aspectRatio;\n            top -= range.Y / 2;\n          }\n\n          if (width < 0) {\n            action = ACTION_WEST;\n            width = 0;\n          }\n\n          break;\n\n        case ACTION_NORTH:\n          if (range.y <= 0 && (top <= minTop || aspectRatio &&\n            (left <= minLeft || right >= maxWidth))) {\n\n            renderable = false;\n            break;\n          }\n\n          height -= range.y;\n          top += range.y;\n\n          if (aspectRatio) {\n            width = height * aspectRatio;\n            left += range.X / 2;\n          }\n\n          if (height < 0) {\n            action = ACTION_SOUTH;\n            height = 0;\n          }\n\n          break;\n\n        case ACTION_WEST:\n          if (range.x <= 0 && (left <= minLeft || aspectRatio &&\n            (top <= minTop || bottom >= maxHeight))) {\n\n            renderable = false;\n            break;\n          }\n\n          width -= range.x;\n          left += range.x;\n\n          if (aspectRatio) {\n            height = width / aspectRatio;\n            top += range.Y / 2;\n          }\n\n          if (width < 0) {\n            action = ACTION_EAST;\n            width = 0;\n          }\n\n          break;\n\n        case ACTION_SOUTH:\n          if (range.y >= 0 && (bottom >= maxHeight || aspectRatio &&\n            (left <= minLeft || right >= maxWidth))) {\n\n            renderable = false;\n            break;\n          }\n\n          height += range.y;\n\n          if (aspectRatio) {\n            width = height * aspectRatio;\n            left -= range.X / 2;\n          }\n\n          if (height < 0) {\n            action = ACTION_NORTH;\n            height = 0;\n          }\n\n          break;\n\n        case ACTION_NORTH_EAST:\n          if (aspectRatio) {\n            if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n              renderable = false;\n              break;\n            }\n\n            height -= range.y;\n            top += range.y;\n            width = height * aspectRatio;\n          } else {\n            if (range.x >= 0) {\n              if (right < maxWidth) {\n                width += range.x;\n              } else if (range.y <= 0 && top <= minTop) {\n                renderable = false;\n              }\n            } else {\n              width += range.x;\n            }\n\n            if (range.y <= 0) {\n              if (top > minTop) {\n                height -= range.y;\n                top += range.y;\n              }\n            } else {\n              height -= range.y;\n              top += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_SOUTH_WEST;\n            height = 0;\n            width = 0;\n          } else if (width < 0) {\n            action = ACTION_NORTH_WEST;\n            width = 0;\n          } else if (height < 0) {\n            action = ACTION_SOUTH_EAST;\n            height = 0;\n          }\n\n          break;\n\n        case ACTION_NORTH_WEST:\n          if (aspectRatio) {\n            if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n              renderable = false;\n              break;\n            }\n\n            height -= range.y;\n            top += range.y;\n            width = height * aspectRatio;\n            left += range.X;\n          } else {\n            if (range.x <= 0) {\n              if (left > minLeft) {\n                width -= range.x;\n                left += range.x;\n              } else if (range.y <= 0 && top <= minTop) {\n                renderable = false;\n              }\n            } else {\n              width -= range.x;\n              left += range.x;\n            }\n\n            if (range.y <= 0) {\n              if (top > minTop) {\n                height -= range.y;\n                top += range.y;\n              }\n            } else {\n              height -= range.y;\n              top += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_SOUTH_EAST;\n            height = 0;\n            width = 0;\n          } else if (width < 0) {\n            action = ACTION_NORTH_EAST;\n            width = 0;\n          } else if (height < 0) {\n            action = ACTION_SOUTH_WEST;\n            height = 0;\n          }\n\n          break;\n\n        case ACTION_SOUTH_WEST:\n          if (aspectRatio) {\n            if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n              renderable = false;\n              break;\n            }\n\n            width -= range.x;\n            left += range.x;\n            height = width / aspectRatio;\n          } else {\n            if (range.x <= 0) {\n              if (left > minLeft) {\n                width -= range.x;\n                left += range.x;\n              } else if (range.y >= 0 && bottom >= maxHeight) {\n                renderable = false;\n              }\n            } else {\n              width -= range.x;\n              left += range.x;\n            }\n\n            if (range.y >= 0) {\n              if (bottom < maxHeight) {\n                height += range.y;\n              }\n            } else {\n              height += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_NORTH_EAST;\n            height = 0;\n            width = 0;\n          } else if (width < 0) {\n            action = ACTION_SOUTH_EAST;\n            width = 0;\n          } else if (height < 0) {\n            action = ACTION_NORTH_WEST;\n            height = 0;\n          }\n\n          break;\n\n        case ACTION_SOUTH_EAST:\n          if (aspectRatio) {\n            if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n              renderable = false;\n              break;\n            }\n\n            width += range.x;\n            height = width / aspectRatio;\n          } else {\n            if (range.x >= 0) {\n              if (right < maxWidth) {\n                width += range.x;\n              } else if (range.y >= 0 && bottom >= maxHeight) {\n                renderable = false;\n              }\n            } else {\n              width += range.x;\n            }\n\n            if (range.y >= 0) {\n              if (bottom < maxHeight) {\n                height += range.y;\n              }\n            } else {\n              height += range.y;\n            }\n          }\n\n          if (width < 0 && height < 0) {\n            action = ACTION_NORTH_WEST;\n            height = 0;\n            width = 0;\n          } else if (width < 0) {\n            action = ACTION_SOUTH_WEST;\n            width = 0;\n          } else if (height < 0) {\n            action = ACTION_NORTH_EAST;\n            height = 0;\n          }\n\n          break;\n\n        // Move canvas\n        case ACTION_MOVE:\n          this.move(range.x, range.y);\n          renderable = false;\n          break;\n\n        // Zoom canvas\n        case ACTION_ZOOM:\n          this.zoom((function (x1, y1, x2, y2) {\n            var z1 = sqrt(x1 * x1 + y1 * y1);\n            var z2 = sqrt(x2 * x2 + y2 * y2);\n\n            return (z2 - z1) / z1;\n          })(\n            abs(this.startX - this.startX2),\n            abs(this.startY - this.startY2),\n            abs(this.endX - this.endX2),\n            abs(this.endY - this.endY2)\n          ), event);\n          this.startX2 = this.endX2;\n          this.startY2 = this.endY2;\n          renderable = false;\n          break;\n\n        // Create crop box\n        case ACTION_CROP:\n          if (!range.x || !range.y) {\n            renderable = false;\n            break;\n          }\n\n          offset = this.$cropper.offset();\n          left = this.startX - offset.left;\n          top = this.startY - offset.top;\n          width = cropBox.minWidth;\n          height = cropBox.minHeight;\n\n          if (range.x > 0) {\n            action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n          } else if (range.x < 0) {\n            left -= width;\n            action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n          }\n\n          if (range.y < 0) {\n            top -= height;\n          }\n\n          // Show the crop box if is hidden\n          if (!this.isCropped) {\n            this.$cropBox.removeClass(CLASS_HIDDEN);\n            this.isCropped = true;\n\n            if (this.isLimited) {\n              this.limitCropBox(true, true);\n            }\n          }\n\n          break;\n\n        // No default\n      }\n\n      if (renderable) {\n        cropBox.width = width;\n        cropBox.height = height;\n        cropBox.left = left;\n        cropBox.top = top;\n        this.action = action;\n\n        this.renderCropBox();\n      }\n\n      // Override\n      this.startX = this.endX;\n      this.startY = this.endY;\n    },\n\n    // Show the crop box manually\n    crop: function () {\n      if (!this.isBuilt || this.isDisabled) {\n        return;\n      }\n\n      if (!this.isCropped) {\n        this.isCropped = true;\n        this.limitCropBox(true, true);\n\n        if (this.options.modal) {\n          this.$dragBox.addClass(CLASS_MODAL);\n        }\n\n        this.$cropBox.removeClass(CLASS_HIDDEN);\n      }\n\n      this.setCropBoxData(this.initialCropBox);\n    },\n\n    // Reset the image and crop box to their initial states\n    reset: function () {\n      if (!this.isBuilt || this.isDisabled) {\n        return;\n      }\n\n      this.image = $.extend({}, this.initialImage);\n      this.canvas = $.extend({}, this.initialCanvas);\n      this.cropBox = $.extend({}, this.initialCropBox);\n\n      this.renderCanvas();\n\n      if (this.isCropped) {\n        this.renderCropBox();\n      }\n    },\n\n    // Clear the crop box\n    clear: function () {\n      if (!this.isCropped || this.isDisabled) {\n        return;\n      }\n\n      $.extend(this.cropBox, {\n        left: 0,\n        top: 0,\n        width: 0,\n        height: 0\n      });\n\n      this.isCropped = false;\n      this.renderCropBox();\n\n      this.limitCanvas(true, true);\n\n      // Render canvas after crop box rendered\n      this.renderCanvas();\n\n      this.$dragBox.removeClass(CLASS_MODAL);\n      this.$cropBox.addClass(CLASS_HIDDEN);\n    },\n\n    /**\n     * Replace the image's src and rebuild the cropper\n     *\n     * @param {String} url\n     * @param {Boolean} onlyColorChanged (optional)\n     */\n    replace: function (url, onlyColorChanged) {\n      if (!this.isDisabled && url) {\n        if (this.isImg) {\n          this.$element.attr('src', url);\n        }\n\n        if (onlyColorChanged) {\n          this.url = url;\n          this.$clone.attr('src', url);\n\n          if (this.isBuilt) {\n            this.$preview.find('img').add(this.$clone2).attr('src', url);\n          }\n        } else {\n          if (this.isImg) {\n            this.isReplaced = true;\n          }\n\n          // Clear previous data\n          this.options.data = null;\n          this.load(url);\n        }\n      }\n    },\n\n    // Enable (unfreeze) the cropper\n    enable: function () {\n      if (this.isBuilt) {\n        this.isDisabled = false;\n        this.$cropper.removeClass(CLASS_DISABLED);\n      }\n    },\n\n    // Disable (freeze) the cropper\n    disable: function () {\n      if (this.isBuilt) {\n        this.isDisabled = true;\n        this.$cropper.addClass(CLASS_DISABLED);\n      }\n    },\n\n    // Destroy the cropper and remove the instance from the image\n    destroy: function () {\n      var $this = this.$element;\n\n      if (this.isLoaded) {\n        if (this.isImg && this.isReplaced) {\n          $this.attr('src', this.originalUrl);\n        }\n\n        this.unbuild();\n        $this.removeClass(CLASS_HIDDEN);\n      } else {\n        if (this.isImg) {\n          $this.off(EVENT_LOAD, this.start);\n        } else if (this.$clone) {\n          this.$clone.remove();\n        }\n      }\n\n      $this.removeData(NAMESPACE);\n    },\n\n    /**\n     * Move the canvas with relative offsets\n     *\n     * @param {Number} offsetX\n     * @param {Number} offsetY (optional)\n     */\n    move: function (offsetX, offsetY) {\n      var canvas = this.canvas;\n\n      this.moveTo(\n        isUndefined(offsetX) ? offsetX : canvas.left + num(offsetX),\n        isUndefined(offsetY) ? offsetY : canvas.top + num(offsetY)\n      );\n    },\n\n    /**\n     * Move the canvas to an absolute point\n     *\n     * @param {Number} x\n     * @param {Number} y (optional)\n     */\n    moveTo: function (x, y) {\n      var canvas = this.canvas;\n      var isChanged = false;\n\n      // If \"y\" is not present, its default value is \"x\"\n      if (isUndefined(y)) {\n        y = x;\n      }\n\n      x = num(x);\n      y = num(y);\n\n      if (this.isBuilt && !this.isDisabled && this.options.movable) {\n        if (isNumber(x)) {\n          canvas.left = x;\n          isChanged = true;\n        }\n\n        if (isNumber(y)) {\n          canvas.top = y;\n          isChanged = true;\n        }\n\n        if (isChanged) {\n          this.renderCanvas(true);\n        }\n      }\n    },\n\n    /**\n     * Zoom the canvas with a relative ratio\n     *\n     * @param {Number} ratio\n     * @param {jQuery Event} _event (private)\n     */\n    zoom: function (ratio, _event) {\n      var canvas = this.canvas;\n\n      ratio = num(ratio);\n\n      if (ratio < 0) {\n        ratio =  1 / (1 - ratio);\n      } else {\n        ratio = 1 + ratio;\n      }\n\n      this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _event);\n    },\n\n    /**\n     * Zoom the canvas to an absolute ratio\n     *\n     * @param {Number} ratio\n     * @param {jQuery Event} _event (private)\n     */\n    zoomTo: function (ratio, _event) {\n      var options = this.options;\n      var canvas = this.canvas;\n      var width = canvas.width;\n      var height = canvas.height;\n      var naturalWidth = canvas.naturalWidth;\n      var naturalHeight = canvas.naturalHeight;\n      var originalEvent;\n      var newWidth;\n      var newHeight;\n      var offset;\n      var center;\n\n      ratio = num(ratio);\n\n      if (ratio >= 0 && this.isBuilt && !this.isDisabled && options.zoomable) {\n        newWidth = naturalWidth * ratio;\n        newHeight = naturalHeight * ratio;\n\n        if (_event) {\n          originalEvent = _event.originalEvent;\n        }\n\n        if (this.trigger(EVENT_ZOOM, {\n          originalEvent: originalEvent,\n          oldRatio: width / naturalWidth,\n          ratio: newWidth / naturalWidth\n        }).isDefaultPrevented()) {\n          return;\n        }\n\n        if (originalEvent) {\n          offset = this.$cropper.offset();\n          center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : {\n            pageX: _event.pageX || originalEvent.pageX || 0,\n            pageY: _event.pageY || originalEvent.pageY || 0\n          };\n\n          // Zoom from the triggering point of the event\n          canvas.left -= (newWidth - width) * (\n            ((center.pageX - offset.left) - canvas.left) / width\n          );\n          canvas.top -= (newHeight - height) * (\n            ((center.pageY - offset.top) - canvas.top) / height\n          );\n        } else {\n\n          // Zoom from the center of the canvas\n          canvas.left -= (newWidth - width) / 2;\n          canvas.top -= (newHeight - height) / 2;\n        }\n\n        canvas.width = newWidth;\n        canvas.height = newHeight;\n        this.renderCanvas(true);\n      }\n    },\n\n    /**\n     * Rotate the canvas with a relative degree\n     *\n     * @param {Number} degree\n     */\n    rotate: function (degree) {\n      this.rotateTo((this.image.rotate || 0) + num(degree));\n    },\n\n    /**\n     * Rotate the canvas to an absolute degree\n     * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()\n     *\n     * @param {Number} degree\n     */\n    rotateTo: function (degree) {\n      degree = num(degree);\n\n      if (isNumber(degree) && this.isBuilt && !this.isDisabled && this.options.rotatable) {\n        this.image.rotate = degree % 360;\n        this.isRotated = true;\n        this.renderCanvas(true);\n      }\n    },\n\n    /**\n     * Scale the image\n     * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()\n     *\n     * @param {Number} scaleX\n     * @param {Number} scaleY (optional)\n     */\n    scale: function (scaleX, scaleY) {\n      var image = this.image;\n      var isChanged = false;\n\n      // If \"scaleY\" is not present, its default value is \"scaleX\"\n      if (isUndefined(scaleY)) {\n        scaleY = scaleX;\n      }\n\n      scaleX = num(scaleX);\n      scaleY = num(scaleY);\n\n      if (this.isBuilt && !this.isDisabled && this.options.scalable) {\n        if (isNumber(scaleX)) {\n          image.scaleX = scaleX;\n          isChanged = true;\n        }\n\n        if (isNumber(scaleY)) {\n          image.scaleY = scaleY;\n          isChanged = true;\n        }\n\n        if (isChanged) {\n          this.renderImage(true);\n        }\n      }\n    },\n\n    /**\n     * Scale the abscissa of the image\n     *\n     * @param {Number} scaleX\n     */\n    scaleX: function (scaleX) {\n      var scaleY = this.image.scaleY;\n\n      this.scale(scaleX, isNumber(scaleY) ? scaleY : 1);\n    },\n\n    /**\n     * Scale the ordinate of the image\n     *\n     * @param {Number} scaleY\n     */\n    scaleY: function (scaleY) {\n      var scaleX = this.image.scaleX;\n\n      this.scale(isNumber(scaleX) ? scaleX : 1, scaleY);\n    },\n\n    /**\n     * Get the cropped area position and size data (base on the original image)\n     *\n     * @param {Boolean} isRounded (optional)\n     * @return {Object} data\n     */\n    getData: function (isRounded) {\n      var options = this.options;\n      var image = this.image;\n      var canvas = this.canvas;\n      var cropBox = this.cropBox;\n      var ratio;\n      var data;\n\n      if (this.isBuilt && this.isCropped) {\n        data = {\n          x: cropBox.left - canvas.left,\n          y: cropBox.top - canvas.top,\n          width: cropBox.width,\n          height: cropBox.height\n        };\n\n        ratio = image.width / image.naturalWidth;\n\n        $.each(data, function (i, n) {\n          n = n / ratio;\n          data[i] = isRounded ? round(n) : n;\n        });\n\n      } else {\n        data = {\n          x: 0,\n          y: 0,\n          width: 0,\n          height: 0\n        };\n      }\n\n      if (options.rotatable) {\n        data.rotate = image.rotate || 0;\n      }\n\n      if (options.scalable) {\n        data.scaleX = image.scaleX || 1;\n        data.scaleY = image.scaleY || 1;\n      }\n\n      return data;\n    },\n\n    /**\n     * Set the cropped area position and size with new data\n     *\n     * @param {Object} data\n     */\n    setData: function (data) {\n      var options = this.options;\n      var image = this.image;\n      var canvas = this.canvas;\n      var cropBoxData = {};\n      var isRotated;\n      var isScaled;\n      var ratio;\n\n      if ($.isFunction(data)) {\n        data = data.call(this.element);\n      }\n\n      if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {\n        if (options.rotatable) {\n          if (isNumber(data.rotate) && data.rotate !== image.rotate) {\n            image.rotate = data.rotate;\n            this.isRotated = isRotated = true;\n          }\n        }\n\n        if (options.scalable) {\n          if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {\n            image.scaleX = data.scaleX;\n            isScaled = true;\n          }\n\n          if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {\n            image.scaleY = data.scaleY;\n            isScaled = true;\n          }\n        }\n\n        if (isRotated) {\n          this.renderCanvas();\n        } else if (isScaled) {\n          this.renderImage();\n        }\n\n        ratio = image.width / image.naturalWidth;\n\n        if (isNumber(data.x)) {\n          cropBoxData.left = data.x * ratio + canvas.left;\n        }\n\n        if (isNumber(data.y)) {\n          cropBoxData.top = data.y * ratio + canvas.top;\n        }\n\n        if (isNumber(data.width)) {\n          cropBoxData.width = data.width * ratio;\n        }\n\n        if (isNumber(data.height)) {\n          cropBoxData.height = data.height * ratio;\n        }\n\n        this.setCropBoxData(cropBoxData);\n      }\n    },\n\n    /**\n     * Get the container size data\n     *\n     * @return {Object} data\n     */\n    getContainerData: function () {\n      return this.isBuilt ? this.container : {};\n    },\n\n    /**\n     * Get the image position and size data\n     *\n     * @return {Object} data\n     */\n    getImageData: function () {\n      return this.isLoaded ? this.image : {};\n    },\n\n    /**\n     * Get the canvas position and size data\n     *\n     * @return {Object} data\n     */\n    getCanvasData: function () {\n      var canvas = this.canvas;\n      var data = {};\n\n      if (this.isBuilt) {\n        $.each([\n          'left',\n          'top',\n          'width',\n          'height',\n          'naturalWidth',\n          'naturalHeight'\n        ], function (i, n) {\n          data[n] = canvas[n];\n        });\n      }\n\n      return data;\n    },\n\n    /**\n     * Set the canvas position and size with new data\n     *\n     * @param {Object} data\n     */\n    setCanvasData: function (data) {\n      var canvas = this.canvas;\n      var aspectRatio = canvas.aspectRatio;\n\n      if ($.isFunction(data)) {\n        data = data.call(this.$element);\n      }\n\n      if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {\n        if (isNumber(data.left)) {\n          canvas.left = data.left;\n        }\n\n        if (isNumber(data.top)) {\n          canvas.top = data.top;\n        }\n\n        if (isNumber(data.width)) {\n          canvas.width = data.width;\n          canvas.height = data.width / aspectRatio;\n        } else if (isNumber(data.height)) {\n          canvas.height = data.height;\n          canvas.width = data.height * aspectRatio;\n        }\n\n        this.renderCanvas(true);\n      }\n    },\n\n    /**\n     * Get the crop box position and size data\n     *\n     * @return {Object} data\n     */\n    getCropBoxData: function () {\n      var cropBox = this.cropBox;\n      var data;\n\n      if (this.isBuilt && this.isCropped) {\n        data = {\n          left: cropBox.left,\n          top: cropBox.top,\n          width: cropBox.width,\n          height: cropBox.height\n        };\n      }\n\n      return data || {};\n    },\n\n    /**\n     * Set the crop box position and size with new data\n     *\n     * @param {Object} data\n     */\n    setCropBoxData: function (data) {\n      var cropBox = this.cropBox;\n      var aspectRatio = this.options.aspectRatio;\n      var isWidthChanged;\n      var isHeightChanged;\n\n      if ($.isFunction(data)) {\n        data = data.call(this.$element);\n      }\n\n      if (this.isBuilt && this.isCropped && !this.isDisabled && $.isPlainObject(data)) {\n\n        if (isNumber(data.left)) {\n          cropBox.left = data.left;\n        }\n\n        if (isNumber(data.top)) {\n          cropBox.top = data.top;\n        }\n\n        if (isNumber(data.width)) {\n          isWidthChanged = true;\n          cropBox.width = data.width;\n        }\n\n        if (isNumber(data.height)) {\n          isHeightChanged = true;\n          cropBox.height = data.height;\n        }\n\n        if (aspectRatio) {\n          if (isWidthChanged) {\n            cropBox.height = cropBox.width / aspectRatio;\n          } else if (isHeightChanged) {\n            cropBox.width = cropBox.height * aspectRatio;\n          }\n        }\n\n        this.renderCropBox();\n      }\n    },\n\n    /**\n     * Get a canvas drawn the cropped image\n     *\n     * @param {Object} options (optional)\n     * @return {HTMLCanvasElement} canvas\n     */\n    getCroppedCanvas: function (options) {\n      var originalWidth;\n      var originalHeight;\n      var canvasWidth;\n      var canvasHeight;\n      var scaledWidth;\n      var scaledHeight;\n      var scaledRatio;\n      var aspectRatio;\n      var canvas;\n      var context;\n      var data;\n\n      if (!this.isBuilt || !SUPPORT_CANVAS) {\n        return;\n      }\n\n      if (!this.isCropped) {\n        return getSourceCanvas(this.$clone[0], this.image);\n      }\n\n      if (!$.isPlainObject(options)) {\n        options = {};\n      }\n\n      data = this.getData();\n      originalWidth = data.width;\n      originalHeight = data.height;\n      aspectRatio = originalWidth / originalHeight;\n\n      if ($.isPlainObject(options)) {\n        scaledWidth = options.width;\n        scaledHeight = options.height;\n\n        if (scaledWidth) {\n          scaledHeight = scaledWidth / aspectRatio;\n          scaledRatio = scaledWidth / originalWidth;\n        } else if (scaledHeight) {\n          scaledWidth = scaledHeight * aspectRatio;\n          scaledRatio = scaledHeight / originalHeight;\n        }\n      }\n\n      // The canvas element will use `Math.floor` on a float number, so floor first\n      canvasWidth = floor(scaledWidth || originalWidth);\n      canvasHeight = floor(scaledHeight || originalHeight);\n\n      canvas = $('<canvas>')[0];\n      canvas.width = canvasWidth;\n      canvas.height = canvasHeight;\n      context = canvas.getContext('2d');\n\n      if (options.fillColor) {\n        context.fillStyle = options.fillColor;\n        context.fillRect(0, 0, canvasWidth, canvasHeight);\n      }\n\n      // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n      context.drawImage.apply(context, (function () {\n        var source = getSourceCanvas(this.$clone[0], this.image);\n        var sourceWidth = source.width;\n        var sourceHeight = source.height;\n        var canvas = this.canvas;\n        var params = [source];\n\n        // Source canvas\n        var srcX = data.x + canvas.naturalWidth * (abs(data.scaleX || 1) - 1) / 2;\n        var srcY = data.y + canvas.naturalHeight * (abs(data.scaleY || 1) - 1) / 2;\n        var srcWidth;\n        var srcHeight;\n\n        // Destination canvas\n        var dstX;\n        var dstY;\n        var dstWidth;\n        var dstHeight;\n\n        if (srcX <= -originalWidth || srcX > sourceWidth) {\n          srcX = srcWidth = dstX = dstWidth = 0;\n        } else if (srcX <= 0) {\n          dstX = -srcX;\n          srcX = 0;\n          srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);\n        } else if (srcX <= sourceWidth) {\n          dstX = 0;\n          srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);\n        }\n\n        if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {\n          srcY = srcHeight = dstY = dstHeight = 0;\n        } else if (srcY <= 0) {\n          dstY = -srcY;\n          srcY = 0;\n          srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);\n        } else if (srcY <= sourceHeight) {\n          dstY = 0;\n          srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);\n        }\n\n        // All the numerical parameters should be integer for `drawImage` (#476)\n        params.push(floor(srcX), floor(srcY), floor(srcWidth), floor(srcHeight));\n\n        // Scale destination sizes\n        if (scaledRatio) {\n          dstX *= scaledRatio;\n          dstY *= scaledRatio;\n          dstWidth *= scaledRatio;\n          dstHeight *= scaledRatio;\n        }\n\n        // Avoid \"IndexSizeError\" in IE and Firefox\n        if (dstWidth > 0 && dstHeight > 0) {\n          params.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));\n        }\n\n        return params;\n      }).call(this));\n\n      return canvas;\n    },\n\n    /**\n     * Change the aspect ratio of the crop box\n     *\n     * @param {Number} aspectRatio\n     */\n    setAspectRatio: function (aspectRatio) {\n      var options = this.options;\n\n      if (!this.isDisabled && !isUndefined(aspectRatio)) {\n\n        // 0 -> NaN\n        options.aspectRatio = max(0, aspectRatio) || NaN;\n\n        if (this.isBuilt) {\n          this.initCropBox();\n\n          if (this.isCropped) {\n            this.renderCropBox();\n          }\n        }\n      }\n    },\n\n    /**\n     * Change the drag mode\n     *\n     * @param {String} mode (optional)\n     */\n    setDragMode: function (mode) {\n      var options = this.options;\n      var croppable;\n      var movable;\n\n      if (this.isLoaded && !this.isDisabled) {\n        croppable = mode === ACTION_CROP;\n        movable = options.movable && mode === ACTION_MOVE;\n        mode = (croppable || movable) ? mode : ACTION_NONE;\n\n        this.$dragBox.\n          data(DATA_ACTION, mode).\n          toggleClass(CLASS_CROP, croppable).\n          toggleClass(CLASS_MOVE, movable);\n\n        if (!options.cropBoxMovable) {\n\n          // Sync drag mode to crop box when it is not movable(#300)\n          this.$face.\n            data(DATA_ACTION, mode).\n            toggleClass(CLASS_CROP, croppable).\n            toggleClass(CLASS_MOVE, movable);\n        }\n      }\n    }\n  };\n\n  Cropper.DEFAULTS = {\n\n    // Define the view mode of the cropper\n    viewMode: 0, // 0, 1, 2, 3\n\n    // Define the dragging mode of the cropper\n    dragMode: 'crop', // 'crop', 'move' or 'none'\n\n    // Define the aspect ratio of the crop box\n    aspectRatio: NaN,\n\n    // An object with the previous cropping result data\n    data: null,\n\n    // A jQuery selector for adding extra containers to preview\n    preview: '',\n\n    // Re-render the cropper when resize the window\n    responsive: true,\n\n    // Restore the cropped area after resize the window\n    restore: true,\n\n    // Check if the current image is a cross-origin image\n    checkCrossOrigin: true,\n\n    // Check the current image's Exif Orientation information\n    checkOrientation: true,\n\n    // Show the black modal\n    modal: true,\n\n    // Show the dashed lines for guiding\n    guides: true,\n\n    // Show the center indicator for guiding\n    center: true,\n\n    // Show the white modal to highlight the crop box\n    highlight: true,\n\n    // Show the grid background\n    background: true,\n\n    // Enable to crop the image automatically when initialize\n    autoCrop: true,\n\n    // Define the percentage of automatic cropping area when initializes\n    autoCropArea: 0.8,\n\n    // Enable to move the image\n    movable: true,\n\n    // Enable to rotate the image\n    rotatable: true,\n\n    // Enable to scale the image\n    scalable: true,\n\n    // Enable to zoom the image\n    zoomable: true,\n\n    // Enable to zoom the image by dragging touch\n    zoomOnTouch: true,\n\n    // Enable to zoom the image by wheeling mouse\n    zoomOnWheel: true,\n\n    // Define zoom ratio when zoom the image by wheeling mouse\n    wheelZoomRatio: 0.1,\n\n    // Enable to move the crop box\n    cropBoxMovable: true,\n\n    // Enable to resize the crop box\n    cropBoxResizable: true,\n\n    // Toggle drag mode between \"crop\" and \"move\" when click twice on the cropper\n    toggleDragModeOnDblclick: true,\n\n    // Size limitation\n    minCanvasWidth: 0,\n    minCanvasHeight: 0,\n    minCropBoxWidth: 0,\n    minCropBoxHeight: 0,\n    minContainerWidth: 200,\n    minContainerHeight: 100,\n\n    // Shortcuts of events\n    build: null,\n    built: null,\n    cropstart: null,\n    cropmove: null,\n    cropend: null,\n    crop: null,\n    zoom: null\n  };\n\n  Cropper.setDefaults = function (options) {\n    $.extend(Cropper.DEFAULTS, options);\n  };\n\n  Cropper.TEMPLATE = (\n    '<div class=\"cropper-container\">' +\n      '<div class=\"cropper-wrap-box\">' +\n        '<div class=\"cropper-canvas\"></div>' +\n      '</div>' +\n      '<div class=\"cropper-drag-box\"></div>' +\n      '<div class=\"cropper-crop-box\">' +\n        '<span class=\"cropper-view-box\"></span>' +\n        '<span class=\"cropper-dashed dashed-h\"></span>' +\n        '<span class=\"cropper-dashed dashed-v\"></span>' +\n        '<span class=\"cropper-center\"></span>' +\n        '<span class=\"cropper-face\"></span>' +\n        '<span class=\"cropper-line line-e\" data-action=\"e\"></span>' +\n        '<span class=\"cropper-line line-n\" data-action=\"n\"></span>' +\n        '<span class=\"cropper-line line-w\" data-action=\"w\"></span>' +\n        '<span class=\"cropper-line line-s\" data-action=\"s\"></span>' +\n        '<span class=\"cropper-point point-e\" data-action=\"e\"></span>' +\n        '<span class=\"cropper-point point-n\" data-action=\"n\"></span>' +\n        '<span class=\"cropper-point point-w\" data-action=\"w\"></span>' +\n        '<span class=\"cropper-point point-s\" data-action=\"s\"></span>' +\n        '<span class=\"cropper-point point-ne\" data-action=\"ne\"></span>' +\n        '<span class=\"cropper-point point-nw\" data-action=\"nw\"></span>' +\n        '<span class=\"cropper-point point-sw\" data-action=\"sw\"></span>' +\n        '<span class=\"cropper-point point-se\" data-action=\"se\"></span>' +\n      '</div>' +\n    '</div>'\n  );\n\n  // Save the other cropper\n  Cropper.other = $.fn.cropper;\n\n  // Register as jQuery plugin\n  $.fn.cropper = function (option) {\n    var args = toArray(arguments, 1);\n    var result;\n\n    this.each(function () {\n      var $this = $(this);\n      var data = $this.data(NAMESPACE);\n      var options;\n      var fn;\n\n      if (!data) {\n        if (/destroy/.test(option)) {\n          return;\n        }\n\n        options = $.extend({}, $this.data(), $.isPlainObject(option) && option);\n        $this.data(NAMESPACE, (data = new Cropper(this, options)));\n      }\n\n      if (typeof option === 'string' && $.isFunction(fn = data[option])) {\n        result = fn.apply(data, args);\n      }\n    });\n\n    return isUndefined(result) ? this : result;\n  };\n\n  $.fn.cropper.Constructor = Cropper;\n  $.fn.cropper.setDefaults = Cropper.setDefaults;\n\n  // No conflict\n  $.fn.cropper.noConflict = function () {\n    $.fn.cropper = Cropper.other;\n    return this;\n  };\n\n});\n"
  },
  {
    "path": "docs/v2.3.4/js/main.js",
    "content": "$(function () {\n\n  'use strict';\n\n  var console = window.console || { log: function () {} };\n  var $image = $('#image');\n  var $download = $('#download');\n  var $dataX = $('#dataX');\n  var $dataY = $('#dataY');\n  var $dataHeight = $('#dataHeight');\n  var $dataWidth = $('#dataWidth');\n  var $dataRotate = $('#dataRotate');\n  var $dataScaleX = $('#dataScaleX');\n  var $dataScaleY = $('#dataScaleY');\n  var options = {\n        aspectRatio: 16 / 9,\n        preview: '.img-preview',\n        crop: function (e) {\n          $dataX.val(Math.round(e.x));\n          $dataY.val(Math.round(e.y));\n          $dataHeight.val(Math.round(e.height));\n          $dataWidth.val(Math.round(e.width));\n          $dataRotate.val(e.rotate);\n          $dataScaleX.val(e.scaleX);\n          $dataScaleY.val(e.scaleY);\n        }\n      };\n\n\n  // Tooltip\n  $('[data-toggle=\"tooltip\"]').tooltip();\n\n\n  // Cropper\n  $image.on({\n    'build.cropper': function (e) {\n      console.log(e.type);\n    },\n    'built.cropper': function (e) {\n      console.log(e.type);\n    },\n    'cropstart.cropper': function (e) {\n      console.log(e.type, e.action);\n    },\n    'cropmove.cropper': function (e) {\n      console.log(e.type, e.action);\n    },\n    'cropend.cropper': function (e) {\n      console.log(e.type, e.action);\n    },\n    'crop.cropper': function (e) {\n      console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);\n    },\n    'zoom.cropper': function (e) {\n      console.log(e.type, e.ratio);\n    }\n  }).cropper(options);\n\n\n  // Buttons\n  if (!$.isFunction(document.createElement('canvas').getContext)) {\n    $('button[data-method=\"getCroppedCanvas\"]').prop('disabled', true);\n  }\n\n  if (typeof document.createElement('cropper').style.transition === 'undefined') {\n    $('button[data-method=\"rotate\"]').prop('disabled', true);\n    $('button[data-method=\"scale\"]').prop('disabled', true);\n  }\n\n\n  // Download\n  if (typeof $download[0].download === 'undefined') {\n    $download.addClass('disabled');\n  }\n\n\n  // Options\n  $('.docs-toggles').on('change', 'input', function () {\n    var $this = $(this);\n    var name = $this.attr('name');\n    var type = $this.prop('type');\n    var cropBoxData;\n    var canvasData;\n\n    if (!$image.data('cropper')) {\n      return;\n    }\n\n    if (type === 'checkbox') {\n      options[name] = $this.prop('checked');\n      cropBoxData = $image.cropper('getCropBoxData');\n      canvasData = $image.cropper('getCanvasData');\n\n      options.built = function () {\n        $image.cropper('setCropBoxData', cropBoxData);\n        $image.cropper('setCanvasData', canvasData);\n      };\n    } else if (type === 'radio') {\n      options[name] = $this.val();\n    }\n\n    $image.cropper('destroy').cropper(options);\n  });\n\n\n  // Methods\n  $('.docs-buttons').on('click', '[data-method]', function () {\n    var $this = $(this);\n    var data = $this.data();\n    var $target;\n    var result;\n\n    if ($this.prop('disabled') || $this.hasClass('disabled')) {\n      return;\n    }\n\n    if ($image.data('cropper') && data.method) {\n      data = $.extend({}, data); // Clone a new one\n\n      if (typeof data.target !== 'undefined') {\n        $target = $(data.target);\n\n        if (typeof data.option === 'undefined') {\n          try {\n            data.option = JSON.parse($target.val());\n          } catch (e) {\n            console.log(e.message);\n          }\n        }\n      }\n\n      if (data.method === 'rotate') {\n        $image.cropper('clear');\n      }\n\n      result = $image.cropper(data.method, data.option, data.secondOption);\n\n      if (data.method === 'rotate') {\n        $image.cropper('crop');\n      }\n\n      switch (data.method) {\n        case 'scaleX':\n        case 'scaleY':\n          $(this).data('option', -data.option);\n          break;\n\n        case 'getCroppedCanvas':\n          if (result) {\n\n            // Bootstrap's Modal\n            $('#getCroppedCanvasModal').modal().find('.modal-body').html(result);\n\n            if (!$download.hasClass('disabled')) {\n              $download.attr('href', result.toDataURL('image/jpeg'));\n            }\n          }\n\n          break;\n      }\n\n      if ($.isPlainObject(result) && $target) {\n        try {\n          $target.val(JSON.stringify(result));\n        } catch (e) {\n          console.log(e.message);\n        }\n      }\n\n    }\n  });\n\n\n  // Keyboard\n  $(document.body).on('keydown', function (e) {\n\n    if (!$image.data('cropper') || this.scrollTop > 300) {\n      return;\n    }\n\n    switch (e.which) {\n      case 37:\n        e.preventDefault();\n        $image.cropper('move', -1, 0);\n        break;\n\n      case 38:\n        e.preventDefault();\n        $image.cropper('move', 0, -1);\n        break;\n\n      case 39:\n        e.preventDefault();\n        $image.cropper('move', 1, 0);\n        break;\n\n      case 40:\n        e.preventDefault();\n        $image.cropper('move', 0, 1);\n        break;\n    }\n\n  });\n\n\n  // Import image\n  var $inputImage = $('#inputImage');\n  var URL = window.URL || window.webkitURL;\n  var blobURL;\n\n  if (URL) {\n    $inputImage.change(function () {\n      var files = this.files;\n      var file;\n\n      if (!$image.data('cropper')) {\n        return;\n      }\n\n      if (files && files.length) {\n        file = files[0];\n\n        if (/^image\\/\\w+$/.test(file.type)) {\n          blobURL = URL.createObjectURL(file);\n          $image.one('built.cropper', function () {\n\n            // Revoke when load complete\n            URL.revokeObjectURL(blobURL);\n          }).cropper('reset').cropper('replace', blobURL);\n          $inputImage.val('');\n        } else {\n          window.alert('Please choose an image file.');\n        }\n      }\n    });\n  } else {\n    $inputImage.prop('disabled', true).parent().addClass('disabled');\n  }\n\n});\n"
  },
  {
    "path": "docs/v3.1.6/css/cropper.css",
    "content": "/*!\n * Cropper v3.1.6\n * https://github.com/fengyuanchen/cropper\n *\n * Copyright (c) 2014-2018 Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2018-03-01T13:33:39.581Z\n */\n\n .cropper-container {\n  direction: ltr;\n  font-size: 0;\n  line-height: 0;\n  position: relative;\n  -ms-touch-action: none;\n  touch-action: none;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n\n.cropper-container img {/*Avoid margin top issue (Occur only when margin-top <= -height)\n */\n  display: block;\n  height: 100%;\n  image-orientation: 0deg;\n  max-height: none !important;\n  max-width: none !important;\n  min-height: 0 !important;\n  min-width: 0 !important;\n  width: 100%;\n}\n\n.cropper-wrap-box,\n.cropper-canvas,\n.cropper-drag-box,\n.cropper-crop-box,\n.cropper-modal {\n  bottom: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 0;\n}\n\n.cropper-wrap-box,\n.cropper-canvas {\n  overflow: hidden;\n}\n\n.cropper-drag-box {\n  background-color: #fff;\n  opacity: 0;\n}\n\n.cropper-modal {\n  background-color: #000;\n  opacity: .5;\n}\n\n.cropper-view-box {\n  display: block;\n  height: 100%;\n  outline-color: rgba(51, 153, 255, 0.75);\n  outline: 1px solid #39f;\n  overflow: hidden;\n  width: 100%;\n}\n\n.cropper-dashed {\n  border: 0 dashed #eee;\n  display: block;\n  opacity: .5;\n  position: absolute;\n}\n\n.cropper-dashed.dashed-h {\n  border-bottom-width: 1px;\n  border-top-width: 1px;\n  height: 33.33333%;\n  left: 0;\n  top: 33.33333%;\n  width: 100%;\n}\n\n.cropper-dashed.dashed-v {\n  border-left-width: 1px;\n  border-right-width: 1px;\n  height: 100%;\n  left: 33.33333%;\n  top: 0;\n  width: 33.33333%;\n}\n\n.cropper-center {\n  display: block;\n  height: 0;\n  left: 50%;\n  opacity: .75;\n  position: absolute;\n  top: 50%;\n  width: 0;\n}\n\n.cropper-center:before,\n.cropper-center:after {\n  background-color: #eee;\n  content: ' ';\n  display: block;\n  position: absolute;\n}\n\n.cropper-center:before {\n  height: 1px;\n  left: -3px;\n  top: 0;\n  width: 7px;\n}\n\n.cropper-center:after {\n  height: 7px;\n  left: 0;\n  top: -3px;\n  width: 1px;\n}\n\n.cropper-face,\n.cropper-line,\n.cropper-point {\n  display: block;\n  height: 100%;\n  opacity: .1;\n  position: absolute;\n  width: 100%;\n}\n\n.cropper-face {\n  background-color: #fff;\n  left: 0;\n  top: 0;\n}\n\n.cropper-line {\n  background-color: #39f;\n}\n\n.cropper-line.line-e {\n  cursor: ew-resize;\n  right: -3px;\n  top: 0;\n  width: 5px;\n}\n\n.cropper-line.line-n {\n  cursor: ns-resize;\n  height: 5px;\n  left: 0;\n  top: -3px;\n}\n\n.cropper-line.line-w {\n  cursor: ew-resize;\n  left: -3px;\n  top: 0;\n  width: 5px;\n}\n\n.cropper-line.line-s {\n  bottom: -3px;\n  cursor: ns-resize;\n  height: 5px;\n  left: 0;\n}\n\n.cropper-point {\n  background-color: #39f;\n  height: 5px;\n  opacity: .75;\n  width: 5px;\n}\n\n.cropper-point.point-e {\n  cursor: ew-resize;\n  margin-top: -3px;\n  right: -3px;\n  top: 50%;\n}\n\n.cropper-point.point-n {\n  cursor: ns-resize;\n  left: 50%;\n  margin-left: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-w {\n  cursor: ew-resize;\n  left: -3px;\n  margin-top: -3px;\n  top: 50%;\n}\n\n.cropper-point.point-s {\n  bottom: -3px;\n  cursor: s-resize;\n  left: 50%;\n  margin-left: -3px;\n}\n\n.cropper-point.point-ne {\n  cursor: nesw-resize;\n  right: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-nw {\n  cursor: nwse-resize;\n  left: -3px;\n  top: -3px;\n}\n\n.cropper-point.point-sw {\n  bottom: -3px;\n  cursor: nesw-resize;\n  left: -3px;\n}\n\n.cropper-point.point-se {\n  bottom: -3px;\n  cursor: nwse-resize;\n  height: 20px;\n  opacity: 1;\n  right: -3px;\n  width: 20px;\n}\n\n@media (min-width: 768px) {\n  .cropper-point.point-se {\n    height: 15px;\n    width: 15px;\n  }\n}\n\n@media (min-width: 992px) {\n  .cropper-point.point-se {\n    height: 10px;\n    width: 10px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .cropper-point.point-se {\n    height: 5px;\n    opacity: .75;\n    width: 5px;\n  }\n}\n\n.cropper-point.point-se:before {\n  background-color: #39f;\n  bottom: -50%;\n  content: ' ';\n  display: block;\n  height: 200%;\n  opacity: 0;\n  position: absolute;\n  right: -50%;\n  width: 200%;\n}\n\n.cropper-invisible {\n  opacity: 0;\n}\n\n.cropper-bg {\n  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');\n}\n\n.cropper-hide {\n  display: block;\n  height: 0;\n  position: absolute;\n  width: 0;\n}\n\n.cropper-hidden {\n  display: none !important;\n}\n\n.cropper-move {\n  cursor: move;\n}\n\n.cropper-crop {\n  cursor: crosshair;\n}\n\n.cropper-disabled .cropper-drag-box,\n.cropper-disabled .cropper-face,\n.cropper-disabled .cropper-line,\n.cropper-disabled .cropper-point {\n  cursor: not-allowed;\n}\n"
  },
  {
    "path": "docs/v3.1.6/css/main.css",
    "content": ".btn {\n  padding-left: .75rem;\n  padding-right: .75rem;\n}\n\nlabel.btn {\n  margin-bottom: 0;\n}\n\n.d-flex > .btn {\n  flex: 1;\n}\n\n.carbonads {\n  border-radius: .25rem;\n  border: 1px solid #ccc;\n  font-size: .875rem;\n  overflow: hidden;\n  padding: 1rem;\n}\n\n.carbon-wrap {\n  overflow: hidden;\n}\n\n.carbon-img {\n  clear: left;\n  display: block;\n  float: left;\n}\n\n.carbon-text,\n.carbon-poweredby {\n  display: block;\n  margin-left: 140px;\n}\n\n.carbon-text,\n.carbon-text:hover,\n.carbon-text:focus {\n  color: #fff;\n  text-decoration: none;\n}\n\n.carbon-poweredby,\n.carbon-poweredby:hover,\n.carbon-poweredby:focus {\n  color: #ddd;\n  text-decoration: none;\n}\n\n@media (min-width: 768px) {\n  .carbonads {\n    float: right;\n    margin-bottom: -1rem;\n    margin-top: -1rem;\n    max-width: 360px;\n  }\n}\n\n.footer {\n  font-size: .875rem;\n  overflow: hidden;\n}\n\n.heart {\n  color: #ddd;\n  display: block;\n  height: 2rem;\n  line-height: 2rem;\n  margin-bottom: 0;\n  margin-top: 1rem;\n  position: relative;\n  text-align: center;\n  width: 100%;\n}\n\n.heart:hover {\n  color: #ff4136;\n}\n\n.heart::before {\n  border-top: 1px solid #eee;\n  content: \" \";\n  display: block;\n  height: 0;\n  left: 0;\n  position: absolute;\n  right: 0;\n  top: 50%;\n}\n\n.heart::after {\n  background-color: #fff;\n  content: \"♥\";\n  padding-left: .5rem;\n  padding-right: .5rem;\n  position: relative;\n  z-index: 1;\n}\n\n.img-container,\n.img-preview {\n  background-color: #f7f7f7;\n  text-align: center;\n  width: 100%;\n}\n\n.img-container {\n  margin-bottom: 1rem;\n  max-height: 497px;\n  min-height: 200px;\n}\n\n@media (min-width: 768px) {\n  .img-container {\n    min-height: 497px;\n  }\n}\n\n.img-container > img {\n  max-width: 100%;\n}\n\n.docs-preview {\n  margin-right: -1rem;\n}\n\n.img-preview {\n  float: left;\n  margin-bottom: .5rem;\n  margin-right: .5rem;\n  overflow: hidden;\n}\n\n.img-preview > img {\n  max-width: 100%;\n}\n\n.preview-lg {\n  height: 9rem;\n  width: 16rem;\n}\n\n.preview-md {\n  height: 4.5rem;\n  width: 8rem;\n}\n\n.preview-sm {\n  height: 2.25rem;\n  width: 4rem;\n}\n\n.preview-xs {\n  height: 1.125rem;\n  margin-right: 0;\n  width: 2rem;\n}\n\n.docs-data > .input-group {\n  margin-bottom: .5rem;\n}\n\n.docs-data .input-group-prepend .input-group-text {\n  min-width: 4rem;\n}\n\n.docs-data .input-group-append .input-group-text {\n  min-width: 3rem;\n}\n\n.docs-buttons > .btn,\n.docs-buttons > .btn-group,\n.docs-buttons > .form-control {\n  margin-bottom: .5rem;\n  margin-right: .25rem;\n}\n\n.docs-toggles > .btn,\n.docs-toggles > .btn-group,\n.docs-toggles > .dropdown {\n  margin-bottom: .5rem;\n}\n\n.docs-tooltip {\n  display: block;\n  margin: -.5rem -.75rem;\n  padding: .5rem .75rem;\n}\n\n.docs-tooltip > .icon {\n  margin: 0 -.25rem;\n  vertical-align: top;\n}\n\n.tooltip-inner {\n  white-space: normal;\n}\n\n.btn-upload .tooltip-inner,\n.btn-toggle .tooltip-inner {\n  white-space: nowrap;\n}\n\n.btn-toggle {\n  padding: .5rem;\n}\n\n.btn-toggle > .docs-tooltip {\n  margin: -.5rem;\n  padding: .5rem;\n}\n\n@media (max-width: 400px) {\n  .btn-group-crop {\n    margin-right: -1rem!important;\n  }\n\n  .btn-group-crop > .btn {\n    padding-left: .5rem;\n    padding-right: .5rem;\n  }\n\n  .btn-group-crop .docs-tooltip {\n    margin-left: -.5rem;\n    margin-right: -.5rem;\n    padding-left: .5rem;\n    padding-right: .5rem;\n  }\n}\n\n.docs-options .dropdown-menu {\n  width: 100%;\n}\n\n.docs-options .dropdown-menu > li {\n  font-size: .875rem;\n  padding: .125rem 1rem;\n}\n\n.docs-options .dropdown-menu .form-check-label {\n  display: block;\n}\n\n.docs-cropped .modal-body {\n  text-align: center;\n}\n\n.docs-cropped .modal-body > img,\n.docs-cropped .modal-body > canvas {\n  max-width: 100%;\n}\n"
  },
  {
    "path": "docs/v3.1.6/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n  <meta name=\"description\" content=\"A simple jQuery image cropping plugin.\">\n  <meta name=\"author\" content=\"Chen Fengyuan\">\n  <title>Cropper</title>\n  <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css\" crossorigin=\"anonymous\">\n  <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\" crossorigin=\"anonymous\">\n  <link rel=\"stylesheet\" href=\"css/cropper.css\">\n  <link rel=\"stylesheet\" href=\"css/main.css\">\n</head>\n<body>\n  <!--[if lt IE 9]>\n  <div class=\"alert alert-warning alert-dismissible fade show m-0 rounded-0\" role=\"alert\">\n    You are using an <strong>outdated</strong> browser. Please <a href=\"https://browsehappy.com/\">upgrade your browser</a> to improve your experience.\n    <button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\">\n      <span aria-hidden=\"true\">&times;</span>\n    </button>\n  </div>\n  <![endif]-->\n\n  <!-- Header -->\n  <header class=\"navbar navbar-light navbar-expand-md\">\n    <div class=\"container\">\n      <a class=\"navbar-brand\" href=\"./\">Cropper</a>\n      <button class=\"navbar-toggler\" type=\"button\" data-toggle=\"collapse\" data-target=\"#navbar-collapse\" aria-controls=\"navbar-collapse\" aria-expanded=\"false\" aria-label=\"Toggle navigation\">\n        <span class=\"navbar-toggler-icon\"></span>\n      </button>\n      <div class=\"collapse navbar-collapse justify-content-end\" id=\"navbar-collapse\" role=\"navigation\">\n        <nav class=\"nav navbar-nav\">\n          <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper/tree/v3.1.6/README.md\" data-toggle=\"tooltip\" title=\"View the documentation\">Docs</a>\n          <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper\" data-toggle=\"tooltip\" title=\"View the GitHub project\">GitHub</a>\n          <a class=\"nav-link\" href=\"https://fengyuanchen.github.io/cropperjs\" data-toggle=\"tooltip\" title=\"the non-jQuery version of Cropper (recommended)\">Cropper.js</a>\n          <a class=\"nav-link\" href=\"https://fengyuanchen.github.io/\" data-toggle=\"tooltip\" title=\"Explore more projects\">Explore</a>\n          <a class=\"nav-link\" href=\"https://chenfengyuan.com/\" data-toggle=\"tooltip\" title=\"About the author\">About</a>\n        </nav>\n      </div>\n    </div>\n  </header>\n\n  <!-- Jumbotron -->\n  <div class=\"jumbotron bg-primary text-white rounded-0\">\n    <div class=\"container\">\n        <h1>Cropper <small class=\"h6\">v3.1.6</small></h1>\n        <p class=\"lead\">A simple jQuery image cropping plugin.</p>\n    </div>\n  </div>\n\n  <!-- Content -->\n  <div class=\"container\">\n    <div class=\"row\">\n      <div class=\"col-md-9\">\n        <!-- <h3>Demo:</h3> -->\n        <div class=\"img-container\">\n          <img id=\"image\" src=\"images/picture.jpg\" alt=\"Picture\">\n        </div>\n      </div>\n      <div class=\"col-md-3\">\n        <!-- <h3>Preview:</h3> -->\n        <div class=\"docs-preview clearfix\">\n          <div class=\"img-preview preview-lg\"></div>\n          <div class=\"img-preview preview-md\"></div>\n          <div class=\"img-preview preview-sm\"></div>\n          <div class=\"img-preview preview-xs\"></div>\n        </div>\n\n        <!-- <h3>Data:</h3> -->\n        <div class=\"docs-data\">\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataX\">X</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataX\" placeholder=\"x\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataY\">Y</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataY\" placeholder=\"y\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataWidth\">Width</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataWidth\" placeholder=\"width\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataHeight\">Height</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataHeight\" placeholder=\"height\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">px</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataRotate\">Rotate</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataRotate\" placeholder=\"rotate\">\n            <span class=\"input-group-append\">\n              <span class=\"input-group-text\">deg</span>\n            </span>\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataScaleX\">ScaleX</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataScaleX\" placeholder=\"scaleX\">\n          </div>\n          <div class=\"input-group input-group-sm\">\n            <span class=\"input-group-prepend\">\n              <label class=\"input-group-text\" for=\"dataScaleY\">ScaleY</label>\n            </span>\n            <input type=\"text\" class=\"form-control\" id=\"dataScaleY\" placeholder=\"scaleY\">\n          </div>\n        </div>\n      </div>\n    </div>\n    <div class=\"row\">\n      <div class=\"col-md-9 docs-buttons\">\n        <!-- <h3>Toolbar:</h3> -->\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"setDragMode\" data-option=\"move\" title=\"Move\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setDragMode&quot;, &quot;move&quot;)\">\n              <span class=\"fa fa-arrows\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"setDragMode\" data-option=\"crop\" title=\"Crop\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setDragMode&quot;, &quot;crop&quot;)\">\n              <span class=\"fa fa-crop\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoom\" data-option=\"0.1\" title=\"Zoom In\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;zoom&quot;, 0.1)\">\n              <span class=\"fa fa-search-plus\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"zoom\" data-option=\"-0.1\" title=\"Zoom Out\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;zoom&quot;, -0.1)\">\n              <span class=\"fa fa-search-minus\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"-10\" data-second-option=\"0\" title=\"Move Left\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, -10, 0)\">\n              <span class=\"fa fa-arrow-left\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"10\" data-second-option=\"0\" title=\"Move Right\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, 10, 0)\">\n              <span class=\"fa fa-arrow-right\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"0\" data-second-option=\"-10\" title=\"Move Up\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, 0, -10)\">\n              <span class=\"fa fa-arrow-up\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"move\" data-option=\"0\" data-second-option=\"10\" title=\"Move Down\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;move&quot;, 0, 10)\">\n              <span class=\"fa fa-arrow-down\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotate\" data-option=\"-45\" title=\"Rotate Left\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;rotate&quot;, -45)\">\n              <span class=\"fa fa-rotate-left\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"rotate\" data-option=\"45\" title=\"Rotate Right\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;rotate&quot;, 45)\">\n              <span class=\"fa fa-rotate-right\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"scaleX\" data-option=\"-1\" title=\"Flip Horizontal\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;scaleX&quot;, -1)\">\n              <span class=\"fa fa-arrows-h\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"scaleY\" data-option=\"-1\" title=\"Flip Vertical\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;scaleY&quot;, -1)\">\n              <span class=\"fa fa-arrows-v\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"crop\" title=\"Crop\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;crop&quot;)\">\n              <span class=\"fa fa-check\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"clear\" title=\"Clear\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;clear&quot;)\">\n              <span class=\"fa fa-remove\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"disable\" title=\"Disable\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;disable&quot;)\">\n              <span class=\"fa fa-lock\"></span>\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"enable\" title=\"Enable\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;enable&quot;)\">\n              <span class=\"fa fa-unlock\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group\">\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"reset\" title=\"Reset\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;reset&quot;)\">\n              <span class=\"fa fa-refresh\"></span>\n            </span>\n          </button>\n          <label class=\"btn btn-primary btn-upload\" for=\"inputImage\" title=\"Upload image file\">\n            <input type=\"file\" class=\"sr-only\" id=\"inputImage\" name=\"file\" accept=\".jpg,.jpeg,.png,.gif,.bmp,.tiff\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"Import image with Blob URLs\">\n              <span class=\"fa fa-upload\"></span>\n            </span>\n          </label>\n          <button type=\"button\" class=\"btn btn-primary\" data-method=\"destroy\" title=\"Destroy\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;destroy&quot;)\">\n              <span class=\"fa fa-power-off\"></span>\n            </span>\n          </button>\n        </div>\n\n        <div class=\"btn-group btn-group-crop\">\n          <button type=\"button\" class=\"btn btn-success\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;maxWidth&quot;: 4096, &quot;maxHeight&quot;: 4096 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { maxWidth: 4096, maxHeight: 4096 })\">\n              Get Cropped Canvas\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-success\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;width&quot;: 160, &quot;height&quot;: 90 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { width: 160, height: 90 })\">\n              160&times;90\n            </span>\n          </button>\n          <button type=\"button\" class=\"btn btn-success\" data-method=\"getCroppedCanvas\" data-option=\"{ &quot;width&quot;: 320, &quot;height&quot;: 180 }\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCroppedCanvas&quot;, { width: 320, height: 180 })\">\n              320&times;180\n            </span>\n          </button>\n        </div>\n\n        <!-- Show the cropped image in modal -->\n        <div class=\"modal fade docs-cropped\" id=\"getCroppedCanvasModal\" aria-hidden=\"true\" aria-labelledby=\"getCroppedCanvasTitle\" role=\"dialog\" tabindex=\"-1\">\n          <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n              <div class=\"modal-header\">\n                <h5 class=\"modal-title\" id=\"getCroppedCanvasTitle\">Cropped</h5>\n                <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\">\n                  <span aria-hidden=\"true\">&times;</span>\n                </button>\n              </div>\n              <div class=\"modal-body\"></div>\n              <div class=\"modal-footer\">\n                <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">Close</button>\n                <a class=\"btn btn-primary\" id=\"download\" href=\"javascript:void(0);\" download=\"cropped.jpg\">Download</a>\n              </div>\n            </div>\n          </div>\n        </div><!-- /.modal -->\n\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getData&quot;)\">\n            Get Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"setData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setData&quot;, data)\">\n            Set Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getContainerData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getContainerData&quot;)\">\n            Get Container Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getImageData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getImageData&quot;)\">\n            Get Image Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getCanvasData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCanvasData&quot;)\">\n            Get Canvas Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"setCanvasData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setCanvasData&quot;, data)\">\n            Set Canvas Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"getCropBoxData\" data-option data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;getCropBoxData&quot;)\">\n            Get Crop Box Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"setCropBoxData\" data-target=\"#putData\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"$().cropper(&quot;setCropBoxData&quot;, data)\">\n            Set Crop Box Data\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"moveTo\" data-option=\"0\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"cropper.moveTo(0)\">\n            Move to [0,0]\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"zoomTo\" data-option=\"1\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"cropper.zoomTo(1)\">\n            Zoom to 100%\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"rotateTo\" data-option=\"180\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"cropper.rotateTo(180)\">\n            Rotate 180°\n          </span>\n        </button>\n        <button type=\"button\" class=\"btn btn-secondary\" data-method=\"scale\" data-option=\"-2\" data-second-option=\"-1\">\n          <span class=\"docs-tooltip\" data-toggle=\"tooltip\" title=\"cropper.scale(-2, -1)\">\n            Scale (-2, -1)\n          </span>\n        </button>\n        <textarea type=\"text\" class=\"form-control\" id=\"putData\" rows=\"1\" placeholder=\"Get data to here or set data with this value\"></textarea>\n      </div><!-- /.docs-buttons -->\n\n      <div class=\"col-md-3 docs-toggles\">\n        <!-- <h3>Toggles:</h3> -->\n        <div class=\"btn-group d-flex flex-nowrap\" data-toggle=\"buttons\">\n          <label class=\"btn btn-primary active\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio0\" name=\"aspectRatio\" value=\"1.7777777777777777\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 16 / 9\">\n              16:9\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio1\" name=\"aspectRatio\" value=\"1.3333333333333333\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 4 / 3\">\n              4:3\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio2\" name=\"aspectRatio\" value=\"1\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 1 / 1\">\n              1:1\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio3\" name=\"aspectRatio\" value=\"0.6666666666666666\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: 2 / 3\">\n              2:3\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"aspectRatio4\" name=\"aspectRatio\" value=\"NaN\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"aspectRatio: NaN\">\n              Free\n            </span>\n          </label>\n        </div>\n\n        <div class=\"btn-group d-flex flex-nowrap\" data-toggle=\"buttons\">\n          <label class=\"btn btn-primary active\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode0\" name=\"viewMode\" value=\"0\" checked>\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 0\">\n              VM0\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode1\" name=\"viewMode\" value=\"1\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 1\">\n              VM1\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode2\" name=\"viewMode\" value=\"2\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 2\">\n              VM2\n            </span>\n          </label>\n          <label class=\"btn btn-primary\">\n            <input type=\"radio\" class=\"sr-only\" id=\"viewMode3\" name=\"viewMode\" value=\"3\">\n            <span class=\"docs-tooltip\" data-toggle=\"tooltip\" data-animation=\"false\" title=\"View Mode 3\">\n              VM3\n            </span>\n          </label>\n        </div>\n\n        <div class=\"dropdown dropup docs-options\">\n          <button type=\"button\" class=\"btn btn-primary btn-block dropdown-toggle\" id=\"toggleOptions\" data-toggle=\"dropdown\" aria-expanded=\"true\">\n            Toggle Options\n            <span class=\"caret\"></span>\n          </button>\n          <ul class=\"dropdown-menu\" aria-labelledby=\"toggleOptions\" role=\"menu\">\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"responsive\" type=\"checkbox\" name=\"responsive\" checked>\n                <label class=\"form-check-label\" for=\"responsive\">responsive</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"restore\" type=\"checkbox\" name=\"restore\" checked>\n                <label class=\"form-check-label\" for=\"restore\">restore</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"checkCrossOrigin\" type=\"checkbox\" name=\"checkCrossOrigin\" checked>\n                <label class=\"form-check-label\" for=\"checkCrossOrigin\">checkCrossOrigin</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"checkOrientation\" type=\"checkbox\" name=\"checkOrientation\" checked>\n                <label class=\"form-check-label\" for=\"checkOrientation\">checkOrientation</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"modal\" type=\"checkbox\" name=\"modal\" checked>\n                <label class=\"form-check-label\" for=\"modal\">modal</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"guides\" type=\"checkbox\" name=\"guides\" checked>\n                <label class=\"form-check-label\" for=\"guides\">guides</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"center\" type=\"checkbox\" name=\"center\" checked>\n                <label class=\"form-check-label\" for=\"center\">center</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"highlight\" type=\"checkbox\" name=\"highlight\" checked>\n                <label class=\"form-check-label\" for=\"highlight\">highlight</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"background\" type=\"checkbox\" name=\"background\" checked>\n                <label class=\"form-check-label\" for=\"background\">background</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"autoCrop\" type=\"checkbox\" name=\"autoCrop\" checked>\n                <label class=\"form-check-label\" for=\"autoCrop\">autoCrop</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"movable\" type=\"checkbox\" name=\"movable\" checked>\n                <label class=\"form-check-label\" for=\"movable\">movable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"rotatable\" type=\"checkbox\" name=\"rotatable\" checked>\n                <label class=\"form-check-label\" for=\"rotatable\">rotatable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"scalable\" type=\"checkbox\" name=\"scalable\" checked>\n                <label class=\"form-check-label\" for=\"scalable\">scalable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"zoomable\" type=\"checkbox\" name=\"zoomable\" checked>\n                <label class=\"form-check-label\" for=\"zoomable\">zoomable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"zoomOnTouch\" type=\"checkbox\" name=\"zoomOnTouch\" checked>\n                <label class=\"form-check-label\" for=\"zoomOnTouch\">zoomOnTouch</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"zoomOnWheel\" type=\"checkbox\" name=\"zoomOnWheel\" checked>\n                <label class=\"form-check-label\" for=\"zoomOnWheel\">zoomOnWheel</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"cropBoxMovable\" type=\"checkbox\" name=\"cropBoxMovable\" checked>\n                <label class=\"form-check-label\" for=\"cropBoxMovable\">cropBoxMovable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"cropBoxResizable\" type=\"checkbox\" name=\"cropBoxResizable\" checked>\n                <label class=\"form-check-label\" for=\"cropBoxResizable\">cropBoxResizable</label>\n              </div>\n            </li>\n            <li class=\"dropdown-item\">\n              <div class=\"form-check\">\n                <input class=\"form-check-input\" id=\"toggleDragModeOnDblclick\" type=\"checkbox\" name=\"toggleDragModeOnDblclick\" checked>\n                <label class=\"form-check-label\" for=\"toggleDragModeOnDblclick\">toggleDragModeOnDblclick</label>\n              </div>\n            </li>\n          </ul>\n        </div><!-- /.dropdown -->\n\n        <a class=\"btn btn-success btn-block\" data-toggle=\"tooltip\" data-animation=\"false\" href=\"https://fengyuanchen.github.io/cropperjs\" title=\"The non-jQuery version of Cropper (recommended)\">Cropper.js</a>\n\n      </div><!-- /.docs-toggles -->\n    </div>\n  </div>\n\n  <!-- Footer -->\n  <footer class=\"footer\">\n    <div class=\"container\">\n      <p class=\"heart\"></p>\n      <nav class=\"nav flex-wrap justify-content-center mb-3\">\n        <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper\">GitHub</a>\n        <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper/tree/v3.1.6/examples\">Examples</a>\n        <a class=\"nav-link\" href=\"https://github.com/fengyuanchen/cropper/tree/v3.1.6/LICENSE\">License</a>\n        <a class=\"nav-link\" href=\"https://chenfengyuan.com/\">About</a>\n      </nav>\n    </div>\n  </footer>\n\n  <!-- Scripts -->\n  <script src=\"https://code.jquery.com/jquery-3.4.1.slim.min.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"https://fengyuanchen.github.io/shared/google-analytics.js\" crossorigin=\"anonymous\"></script>\n  <script src=\"js/cropper.js\"></script>\n  <script src=\"js/main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "docs/v3.1.6/js/cropper.js",
    "content": "/*!\n * Cropper v3.1.6\n * https://github.com/fengyuanchen/cropper\n *\n * Copyright (c) 2014-2018 Chen Fengyuan\n * Released under the MIT license\n *\n * Date: 2018-03-01T13:33:48.179Z\n */\n\n(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :\n\ttypeof define === 'function' && define.amd ? define(['jquery'], factory) :\n\t(factory(global.jQuery));\n}(this, (function ($) { 'use strict';\n\n$ = $ && $.hasOwnProperty('default') ? $['default'] : $;\n\nvar WINDOW = typeof window !== 'undefined' ? window : {};\nvar NAMESPACE = 'cropper';\n\n// Actions\nvar ACTION_ALL = 'all';\nvar ACTION_CROP = 'crop';\nvar ACTION_MOVE = 'move';\nvar ACTION_ZOOM = 'zoom';\nvar ACTION_EAST = 'e';\nvar ACTION_WEST = 'w';\nvar ACTION_SOUTH = 's';\nvar ACTION_NORTH = 'n';\nvar ACTION_NORTH_EAST = 'ne';\nvar ACTION_NORTH_WEST = 'nw';\nvar ACTION_SOUTH_EAST = 'se';\nvar ACTION_SOUTH_WEST = 'sw';\n\n// Classes\nvar CLASS_CROP = NAMESPACE + '-crop';\nvar CLASS_DISABLED = NAMESPACE + '-disabled';\nvar CLASS_HIDDEN = NAMESPACE + '-hidden';\nvar CLASS_HIDE = NAMESPACE + '-hide';\nvar CLASS_INVISIBLE = NAMESPACE + '-invisible';\nvar CLASS_MODAL = NAMESPACE + '-modal';\nvar CLASS_MOVE = NAMESPACE + '-move';\n\n// Data keys\nvar DATA_ACTION = 'action';\nvar DATA_PREVIEW = 'preview';\n\n// Drag modes\nvar DRAG_MODE_CROP = 'crop';\nvar DRAG_MODE_MOVE = 'move';\nvar DRAG_MODE_NONE = 'none';\n\n// Events\nvar EVENT_CROP = 'crop';\nvar EVENT_CROP_END = 'cropend';\nvar EVENT_CROP_MOVE = 'cropmove';\nvar EVENT_CROP_START = 'cropstart';\nvar EVENT_DBLCLICK = 'dblclick';\nvar EVENT_ERROR = 'error';\nvar EVENT_LOAD = 'load';\nvar EVENT_POINTER_DOWN = WINDOW.PointerEvent ? 'pointerdown' : 'touchstart mousedown';\nvar EVENT_POINTER_MOVE = WINDOW.PointerEvent ? 'pointermove' : 'touchmove mousemove';\nvar EVENT_POINTER_UP = WINDOW.PointerEvent ? 'pointerup pointercancel' : 'touchend touchcancel mouseup';\nvar EVENT_READY = 'ready';\nvar EVENT_RESIZE = 'resize';\nvar EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';\nvar EVENT_ZOOM = 'zoom';\n\n// RegExps\nvar REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;\nvar REGEXP_DATA_URL = /^data:/;\nvar REGEXP_DATA_URL_JPEG = /^data:image\\/jpeg;base64,/;\nvar REGEXP_TAG_NAME = /^(img|canvas)$/i;\n\nvar DEFAULTS = {\n  // Define the view mode of the cropper\n  viewMode: 0, // 0, 1, 2, 3\n\n  // Define the dragging mode of the cropper\n  dragMode: DRAG_MODE_CROP, // 'crop', 'move' or 'none'\n\n  // Define the aspect ratio of the crop box\n  aspectRatio: NaN,\n\n  // An object with the previous cropping result data\n  data: null,\n\n  // A selector for adding extra containers to preview\n  preview: '',\n\n  // Re-render the cropper when resize the window\n  responsive: true,\n\n  // Restore the cropped area after resize the window\n  restore: true,\n\n  // Check if the current image is a cross-origin image\n  checkCrossOrigin: true,\n\n  // Check the current image's Exif Orientation information\n  checkOrientation: true,\n\n  // Show the black modal\n  modal: true,\n\n  // Show the dashed lines for guiding\n  guides: true,\n\n  // Show the center indicator for guiding\n  center: true,\n\n  // Show the white modal to highlight the crop box\n  highlight: true,\n\n  // Show the grid background\n  background: true,\n\n  // Enable to crop the image automatically when initialize\n  autoCrop: true,\n\n  // Define the percentage of automatic cropping area when initializes\n  autoCropArea: 0.8,\n\n  // Enable to move the image\n  movable: true,\n\n  // Enable to rotate the image\n  rotatable: true,\n\n  // Enable to scale the image\n  scalable: true,\n\n  // Enable to zoom the image\n  zoomable: true,\n\n  // Enable to zoom the image by dragging touch\n  zoomOnTouch: true,\n\n  // Enable to zoom the image by wheeling mouse\n  zoomOnWheel: true,\n\n  // Define zoom ratio when zoom the image by wheeling mouse\n  wheelZoomRatio: 0.1,\n\n  // Enable to move the crop box\n  cropBoxMovable: true,\n\n  // Enable to resize the crop box\n  cropBoxResizable: true,\n\n  // Toggle drag mode between \"crop\" and \"move\" when click twice on the cropper\n  toggleDragModeOnDblclick: true,\n\n  // Size limitation\n  minCanvasWidth: 0,\n  minCanvasHeight: 0,\n  minCropBoxWidth: 0,\n  minCropBoxHeight: 0,\n  minContainerWidth: 200,\n  minContainerHeight: 100,\n\n  // Shortcuts of events\n  ready: null,\n  cropstart: null,\n  cropmove: null,\n  cropend: null,\n  crop: null,\n  zoom: null\n};\n\nvar TEMPLATE = '<div class=\"cropper-container\">' + '<div class=\"cropper-wrap-box\">' + '<div class=\"cropper-canvas\"></div>' + '</div>' + '<div class=\"cropper-drag-box\"></div>' + '<div class=\"cropper-crop-box\">' + '<span class=\"cropper-view-box\"></span>' + '<span class=\"cropper-dashed dashed-h\"></span>' + '<span class=\"cropper-dashed dashed-v\"></span>' + '<span class=\"cropper-center\"></span>' + '<span class=\"cropper-face\"></span>' + '<span class=\"cropper-line line-e\" data-action=\"e\"></span>' + '<span class=\"cropper-line line-n\" data-action=\"n\"></span>' + '<span class=\"cropper-line line-w\" data-action=\"w\"></span>' + '<span class=\"cropper-line line-s\" data-action=\"s\"></span>' + '<span class=\"cropper-point point-e\" data-action=\"e\"></span>' + '<span class=\"cropper-point point-n\" data-action=\"n\"></span>' + '<span class=\"cropper-point point-w\" data-action=\"w\"></span>' + '<span class=\"cropper-point point-s\" data-action=\"s\"></span>' + '<span class=\"cropper-point point-ne\" data-action=\"ne\"></span>' + '<span class=\"cropper-point point-nw\" data-action=\"nw\"></span>' + '<span class=\"cropper-point point-sw\" data-action=\"sw\"></span>' + '<span class=\"cropper-point point-se\" data-action=\"se\"></span>' + '</div>' + '</div>';\n\nvar classCallCheck = function (instance, Constructor) {\n  if (!(instance instanceof Constructor)) {\n    throw new TypeError(\"Cannot call a class as a function\");\n  }\n};\n\nvar createClass = function () {\n  function defineProperties(target, props) {\n    for (var i = 0; i < props.length; i++) {\n      var descriptor = props[i];\n      descriptor.enumerable = descriptor.enumerable || false;\n      descriptor.configurable = true;\n      if (\"value\" in descriptor) descriptor.writable = true;\n      Object.defineProperty(target, descriptor.key, descriptor);\n    }\n  }\n\n  return function (Constructor, protoProps, staticProps) {\n    if (protoProps) defineProperties(Constructor.prototype, protoProps);\n    if (staticProps) defineProperties(Constructor, staticProps);\n    return Constructor;\n  };\n}();\n\nvar toConsumableArray = function (arr) {\n  if (Array.isArray(arr)) {\n    for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n    return arr2;\n  } else {\n    return Array.from(arr);\n  }\n};\n\n/**\n * Check if the given value is a string.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a string, else `false`.\n */\nfunction isString(value) {\n  return typeof value === 'string';\n}\n\n/**\n * Check if the given value is not a number.\n */\nvar isNaN = Number.isNaN || WINDOW.isNaN;\n\n/**\n * Check if the given value is a number.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is a number, else `false`.\n */\nfunction isNumber(value) {\n  return typeof value === 'number' && !isNaN(value);\n}\n\n/**\n * Check if the given value is undefined.\n * @param {*} value - The value to check.\n * @returns {boolean} Returns `true` if the given value is undefined, else `false`.\n */\nfunction isUndefined(value) {\n  return typeof value === 'undefined';\n}\n\n/**\n * Takes a function and returns a new one that will always have a particular context.\n * Custom proxy to avoid jQuery's guid.\n * @param {Function} fn - The target function.\n * @param {Object} context - The new context for the function.\n * @returns {Function} The new function.\n */\nfunction proxy(fn, context) {\n  for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n    args[_key - 2] = arguments[_key];\n  }\n\n  return function () {\n    for (var _len2 = arguments.length, args2 = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n      args2[_key2] = arguments[_key2];\n    }\n\n    return fn.apply(context, args.concat(args2));\n  };\n}\n\n/**\n * Get the own enumerable properties of a given object.\n * @param {Object} obj - The target object.\n * @returns {Array} All the own enumerable properties of the given object.\n */\nvar objectKeys = Object.keys || function objectKeys(obj) {\n  var keys = [];\n\n  $.each(obj, function (key) {\n    keys.push(key);\n  });\n\n  return keys;\n};\n\nvar REGEXP_DECIMALS = /\\.\\d*(?:0|9){12}\\d*$/i;\n\n/**\n * Normalize decimal number.\n * Check out {@link https://0.30000000000000004.com/ }\n * @param {number} value - The value to normalize.\n * @param {number} [times=100000000000] - The times for normalizing.\n * @returns {number} Returns the normalized number.\n */\nfunction normalizeDecimalNumber(value) {\n  var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;\n\n  return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;\n}\n\nvar location = WINDOW.location;\n\nvar REGEXP_ORIGINS = /^(https?:)\\/\\/([^:/?#]+):?(\\d*)/i;\n\n/**\n * Check if the given URL is a cross origin URL.\n * @param {string} url - The target URL.\n * @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.\n */\nfunction isCrossOriginURL(url) {\n  var parts = url.match(REGEXP_ORIGINS);\n\n  return parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);\n}\n\n/**\n * Add timestamp to the given URL.\n * @param {string} url - The target URL.\n * @returns {string} The result URL.\n */\nfunction addTimestamp(url) {\n  var timestamp = 'timestamp=' + new Date().getTime();\n\n  return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;\n}\n\n/**\n * Get transform values base on the given object.\n * @param {Object} obj - The target object.\n * @returns {string} A string contains transform values.\n */\nfunction getTransformValues(_ref) {\n  var rotate = _ref.rotate,\n      scaleX = _ref.scaleX,\n      scaleY = _ref.scaleY,\n      translateX = _ref.translateX,\n      translateY = _ref.translateY;\n\n  var values = [];\n\n  if (isNumber(translateX) && translateX !== 0) {\n    values.push('translateX(' + translateX + 'px)');\n  }\n\n  if (isNumber(translateY) && translateY !== 0) {\n    values.push('translateY(' + translateY + 'px)');\n  }\n\n  // Rotate should come first before scale to match orientation transform\n  if (isNumber(rotate) && rotate !== 0) {\n    values.push('rotate(' + rotate + 'deg)');\n  }\n\n  if (isNumber(scaleX) && scaleX !== 1) {\n    values.push('scaleX(' + scaleX + ')');\n  }\n\n  if (isNumber(scaleY) && scaleY !== 1) {\n    values.push('scaleY(' + scaleY + ')');\n  }\n\n  return values.length ? values.join(' ') : 'none';\n}\n\nvar navigator = WINDOW.navigator;\n\nvar IS_SAFARI_OR_UIWEBVIEW = navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(navigator.userAgent);\n\n/**\n * Get an image's natural sizes.\n * @param {string} image - The target image.\n * @param {Function} callback - The callback function.\n */\nfunction getImageNaturalSizes(image, callback) {\n  // Modern browsers (except Safari)\n  if (image.naturalWidth && !IS_SAFARI_OR_UIWEBVIEW) {\n    callback(image.naturalWidth, image.naturalHeight);\n    return;\n  }\n\n  var newImage = document.createElement('img');\n\n  newImage.onload = function () {\n    callback(newImage.width, newImage.height);\n  };\n\n  newImage.src = image.src;\n}\n\n/**\n * Get the max ratio of a group of pointers.\n * @param {string} pointers - The target pointers.\n * @returns {number} The result ratio.\n */\nfunction getMaxZoomRatio(pointers) {\n  var pointers2 = $.extend({}, pointers);\n  var ratios = [];\n\n  $.each(pointers, function (pointerId, pointer) {\n    delete pointers2[pointerId];\n\n    $.each(pointers2, function (pointerId2, pointer2) {\n      var x1 = Math.abs(pointer.startX - pointer2.startX);\n      var y1 = Math.abs(pointer.startY - pointer2.startY);\n      var x2 = Math.abs(pointer.endX - pointer2.endX);\n      var y2 = Math.abs(pointer.endY - pointer2.endY);\n      var z1 = Math.sqrt(x1 * x1 + y1 * y1);\n      var z2 = Math.sqrt(x2 * x2 + y2 * y2);\n      var ratio = (z2 - z1) / z1;\n\n      ratios.push(ratio);\n    });\n  });\n\n  ratios.sort(function (a, b) {\n    return Math.abs(a) < Math.abs(b);\n  });\n\n  return ratios[0];\n}\n\n/**\n * Get a pointer from an event object.\n * @param {Object} event - The target event object.\n * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.\n * @returns {Object} The result pointer contains start and/or end point coordinates.\n */\nfunction getPointer(_ref2, endOnly) {\n  var pageX = _ref2.pageX,\n      pageY = _ref2.pageY;\n\n  var end = {\n    endX: pageX,\n    endY: pageY\n  };\n\n  if (endOnly) {\n    return end;\n  }\n\n  return $.extend({\n    startX: pageX,\n    startY: pageY\n  }, end);\n}\n\n/**\n * Get the center point coordinate of a group of pointers.\n * @param {Object} pointers - The target pointers.\n * @returns {Object} The center point coordinate.\n */\nfunction getPointersCenter(pointers) {\n  var pageX = 0;\n  var pageY = 0;\n  var count = 0;\n\n  $.each(pointers, function (pointerId, _ref3) {\n    var startX = _ref3.startX,\n        startY = _ref3.startY;\n\n    pageX += startX;\n    pageY += startY;\n    count += 1;\n  });\n\n  pageX /= count;\n  pageY /= count;\n\n  return {\n    pageX: pageX,\n    pageY: pageY\n  };\n}\n\n/**\n * Check if the given value is a finite number.\n */\nvar isFinite = Number.isFinite || WINDOW.isFinite;\n\n/**\n * Get the max sizes in a rectangle under the given aspect ratio.\n * @param {Object} data - The original sizes.\n * @param {string} [type='contain'] - The adjust type.\n * @returns {Object} The result sizes.\n */\nfunction getAdjustedSizes(_ref4) // or 'cover'\n{\n  var aspectRatio = _ref4.aspectRatio,\n      height = _ref4.height,\n      width = _ref4.width;\n  var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';\n\n  var isValidNumber = function isValidNumber(value) {\n    return isFinite(value) && value > 0;\n  };\n\n  if (isValidNumber(width) && isValidNumber(height)) {\n    var adjustedWidth = height * aspectRatio;\n\n    if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {\n      height = width / aspectRatio;\n    } else {\n      width = height * aspectRatio;\n    }\n  } else if (isValidNumber(width)) {\n    height = width / aspectRatio;\n  } else if (isValidNumber(height)) {\n    width = height * aspectRatio;\n  }\n\n  return {\n    width: width,\n    height: height\n  };\n}\n\n/**\n * Get the new sizes of a rectangle after rotated.\n * @param {Object} data - The original sizes.\n * @returns {Object} The result sizes.\n */\nfunction getRotatedSizes(_ref5) {\n  var width = _ref5.width,\n      height = _ref5.height,\n      degree = _ref5.degree;\n\n  degree = Math.abs(degree) % 180;\n\n  if (degree === 90) {\n    return {\n      width: height,\n      height: width\n    };\n  }\n\n  var arc = degree % 90 * Math.PI / 180;\n  var sinArc = Math.sin(arc);\n  var cosArc = Math.cos(arc);\n  var newWidth = width * cosArc + height * sinArc;\n  var newHeight = width * sinArc + height * cosArc;\n\n  return degree > 90 ? {\n    width: newHeight,\n    height: newWidth\n  } : {\n    width: newWidth,\n    height: newHeight\n  };\n}\n\n/**\n * Get a canvas which drew the given image.\n * @param {HTMLImageElement} image - The image for drawing.\n * @param {Object} imageData - The image data.\n * @param {Object} canvasData - The canvas data.\n * @param {Object} options - The options.\n * @returns {HTMLCanvasElement} The result canvas.\n */\nfunction getSourceCanvas(image, _ref6, _ref7, _ref8) {\n  var _ref6$rotate = _ref6.rotate,\n      rotate = _ref6$rotate === undefined ? 0 : _ref6$rotate,\n      _ref6$scaleX = _ref6.scaleX,\n      scaleX = _ref6$scaleX === undefined ? 1 : _ref6$scaleX,\n      _ref6$scaleY = _ref6.scaleY,\n      scaleY = _ref6$scaleY === undefined ? 1 : _ref6$scaleY;\n  var aspectRatio = _ref7.aspectRatio,\n      naturalWidth = _ref7.naturalWidth,\n      naturalHeight = _ref7.naturalHeight;\n  var _ref8$fillColor = _ref8.fillColor,\n      fillColor = _ref8$fillColor === undefined ? 'transparent' : _ref8$fillColor,\n      _ref8$imageSmoothingE = _ref8.imageSmoothingEnabled,\n      imageSmoothingEnabled = _ref8$imageSmoothingE === undefined ? true : _ref8$imageSmoothingE,\n      _ref8$imageSmoothingQ = _ref8.imageSmoothingQuality,\n      imageSmoothingQuality = _ref8$imageSmoothingQ === undefined ? 'low' : _ref8$imageSmoothingQ,\n      _ref8$maxWidth = _ref8.maxWidth,\n      maxWidth = _ref8$maxWidth === undefined ? Infinity : _ref8$maxWidth,\n      _ref8$maxHeight = _ref8.maxHeight,\n      maxHeight = _ref8$maxHeight === undefined ? Infinity : _ref8$maxHeight,\n      _ref8$minWidth = _ref8.minWidth,\n      minWidth = _ref8$minWidth === undefined ? 0 : _ref8$minWidth,\n      _ref8$minHeight = _ref8.minHeight,\n      minHeight = _ref8$minHeight === undefined ? 0 : _ref8$minHeight;\n\n  var canvas = document.createElement('canvas');\n  var context = canvas.getContext('2d');\n  var maxSizes = getAdjustedSizes({\n    aspectRatio: aspectRatio,\n    width: maxWidth,\n    height: maxHeight\n  });\n  var minSizes = getAdjustedSizes({\n    aspectRatio: aspectRatio,\n    width: minWidth,\n    height: minHeight\n  }, 'cover');\n  var width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));\n  var height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight));\n  var params = [-width / 2, -height / 2, width, height];\n\n  canvas.width = normalizeDecimalNumber(width);\n  canvas.height = normalizeDecimalNumber(height);\n  context.fillStyle = fillColor;\n  context.fillRect(0, 0, width, height);\n  context.save();\n  context.translate(width / 2, height / 2);\n  context.rotate(rotate * Math.PI / 180);\n  context.scale(scaleX, scaleY);\n  context.imageSmoothingEnabled = imageSmoothingEnabled;\n  context.imageSmoothingQuality = imageSmoothingQuality;\n  context.drawImage.apply(context, [image].concat(toConsumableArray($.map(params, function (param) {\n    return Math.floor(normalizeDecimalNumber(param));\n  }))));\n  context.restore();\n  return canvas;\n}\n\nvar fromCharCode = String.fromCharCode;\n\n/**\n * Get string from char code in data view.\n * @param {DataView} dataView - The data view for read.\n * @param {number} start - The start index.\n * @param {number} length - The read length.\n * @returns {string} The read result.\n */\n\nfunction getStringFromCharCode(dataView, start, length) {\n  var str = '';\n  var i = void 0;\n\n  length += start;\n\n  for (i = start; i < length; i += 1) {\n    str += fromCharCode(dataView.getUint8(i));\n  }\n\n  return str;\n}\n\nvar REGEXP_DATA_URL_HEAD = /^data:.*,/;\n\n/**\n * Transform Data URL to array buffer.\n * @param {string} dataURL - The Data URL to transform.\n * @returns {ArrayBuffer} The result array buffer.\n */\nfunction dataURLToArrayBuffer(dataURL) {\n  var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');\n  var binary = atob(base64);\n  var arrayBuffer = new ArrayBuffer(binary.length);\n  var uint8 = new Uint8Array(arrayBuffer);\n\n  $.each(uint8, function (i) {\n    uint8[i] = binary.charCodeAt(i);\n  });\n\n  return arrayBuffer;\n}\n\n/**\n * Transform array buffer to Data URL.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to transform.\n * @param {string} mimeType - The mime type of the Data URL.\n * @returns {string} The result Data URL.\n */\nfunction arrayBufferToDataURL(arrayBuffer, mimeType) {\n  var uint8 = new Uint8Array(arrayBuffer);\n  var data = '';\n\n  // TypedArray.prototype.forEach is not supported in some browsers.\n  $.each(uint8, function (i, value) {\n    data += fromCharCode(value);\n  });\n\n  return 'data:' + mimeType + ';base64,' + btoa(data);\n}\n\n/**\n * Get orientation value from given array buffer.\n * @param {ArrayBuffer} arrayBuffer - The array buffer to read.\n * @returns {number} The read orientation value.\n */\nfunction getOrientation(arrayBuffer) {\n  var dataView = new DataView(arrayBuffer);\n  var orientation = void 0;\n  var littleEndian = void 0;\n  var app1Start = void 0;\n  var ifdStart = void 0;\n\n  // Only handle JPEG image (start by 0xFFD8)\n  if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {\n    var length = dataView.byteLength;\n    var offset = 2;\n\n    while (offset < length) {\n      if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {\n        app1Start = offset;\n        break;\n      }\n\n      offset += 1;\n    }\n  }\n\n  if (app1Start) {\n    var exifIDCode = app1Start + 4;\n    var tiffOffset = app1Start + 10;\n\n    if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {\n      var endianness = dataView.getUint16(tiffOffset);\n\n      littleEndian = endianness === 0x4949;\n\n      if (littleEndian || endianness === 0x4D4D /* bigEndian */) {\n          if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {\n            var firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);\n\n            if (firstIFDOffset >= 0x00000008) {\n              ifdStart = tiffOffset + firstIFDOffset;\n            }\n          }\n        }\n    }\n  }\n\n  if (ifdStart) {\n    var _length = dataView.getUint16(ifdStart, littleEndian);\n    var _offset = void 0;\n    var i = void 0;\n\n    for (i = 0; i < _length; i += 1) {\n      _offset = ifdStart + i * 12 + 2;\n\n      if (dataView.getUint16(_offset, littleEndian) === 0x0112 /* Orientation */) {\n          // 8 is the offset of the current tag's value\n          _offset += 8;\n\n          // Get the original orientation value\n          orientation = dataView.getUint16(_offset, littleEndian);\n\n          // Override the orientation with its default value\n          dataView.setUint16(_offset, 1, littleEndian);\n          break;\n        }\n    }\n  }\n\n  return orientation;\n}\n\n/**\n * Parse Exif Orientation value.\n * @param {number} orientation - The orientation to parse.\n * @returns {Object} The parsed result.\n */\nfunction parseOrientation(orientation) {\n  var rotate = 0;\n  var scaleX = 1;\n  var scaleY = 1;\n\n  switch (orientation) {\n    // Flip horizontal\n    case 2:\n      scaleX = -1;\n      break;\n\n    // Rotate left 180°\n    case 3:\n      rotate = -180;\n      break;\n\n    // Flip vertical\n    case 4:\n      scaleY = -1;\n      break;\n\n    // Flip vertical and rotate right 90°\n    case 5:\n      rotate = 90;\n      scaleY = -1;\n      break;\n\n    // Rotate right 90°\n    case 6:\n      rotate = 90;\n      break;\n\n    // Flip horizontal and rotate right 90°\n    case 7:\n      rotate = 90;\n      scaleX = -1;\n      break;\n\n    // Rotate left 90°\n    case 8:\n      rotate = -90;\n      break;\n\n    default:\n  }\n\n  return {\n    rotate: rotate,\n    scaleX: scaleX,\n    scaleY: scaleY\n  };\n}\n\nvar render = {\n  render: function render() {\n    this.initContainer();\n    this.initCanvas();\n    this.initCropBox();\n    this.renderCanvas();\n\n    if (this.cropped) {\n      this.renderCropBox();\n    }\n  },\n  initContainer: function initContainer() {\n    var $element = this.$element,\n        options = this.options,\n        $container = this.$container,\n        $cropper = this.$cropper;\n\n\n    $cropper.addClass(CLASS_HIDDEN);\n    $element.removeClass(CLASS_HIDDEN);\n\n    $cropper.css(this.container = {\n      width: Math.max($container.width(), Number(options.minContainerWidth) || 200),\n      height: Math.max($container.height(), Number(options.minContainerHeight) || 100)\n    });\n\n    $element.addClass(CLASS_HIDDEN);\n    $cropper.removeClass(CLASS_HIDDEN);\n  },\n\n\n  // Canvas (image wrapper)\n  initCanvas: function initCanvas() {\n    var container = this.container,\n        image = this.image;\n    var viewMode = this.options.viewMode;\n\n    var rotated = Math.abs(image.rotate) % 180 === 90;\n    var naturalWidth = rotated ? image.naturalHeight : image.naturalWidth;\n    var naturalHeight = rotated ? image.naturalWidth : image.naturalHeight;\n    var aspectRatio = naturalWidth / naturalHeight;\n    var canvasWidth = container.width;\n    var canvasHeight = container.height;\n\n    if (container.height * aspectRatio > container.width) {\n      if (viewMode === 3) {\n        canvasWidth = container.height * aspectRatio;\n      } else {\n        canvasHeight = container.width / aspectRatio;\n      }\n    } else if (viewMode === 3) {\n      canvasHeight = container.width / aspectRatio;\n    } else {\n      canvasWidth = container.height * aspectRatio;\n    }\n\n    var canvas = {\n      aspectRatio: aspectRatio,\n      naturalWidth: naturalWidth,\n      naturalHeight: naturalHeight,\n      width: canvasWidth,\n      height: canvasHeight\n    };\n\n    canvas.left = (container.width - canvasWidth) / 2;\n    canvas.top = (container.height - canvasHeight) / 2;\n    canvas.oldLeft = canvas.left;\n    canvas.oldTop = canvas.top;\n\n    this.canvas = canvas;\n    this.limited = viewMode === 1 || viewMode === 2;\n    this.limitCanvas(true, true);\n    this.initialImage = $.extend({}, image);\n    this.initialCanvas = $.extend({}, canvas);\n  },\n  limitCanvas: function limitCanvas(isSizeLimited, isPositionLimited) {\n    var options = this.options,\n        container = this.container,\n        canvas = this.canvas,\n        cropBox = this.cropBox;\n    var viewMode = options.viewMode;\n    var aspectRatio = canvas.aspectRatio;\n\n    var cropped = this.cropped && cropBox;\n\n    if (isSizeLimited) {\n      var minCanvasWidth = Number(options.minCanvasWidth) || 0;\n      var minCanvasHeight = Number(options.minCanvasHeight) || 0;\n\n      if (viewMode > 0) {\n        if (viewMode > 1) {\n          minCanvasWidth = Math.max(minCanvasWidth, container.width);\n          minCanvasHeight = Math.max(minCanvasHeight, container.height);\n\n          if (viewMode === 3) {\n            if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n              minCanvasWidth = minCanvasHeight * aspectRatio;\n            } else {\n              minCanvasHeight = minCanvasWidth / aspectRatio;\n            }\n          }\n        } else if (minCanvasWidth) {\n          minCanvasWidth = Math.max(minCanvasWidth, cropped ? cropBox.width : 0);\n        } else if (minCanvasHeight) {\n          minCanvasHeight = Math.max(minCanvasHeight, cropped ? cropBox.height : 0);\n        } else if (cropped) {\n          minCanvasWidth = cropBox.width;\n          minCanvasHeight = cropBox.height;\n\n          if (minCanvasHeight * aspectRatio > minCanvasWidth) {\n            minCanvasWidth = minCanvasHeight * aspectRatio;\n          } else {\n            minCanvasHeight = minCanvasWidth / aspectRatio;\n          }\n        }\n      }\n\n      var _getAdjustedSizes = getAdjustedSizes({\n        aspectRatio: aspectRatio,\n        width: minCanvasWidth,\n        height: minCanvasHeight\n      });\n\n      minCanvasWidth = _getAdjustedSizes.width;\n      minCanvasHeight = _getAdjustedSizes.height;\n\n\n      canvas.minWidth = minCanvasWidth;\n      canvas.minHeight = minCanvasHeight;\n      canvas.maxWidth = Infinity;\n      canvas.maxHeight = Infinity;\n    }\n\n    if (isPositionLimited) {\n      if (viewMode > 0) {\n        var newCanvasLeft = container.width - canvas.width;\n        var newCanvasTop = container.height - canvas.height;\n\n        canvas.minLeft = Math.min(0, newCanvasLeft);\n        canvas.minTop = Math.min(0, newCanvasTop);\n        canvas.maxLeft = Math.max(0, newCanvasLeft);\n        canvas.maxTop = Math.max(0, newCanvasTop);\n\n        if (cropped && this.limited) {\n          canvas.minLeft = Math.min(cropBox.left, cropBox.left + cropBox.width - canvas.width);\n          canvas.minTop = Math.min(cropBox.top, cropBox.top + cropBox.height - canvas.height);\n          canvas.maxLeft = cropBox.left;\n          canvas.maxTop = cropBox.top;\n\n          if (viewMode === 2) {\n            if (canvas.width >= container.width) {\n              canvas.minLeft = Math.min(0, newCanvasLeft);\n              canvas.maxLeft = Math.max(0, newCanvasLeft);\n            }\n\n            if (canvas.height >= container.height) {\n              canvas.minTop = Math.min(0, newCanvasTop);\n              canvas.maxTop = Math.max(0, newCanvasTop);\n            }\n          }\n        }\n      } else {\n        canvas.minLeft = -canvas.width;\n        canvas.minTop = -canvas.height;\n        canvas.maxLeft = container.width;\n        canvas.maxTop = container.height;\n      }\n    }\n  },\n  renderCanvas: function renderCanvas(changed, transformed) {\n    var canvas = this.canvas,\n        image = this.image;\n\n\n    if (transformed) {\n      var _getRotatedSizes = getRotatedSizes({\n        width: image.naturalWidth * Math.abs(image.scaleX || 1),\n        height: image.naturalHeight * Math.abs(image.scaleY || 1),\n        degree: image.rotate || 0\n      }),\n          naturalWidth = _getRotatedSizes.width,\n          naturalHeight = _getRotatedSizes.height;\n\n      var width = canvas.width * (naturalWidth / canvas.naturalWidth);\n      var height = canvas.height * (naturalHeight / canvas.naturalHeight);\n\n      canvas.left -= (width - canvas.width) / 2;\n      canvas.top -= (height - canvas.height) / 2;\n      canvas.width = width;\n      canvas.height = height;\n      canvas.aspectRatio = naturalWidth / naturalHeight;\n      canvas.naturalWidth = naturalWidth;\n      canvas.naturalHeight = naturalHeight;\n      this.limitCanvas(true, false);\n    }\n\n    if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {\n      canvas.left = canvas.oldLeft;\n    }\n\n    if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {\n      canvas.top = canvas.oldTop;\n    }\n\n    canvas.width = Math.min(Math.max(canvas.width, canvas.minWidth), canvas.maxWidth);\n    canvas.height = Math.min(Math.max(canvas.height, canvas.minHeight), canvas.maxHeight);\n\n    this.limitCanvas(false, true);\n\n    canvas.left = Math.min(Math.max(canvas.left, canvas.minLeft), canvas.maxLeft);\n    canvas.top = Math.min(Math.max(canvas.top, canvas.minTop), canvas.maxTop);\n    canvas.oldLeft = canvas.left;\n    canvas.oldTop = canvas.top;\n\n    this.$canvas.css({\n      width: canvas.width,\n      height: canvas.height,\n      transform: getTransformValues({\n        translateX: canvas.left,\n        translateY: canvas.top\n      })\n    });\n\n    this.renderImage(changed);\n\n    if (this.cropped && this.limited) {\n      this.limitCropBox(true, true);\n    }\n  },\n  renderImage: function renderImage(changed) {\n    var canvas = this.canvas,\n        image = this.image;\n\n    var width = image.naturalWidth * (canvas.width / canvas.naturalWidth);\n    var height = image.naturalHeight * (canvas.height / canvas.naturalHeight);\n\n    $.extend(image, {\n      width: width,\n      height: height,\n      left: (canvas.width - width) / 2,\n      top: (canvas.height - height) / 2\n    });\n\n    this.$clone.css({\n      width: image.width,\n      height: image.height,\n      transform: getTransformValues($.extend({\n        translateX: image.left,\n        translateY: image.top\n      }, image))\n    });\n\n    if (changed) {\n      this.output();\n    }\n  },\n  initCropBox: function initCropBox() {\n    var options = this.options,\n        canvas = this.canvas;\n    var aspectRatio = options.aspectRatio;\n\n    var autoCropArea = Number(options.autoCropArea) || 0.8;\n    var cropBox = {\n      width: canvas.width,\n      height: canvas.height\n    };\n\n    if (aspectRatio) {\n      if (canvas.height * aspectRatio > canvas.width) {\n        cropBox.height = cropBox.width / aspectRatio;\n      } else {\n        cropBox.width = cropBox.height * aspectRatio;\n      }\n    }\n\n    this.cropBox = cropBox;\n    this.limitCropBox(true, true);\n\n    // Initialize auto crop area\n    cropBox.width = Math.min(Math.max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);\n    cropBox.height = Math.min(Math.max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);\n\n    // The width of auto crop area must large than \"minWidth\", and the height too. (#164)\n    cropBox.width = Math.max(cropBox.minWidth, cropBox.width * autoCropArea);\n    cropBox.height = Math.max(cropBox.minHeight, cropBox.height * autoCropArea);\n    cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;\n    cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;\n    cropBox.oldLeft = cropBox.left;\n    cropBox.oldTop = cropBox.top;\n\n    this.initialCropBox = $.extend({}, cropBox);\n  },\n  limitCropBox: function limitCropBox(isSizeLimited, isPositionLimited) {\n    var options = this.options,\n        container = this.container,\n        canvas = this.canvas,\n        cropBox = this.cropBox,\n        limited = this.limited;\n    var aspectRatio = options.aspectRatio;\n\n\n    if (isSizeLimited) {\n      var minCropBoxWidth = Number(options.minCropBoxWidth) || 0;\n      var minCropBoxHeight = Number(options.minCropBoxHeight) || 0;\n      var maxCropBoxWidth = Math.min(container.width, limited ? canvas.width : container.width);\n      var maxCropBoxHeight = Math.min(container.height, limited ? canvas.height : container.height);\n\n      // The min/maxCropBoxWidth/Height must be less than container's width/Height\n      minCropBoxWidth = Math.min(minCropBoxWidth, container.width);\n      minCropBoxHeight = Math.min(minCropBoxHeight, container.height);\n\n      if (aspectRatio) {\n        if (minCropBoxWidth && minCropBoxHeight) {\n          if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {\n            minCropBoxHeight = minCropBoxWidth / aspectRatio;\n          } else {\n            minCropBoxWidth = minCropBoxHeight * aspectRatio;\n          }\n        } else if (minCropBoxWidth) {\n          minCropBoxHeight = minCropBoxWidth / aspectRatio;\n        } else if (minCropBoxHeight) {\n          minCropBoxWidth = minCropBoxHeight * aspectRatio;\n        }\n\n        if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {\n          maxCropBoxHeight = maxCropBoxWidth / aspectRatio;\n        } else {\n          maxCropBoxWidth = maxCropBoxHeight * aspectRatio;\n        }\n      }\n\n      // The minWidth/Height must be less than maxWidth/Height\n      cropBox.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);\n      cropBox.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);\n      cropBox.maxWidth = maxCropBoxWidth;\n      cropBox.maxHeight = maxCropBoxHeight;\n    }\n\n    if (isPositionLimited) {\n      if (limited) {\n        cropBox.minLeft = Math.max(0, canvas.left);\n        cropBox.minTop = Math.max(0, canvas.top);\n        cropBox.maxLeft = Math.min(container.width, canvas.left + canvas.width) - cropBox.width;\n        cropBox.maxTop = Math.min(container.height, canvas.top + canvas.height) - cropBox.height;\n      } else {\n        cropBox.minLeft = 0;\n        cropBox.minTop = 0;\n        cropBox.maxLeft = container.width - cropBox.width;\n        cropBox.maxTop = container.height - cropBox.height;\n      }\n    }\n  },\n  renderCropBox: function renderCropBox() {\n    var options = this.options,\n        container = this.container,\n        cropBox = this.cropBox;\n\n\n    if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {\n      cropBox.left = cropBox.oldLeft;\n    }\n\n    if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {\n      cropBox.top = cropBox.oldTop;\n    }\n\n    cropBox.width = Math.min(Math.max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);\n    cropBox.height = Math.min(Math.max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);\n\n    this.limitCropBox(false, true);\n\n    cropBox.left = Math.min(Math.max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);\n    cropBox.top = Math.min(Math.max(cropBox.top, cropBox.minTop), cropBox.maxTop);\n    cropBox.oldLeft = cropBox.left;\n    cropBox.oldTop = cropBox.top;\n\n    if (options.movable && options.cropBoxMovable) {\n      // Turn to move the canvas when the crop box is equal to the container\n      this.$face.data(DATA_ACTION, cropBox.width >= container.width && cropBox.height >= container.height ? ACTION_MOVE : ACTION_ALL);\n    }\n\n    this.$cropBox.css({\n      width: cropBox.width,\n      height: cropBox.height,\n      transform: getTransformValues({\n        translateX: cropBox.left,\n        translateY: cropBox.top\n      })\n    });\n\n    if (this.cropped && this.limited) {\n      this.limitCanvas(true, true);\n    }\n\n    if (!this.disabled) {\n      this.output();\n    }\n  },\n  output: function output() {\n    this.preview();\n\n    if (this.completed) {\n      this.trigger(EVENT_CROP, this.getData());\n    }\n  }\n};\n\nvar preview = {\n  initPreview: function initPreview() {\n    var crossOrigin = this.crossOrigin;\n\n    var url = crossOrigin ? this.crossOriginUrl : this.url;\n    var image = document.createElement('img');\n\n    if (crossOrigin) {\n      image.crossOrigin = crossOrigin;\n    }\n\n    image.src = url;\n\n    var $clone2 = $(image);\n\n    this.$preview = $(this.options.preview);\n    this.$clone2 = $clone2;\n    this.$viewBox.html($clone2);\n    this.$preview.each(function (i, element) {\n      var $element = $(element);\n      var img = document.createElement('img');\n\n      // Save the original size for recover\n      $element.data(DATA_PREVIEW, {\n        width: $element.width(),\n        height: $element.height(),\n        html: $element.html()\n      });\n\n      if (crossOrigin) {\n        img.crossOrigin = crossOrigin;\n      }\n\n      img.src = url;\n\n      /**\n       * Override img element styles\n       * Add `display:block` to avoid margin top issue\n       * Add `height:auto` to override `height` attribute on IE8\n       * (Occur only when margin-top <= -height)\n       */\n      img.style.cssText = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;\"';\n\n      $element.html(img);\n    });\n  },\n  resetPreview: function resetPreview() {\n    this.$preview.each(function (i, element) {\n      var $element = $(element);\n      var data = $element.data(DATA_PREVIEW);\n\n      $element.css({\n        width: data.width,\n        height: data.height\n      }).html(data.html).removeData(DATA_PREVIEW);\n    });\n  },\n  preview: function preview() {\n    var image = this.image,\n        canvas = this.canvas,\n        cropBox = this.cropBox;\n    var cropBoxWidth = cropBox.width,\n        cropBoxHeight = cropBox.height;\n    var width = image.width,\n        height = image.height;\n\n    var left = cropBox.left - canvas.left - image.left;\n    var top = cropBox.top - canvas.top - image.top;\n\n    if (!this.cropped || this.disabled) {\n      return;\n    }\n\n    this.$clone2.css({\n      width: width,\n      height: height,\n      transform: getTransformValues($.extend({\n        translateX: -left,\n        translateY: -top\n      }, image))\n    });\n\n    this.$preview.each(function (i, element) {\n      var $element = $(element);\n      var data = $element.data(DATA_PREVIEW);\n      var originalWidth = data.width;\n      var originalHeight = data.height;\n      var newWidth = originalWidth;\n      var newHeight = originalHeight;\n      var ratio = 1;\n\n      if (cropBoxWidth) {\n        ratio = originalWidth / cropBoxWidth;\n        newHeight = cropBoxHeight * ratio;\n      }\n\n      if (cropBoxHeight && newHeight > originalHeight) {\n        ratio = originalHeight / cropBoxHeight;\n        newWidth = cropBoxWidth * ratio;\n        newHeight = originalHeight;\n      }\n\n      $element.css({\n        width: newWidth,\n        height: newHeight\n      }).find('img').css({\n        width: width * ratio,\n        height: height * ratio,\n        transform: getTransformValues($.extend({\n          translateX: -left * ratio,\n          translateY: -top * ratio\n        }, image))\n      });\n    });\n  }\n};\n\nvar events = {\n  bind: function bind() {\n    var $element = this.$element,\n        options = this.options,\n        $cropper = this.$cropper;\n\n\n    if ($.isFunction(options.cropstart)) {\n      $element.on(EVENT_CROP_START, options.cropstart);\n    }\n\n    if ($.isFunction(options.cropmove)) {\n      $element.on(EVENT_CROP_MOVE, options.cropmove);\n    }\n\n    if ($.isFunction(options.cropend)) {\n      $element.on(EVENT_CROP_END, options.cropend);\n    }\n\n    if ($.isFunction(options.crop)) {\n      $element.on(EVENT_CROP, options.crop);\n    }\n\n    if ($.isFunction(options.zoom)) {\n      $element.on(EVENT_ZOOM, options.zoom);\n    }\n\n    $cropper.on(EVENT_POINTER_DOWN, proxy(this.cropStart, this));\n\n    if (options.zoomable && options.zoomOnWheel) {\n      $cropper.on(EVENT_WHEEL, proxy(this.wheel, this));\n    }\n\n    if (options.toggleDragModeOnDblclick) {\n      $cropper.on(EVENT_DBLCLICK, proxy(this.dblclick, this));\n    }\n\n    $(this.element.ownerDocument).on(EVENT_POINTER_MOVE, this.onCropMove = proxy(this.cropMove, this)).on(EVENT_POINTER_UP, this.onCropEnd = proxy(this.cropEnd, this));\n\n    if (options.responsive) {\n      $(window).on(EVENT_RESIZE, this.onResize = proxy(this.resize, this));\n    }\n  },\n  unbind: function unbind() {\n    var $element = this.$element,\n        options = this.options,\n        $cropper = this.$cropper;\n\n\n    if ($.isFunction(options.cropstart)) {\n      $element.off(EVENT_CROP_START, options.cropstart);\n    }\n\n    if ($.isFunction(options.cropmove)) {\n      $element.off(EVENT_CROP_MOVE, options.cropmove);\n    }\n\n    if ($.isFunction(options.cropend)) {\n      $element.off(EVENT_CROP_END, options.cropend);\n    }\n\n    if ($.isFunction(options.crop)) {\n      $element.off(EVENT_CROP, options.crop);\n    }\n\n    if ($.isFunction(options.zoom)) {\n      $element.off(EVENT_ZOOM, options.zoom);\n    }\n\n    $cropper.off(EVENT_POINTER_DOWN, this.cropStart);\n\n    if (options.zoomable && options.zoomOnWheel) {\n      $cropper.off(EVENT_WHEEL, this.wheel);\n    }\n\n    if (options.toggleDragModeOnDblclick) {\n      $cropper.off(EVENT_DBLCLICK, this.dblclick);\n    }\n\n    $(this.element.ownerDocument).off(EVENT_POINTER_MOVE, this.onCropMove).off(EVENT_POINTER_UP, this.onCropEnd);\n\n    if (options.responsive) {\n      $(window).off(EVENT_RESIZE, this.onResize);\n    }\n  }\n};\n\nvar handlers = {\n  resize: function resize() {\n    var options = this.options,\n        $container = this.$container,\n        container = this.container;\n\n    var minContainerWidth = Number(options.minContainerWidth) || 200;\n    var minContainerHeight = Number(options.minContainerHeight) || 100;\n\n    if (this.disabled || container.width <= minContainerWidth || container.height <= minContainerHeight) {\n      return;\n    }\n\n    var ratio = $container.width() / container.width;\n\n    // Resize when width changed or height changed\n    if (ratio !== 1 || $container.height() !== container.height) {\n      var canvasData = void 0;\n      var cropBoxData = void 0;\n\n      if (options.restore) {\n        canvasData = this.getCanvasData();\n        cropBoxData = this.getCropBoxData();\n      }\n\n      this.render();\n\n      if (options.restore) {\n        this.setCanvasData($.each(canvasData, function (i, n) {\n          canvasData[i] = n * ratio;\n        }));\n        this.setCropBoxData($.each(cropBoxData, function (i, n) {\n          cropBoxData[i] = n * ratio;\n        }));\n      }\n    }\n  },\n  dblclick: function dblclick() {\n    if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {\n      return;\n    }\n\n    this.setDragMode(this.$dragBox.hasClass(CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);\n  },\n  wheel: function wheel(event) {\n    var _this = this;\n\n    var e = event.originalEvent || event;\n    var ratio = Number(this.options.wheelZoomRatio) || 0.1;\n\n    if (this.disabled) {\n      return;\n    }\n\n    event.preventDefault();\n\n    // Limit wheel speed to prevent zoom too fast\n    if (this.wheeling) {\n      return;\n    }\n\n    this.wheeling = true;\n\n    setTimeout(function () {\n      _this.wheeling = false;\n    }, 50);\n\n    var delta = 1;\n\n    if (e.deltaY) {\n      delta = e.deltaY > 0 ? 1 : -1;\n    } else if (e.wheelDelta) {\n      delta = -e.wheelDelta / 120;\n    } else if (e.detail) {\n      delta = e.detail > 0 ? 1 : -1;\n    }\n\n    this.zoom(-delta * ratio, event);\n  },\n  cropStart: function cropStart(e) {\n    if (this.disabled) {\n      return;\n    }\n\n    var options = this.options,\n        pointers = this.pointers;\n    var originalEvent = e.originalEvent;\n\n    var action = void 0;\n\n    if (originalEvent && originalEvent.changedTouches) {\n      // Handle touch event\n      $.each(originalEvent.changedTouches, function (i, touch) {\n        pointers[touch.identifier] = getPointer(touch);\n      });\n    } else {\n      // Handle mouse event and pointer event\n      pointers[originalEvent && originalEvent.pointerId || 0] = getPointer(originalEvent || e);\n    }\n\n    if (objectKeys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {\n      action = ACTION_ZOOM;\n    } else {\n      action = $(e.target).data(DATA_ACTION);\n    }\n\n    if (!REGEXP_ACTIONS.test(action)) {\n      return;\n    }\n\n    if (this.trigger(EVENT_CROP_START, {\n      originalEvent: originalEvent,\n      action: action\n    }).isDefaultPrevented()) {\n      return;\n    }\n\n    e.preventDefault();\n\n    this.action = action;\n    this.cropping = false;\n\n    if (action === ACTION_CROP) {\n      this.cropping = true;\n      this.$dragBox.addClass(CLASS_MODAL);\n    }\n  },\n  cropMove: function cropMove(e) {\n    var action = this.action;\n\n\n    if (this.disabled || !action) {\n      return;\n    }\n\n    var pointers = this.pointers;\n    var originalEvent = e.originalEvent;\n\n\n    e.preventDefault();\n\n    if (this.trigger(EVENT_CROP_MOVE, {\n      originalEvent: originalEvent,\n      action: action\n    }).isDefaultPrevented()) {\n      return;\n    }\n\n    if (originalEvent && originalEvent.changedTouches) {\n      $.each(originalEvent.changedTouches, function (i, touch) {\n        $.extend(pointers[touch.identifier], getPointer(touch, true));\n      });\n    } else {\n      $.extend(pointers[originalEvent && originalEvent.pointerId || 0], getPointer(originalEvent || e, true));\n    }\n\n    this.change(e);\n  },\n  cropEnd: function cropEnd(e) {\n    if (this.disabled) {\n      return;\n    }\n\n    var action = this.action;\n    var pointers = this.pointers;\n    var originalEvent = e.originalEvent;\n\n\n    if (originalEvent && originalEvent.changedTouches) {\n      $.each(originalEvent.changedTouches, function (i, touch) {\n        delete pointers[touch.identifier];\n      });\n    } else {\n      delete pointers[originalEvent && originalEvent.pointerId || 0];\n    }\n\n    if (!action) {\n      return;\n    }\n\n    e.preventDefault();\n\n    if (!objectKeys(pointers).length) {\n      this.action = '';\n    }\n\n    if (this.cropping) {\n      this.cropping = false;\n      this.$dragBox.toggleClass(CLASS_MODAL, this.cropped && this.options.modal);\n    }\n\n    this.trigger(EVENT_CROP_END, {\n      originalEvent: originalEvent,\n      action: action\n    });\n  }\n};\n\nvar change = {\n  change: function change(e) {\n    var options = this.options,\n        pointers = this.pointers,\n        container = this.container,\n        canvas = this.canvas,\n        cropBox = this.cropBox;\n    var action = this.action;\n    var aspectRatio = options.aspectRatio;\n    var left = cropBox.left,\n        top = cropBox.top,\n        width = cropBox.width,\n        height = cropBox.height;\n\n    var right = left + width;\n    var bottom = top + height;\n    var minLeft = 0;\n    var minTop = 0;\n    var maxWidth = container.width;\n    var maxHeight = container.height;\n    var renderable = true;\n    var offset = void 0;\n\n    // Locking aspect ratio in \"free mode\" by holding shift key (#259)\n    if (!aspectRatio && e.shiftKey) {\n      aspectRatio = width && height ? width / height : 1;\n    }\n\n    if (this.limited) {\n      minLeft = cropBox.minLeft;\n      minTop = cropBox.minTop;\n\n      maxWidth = minLeft + Math.min(container.width, canvas.width, canvas.left + canvas.width);\n      maxHeight = minTop + Math.min(container.height, canvas.height, canvas.top + canvas.height);\n    }\n\n    var pointer = pointers[objectKeys(pointers)[0]];\n    var range = {\n      x: pointer.endX - pointer.startX,\n      y: pointer.endY - pointer.startY\n    };\n    var check = function check(side) {\n      switch (side) {\n        case ACTION_EAST:\n          if (right + range.x > maxWidth) {\n            range.x = maxWidth - right;\n          }\n\n          break;\n\n        case ACTION_WEST:\n          if (left + range.x < minLeft) {\n            range.x = minLeft - left;\n          }\n\n          break;\n\n        case ACTION_NORTH:\n          if (top + range.y < minTop) {\n            range.y = minTop - top;\n          }\n\n          break;\n\n        case ACTION_SOUTH:\n          if (bottom + range.y > maxHeight) {\n            range.y = maxHeight - bottom;\n          }\n\n          break;\n\n        default:\n      }\n    };\n\n    switch (action) {\n      // Move crop box\n      case ACTION_ALL:\n        left += range.x;\n        top += range.y;\n        break;\n\n      // Resize crop box\n      case ACTION_EAST:\n        if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_EAST);\n        width += range.x;\n\n        if (aspectRatio) {\n          height = width / aspectRatio;\n          top -= range.x / aspectRatio / 2;\n        }\n\n        if (width < 0) {\n          action = ACTION_WEST;\n          width = 0;\n        }\n\n        break;\n\n      case ACTION_NORTH:\n        if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_NORTH);\n        height -= range.y;\n        top += range.y;\n\n        if (aspectRatio) {\n          width = height * aspectRatio;\n          left += range.y * aspectRatio / 2;\n        }\n\n        if (height < 0) {\n          action = ACTION_SOUTH;\n          height = 0;\n        }\n\n        break;\n\n      case ACTION_WEST:\n        if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_WEST);\n        width -= range.x;\n        left += range.x;\n\n        if (aspectRatio) {\n          height = width / aspectRatio;\n          top += range.x / aspectRatio / 2;\n        }\n\n        if (width < 0) {\n          action = ACTION_EAST;\n          width = 0;\n        }\n\n        break;\n\n      case ACTION_SOUTH:\n        if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {\n          renderable = false;\n          break;\n        }\n\n        check(ACTION_SOUTH);\n        height += range.y;\n\n        if (aspectRatio) {\n          width = height * aspectRatio;\n          left -= range.y * aspectRatio / 2;\n        }\n\n        if (height < 0) {\n          action = ACTION_NORTH;\n          height = 0;\n        }\n\n        break;\n\n      case ACTION_NORTH_EAST:\n        if (aspectRatio) {\n          if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n          width = height * aspectRatio;\n        } else {\n          check(ACTION_NORTH);\n          check(ACTION_EAST);\n\n          if (range.x >= 0) {\n            if (right < maxWidth) {\n              width += range.x;\n            } else if (range.y <= 0 && top <= minTop) {\n              renderable = false;\n            }\n          } else {\n            width += range.x;\n          }\n\n          if (range.y <= 0) {\n            if (top > minTop) {\n              height -= range.y;\n              top += range.y;\n            }\n          } else {\n            height -= range.y;\n            top += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_SOUTH_WEST;\n          height = 0;\n          width = 0;\n        } else if (width < 0) {\n          action = ACTION_NORTH_WEST;\n          width = 0;\n        } else if (height < 0) {\n          action = ACTION_SOUTH_EAST;\n          height = 0;\n        }\n\n        break;\n\n      case ACTION_NORTH_WEST:\n        if (aspectRatio) {\n          if (range.y <= 0 && (top <= minTop || left <= minLeft)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_NORTH);\n          height -= range.y;\n          top += range.y;\n          width = height * aspectRatio;\n          left += range.y * aspectRatio;\n        } else {\n          check(ACTION_NORTH);\n          check(ACTION_WEST);\n\n          if (range.x <= 0) {\n            if (left > minLeft) {\n              width -= range.x;\n              left += range.x;\n            } else if (range.y <= 0 && top <= minTop) {\n              renderable = false;\n            }\n          } else {\n            width -= range.x;\n            left += range.x;\n          }\n\n          if (range.y <= 0) {\n            if (top > minTop) {\n              height -= range.y;\n              top += range.y;\n            }\n          } else {\n            height -= range.y;\n            top += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_SOUTH_EAST;\n          height = 0;\n          width = 0;\n        } else if (width < 0) {\n          action = ACTION_NORTH_EAST;\n          width = 0;\n        } else if (height < 0) {\n          action = ACTION_SOUTH_WEST;\n          height = 0;\n        }\n\n        break;\n\n      case ACTION_SOUTH_WEST:\n        if (aspectRatio) {\n          if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_WEST);\n          width -= range.x;\n          left += range.x;\n          height = width / aspectRatio;\n        } else {\n          check(ACTION_SOUTH);\n          check(ACTION_WEST);\n\n          if (range.x <= 0) {\n            if (left > minLeft) {\n              width -= range.x;\n              left += range.x;\n            } else if (range.y >= 0 && bottom >= maxHeight) {\n              renderable = false;\n            }\n          } else {\n            width -= range.x;\n            left += range.x;\n          }\n\n          if (range.y >= 0) {\n            if (bottom < maxHeight) {\n              height += range.y;\n            }\n          } else {\n            height += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_NORTH_EAST;\n          height = 0;\n          width = 0;\n        } else if (width < 0) {\n          action = ACTION_SOUTH_EAST;\n          width = 0;\n        } else if (height < 0) {\n          action = ACTION_NORTH_WEST;\n          height = 0;\n        }\n\n        break;\n\n      case ACTION_SOUTH_EAST:\n        if (aspectRatio) {\n          if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {\n            renderable = false;\n            break;\n          }\n\n          check(ACTION_EAST);\n          width += range.x;\n          height = width / aspectRatio;\n        } else {\n          check(ACTION_SOUTH);\n          check(ACTION_EAST);\n\n          if (range.x >= 0) {\n            if (right < maxWidth) {\n              width += range.x;\n            } else if (range.y >= 0 && bottom >= maxHeight) {\n              renderable = false;\n            }\n          } else {\n            width += range.x;\n          }\n\n          if (range.y >= 0) {\n            if (bottom < maxHeight) {\n              height += range.y;\n            }\n          } else {\n            height += range.y;\n          }\n        }\n\n        if (width < 0 && height < 0) {\n          action = ACTION_NORTH_WEST;\n          height = 0;\n          width = 0;\n        } else if (width < 0) {\n          action = ACTION_SOUTH_WEST;\n          width = 0;\n        } else if (height < 0) {\n          action = ACTION_NORTH_EAST;\n          height = 0;\n        }\n\n        break;\n\n      // Move canvas\n      case ACTION_MOVE:\n        this.move(range.x, range.y);\n        renderable = false;\n        break;\n\n      // Zoom canvas\n      case ACTION_ZOOM:\n        this.zoom(getMaxZoomRatio(pointers), e.originalEvent);\n        renderable = false;\n        break;\n\n      // Create crop box\n      case ACTION_CROP:\n        if (!range.x || !range.y) {\n          renderable = false;\n          break;\n        }\n\n        offset = this.$cropper.offset();\n        left = pointer.startX - offset.left;\n        top = pointer.startY - offset.top;\n        width = cropBox.minWidth;\n        height = cropBox.minHeight;\n\n        if (range.x > 0) {\n          action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;\n        } else if (range.x < 0) {\n          left -= width;\n          action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;\n        }\n\n        if (range.y < 0) {\n          top -= height;\n        }\n\n        // Show the crop box if is hidden\n        if (!this.cropped) {\n          this.$cropBox.removeClass(CLASS_HIDDEN);\n          this.cropped = true;\n\n          if (this.limited) {\n            this.limitCropBox(true, true);\n          }\n        }\n\n        break;\n\n      default:\n    }\n\n    if (renderable) {\n      cropBox.width = width;\n      cropBox.height = height;\n      cropBox.left = left;\n      cropBox.top = top;\n      this.action = action;\n      this.renderCropBox();\n    }\n\n    // Override\n    $.each(pointers, function (i, p) {\n      p.startX = p.endX;\n      p.startY = p.endY;\n    });\n  }\n};\n\nvar methods = {\n  // Show the crop box manually\n  crop: function crop() {\n    if (!this.ready || this.disabled) {\n      return;\n    }\n\n    if (!this.cropped) {\n      this.cropped = true;\n      this.limitCropBox(true, true);\n\n      if (this.options.modal) {\n        this.$dragBox.addClass(CLASS_MODAL);\n      }\n\n      this.$cropBox.removeClass(CLASS_HIDDEN);\n    }\n\n    this.setCropBoxData(this.initialCropBox);\n  },\n\n\n  // Reset the image and crop box to their initial states\n  reset: function reset() {\n    if (!this.ready || this.disabled) {\n      return;\n    }\n\n    this.image = $.extend({}, this.initialImage);\n    this.canvas = $.extend({}, this.initialCanvas);\n    this.cropBox = $.extend({}, this.initialCropBox);\n    this.renderCanvas();\n\n    if (this.cropped) {\n      this.renderCropBox();\n    }\n  },\n\n\n  // Clear the crop box\n  clear: function clear() {\n    if (!this.cropped || this.disabled) {\n      return;\n    }\n\n    $.extend(this.cropBox, {\n      left: 0,\n      top: 0,\n      width: 0,\n      height: 0\n    });\n\n    this.cropped = false;\n    this.renderCropBox();\n    this.limitCanvas(true, true);\n\n    // Render canvas after crop box rendered\n    this.renderCanvas();\n    this.$dragBox.removeClass(CLASS_MODAL);\n    this.$cropBox.addClass(CLASS_HIDDEN);\n  },\n\n\n  /**\n   * Replace the image's src and rebuild the cropper\n   * @param {string} url - The new URL.\n   * @param {boolean} [onlyColorChanged] - Indicate if the new image only changed color.\n   */\n  replace: function replace(url, onlyColorChanged) {\n    if (!this.disabled && url) {\n      if (this.isImg) {\n        this.$element.attr('src', url);\n      }\n\n      if (onlyColorChanged) {\n        this.url = url;\n        this.$clone.attr('src', url);\n\n        if (this.ready) {\n          this.$preview.find('img').add(this.$clone2).attr('src', url);\n        }\n      } else {\n        if (this.isImg) {\n          this.replaced = true;\n        }\n\n        // Clear previous data\n        this.options.data = null;\n        this.load(url);\n      }\n    }\n  },\n\n\n  // Enable (unfreeze) the cropper\n  enable: function enable() {\n    if (this.ready) {\n      this.disabled = false;\n      this.$cropper.removeClass(CLASS_DISABLED);\n    }\n  },\n\n\n  // Disable (freeze) the cropper\n  disable: function disable() {\n    if (this.ready) {\n      this.disabled = true;\n      this.$cropper.addClass(CLASS_DISABLED);\n    }\n  },\n\n\n  // Destroy the cropper and remove the instance from the image\n  destroy: function destroy() {\n    var $element = this.$element;\n\n\n    if (this.loaded) {\n      if (this.isImg && this.replaced) {\n        $element.attr('src', this.originalUrl);\n      }\n\n      this.unbuild();\n      $element.removeClass(CLASS_HIDDEN);\n    } else if (this.isImg) {\n      $element.off(EVENT_LOAD, this.start);\n    } else if (this.$clone) {\n      this.$clone.remove();\n    }\n\n    $element.removeData(NAMESPACE);\n  },\n\n\n  /**\n   * Move the canvas with relative offsets\n   * @param {number} offsetX - The relative offset distance on the x-axis.\n   * @param {number} offsetY - The relative offset distance on the y-axis.\n   */\n  move: function move(offsetX, offsetY) {\n    var _canvas = this.canvas,\n        left = _canvas.left,\n        top = _canvas.top;\n\n\n    this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));\n  },\n\n\n  /**\n   * Move the canvas to an absolute point\n   * @param {number} x - The x-axis coordinate.\n   * @param {number} [y=x] - The y-axis coordinate.\n   */\n  moveTo: function moveTo(x, y) {\n    var canvas = this.canvas;\n\n    var changed = false;\n\n    // If \"y\" is not present, its default value is \"x\"\n    if (isUndefined(y)) {\n      y = x;\n    }\n\n    x = Number(x);\n    y = Number(y);\n\n    if (this.ready && !this.disabled && this.options.movable) {\n      if (isNumber(x)) {\n        canvas.left = x;\n        changed = true;\n      }\n\n      if (isNumber(y)) {\n        canvas.top = y;\n        changed = true;\n      }\n\n      if (changed) {\n        this.renderCanvas(true);\n      }\n    }\n  },\n\n\n  /**\n   * Zoom the canvas with a relative ratio\n   * @param {Number} ratio - The target ratio.\n   * @param {Event} _event - The related event if any.\n   */\n  zoom: function zoom(ratio, _event) {\n    var canvas = this.canvas;\n\n\n    ratio = Number(ratio);\n\n    if (ratio < 0) {\n      ratio = 1 / (1 - ratio);\n    } else {\n      ratio = 1 + ratio;\n    }\n\n    this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _event);\n  },\n\n\n  /**\n   * Zoom the canvas to an absolute ratio\n   * @param {number} ratio - The target ratio.\n   * @param {Event} _event - The related event if any.\n   */\n  zoomTo: function zoomTo(ratio, _event) {\n    var options = this.options,\n        pointers = this.pointers,\n        canvas = this.canvas;\n    var width = canvas.width,\n        height = canvas.height,\n        naturalWidth = canvas.naturalWidth,\n        naturalHeight = canvas.naturalHeight;\n\n\n    ratio = Number(ratio);\n\n    if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {\n      var newWidth = naturalWidth * ratio;\n      var newHeight = naturalHeight * ratio;\n      var originalEvent = void 0;\n\n      if (_event) {\n        originalEvent = _event.originalEvent;\n      }\n\n      if (this.trigger(EVENT_ZOOM, {\n        originalEvent: originalEvent,\n        oldRatio: width / naturalWidth,\n        ratio: newWidth / naturalWidth\n      }).isDefaultPrevented()) {\n        return;\n      }\n\n      if (originalEvent) {\n        var offset = this.$cropper.offset();\n        var center = pointers && objectKeys(pointers).length ? getPointersCenter(pointers) : {\n          pageX: _event.pageX || originalEvent.pageX || 0,\n          pageY: _event.pageY || originalEvent.pageY || 0\n        };\n\n        // Zoom from the triggering point of the event\n        canvas.left -= (newWidth - width) * ((center.pageX - offset.left - canvas.left) / width);\n        canvas.top -= (newHeight - height) * ((center.pageY - offset.top - canvas.top) / height);\n      } else {\n        // Zoom from the center of the canvas\n        canvas.left -= (newWidth - width) / 2;\n        canvas.top -= (newHeight - height) / 2;\n      }\n\n      canvas.width = newWidth;\n      canvas.height = newHeight;\n      this.renderCanvas(true);\n    }\n  },\n\n\n  /**\n   * Rotate the canvas with a relative degree\n   * @param {number} degree - The rotate degree.\n   */\n  rotate: function rotate(degree) {\n    this.rotateTo((this.image.rotate || 0) + Number(degree));\n  },\n\n\n  /**\n   * Rotate the canvas to an absolute degree\n   * @param {number} degree - The rotate degree.\n   */\n  rotateTo: function rotateTo(degree) {\n    degree = Number(degree);\n\n    if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {\n      this.image.rotate = degree % 360;\n      this.renderCanvas(true, true);\n    }\n  },\n\n\n  /**\n   * Scale the image on the x-axis.\n   * @param {number} scaleX - The scale ratio on the x-axis.\n   */\n  scaleX: function scaleX(_scaleX) {\n    var scaleY = this.image.scaleY;\n\n\n    this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);\n  },\n\n\n  /**\n   * Scale the image on the y-axis.\n   * @param {number} scaleY - The scale ratio on the y-axis.\n   */\n  scaleY: function scaleY(_scaleY) {\n    var scaleX = this.image.scaleX;\n\n\n    this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);\n  },\n\n\n  /**\n   * Scale the image\n   * @param {number} scaleX - The scale ratio on the x-axis.\n   * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.\n   */\n  scale: function scale(scaleX) {\n    var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;\n    var image = this.image;\n\n    var transformed = false;\n\n    scaleX = Number(scaleX);\n    scaleY = Number(scaleY);\n\n    if (this.ready && !this.disabled && this.options.scalable) {\n      if (isNumber(scaleX)) {\n        image.scaleX = scaleX;\n        transformed = true;\n      }\n\n      if (isNumber(scaleY)) {\n        image.scaleY = scaleY;\n        transformed = true;\n      }\n\n      if (transformed) {\n        this.renderCanvas(true, true);\n      }\n    }\n  },\n\n\n  /**\n   * Get the cropped area position and size data (base on the original image)\n   * @param {boolean} [rounded=false] - Indicate if round the data values or not.\n   * @returns {Object} The result cropped data.\n   */\n  getData: function getData() {\n    var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;\n    var options = this.options,\n        image = this.image,\n        canvas = this.canvas,\n        cropBox = this.cropBox;\n\n    var data = void 0;\n\n    if (this.ready && this.cropped) {\n      data = {\n        x: cropBox.left - canvas.left,\n        y: cropBox.top - canvas.top,\n        width: cropBox.width,\n        height: cropBox.height\n      };\n\n      var ratio = image.width / image.naturalWidth;\n\n      $.each(data, function (i, n) {\n        n /= ratio;\n        data[i] = rounded ? Math.round(n) : n;\n      });\n    } else {\n      data = {\n        x: 0,\n        y: 0,\n        width: 0,\n        height: 0\n      };\n    }\n\n    if (options.rotatable) {\n      data.rotate = image.rotate || 0;\n    }\n\n    if (options.scalable) {\n      data.scaleX = image.scaleX || 1;\n      data.scaleY = image.scaleY || 1;\n    }\n\n    return data;\n  },\n\n\n  /**\n   * Set the cropped area position and size with new data\n   * @param {Object} data - The new data.\n   */\n  setData: function setData(data) {\n    var options = this.options,\n        image = this.image,\n        canvas = this.canvas;\n\n    var cropBoxData = {};\n\n    if ($.isFunction(data)) {\n      data = data.call(this.element);\n    }\n\n    if (this.ready && !this.disabled && $.isPlainObject(data)) {\n      var transformed = false;\n\n      if (options.rotatable) {\n        if (isNumber(data.rotate) && data.rotate !== image.rotate) {\n          image.rotate = data.rotate;\n          transformed = true;\n        }\n      }\n\n      if (options.scalable) {\n        if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {\n          image.scaleX = data.scaleX;\n          transformed = true;\n        }\n\n        if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {\n          image.scaleY = data.scaleY;\n          transformed = true;\n        }\n      }\n\n      if (transformed) {\n        this.renderCanvas(true, true);\n      }\n\n      var ratio = image.width / image.naturalWidth;\n\n      if (isNumber(data.x)) {\n        cropBoxData.left = data.x * ratio + canvas.left;\n      }\n\n      if (isNumber(data.y)) {\n        cropBoxData.top = data.y * ratio + canvas.top;\n      }\n\n      if (isNumber(data.width)) {\n        cropBoxData.width = data.width * ratio;\n      }\n\n      if (isNumber(data.height)) {\n        cropBoxData.height = data.height * ratio;\n      }\n\n      this.setCropBoxData(cropBoxData);\n    }\n  },\n\n\n  /**\n   * Get the container size data.\n   * @returns {Object} The result container data.\n   */\n  getContainerData: function getContainerData() {\n    return this.ready ? $.extend({}, this.container) : {};\n  },\n\n\n  /**\n   * Get the image position and size data.\n   * @returns {Object} The result image data.\n   */\n  getImageData: function getImageData() {\n    return this.loaded ? $.extend({}, this.image) : {};\n  },\n\n\n  /**\n   * Get the canvas position and size data.\n   * @returns {Object} The result canvas data.\n   */\n  getCanvasData: function getCanvasData() {\n    var canvas = this.canvas;\n\n    var data = {};\n\n    if (this.ready) {\n      $.each(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (i, n) {\n        data[n] = canvas[n];\n      });\n    }\n\n    return data;\n  },\n\n\n  /**\n   * Set the canvas position and size with new data.\n   * @param {Object} data - The new canvas data.\n   */\n  setCanvasData: function setCanvasData(data) {\n    var canvas = this.canvas;\n    var aspectRatio = canvas.aspectRatio;\n\n\n    if ($.isFunction(data)) {\n      data = data.call(this.$element);\n    }\n\n    if (this.ready && !this.disabled && $.isPlainObject(data)) {\n      if (isNumber(data.left)) {\n        canvas.left = data.left;\n      }\n\n      if (isNumber(data.top)) {\n        canvas.top = data.top;\n      }\n\n      if (isNumber(data.width)) {\n        canvas.width = data.width;\n        canvas.height = data.width / aspectRatio;\n      } else if (isNumber(data.height)) {\n        canvas.height = data.height;\n        canvas.width = data.height * aspectRatio;\n      }\n\n      this.renderCanvas(true);\n    }\n  },\n\n\n  /**\n   * Get the crop box position and size data.\n   * @returns {Object} The result crop box data.\n   */\n  getCropBoxData: function getCropBoxData() {\n    var cropBox = this.cropBox;\n\n\n    return this.ready && this.cropped ? {\n      left: cropBox.left,\n      top: cropBox.top,\n      width: cropBox.width,\n      height: cropBox.height\n    } : {};\n  },\n\n\n  /**\n   * Set the crop box position and size with new data.\n   * @param {Object} data - The new crop box data.\n   */\n  setCropBoxData: function setCropBoxData(data) {\n    var cropBox = this.cropBox;\n    var aspectRatio = this.options.aspectRatio;\n\n    var widthChanged = void 0;\n    var heightChanged = void 0;\n\n    if ($.isFunction(data)) {\n      data = data.call(this.$element);\n    }\n\n    if (this.ready && this.cropped && !this.disabled && $.isPlainObject(data)) {\n      if (isNumber(data.left)) {\n        cropBox.left = data.left;\n      }\n\n      if (isNumber(data.top)) {\n        cropBox.top = data.top;\n      }\n\n      if (isNumber(data.width) && data.width !== cropBox.width) {\n        widthChanged = true;\n        cropBox.width = data.width;\n      }\n\n      if (isNumber(data.height) && data.height !== cropBox.height) {\n        heightChanged = true;\n        cropBox.height = data.height;\n      }\n\n      if (aspectRatio) {\n        if (widthChanged) {\n          cropBox.height = cropBox.width / aspectRatio;\n        } else if (heightChanged) {\n          cropBox.width = cropBox.height * aspectRatio;\n        }\n      }\n\n      this.renderCropBox();\n    }\n  },\n\n\n  /**\n   * Get a canvas drawn the cropped image.\n   * @param {Object} [options={}] - The config options.\n   * @returns {HTMLCanvasElement} - The result canvas.\n   */\n  getCroppedCanvas: function getCroppedCanvas() {\n    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n    if (!this.ready || !window.HTMLCanvasElement) {\n      return null;\n    }\n\n    var canvasData = this.canvas;\n\n    var source = getSourceCanvas(this.$clone[0], this.image, canvasData, options);\n\n    // Returns the source canvas if it is not cropped.\n    if (!this.cropped) {\n      return source;\n    }\n\n    var _getData = this.getData(),\n        initialX = _getData.x,\n        initialY = _getData.y,\n        initialWidth = _getData.width,\n        initialHeight = _getData.height;\n\n    var ratio = source.width / Math.floor(canvasData.naturalWidth);\n\n    if (ratio !== 1) {\n      initialX *= ratio;\n      initialY *= ratio;\n      initialWidth *= ratio;\n      initialHeight *= ratio;\n    }\n\n    var aspectRatio = initialWidth / initialHeight;\n    var maxSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.maxWidth || Infinity,\n      height: options.maxHeight || Infinity\n    });\n    var minSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.minWidth || 0,\n      height: options.minHeight || 0\n    }, 'cover');\n\n    var _getAdjustedSizes = getAdjustedSizes({\n      aspectRatio: aspectRatio,\n      width: options.width || (ratio !== 1 ? source.width : initialWidth),\n      height: options.height || (ratio !== 1 ? source.height : initialHeight)\n    }),\n        width = _getAdjustedSizes.width,\n        height = _getAdjustedSizes.height;\n\n    width = Math.min(maxSizes.width, Math.max(minSizes.width, width));\n    height = Math.min(maxSizes.height, Math.max(minSizes.height, height));\n\n    var canvas = document.createElement('canvas');\n    var context = canvas.getContext('2d');\n\n    canvas.width = normalizeDecimalNumber(width);\n    canvas.height = normalizeDecimalNumber(height);\n    context.fillStyle = options.fillColor || 'transparent';\n    context.fillRect(0, 0, width, height);\n\n    var _options$imageSmoothi = options.imageSmoothingEnabled,\n        imageSmoothingEnabled = _options$imageSmoothi === undefined ? true : _options$imageSmoothi,\n        imageSmoothingQuality = options.imageSmoothingQuality;\n\n\n    context.imageSmoothingEnabled = imageSmoothingEnabled;\n\n    if (imageSmoothingQuality) {\n      context.imageSmoothingQuality = imageSmoothingQuality;\n    }\n\n    // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage\n    var sourceWidth = source.width;\n    var sourceHeight = source.height;\n\n    // Source canvas parameters\n    var srcX = initialX;\n    var srcY = initialY;\n    var srcWidth = void 0;\n    var srcHeight = void 0;\n\n    // Destination canvas parameters\n    var dstX = void 0;\n    var dstY = void 0;\n    var dstWidth = void 0;\n    var dstHeight = void 0;\n\n    if (srcX <= -initialWidth || srcX > sourceWidth) {\n      srcX = 0;\n      srcWidth = 0;\n      dstX = 0;\n      dstWidth = 0;\n    } else if (srcX <= 0) {\n      dstX = -srcX;\n      srcX = 0;\n      srcWidth = Math.min(sourceWidth, initialWidth + srcX);\n      dstWidth = srcWidth;\n    } else if (srcX <= sourceWidth) {\n      dstX = 0;\n      srcWidth = Math.min(initialWidth, sourceWidth - srcX);\n      dstWidth = srcWidth;\n    }\n\n    if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {\n      srcY = 0;\n      srcHeight = 0;\n      dstY = 0;\n      dstHeight = 0;\n    } else if (srcY <= 0) {\n      dstY = -srcY;\n      srcY = 0;\n      srcHeight = Math.min(sourceHeight, initialHeight + srcY);\n      dstHeight = srcHeight;\n    } else if (srcY <= sourceHeight) {\n      dstY = 0;\n      srcHeight = Math.min(initialHeight, sourceHeight - srcY);\n      dstHeight = srcHeight;\n    }\n\n    // All the numerical parameters should be integer for `drawImage`\n    // https://github.com/fengyuanchen/cropper/issues/476\n    var params = [srcX, srcY, srcWidth, srcHeight];\n\n    // Avoid \"IndexSizeError\"\n    if (dstWidth > 0 && dstHeight > 0) {\n      var scale = width / initialWidth;\n\n      params.push(dstX * scale, dstY * scale, dstWidth * scale, dstHeight * scale);\n    }\n\n    context.drawImage.apply(context, [source].concat(toConsumableArray($.map(params, function (param) {\n      return Math.floor(normalizeDecimalNumber(param));\n    }))));\n    return canvas;\n  },\n\n\n  /**\n   * Change the aspect ratio of the crop box.\n   * @param {number} aspectRatio - The new aspect ratio.\n   */\n  setAspectRatio: function setAspectRatio(aspectRatio) {\n    var options = this.options;\n\n\n    if (!this.disabled && !isUndefined(aspectRatio)) {\n      // 0 -> NaN\n      options.aspectRatio = Math.max(0, aspectRatio) || NaN;\n\n      if (this.ready) {\n        this.initCropBox();\n\n        if (this.cropped) {\n          this.renderCropBox();\n        }\n      }\n    }\n  },\n\n\n  /**\n   * Change the drag mode.\n   * @param {string} mode - The new drag mode.\n   */\n  setDragMode: function setDragMode(mode) {\n    var options = this.options;\n\n    var croppable = void 0;\n    var movable = void 0;\n\n    if (this.loaded && !this.disabled) {\n      croppable = mode === DRAG_MODE_CROP;\n      movable = options.movable && mode === DRAG_MODE_MOVE;\n      mode = croppable || movable ? mode : DRAG_MODE_NONE;\n\n      this.$dragBox.data(DATA_ACTION, mode).toggleClass(CLASS_CROP, croppable).toggleClass(CLASS_MOVE, movable);\n\n      if (!options.cropBoxMovable) {\n        // Sync drag mode to crop box when it is not movable(#300)\n        this.$face.data(DATA_ACTION, mode).toggleClass(CLASS_CROP, croppable).toggleClass(CLASS_MOVE, movable);\n      }\n    }\n  }\n};\n\nvar Cropper = function () {\n  /**\n   * Create a new Cropper.\n   * @param {Element} element - The target element for cropping.\n   * @param {Object} [options={}] - The configuration options.\n   */\n  function Cropper(element) {\n    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n    classCallCheck(this, Cropper);\n\n    if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {\n      throw new Error('The first argument is required and must be an <img> or <canvas> element.');\n    }\n\n    this.element = element;\n    this.$element = $(element);\n    this.options = $.extend({}, DEFAULTS, $.isPlainObject(options) && options);\n    this.completed = false;\n    this.cropped = false;\n    this.disabled = false;\n    this.isImg = false;\n    this.limited = false;\n    this.loaded = false;\n    this.ready = false;\n    this.replaced = false;\n    this.wheeling = false;\n    this.originalUrl = '';\n    this.canvas = null;\n    this.cropBox = null;\n    this.pointers = {};\n    this.init();\n  }\n\n  createClass(Cropper, [{\n    key: 'init',\n    value: function init() {\n      var $element = this.$element;\n\n      var url = void 0;\n\n      if ($element.is('img')) {\n        this.isImg = true;\n\n        // Should use `$.fn.attr` here. e.g.: \"img/picture.jpg\"\n        url = $element.attr('src') || '';\n        this.originalUrl = url;\n\n        // Stop when it's a blank image\n        if (!url) {\n          return;\n        }\n\n        // Should use `$.fn.prop` here. e.g.: \"https://example.com/img/picture.jpg\"\n        url = $element.prop('src');\n      } else if ($element.is('canvas') && window.HTMLCanvasElement) {\n        url = $element[0].toDataURL();\n      }\n\n      this.load(url);\n    }\n\n    // A shortcut for triggering custom events\n\n  }, {\n    key: 'trigger',\n    value: function trigger(type, data) {\n      var e = $.Event(type, data);\n\n      this.$element.trigger(e);\n\n      return e;\n    }\n  }, {\n    key: 'load',\n    value: function load(url) {\n      var _this = this;\n\n      if (!url) {\n        return;\n      }\n\n      this.url = url;\n      this.image = {};\n\n      var $element = this.$element,\n          options = this.options;\n\n\n      if (!options.checkOrientation || !window.ArrayBuffer) {\n        this.clone();\n        return;\n      }\n\n      // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari\n      if (REGEXP_DATA_URL.test(url)) {\n        if (REGEXP_DATA_URL_JPEG.test(url)) {\n          this.read(dataURLToArrayBuffer(url));\n        } else {\n          this.clone();\n        }\n\n        return;\n      }\n\n      var xhr = new XMLHttpRequest();\n\n      xhr.onerror = function () {\n        _this.clone();\n      };\n\n      xhr.onload = function () {\n        _this.read(xhr.response);\n      };\n\n      // Bust cache when there is a \"crossOrigin\" property\n      if (options.checkCrossOrigin && isCrossOriginURL(url) && !$element.prop('crossOrigin')) {\n        url = addTimestamp(url);\n      }\n\n      xhr.open('get', url);\n      xhr.responseType = 'arraybuffer';\n      xhr.withCredentials = $element.prop('crossOrigin') === 'use-credentials';\n      xhr.send();\n    }\n  }, {\n    key: 'read',\n    value: function read(arrayBuffer) {\n      var options = this.options,\n          image = this.image;\n\n      var orientation = getOrientation(arrayBuffer);\n      var rotate = 0;\n      var scaleX = 1;\n      var scaleY = 1;\n\n      if (orientation > 1) {\n        this.url = arrayBufferToDataURL(arrayBuffer, 'image/jpeg');\n\n        var _parseOrientation = parseOrientation(orientation);\n\n        rotate = _parseOrientation.rotate;\n        scaleX = _parseOrientation.scaleX;\n        scaleY = _parseOrientation.scaleY;\n      }\n\n      if (options.rotatable) {\n        image.rotate = rotate;\n      }\n\n      if (options.scalable) {\n        image.scaleX = scaleX;\n        image.scaleY = scaleY;\n      }\n\n      this.clone();\n    }\n  }, {\n    key: 'clone',\n    value: function clone() {\n      var $element = this.$element,\n          options = this.options,\n          url = this.url;\n\n      var crossOrigin = '';\n      var crossOriginUrl = void 0;\n\n      if (options.checkCrossOrigin && isCrossOriginURL(url)) {\n        crossOrigin = $element.prop('crossOrigin');\n\n        if (crossOrigin) {\n          crossOriginUrl = url;\n        } else {\n          crossOrigin = 'anonymous';\n\n          // Bust cache (#148) when there is not a \"crossOrigin\" property\n          crossOriginUrl = addTimestamp(url);\n        }\n      }\n\n      this.crossOrigin = crossOrigin;\n      this.crossOriginUrl = crossOriginUrl;\n\n      var image = document.createElement('img');\n\n      if (crossOrigin) {\n        image.crossOrigin = crossOrigin;\n      }\n\n      image.src = crossOriginUrl || url;\n\n      var $clone = $(image);\n\n      this.$clone = $clone;\n\n      if (this.isImg) {\n        if (this.element.complete) {\n          this.start();\n        } else {\n          $element.one(EVENT_LOAD, $.proxy(this.start, this));\n        }\n      } else {\n        $clone.one(EVENT_LOAD, $.proxy(this.start, this)).one(EVENT_ERROR, $.proxy(this.stop, this)).addClass(CLASS_HIDE).insertAfter($element);\n      }\n    }\n  }, {\n    key: 'start',\n    value: function start() {\n      var _this2 = this;\n\n      var $clone = this.$clone;\n\n      var $image = this.$element;\n\n      if (!this.isImg) {\n        $clone.off(EVENT_ERROR, this.stop);\n        $image = $clone;\n      }\n\n      getImageNaturalSizes($image[0], function (naturalWidth, naturalHeight) {\n        $.extend(_this2.image, {\n          naturalWidth: naturalWidth,\n          naturalHeight: naturalHeight,\n          aspectRatio: naturalWidth / naturalHeight\n        });\n\n        _this2.loaded = true;\n        _this2.build();\n      });\n    }\n  }, {\n    key: 'stop',\n    value: function stop() {\n      this.$clone.remove();\n      this.$clone = null;\n    }\n  }, {\n    key: 'build',\n    value: function build() {\n      var _this3 = this;\n\n      if (!this.loaded) {\n        return;\n      }\n\n      // Unbuild first when replace\n      if (this.ready) {\n        this.unbuild();\n      }\n\n      var $element = this.$element,\n          options = this.options,\n          $clone = this.$clone;\n\n      var $cropper = $(TEMPLATE);\n      var $cropBox = $cropper.find('.' + NAMESPACE + '-crop-box');\n      var $face = $cropBox.find('.' + NAMESPACE + '-face');\n\n      // Create cropper elements\n      this.$container = $element.parent();\n      this.$cropper = $cropper;\n      this.$canvas = $cropper.find('.' + NAMESPACE + '-canvas').append($clone);\n      this.$dragBox = $cropper.find('.' + NAMESPACE + '-drag-box');\n      this.$cropBox = $cropBox;\n      this.$viewBox = $cropper.find('.' + NAMESPACE + '-view-box');\n      this.$face = $face;\n\n      // Hide the original image\n      $element.addClass(CLASS_HIDDEN).after($cropper);\n\n      // Show the clone image if is hidden\n      if (!this.isImg) {\n        $clone.removeClass(CLASS_HIDE);\n      }\n\n      this.initPreview();\n      this.bind();\n\n      options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;\n      options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;\n\n      this.cropped = options.autoCrop;\n\n      if (options.autoCrop) {\n        if (options.modal) {\n          this.$dragBox.addClass(CLASS_MODAL);\n        }\n      } else {\n        $cropBox.addClass(CLASS_HIDDEN);\n      }\n\n      if (!options.guides) {\n        $cropBox.find('.' + NAMESPACE + '-dashed').addClass(CLASS_HIDDEN);\n      }\n\n      if (!options.center) {\n        $cropBox.find('.' + NAMESPACE + '-center').addClass(CLASS_HIDDEN);\n      }\n\n      if (options.cropBoxMovable) {\n        $face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL);\n      }\n\n      if (!options.highlight) {\n        $face.addClass(CLASS_INVISIBLE);\n      }\n\n      if (options.background) {\n        $cropper.addClass(NAMESPACE + '-bg');\n      }\n\n      if (!options.cropBoxResizable) {\n        $cropBox.find('.' + NAMESPACE + '-line,.' + NAMESPACE + '-point').addClass(CLASS_HIDDEN);\n      }\n\n      this.setDragMode(options.dragMode);\n      this.render();\n      this.ready = true;\n      this.setData(options.data);\n\n      // Trigger the ready event asynchronously to keep `data('cropper')` is defined\n      this.completing = setTimeout(function () {\n        if ($.isFunction(options.ready)) {\n          $element.one(EVENT_READY, options.ready);\n        }\n\n        _this3.trigger(EVENT_READY);\n        _this3.trigger(EVENT_CROP, _this3.getData());\n        _this3.completed = true;\n      }, 0);\n    }\n  }, {\n    key: 'unbuild',\n    value: function unbuild() {\n      if (!this.ready) {\n        return;\n      }\n\n      if (!this.completed) {\n        clearTimeout(this.completing);\n      }\n\n      this.ready = false;\n      this.completed = false;\n      this.initialImage = null;\n\n      // Clear `initialCanvas` is necessary when replace\n      this.initialCanvas = null;\n      this.initialCropBox = null;\n      this.container = null;\n      this.canvas = null;\n\n      // Clear `cropBox` is necessary when replace\n      this.cropBox = null;\n      this.unbind();\n\n      this.resetPreview();\n      this.$preview = null;\n\n      this.$viewBox = null;\n      this.$cropBox = null;\n      this.$dragBox = null;\n      this.$canvas = null;\n      this.$container = null;\n\n      this.$cropper.remove();\n      this.$cropper = null;\n    }\n\n    /**\n     * Change the default options.\n     * @param {Object} options - The new default options.\n     */\n\n  }], [{\n    key: 'setDefaults',\n    value: function setDefaults(options) {\n      $.extend(DEFAULTS, $.isPlainObject(options) && options);\n    }\n  }]);\n  return Cropper;\n}();\n\nif ($.extend) {\n  $.extend(Cropper.prototype, render, preview, events, handlers, change, methods);\n}\n\nif ($.fn) {\n  var AnotherCropper = $.fn.cropper;\n\n  $.fn.cropper = function jQueryCropper(option) {\n    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n      args[_key - 1] = arguments[_key];\n    }\n\n    var result = void 0;\n\n    this.each(function (i, element) {\n      var $element = $(element);\n      var data = $element.data(NAMESPACE);\n\n      if (!data) {\n        if (/destroy/.test(option)) {\n          return;\n        }\n\n        var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);\n\n        data = new Cropper(element, options);\n        $element.data(NAMESPACE, data);\n      }\n\n      if (isString(option)) {\n        var fn = data[option];\n\n        if ($.isFunction(fn)) {\n          result = fn.apply(data, args);\n        }\n      }\n    });\n\n    return isUndefined(result) ? this : result;\n  };\n\n  $.fn.cropper.Constructor = Cropper;\n  $.fn.cropper.setDefaults = Cropper.setDefaults;\n  $.fn.cropper.noConflict = function noConflict() {\n    $.fn.cropper = AnotherCropper;\n    return this;\n  };\n}\n\n})));\n"
  },
  {
    "path": "docs/v3.1.6/js/main.js",
    "content": "$(function () {\n\n  'use strict';\n\n  var console = window.console || { log: function () {} };\n  var URL = window.URL || window.webkitURL;\n  var $image = $('#image');\n  var $download = $('#download');\n  var $dataX = $('#dataX');\n  var $dataY = $('#dataY');\n  var $dataHeight = $('#dataHeight');\n  var $dataWidth = $('#dataWidth');\n  var $dataRotate = $('#dataRotate');\n  var $dataScaleX = $('#dataScaleX');\n  var $dataScaleY = $('#dataScaleY');\n  var options = {\n        aspectRatio: 16 / 9,\n        preview: '.img-preview',\n        crop: function (e) {\n          $dataX.val(Math.round(e.x));\n          $dataY.val(Math.round(e.y));\n          $dataHeight.val(Math.round(e.height));\n          $dataWidth.val(Math.round(e.width));\n          $dataRotate.val(e.rotate);\n          $dataScaleX.val(e.scaleX);\n          $dataScaleY.val(e.scaleY);\n        }\n      };\n  var originalImageURL = $image.attr('src');\n  var uploadedImageName = 'cropped.jpg';\n  var uploadedImageType = 'image/jpeg';\n  var uploadedImageURL;\n\n\n  // Tooltip\n  $('[data-toggle=\"tooltip\"]').tooltip();\n\n\n  // Cropper\n  $image.on({\n    ready: function (e) {\n      console.log(e.type);\n    },\n    cropstart: function (e) {\n      console.log(e.type, e.action);\n    },\n    cropmove: function (e) {\n      console.log(e.type, e.action);\n    },\n    cropend: function (e) {\n      console.log(e.type, e.action);\n    },\n    crop: function (e) {\n      console.log(e.type, e.x, e.y, e.width, e.height, e.rotate, e.scaleX, e.scaleY);\n    },\n    zoom: function (e) {\n      console.log(e.type, e.ratio);\n    }\n  }).cropper(options);\n\n\n  // Buttons\n  if (!$.isFunction(document.createElement('canvas').getContext)) {\n    $('button[data-method=\"getCroppedCanvas\"]').prop('disabled', true);\n  }\n\n  if (typeof document.createElement('cropper').style.transition === 'undefined') {\n    $('button[data-method=\"rotate\"]').prop('disabled', true);\n    $('button[data-method=\"scale\"]').prop('disabled', true);\n  }\n\n\n  // Download\n  if (typeof $download[0].download === 'undefined') {\n    $download.addClass('disabled');\n  }\n\n\n  // Options\n  $('.docs-toggles').on('change', 'input', function () {\n    var $this = $(this);\n    var name = $this.attr('name');\n    var type = $this.prop('type');\n    var cropBoxData;\n    var canvasData;\n\n    if (!$image.data('cropper')) {\n      return;\n    }\n\n    if (type === 'checkbox') {\n      options[name] = $this.prop('checked');\n      cropBoxData = $image.cropper('getCropBoxData');\n      canvasData = $image.cropper('getCanvasData');\n\n      options.ready = function () {\n        $image.cropper('setCropBoxData', cropBoxData);\n        $image.cropper('setCanvasData', canvasData);\n      };\n    } else if (type === 'radio') {\n      options[name] = $this.val();\n    }\n\n    $image.cropper('destroy').cropper(options);\n  });\n\n\n  // Methods\n  $('.docs-buttons').on('click', '[data-method]', function () {\n    var $this = $(this);\n    var data = $this.data();\n    var cropper = $image.data('cropper');\n    var cropped;\n    var $target;\n    var result;\n\n    if ($this.prop('disabled') || $this.hasClass('disabled')) {\n      return;\n    }\n\n    if (cropper && data.method) {\n      data = $.extend({}, data); // Clone a new one\n\n      if (typeof data.target !== 'undefined') {\n        $target = $(data.target);\n\n        if (typeof data.option === 'undefined') {\n          try {\n            data.option = JSON.parse($target.val());\n          } catch (e) {\n            console.log(e.message);\n          }\n        }\n      }\n\n      cropped = cropper.cropped;\n\n      switch (data.method) {\n        case 'rotate':\n          if (cropped && options.viewMode > 0) {\n            $image.cropper('clear');\n          }\n\n          break;\n\n        case 'getCroppedCanvas':\n          if (uploadedImageType === 'image/jpeg') {\n            if (!data.option) {\n              data.option = {};\n            }\n\n            data.option.fillColor = '#fff';\n          }\n\n          break;\n      }\n\n      result = $image.cropper(data.method, data.option, data.secondOption);\n\n      switch (data.method) {\n        case 'rotate':\n          if (cropped && options.viewMode > 0) {\n            $image.cropper('crop');\n          }\n\n          break;\n\n        case 'scaleX':\n        case 'scaleY':\n          $(this).data('option', -data.option);\n          break;\n\n        case 'getCroppedCanvas':\n          if (result) {\n            // Bootstrap's Modal\n            $('#getCroppedCanvasModal').modal().find('.modal-body').html(result);\n\n            if (!$download.hasClass('disabled')) {\n              download.download = uploadedImageName;\n              $download.attr('href', result.toDataURL(uploadedImageType));\n            }\n          }\n\n          break;\n\n        case 'destroy':\n          if (uploadedImageURL) {\n            URL.revokeObjectURL(uploadedImageURL);\n            uploadedImageURL = '';\n            $image.attr('src', originalImageURL);\n          }\n\n          break;\n      }\n\n      if ($.isPlainObject(result) && $target) {\n        try {\n          $target.val(JSON.stringify(result));\n        } catch (e) {\n          console.log(e.message);\n        }\n      }\n\n    }\n  });\n\n\n  // Keyboard\n  $(document.body).on('keydown', function (e) {\n\n    if (!$image.data('cropper') || this.scrollTop > 300) {\n      return;\n    }\n\n    switch (e.which) {\n      case 37:\n        e.preventDefault();\n        $image.cropper('move', -1, 0);\n        break;\n\n      case 38:\n        e.preventDefault();\n        $image.cropper('move', 0, -1);\n        break;\n\n      case 39:\n        e.preventDefault();\n        $image.cropper('move', 1, 0);\n        break;\n\n      case 40:\n        e.preventDefault();\n        $image.cropper('move', 0, 1);\n        break;\n    }\n\n  });\n\n\n  // Import image\n  var $inputImage = $('#inputImage');\n\n  if (URL) {\n    $inputImage.change(function () {\n      var files = this.files;\n      var file;\n\n      if (!$image.data('cropper')) {\n        return;\n      }\n\n      if (files && files.length) {\n        file = files[0];\n\n        if (/^image\\/\\w+$/.test(file.type)) {\n          uploadedImageName = file.name;\n          uploadedImageType = file.type;\n\n          if (uploadedImageURL) {\n            URL.revokeObjectURL(uploadedImageURL);\n          }\n\n          uploadedImageURL = URL.createObjectURL(file);\n          $image.cropper('destroy').attr('src', uploadedImageURL).cropper(options);\n          $inputImage.val('');\n        } else {\n          window.alert('Please choose an image file.');\n        }\n      }\n    });\n  } else {\n    $inputImage.prop('disabled', true).parent().addClass('disabled');\n  }\n\n});\n"
  },
  {
    "path": "karma.conf.js",
    "content": "const babel = require('rollup-plugin-babel');\nconst commonjs = require('rollup-plugin-commonjs');\nconst nodeResolve = require('rollup-plugin-node-resolve');\nconst puppeteer = require('puppeteer');\n\nprocess.env.CHROME_BIN = puppeteer.executablePath();\nprocess.env.NODE_ENV = 'test';\n\nmodule.exports = (config) => {\n  config.set({\n    autoWatch: false,\n    browsers: ['ChromeHeadless'],\n    coverageIstanbulReporter: {\n      reports: ['html', 'lcovonly', 'text-summary'],\n    },\n    files: [\n      'dist/cropper.css',\n      'test/index.js',\n      {\n        pattern: 'docs/images/*',\n        included: false,\n      },\n    ],\n    frameworks: ['mocha', 'chai'],\n    preprocessors: {\n      'test/index.js': ['rollup'],\n    },\n    reporters: ['mocha', 'coverage-istanbul'],\n    rollupPreprocessor: {\n      output: {\n        format: 'iife',\n        name: 'Anonymous',\n        sourcemap: 'inline',\n      },\n      plugins: [\n        nodeResolve(),\n        commonjs(),\n        babel(),\n      ],\n    },\n    singleRun: true,\n  });\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"cropper\",\n  \"description\": \"A simple jQuery image cropping plugin.\",\n  \"version\": \"4.1.0\",\n  \"main\": \"dist/cropper.common.js\",\n  \"module\": \"dist/cropper.esm.js\",\n  \"browser\": \"dist/cropper.js\",\n  \"style\": \"dist/cropper.css\",\n  \"files\": [\n    \"src\",\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"build\": \"npm run build:css && npm run build:js\",\n    \"build:css\": \"postcss src/index.css -o dist/cropper.css --no-map\",\n    \"build:js\": \"rollup -c\",\n    \"clear\": \"del-cli dist\",\n    \"compress\": \"npm run compress:css && npm run compress:js\",\n    \"compress:css\": \"postcss dist/cropper.css -u cssnano -o dist/cropper.min.css --no-map\",\n    \"compress:js\": \"uglifyjs dist/cropper.js -o dist/cropper.min.js -c -m --comments /^!/\",\n    \"copy\": \"cpy dist/cropper.css docs/css\",\n    \"lint\": \"eslint src test *.js --fix\",\n    \"release\": \"npm run clear && npm run lint && npm run build && npm run compress && npm run copy && npm test\",\n    \"start\": \"npm-run-all --parallel watch:*\",\n    \"test\": \"karma start\",\n    \"watch:css\": \"postcss src/index.css -o docs/css/cropper.css -m -w\",\n    \"watch:js\": \"rollup -c -m -w\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/fengyuanchen/cropper.git\"\n  },\n  \"keywords\": [\n    \"image\",\n    \"crop\",\n    \"move\",\n    \"zoom\",\n    \"rotate\",\n    \"scale\",\n    \"cropper\",\n    \"cropper.js\",\n    \"cropping\",\n    \"processing\",\n    \"jquery\",\n    \"plugin\",\n    \"jquery-plugin\",\n    \"html\",\n    \"css\",\n    \"javascript\",\n    \"front-end\",\n    \"web\"\n  ],\n  \"author\": {\n    \"name\": \"Chen Fengyuan\",\n    \"url\": \"https://chenfengyuan.com/\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/fengyuanchen/cropper/issues\"\n  },\n  \"homepage\": \"https://fengyuanchen.github.io/cropper\",\n  \"dependencies\": {\n    \"cropperjs\": \"^1.5.6\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.6.4\",\n    \"@babel/preset-env\": \"^7.6.3\",\n    \"@commitlint/cli\": \"^8.2.0\",\n    \"@commitlint/config-conventional\": \"^8.2.0\",\n    \"babel-plugin-istanbul\": \"^5.2.0\",\n    \"chai\": \"^4.2.0\",\n    \"change-case\": \"^3.1.0\",\n    \"cpy-cli\": \"^2.0.0\",\n    \"create-banner\": \"^1.0.0\",\n    \"cssnano\": \"^4.1.10\",\n    \"del-cli\": \"^3.0.0\",\n    \"eslint\": \"^6.5.1\",\n    \"eslint-config-airbnb-base\": \"^14.0.0\",\n    \"eslint-plugin-import\": \"^2.18.2\",\n    \"husky\": \"^3.0.8\",\n    \"jquery\": \"^3.3.1\",\n    \"karma\": \"^4.3.0\",\n    \"karma-chai\": \"^0.1.0\",\n    \"karma-chrome-launcher\": \"^3.1.0\",\n    \"karma-coverage-istanbul-reporter\": \"^2.1.0\",\n    \"karma-mocha\": \"^1.3.0\",\n    \"karma-mocha-reporter\": \"^2.2.5\",\n    \"karma-rollup-preprocessor\": \"^7.0.2\",\n    \"lint-staged\": \"^8.2.1\",\n    \"mocha\": \"^6.2.1\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"postcss-cli\": \"^6.1.3\",\n    \"postcss-header\": \"^1.0.0\",\n    \"postcss-import\": \"^12.0.1\",\n    \"postcss-preset-env\": \"^6.7.0\",\n    \"postcss-url\": \"^8.0.0\",\n    \"puppeteer\": \"^1.20.0\",\n    \"rollup\": \"^1.23.1\",\n    \"rollup-plugin-babel\": \"^4.3.3\",\n    \"rollup-plugin-commonjs\": \"^10.1.0\",\n    \"rollup-plugin-node-resolve\": \"^5.2.0\",\n    \"stylelint\": \"^11.1.1\",\n    \"stylelint-config-standard\": \"^19.0.0\",\n    \"stylelint-order\": \"^3.1.1\",\n    \"uglify-js\": \"^3.6.1\"\n  },\n  \"peerDependencies\": {\n    \"jquery\": \">=1.9.1\"\n  },\n  \"browserslist\": [\n    \"last 2 versions\",\n    \"> 1%\",\n    \"not ie <= 8\"\n  ],\n  \"commitlint\": {\n    \"extends\": [\n      \"@commitlint/config-conventional\"\n    ]\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"lint-staged\",\n      \"commit-msg\": \"commitlint -E HUSKY_GIT_PARAMS\"\n    }\n  },\n  \"lint-staged\": {\n    \"linters\": {\n      \"*.js\": [\n        \"eslint --fix\",\n        \"git add\"\n      ],\n      \"*.{css,scss,html}\": [\n        \"stylelint --fix\",\n        \"git add\"\n      ]\n    },\n    \"ignore\": [\n      \"{dist,docs}/**/*.js\",\n      \"*.min.*\"\n    ]\n  }\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "const rollupConfig = require('./rollup.config');\n\nmodule.exports = {\n  plugins: {\n    'postcss-import': {},\n    'postcss-preset-env': {\n      stage: 3,\n      features: {\n        'nesting-rules': true,\n      },\n    },\n    'postcss-url': {\n      url: 'inline',\n    },\n    'postcss-header': {\n      header: rollupConfig.output[0].banner,\n    },\n    stylelint: {\n      fix: true,\n    },\n  },\n};\n"
  },
  {
    "path": "rollup.config.js",
    "content": "const babel = require('rollup-plugin-babel');\nconst commonjs = require('rollup-plugin-commonjs');\nconst nodeResolve = require('rollup-plugin-node-resolve');\nconst changeCase = require('change-case');\nconst createBanner = require('create-banner');\nconst pkg = require('./package');\n\nconst name = changeCase.pascalCase(pkg.name);\nconst banner = createBanner({\n  data: {\n    name,\n    year: '2014-present',\n  },\n});\n\nmodule.exports = {\n  input: 'src/index.js',\n  output: [\n    {\n      banner,\n      name,\n      file: `dist/${pkg.name}.js`,\n      format: 'umd',\n      globals: {\n        jquery: 'jQuery',\n      },\n    },\n    {\n      banner,\n      file: `dist/${pkg.name}.common.js`,\n      format: 'cjs',\n    },\n    {\n      banner,\n      file: `dist/${pkg.name}.esm.js`,\n      format: 'es',\n    },\n    {\n      banner,\n      name,\n      file: `docs/js/${pkg.name}.js`,\n      format: 'umd',\n      globals: {\n        jquery: 'jQuery',\n      },\n    },\n  ],\n  external: ['jquery'],\n  plugins: [\n    nodeResolve(),\n    commonjs(),\n    babel(),\n  ],\n};\n"
  },
  {
    "path": "src/index.css",
    "content": "@import 'cropperjs/src';\n"
  },
  {
    "path": "src/index.js",
    "content": "import $ from 'jquery';\nimport Cropper from 'cropperjs/src';\n\nif ($.fn) {\n  const AnotherCropper = $.fn.cropper;\n  const NAMESPACE = 'cropper';\n\n  $.fn.cropper = function jQueryCropper(option, ...args) {\n    let result;\n\n    this.each((i, element) => {\n      const $element = $(element);\n      const isDestroy = option === 'destroy';\n      let cropper = $element.data(NAMESPACE);\n\n      if (!cropper) {\n        if (isDestroy) {\n          return;\n        }\n\n        const options = $.extend({}, $element.data(), $.isPlainObject(option) && option);\n\n        cropper = new Cropper(element, options);\n        $element.data(NAMESPACE, cropper);\n      }\n\n      if (typeof option === 'string') {\n        const fn = cropper[option];\n\n        if ($.isFunction(fn)) {\n          result = fn.apply(cropper, args);\n\n          if (result === cropper) {\n            result = undefined;\n          }\n\n          if (isDestroy) {\n            $element.removeData(NAMESPACE);\n          }\n        }\n      }\n    });\n\n    return result !== undefined ? result : this;\n  };\n\n  $.fn.cropper.Constructor = Cropper;\n  $.fn.cropper.setDefaults = Cropper.setDefaults;\n  $.fn.cropper.noConflict = function noConflict() {\n    $.fn.cropper = AnotherCropper;\n    return this;\n  };\n}\n"
  },
  {
    "path": "test/index.js",
    "content": "import $ from 'jquery';\nimport '../src';\n\ndescribe('cropper', () => {\n  const createImage = () => {\n    const container = document.createElement('div');\n    const image = document.createElement('img');\n\n    image.src = '/base/docs/images/picture.jpg';\n    container.appendChild(image);\n    document.body.appendChild(container);\n\n    return image;\n  };\n\n  it('should register as a plugin correctly', () => {\n    expect($.fn.cropper).to.be.a('function');\n    expect($.fn.cropper.Constructor).to.be.a('function');\n    expect($.fn.cropper.noConflict).to.be.a('function');\n    expect($.fn.cropper.setDefaults).to.be.a('function');\n  });\n\n  it('should not initialize the cropper when call the destroy method first', () => {\n    const $image = $(createImage());\n\n    $image.cropper('destroy');\n    expect($image.data('cropper')).to.be.undefined;\n  });\n\n  it('should remove data after destroyed', () => {\n    const $image = $(createImage());\n\n    $image.cropper();\n    expect($image.data('cropper')).to.be.an.instanceof($.fn.cropper.Constructor);\n    $image.cropper('destroy');\n    expect($image.data('cropper')).to.be.undefined;\n  });\n\n  it('should apply the given option', (done) => {\n    $(createImage()).cropper({\n      aspectRatio: 1,\n\n      ready() {\n        const $this = $(this);\n        const cropper = $this.data('cropper');\n        const cropBoxData = $this.cropper('getCropBoxData');\n\n        expect(cropper.options.aspectRatio).to.equal(1);\n        expect(cropBoxData.width).to.equal(cropBoxData.height);\n        done();\n      },\n    });\n  });\n\n  it('should execute the given method', (done) => {\n    $(createImage()).cropper({\n      ready() {\n        const $this = $(this);\n        const cropper = $this.data('cropper');\n\n        expect(cropper.cropped).to.be.true;\n        $this.cropper('clear');\n        expect(cropper.cropped).to.be.false;\n        done();\n      },\n    });\n  });\n\n  it('should trigger the binding event', (done) => {\n    $(createImage()).one('ready', (event) => {\n      expect(event.type).to.equal('ready');\n      done();\n    }).cropper();\n  });\n\n  it('should rollback when call the $.fn.cropper.conflict', () => {\n    const { cropper } = $.fn;\n    const noConflictCropper = $.fn.cropper.noConflict();\n\n    expect(noConflictCropper).to.equal(cropper);\n    expect($.fn.cropper).to.be.undefined;\n\n    // Reverts it for the rest test suites\n    $.fn.cropper = noConflictCropper;\n  });\n});\n"
  }
]