diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..74fb9e0b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf diff --git a/.gitignore b/.gitignore index 4fc0efa3..1b2c6d29 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ npm-debug.log node_modules/ bower_components -/nbproject/private/ \ No newline at end of file +/nbproject/private/ +.vscode/ +dist/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..22d2f029 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "endOfLine": "lf", + "printWidth": 80, + "semi": true, + "singleQuote": true, + "trailingComma": "none", + "tabWidth": 2, + "useTabs": false +} diff --git a/build.ts b/build.ts new file mode 100644 index 00000000..7caa4bf8 --- /dev/null +++ b/build.ts @@ -0,0 +1,76 @@ +import { rollup } from 'rollup'; +const { terser } = require('rollup-plugin-terser'); +const typescript = require('rollup-plugin-typescript'); + +const build = async () => { + const buildEsm2015 = async () => { + const esm2015Bundle = rollup({ + input: 'src/index.ts', + plugins: [typescript()] + }); + return (await esm2015Bundle).write({ + file: 'dist/esm2015/croppie.js', + format: 'esm', + sourcemap: true + }); + }; + + const buildEsm5 = async () => { + const esm5Bundle = rollup({ + input: 'src/index.ts', + plugins: [ + typescript({ + target: 'es5', + declaration: false + }) + ] + }); + return (await esm5Bundle).write({ + file: 'dist/esm5/croppie.js', + format: 'esm', + sourcemap: true + }); + }; + + const buildUmd = async () => { + const umdBundle = rollup({ + input: 'src/index.ts', + plugins: [ + typescript({ + target: 'es5' + }) + ] + }); + return (await umdBundle).write({ + file: 'dist/bundles/croppie.js', + format: 'umd', + sourcemap: true, + name: 'Croppie' + }); + }; + + const buildUmdMinified = async () => { + const umdMinifiedBundle = rollup({ + input: 'src/index.ts', + plugins: [ + typescript({ + target: 'es5' + }), + terser() + ] + }); + return (await umdMinifiedBundle).write({ + file: 'dist/bundles/croppie.min.js', + format: 'umd', + sourcemap: true, + name: 'Croppie' + }); + }; + + await buildEsm2015(); + await buildEsm5(); + await buildUmd(); + await buildUmdMinified(); +}; + +build().then((_) => console.log('Done')); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..03ac88c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1319 @@ +{ + "name": "croppie", + "version": "2.6.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "10.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.12.tgz", + "integrity": "sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "collections": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/collections/-/collections-0.2.2.tgz", + "integrity": "sha1-HyMCay7zb5J+7MkB6ZxfDUj6M04=", + "dev": true, + "requires": { + "weak-map": "1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estree-walker": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz", + "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "http://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "fs-extra": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", + "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "gh-pages": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-0.11.0.tgz", + "integrity": "sha1-kzE8bcv8dNQmvIminr/2QgrMPBs=", + "dev": true, + "requires": { + "async": "1.5.2", + "commander": "2.9.0", + "globby": "^4.0.0", + "graceful-fs": "4.1.2", + "q": "1.4.1", + "q-io": "1.13.2", + "wrench": "1.5.8" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "globby": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^6.0.1", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.2.tgz", + "integrity": "sha1-/iI5t1dJcuZ+QfgIgj+b+kqZHjc=", + "dev": true + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", + "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "dev": true, + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "dev": true + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "dev": true + } + } + }, + "jest-worker": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz", + "integrity": "sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=", + "dev": true, + "requires": { + "merge-stream": "^1.0.1" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true, + "optional": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", + "dev": true + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mimeparse": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/mimeparse/-/mimeparse-0.1.4.tgz", + "integrity": "sha1-2vsCdSNw/SJgk64xUsJxrwGsJUo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.4.5.tgz", + "integrity": "sha1-FRdo3Sh161G8gpXpgAAm6fK7OY8=", + "dev": true, + "requires": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.3", + "growl": "1.8.1", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0" + }, + "dependencies": { + "commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", + "dev": true + }, + "glob": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "dev": true, + "requires": { + "graceful-fs": "~2.0.0", + "inherits": "2", + "minimatch": "~0.2.11" + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "q-io": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/q-io/-/q-io-1.13.2.tgz", + "integrity": "sha1-7qEw1IHdteGqG8WmaFX3OR0G8AM=", + "dev": true, + "requires": { + "collections": "^0.2.0", + "mime": "^1.2.11", + "mimeparse": "^0.1.4", + "q": "^1.0.1", + "qs": "^1.2.1", + "url2": "^0.0.0" + } + }, + "qs": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz", + "integrity": "sha1-GbV/8k3CqZzh+L32r82ln472H4g=", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "rollup": { + "version": "0.67.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.67.4.tgz", + "integrity": "sha512-AVuP73mkb4BBMUmksQ3Jw0jTrBTU1i7rLiUYjFxLZGb3xiFmtVEg40oByphkZAsiL0bJC3hRAJUQos/e5EBd+w==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "*" + } + }, + "rollup-plugin-terser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-3.0.0.tgz", + "integrity": "sha512-Ed9zRD7OoCBnh0XGlEAJle5TCUsFXMLClwKzZWnS1zbNO4MelHjfCSdFZxCAdH70M40nhZ1nRrY2GZQJhSMcjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "jest-worker": "^23.2.0", + "serialize-javascript": "^1.5.0", + "terser": "^3.8.2" + } + }, + "rollup-plugin-typescript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript/-/rollup-plugin-typescript-1.0.0.tgz", + "integrity": "sha512-d2KDNMJXgaaB//dDGd/YmyMiopt1Pz965Iu3zmEoL08YqNcKRBz26uHqqc47rFGfrJV5kFqifC9IYlh6dpSCLg==", + "dev": true, + "requires": { + "resolve": "^1.8.1", + "rollup-pluginutils": "^2.3.1" + } + }, + "rollup-plugin-typescript2": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.18.0.tgz", + "integrity": "sha512-AL7LJl31bYO/x8zO1fuE7ACn/2nDs9DVYL3qjiWSYg5LC4EV/iKuCL4Fm6pjzEqCB4fFIMXoUuGUf5R+BLNKSg==", + "dev": true, + "requires": { + "fs-extra": "7.0.0", + "resolve": "1.8.1", + "rollup-pluginutils": "2.3.3", + "tslib": "1.9.3" + } + }, + "rollup-pluginutils": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz", + "integrity": "sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA==", + "dev": true, + "requires": { + "estree-walker": "^0.5.2", + "micromatch": "^2.3.11" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", + "dev": true + }, + "terser": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.11.0.tgz", + "integrity": "sha512-5iLMdhEPIq3zFWskpmbzmKwMQixKmTYwY3Ox9pjtSklBLnHiuQ0GKJLhL1HSYtyffHM3/lDIFBnb82m9D7ewwQ==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.6" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + } + } + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "dependencies": { + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "tslint-config-prettier": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.17.0.tgz", + "integrity": "sha512-NKWNkThwqE4Snn4Cm6SZB7lV5RMDDFsBwz6fWUkTxOKGjMx8ycOHnjIbhn7dZd5XmssW3CwqUjlANR6EhP9YQw==", + "dev": true + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.1.tgz", + "integrity": "sha512-jw7P2z/h6aPT4AENXDGjcfHTu5CSqzsbZc6YlUIebTyBAq8XaKp78x7VcSh30xwSCcsu5irZkYZUSFP1MrAMbg==", + "dev": true + }, + "uglify-js": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.15.tgz", + "integrity": "sha512-bqtBCAINYXX/OkdnqMGpbXr+OPWc00hsozRpk+dAtfnbdk2jjKiLmyOkQ7zamg648lVMnzATL8JrSN6LmaVpYA==", + "dev": true, + "requires": { + "commander": "~2.15.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + } + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "url2": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/url2/-/url2-0.0.0.tgz", + "integrity": "sha1-Tqq9HVw6yQ1iq0SFyZhCKGWgSxo=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "weak-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.0.tgz", + "integrity": "sha1-tm5Wqd8L0lp2u/G1FNsSkIBhSjc=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "wrench": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.8.tgz", + "integrity": "sha1-ejHJf3hpJG12xc8vXJd6HEyOWrU=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 80e19655..bb1754eb 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,25 @@ "devDependencies": { "gh-pages": "^0.11.0", "mocha": "2.4.5", + "rimraf": "^2.6.2", + "rollup": "^0.67.4", + "rollup-plugin-terser": "^3.0.0", + "rollup-plugin-typescript": "^1.0.0", + "rollup-plugin-typescript2": "^0.18.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.0", + "tslint-config-prettier": "^1.17.0", + "typescript": "^3.2.1", "uglify-js": "3.3.15" }, "scripts": { "test": "mocha test/unit", "deploy": "node deploy.js", - "uglify": "uglifyjs croppie.js -c -m -r '$,require,exports' -o croppie.min.js" + "uglify": "uglifyjs croppie.js -c -m -r '$,require,exports' -o croppie.min.js", + "build:code": "ts-node -P ./tsconfig.build.json build.ts", + "build:typings": "tsc --emitDeclarationOnly --outDir ./dist --declaration -p .", + "build": "npm run rimraf && npm run build:code && npm run build:typings", + "rimraf": "rimraf ./dist" }, "repository": { "type": "git", diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..138c86c0 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,1689 @@ +/************************* + * Croppie + * Copyright 2018 + * Foliotek + * Version: 2.6.2 + *************************/ +// Promise polyfill + +// CustomEvent polyfill + +// HTMLCanvasElement.prototype.toBlob polyfill + +declare global { + interface Window { + jQuery: any; + EXIF: any; + } +} + +declare const Prototype: any; +declare const EXIF: any; + +interface CroppieOptions { + viewport: { + width: number; + height: number; + type: string; + }; + boundary: { + width: number; + height: number; + }; + orientationControls: { + enabled: boolean; + leftClass: string; + rightClass: string; + }; + resizeControls: { + width: boolean; + height: boolean; + }; + customClass: string; + showZoomer: boolean; + enableZoom: boolean; + enableResize: boolean; + mouseWheelZoom: boolean | string; + enableExif: boolean; + enforceBoundary: boolean; + enableOrientation: boolean; + enableKeyMovement: boolean; + update: ( + data: { points: number[]; zoom: number; orientation: number } + ) => void; + url: string; + points: number[]; + relative: boolean; + useCanvas: boolean; + maxZoom: number; +} + +interface CroppieBindOptions { + url?: string; + points?: number[]; + zoom?: number; + orientation?: number; +} + +interface CroppieResultData { + type?: 'canvas' | 'base64' | 'html' | 'blob' | 'rawcanvas'; + size?: + | 'viewport' + | 'original' + | { + width: number; + height: number; + }; + format?: string; + quality?: number; + circle?: boolean; + points: number[]; + zoom: number; + orientation: number; + outputWidth?: number; + outputHeight?: number; + url?: string; + backgroundColor?: string | CanvasGradient | CanvasPattern; +} + +const cssPrefixes: string[] = ['Webkit', 'Moz', 'ms'], + emptyStyles = document.createElement('div').style, + EXIF_NORM = [1, 8, 3, 6], + EXIF_FLIP = [2, 7, 4, 5]; +let CSS_TRANS_ORG: string, CSS_TRANSFORM: string, CSS_USERSELECT: string; + +const vendorPrefix = (prop: string) => { + if (prop in emptyStyles) { + return prop; + } + + const capProp = prop[0].toUpperCase() + prop.slice(1); + let i = cssPrefixes.length; + + while (i--) { + prop = cssPrefixes[i] + capProp; + if (prop in emptyStyles) { + return prop; + } + } +}; + +CSS_TRANSFORM = vendorPrefix('transform'); +CSS_TRANS_ORG = vendorPrefix('transformOrigin'); +CSS_USERSELECT = vendorPrefix('userSelect'); + +const getExifOffset = ( + ornt: number, + rotate: 90 | 180 | 270 | -90 | -180 | -270 +) => { + const arr = EXIF_NORM.indexOf(ornt) > -1 ? EXIF_NORM : EXIF_FLIP, + index = arr.indexOf(ornt), + offset = (rotate / 90) % arr.length; // 180 = 2%4 = 2 shift exif by 2 indexes + + return arr[(arr.length + index + (offset % arr.length)) % arr.length]; +}; + +// Credits to : Andrew Dupont - http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/ +const deepExtend = (destination: any, source: any) => { + destination = destination || {}; + for (const property in source) { + if ( + source[property] && + source[property].constructor && + source[property].constructor === Object + ) { + destination[property] = destination[property] || {}; + deepExtend(destination[property], source[property]); + } else { + destination[property] = source[property]; + } + } + return destination; +}; + +const clone = (object: any) => { + return deepExtend({}, object); +}; + +//http://jsperf.com/vanilla-css +const css = (el: HTMLElement, styles: any | string, val?: string) => { + if (typeof styles === 'string') { + const tmp: string = styles; + styles = {}; + styles[tmp] = val; + } + + for (const prop in styles) { + (el.style as any)[prop] = styles[prop]; + } +}; + +const addClass = (el: HTMLElement, c: string) => { + if (el.classList) { + el.classList.add(c); + } else { + el.className += ' ' + c; + } +}; + +const removeClass = (el: HTMLElement, c: string) => { + if (el.classList) { + el.classList.remove(c); + } else { + el.className = el.className.replace(c, ''); + } +}; + +const setAttributes = (el: HTMLElement, attrs: object) => { + for (const key in attrs) { + el.setAttribute(key, (attrs as any)[key]); + } +}; + +const num = (v: string) => { + return parseInt(v, 10); +}; + +/* Utilities */ +const loadImage = (src: string, doExif: boolean) => { + const img = new Image(); + img.style.opacity = '0'; + return new Promise( + (resolve, reject) => { + const _resolve = () => { + img.style.opacity = '1'; + setTimeout(() => { + resolve(img as HTMLImageElement & { + exifdata: { Orientation: string }; + }); + }, 1); + }; + + img.removeAttribute('crossOrigin'); + if (src.match(/^https?:\/\/|^\/\//)) { + img.setAttribute('crossOrigin', 'anonymous'); + } + + img.onload = () => { + if (doExif) { + EXIF.getData(img, () => { + _resolve(); + }); + } else { + _resolve(); + } + }; + img.onerror = (ev) => { + img.style.opacity = '1'; + setTimeout(() => { + reject(ev); + }, 1); + }; + img.src = src; + } + ); +}; + +const naturalImageDimensions = ( + img: HTMLImageElement & { exifdata: { Orientation: string } }, + ornt?: number +) => { + let w = img.naturalWidth; + let h = img.naturalHeight; + const orient = ornt || getExifOrientation(img); + if (orient && orient >= 5) { + const x = w; + w = h; + h = x; + } + return { width: w, height: h }; +}; + +class Transform { + constructor(public x: number, public y: number, public scale: number) {} + toString() { + return `translate3d(${this.x}px, ${this.y}px, 0px) scale(${this.scale})`; + } + static parse(v: HTMLElement | string): Transform { + if ((v as HTMLElement).style) { + return Transform.parse(((v as HTMLElement).style as any)[CSS_TRANSFORM]); + } else if ( + (v as string).indexOf('matrix') > -1 || + (v as string).indexOf('none') > -1 + ) { + return Transform.fromMatrix(v as string); + } else { + return Transform.fromString(v as string); + } + } + static fromMatrix(v: string) { + let vals = v.substring(7).split(','); + if (!vals.length || v === 'none') { + return new Transform(0, 0, 1); + } + return new Transform(num(vals[4]), num(vals[5]), parseFloat(vals[0])); + } + static fromString(v: string) { + const values = v.split(') '), + translate = values[0].substring(12).split(','), + scale = values.length > 1 ? parseFloat(values[1].substring(6)) : 1, + x = translate.length > 1 ? parseFloat(translate[0]) : 0, + y = translate.length > 1 ? parseFloat(translate[1]) : 0; + return new Transform(x, y, scale); + } +} + +class TransformOrigin { + x: number; + y: number; + constructor(el?: HTMLElement) { + if (!el || !(el.style as any)[CSS_TRANS_ORG]) { + this.x = 0; + this.y = 0; + return; + } + const css = (el.style as any)[CSS_TRANS_ORG].split(' '); + this.x = parseFloat(css[0]); + this.y = parseFloat(css[1]); + } + toString() { + return `${this.x}px ${this.y}px`; + } +} + +const getExifOrientation = ( + img: HTMLImageElement & { exifdata: { Orientation: string } } +) => { + return img.exifdata && img.exifdata.Orientation + ? num(img.exifdata.Orientation) + : 1; +}; + +const drawCanvas = ( + canvas: HTMLCanvasElement, + img: HTMLImageElement, + orientation: number +) => { + const width = img.width, + height = img.height, + ctx = canvas.getContext('2d'); + + canvas.width = img.width; + canvas.height = img.height; + + ctx.save(); + switch (orientation) { + case 2: + ctx.translate(width, 0); + ctx.scale(-1, 1); + break; + + case 3: + ctx.translate(width, height); + ctx.rotate((180 * Math.PI) / 180); + break; + + case 4: + ctx.translate(0, height); + ctx.scale(1, -1); + break; + + case 5: + canvas.width = height; + canvas.height = width; + ctx.rotate((90 * Math.PI) / 180); + ctx.scale(1, -1); + break; + + case 6: + canvas.width = height; + canvas.height = width; + ctx.rotate((90 * Math.PI) / 180); + ctx.translate(0, -height); + break; + + case 7: + canvas.width = height; + canvas.height = width; + ctx.rotate((-90 * Math.PI) / 180); + ctx.translate(-width, height); + ctx.scale(1, -1); + break; + + case 8: + canvas.width = height; + canvas.height = width; + ctx.translate(0, width); + ctx.rotate((-90 * Math.PI) / 180); + break; + } + ctx.drawImage(img, 0, 0, width, height); + ctx.restore(); +}; + +const fix = (v: number, decimalPoints?: number) => { + return parseFloat(v.toFixed(decimalPoints || 0)); // TODO: de-wrapped? +}; + +//#region jQuery +if (window.jQuery) { + const $ = window.jQuery; + $.fn.croppie = function(...allOpts: any[]) { + const opts = allOpts[0]; + const ot = typeof opts; + + if (ot === 'string') { + const args = allOpts.slice(1); + const singleInst = $(this).data('croppie'); + + if (opts === 'get') { + return singleInst.get(); + } else if (opts === 'result') { + return singleInst.result.apply(singleInst, args); + } else if (opts === 'bind') { + return singleInst.bind.apply(singleInst, args); + } + + return this.each(function() { + const i = $(this).data('croppie'); + if (!i) return; + + const method = i[opts]; + if ($.isFunction(method)) { + method.apply(i, args); + if (opts === 'destroy') { + $(this).removeData('croppie'); + } + } else { + throw 'Croppie ' + opts + ' method not found'; + } + }); + } else { + return this.each(function() { + const i = new Croppie(this, opts); + i.$ = $; + $(this).data('croppie', i); + }); + } + }; +} +//#endregion +class Croppie { + elements: { + viewport?: HTMLDivElement; + img?: HTMLImageElement & { exifdata: { Orientation: string } }; + zoomer?: HTMLInputElement; + canvas?: HTMLCanvasElement; + boundary?: HTMLDivElement; + zoomerWrap?: HTMLDivElement; + overlay?: HTMLDivElement; + preview?: HTMLImageElement | HTMLCanvasElement; + } = {}; + data: { + bound?: boolean; + url?: string; + boundZoom?: number; + points?: number[]; + orientation?: number; + } = {}; + _currentZoom: number; + _debouncedOverlay = this._getDebouncedOverlay(); + _originalImageWidth: number; + _originalImageHeight: number; + $: any; + element: HTMLElement; + options: CroppieOptions; + + constructor(element: HTMLElement, opts: Partial) { + if (element.className.indexOf('croppie-container') > -1) { + throw new Error("Croppie: Can't initialize croppie more than once"); + } + this.element = element; + this.options = deepExtend(clone(Croppie.defaults), opts); + if (this.element.tagName.toLowerCase() === 'img') { + const origImage = this.element; + addClass(origImage, 'cr-original-image'); + setAttributes(origImage, { 'aria-hidden': 'true', alt: '' }); + const replacementDiv = document.createElement('div'); + this.element.parentNode.appendChild(replacementDiv); + replacementDiv.appendChild(origImage); + this.element = replacementDiv; + this.options.url = + this.options.url || (origImage as HTMLImageElement).src; + } + this._create(); + if (this.options.url) { + const bindOpts = { + url: this.options.url, + points: this.options.points + }; + delete this.options['url']; + delete this.options['points']; + this.bind(bindOpts); + } + } + static defaults: Partial = { + viewport: { + width: 100, + height: 100, + type: 'square' + }, + boundary: {} as any, + orientationControls: { + enabled: true, + leftClass: '', + rightClass: '' + }, + resizeControls: { + width: true, + height: true + }, + customClass: '', + showZoomer: true, + enableZoom: true, + enableResize: false, + mouseWheelZoom: true, + enableExif: false, + enforceBoundary: true, + enableOrientation: false, + enableKeyMovement: true, + update: function() {} + }; + + static RESULT_DEFAULTS = { + type: 'canvas', + format: 'png', + quality: 1 + }; + + static RESULT_FORMATS = ['jpeg', 'webp', 'png']; + + async bind( + options: string | CroppieBindOptions | number[], + cb?: () => void + ): Promise { + const hasExif = this._hasExif(); + + let url: string, + points: number[] = [], + zoom: number = null; + + if (typeof options === 'string') { + url = options; + options = {}; + } else if (Array.isArray(options)) { + points = options.slice(); + } else if (typeof options === 'undefined' && this.data.url) { + // Refreshing + this._updatePropertiesFromImage(); + this._triggerUpdate(); + return null; + } else { + url = options.url; + points = options.points || []; + zoom = typeof options.zoom === 'undefined' ? null : options.zoom; + } + + this.data.bound = false; + this.data.url = url || this.data.url; + this.data.boundZoom = zoom; + + const img = await loadImage(url, hasExif); + this._replaceImage(img); + if (!points.length) { + const natDim = naturalImageDimensions(img); + const rect = this.elements.viewport.getBoundingClientRect(); + const aspectRatio = rect.width / rect.height; + const imgAspectRatio = natDim.width / natDim.height; + let width: number, height: number; + if (imgAspectRatio > aspectRatio) { + height = natDim.height; + width = height * aspectRatio; + } else { + width = natDim.width; + height = natDim.height / aspectRatio; + } + const x0 = (natDim.width - width) / 2; + const y0 = (natDim.height - height) / 2; + const x1 = x0 + width; + const y1 = y0 + height; + this.data.points = [x0, y0, x1, y1]; + } else if (this.options.relative) { + points = [ + (points[0] * img.naturalWidth) / 100, + (points[1] * img.naturalHeight) / 100, + (points[2] * img.naturalWidth) / 100, + (points[3] * img.naturalHeight) / 100 + ]; + } + this.data.points = points; + if (this.options.useCanvas) { + this._transferImageToCanvas((options as CroppieBindOptions).orientation); + } + this._updatePropertiesFromImage(); + this._triggerUpdate(); + cb && cb(); + } + + get() { + const data = this._get(); + const points = data.points; + if (this.options.relative) { + points[0] /= this.elements.img.naturalWidth / 100; + points[1] /= this.elements.img.naturalHeight / 100; + points[2] /= this.elements.img.naturalWidth / 100; + points[3] /= this.elements.img.naturalHeight / 100; + } + return data; + } + + result(type: { + type: 'canvas' | 'base64' | 'html' | 'blob' | 'rawcanvas'; + size: + | 'viewport' + | 'original' + | { + width: number; + height: number; + }; + format: 'jpeg' | 'png' | 'webp'; + quality: number; + circle: boolean; + }) { + const data: CroppieResultData = this._get(), + opts: CroppieResultData = deepExtend( + clone(Croppie.RESULT_DEFAULTS), + clone(type) + ), + resultType = typeof type === 'string' ? type : opts.type || 'base64', + size = opts.size || 'viewport', + format = opts.format, + quality = opts.quality, + backgroundColor = opts.backgroundColor, + circle = + typeof opts.circle === 'boolean' + ? opts.circle + : this.options.viewport.type === 'circle', + vpRect = this.elements.viewport.getBoundingClientRect(), + ratio = vpRect.width / vpRect.height; + + if (size === 'viewport') { + data.outputWidth = vpRect.width; + data.outputHeight = vpRect.height; + } else if (typeof size === 'object') { + if (size.width && size.height) { + data.outputWidth = size.width; + data.outputHeight = size.height; + } else if (size.width) { + data.outputWidth = size.width; + data.outputHeight = size.width / ratio; + } else if (size.height) { + data.outputWidth = size.height * ratio; + data.outputHeight = size.height; + } + } + + if (Croppie.RESULT_FORMATS.indexOf(format) > -1) { + data.format = 'image/' + format; + data.quality = quality; + } + + data.circle = circle; + data.url = this.data.url; + data.backgroundColor = backgroundColor; + + const prom = new Promise< + HTMLCanvasElement | string | Blob | HTMLDivElement + >((resolve) => { + switch (resultType.toLowerCase()) { + case 'rawcanvas': + resolve(this._getCanvas(data)); + break; + case 'canvas': + case 'base64': + resolve(this._getBase64Result(data)); + break; + case 'blob': + this._getBlobResult(data).then(resolve); + break; + default: + resolve(this._getHtmlResult(data)); + } + }); + return prom; + } + + refresh() { + this._updatePropertiesFromImage(); + } + + setZoom(v: number) { + this._setZoomerVal(v); + this.dispatchChange(this.elements.zoomer); + } + + rotate(deg: 90 | 180 | 270 | -90 | -180 | -270) { + if (!this.options.useCanvas || !this.options.enableOrientation) { + throw 'Croppie: Cannot rotate without enableOrientation && EXIF.js included'; + } + const canvas = this.elements.canvas; + + this.data.orientation = getExifOffset(this.data.orientation, deg); + drawCanvas(canvas, this.elements.img, this.data.orientation); + this._updateCenterPoint(true); + this._updateZoomLimits(); + } + + destroy() { + this.element.removeChild(this.elements.boundary); + removeClass(this.element, 'croppie-container'); + if (this.options.enableZoom) { + this.element.removeChild(this.elements.zoomerWrap); + } + delete this.elements; + } + + //#region Private Methods + _create() { + const contClass = 'croppie-container', + customViewportClass = this.options.viewport.type + ? 'cr-vp-' + this.options.viewport.type + : null; + + this.options.useCanvas = this.options.enableOrientation || this._hasExif(); + + const boundary = (this.elements.boundary = document.createElement('div')); + const viewport = (this.elements.viewport = document.createElement('div')); + const img = (this.elements.img = document.createElement( + 'img' + ) as HTMLImageElement & { + exifdata: { Orientation: string }; + }); + const overlay = (this.elements.overlay = document.createElement('div')); + + if (this.options.useCanvas) { + this.elements.canvas = document.createElement('canvas'); + // Disable this? why? + this.elements.preview = this.elements.canvas; + // this.elements.preview = img; + } else { + this.elements.preview = img; + } + + addClass(boundary, 'cr-boundary'); + boundary.setAttribute('aria-dropeffect', 'none'); + const bw = this.options.boundary.width; + const bh = this.options.boundary.height; + css(boundary, { + width: bw + (isNaN(bw) ? '' : 'px'), + height: bh + (isNaN(bh) ? '' : 'px') + }); + + addClass(viewport, 'cr-viewport'); + if (customViewportClass) { + addClass(viewport, customViewportClass); + } + css(viewport, { + width: this.options.viewport.width + 'px', + height: this.options.viewport.height + 'px' + }); + viewport.setAttribute('tabindex', '0'); + + addClass(this.elements.preview, 'cr-image'); + setAttributes(this.elements.preview, { + alt: 'preview', + 'aria-grabbed': 'false' + }); + addClass(overlay, 'cr-overlay'); + + this.element.appendChild(boundary); + boundary.appendChild(this.elements.preview); + boundary.appendChild(viewport); + boundary.appendChild(overlay); + + addClass(this.element, contClass); + if (this.options.customClass) { + addClass(this.element, this.options.customClass); + } + + this._initDraggable(); + + if (this.options.enableZoom) { + this._initializeZoom(); + } + + // if (this.options.enableOrientation) { + // this._initRotationControls(); + // } + + if (this.options.enableResize) { + this._initializeResize(); + } + } + + _initDraggable() { + let isDragging = false, + originalX: number, + originalY: number, + originalDistance: number, + vpRect: ClientRect | DOMRect, + transform: Transform; + + const assignTransformCoordinates = (deltaX: number, deltaY: number) => { + const imgRect = this.elements.preview.getBoundingClientRect(), + top = transform.y + deltaY, + left = transform.x + deltaX; + + if (this.options.enforceBoundary) { + if ( + vpRect.top > imgRect.top + deltaY && + vpRect.bottom < imgRect.bottom + deltaY + ) { + transform.y = top; + } + + if ( + vpRect.left > imgRect.left + deltaX && + vpRect.right < imgRect.right + deltaX + ) { + transform.x = left; + } + } else { + transform.y = top; + transform.x = left; + } + }; + + const keyDown = (ev: KeyboardEvent) => { + const LEFT_ARROW = 37, + UP_ARROW = 38, + RIGHT_ARROW = 39, + DOWN_ARROW = 40; + + const parseKeyDown = (key: number) => { + switch (key) { + case LEFT_ARROW: + return [1, 0]; + case UP_ARROW: + return [0, 1]; + case RIGHT_ARROW: + return [-1, 0]; + case DOWN_ARROW: + return [0, -1]; + } + }; + + if ( + ev.shiftKey && + (ev.keyCode === UP_ARROW || ev.keyCode === DOWN_ARROW) + ) { + let zoom: number; + // Should we use keycode? It is deprecated. + if (ev.keyCode === UP_ARROW) { + zoom = + parseFloat(this.elements.zoomer.value) + + parseFloat(this.elements.zoomer.step); + } else { + zoom = + parseFloat(this.elements.zoomer.value) - + parseFloat(this.elements.zoomer.step); + } + this.setZoom(zoom); + } else if ( + this.options.enableKeyMovement && + (ev.keyCode >= 37 && ev.keyCode <= 40) + ) { + ev.preventDefault(); + const movement = parseKeyDown(ev.keyCode); + + transform = Transform.parse(this.elements.preview); + (document.body.style as any)[CSS_USERSELECT] = 'none'; + vpRect = this.elements.viewport.getBoundingClientRect(); + keyMove(movement); + } + }; + + const keyMove = (movement: number[]) => { + const deltaX = movement[0], + deltaY = movement[1], + newCss: object = {}; + + assignTransformCoordinates(deltaX, deltaY); + + (newCss as any)[CSS_TRANSFORM] = transform.toString(); + css(this.elements.preview, newCss); + this._updateOverlay(); + (document.body.style as any)[CSS_USERSELECT] = ''; + this._updateCenterPoint(); + this._triggerUpdate(); + originalDistance = 0; + }; + + const mouseDown = (ev: MouseEvent | TouchEvent) => { + if ( + (ev as MouseEvent).button !== undefined && + (ev as MouseEvent).button !== 0 + ) + return; + + ev.preventDefault(); + if (isDragging) return; + isDragging = true; + originalX = (ev as MouseEvent).pageX; + originalY = (ev as MouseEvent).pageY; + + if ((ev as TouchEvent).touches) { + const touches = (ev as TouchEvent).touches[0]; + originalX = touches.pageX; + originalY = touches.pageY; + } + this.toggleGrabState(isDragging); + transform = Transform.parse(this.elements.preview); + window.addEventListener('mousemove', mouseMove); + window.addEventListener('touchmove', mouseMove); + window.addEventListener('mouseup', mouseUp); + window.addEventListener('touchend', mouseUp); + (document.body.style as any)[CSS_USERSELECT] = 'none'; + vpRect = this.elements.viewport.getBoundingClientRect(); + }; + + const mouseMove = (ev: MouseEvent | TouchEvent) => { + ev.preventDefault(); + let pageX = (ev as MouseEvent).pageX, + pageY = (ev as MouseEvent).pageY; + + if ((ev as TouchEvent).touches) { + const touches = (ev as TouchEvent).touches[0]; + pageX = touches.pageX; + pageY = touches.pageY; + } + + const deltaX = pageX - originalX, + deltaY = pageY - originalY, + newCss: object = {}; + + if (ev.type === 'touchmove') { + if ((ev as TouchEvent).touches.length > 1) { + const touch1 = (ev as TouchEvent).touches[0]; + const touch2 = (ev as TouchEvent).touches[1]; + const dist = Math.sqrt( + (touch1.pageX - touch2.pageX) * (touch1.pageX - touch2.pageX) + + (touch1.pageY - touch2.pageY) * (touch1.pageY - touch2.pageY) + ); + + if (!originalDistance) { + originalDistance = dist / this._currentZoom; + } + + const scale = dist / originalDistance; + + this._setZoomerVal(scale); + this.dispatchChange(this.elements.zoomer); + return; + } + } + + assignTransformCoordinates(deltaX, deltaY); + + (newCss as any)[CSS_TRANSFORM] = transform.toString(); + css(this.elements.preview, newCss); + this._updateOverlay(); + originalY = pageY; + originalX = pageX; + }; + + const mouseUp = () => { + isDragging = false; + this.toggleGrabState(isDragging); + window.removeEventListener('mousemove', mouseMove); + window.removeEventListener('touchmove', mouseMove); + window.removeEventListener('mouseup', mouseUp); + window.removeEventListener('touchend', mouseUp); + (document.body.style as any)[CSS_USERSELECT] = ''; + this._updateCenterPoint(); + this._triggerUpdate(); + originalDistance = 0; + }; + + this.elements.overlay.addEventListener('mousedown', mouseDown); + this.elements.viewport.addEventListener('keydown', keyDown); + this.elements.overlay.addEventListener('touchstart', mouseDown); + } + + toggleGrabState(isDragging: boolean) { + this.elements.preview.setAttribute( + 'aria-grabbed', + isDragging ? 'true' : 'false' + ); + this.elements.boundary.setAttribute( + 'aria-dropeffect', + isDragging ? 'move' : 'none' + ); + } + + _setZoomerVal(v: number) { + if (this.options.enableZoom) { + const z = this.elements.zoomer, + val = fix(v, 4); + + z.value = Math.max( + parseFloat(z.min), + Math.min(parseFloat(z.max), val) + ).toString(); + } + } + + dispatchChange(element: HTMLElement) { + if ('createEvent' in document) { + const evt = document.createEvent('HTMLEvents'); + evt.initEvent('change', false, true); + element.dispatchEvent(evt); + } else { + // For Internet Explorer 6-8 + (element as any).fireEvent('onchange'); + } + } + + _updateOverlay() { + if (!this.elements) return; // since this is debounced, it can be fired after destroy + const boundRect = this.elements.boundary.getBoundingClientRect(), + imgData = this.elements.preview.getBoundingClientRect(); + + css(this.elements.overlay, { + width: imgData.width + 'px', + height: imgData.height + 'px', + top: imgData.top - boundRect.top + 'px', + left: imgData.left - boundRect.left + 'px' + }); + } + + _updateCenterPoint(rotate?: boolean) { + const scale = this._currentZoom, + data = this.elements.preview.getBoundingClientRect(), + vpData = this.elements.viewport.getBoundingClientRect(), + transform = Transform.parse( + (this.elements.preview.style as any)[CSS_TRANSFORM] + ), + pc = new TransformOrigin(this.elements.preview), + top = vpData.top - data.top + vpData.height / 2, + left = vpData.left - data.left + vpData.width / 2, + center: { x?: number; y?: number } = {}, + adj: { x?: number; y?: number } = {}; + + if (rotate) { + const cx = pc.x; + const cy = pc.y; + const tx = transform.x; + const ty = transform.y; + + center.y = cx; + center.x = cy; + transform.y = tx; + transform.x = ty; + } else { + center.y = top / scale; + center.x = left / scale; + + adj.y = (center.y - pc.y) * (1 - scale); + adj.x = (center.x - pc.x) * (1 - scale); + + transform.x -= adj.x; + transform.y -= adj.y; + } + + const newCss = {}; + (newCss as any)[CSS_TRANS_ORG] = center.x + 'px ' + center.y + 'px'; + (newCss as any)[CSS_TRANSFORM] = transform.toString(); + css(this.elements.preview, newCss); + } + + _triggerUpdate() { + const data = this.get(); + + if (!this._isVisible()) { + return; + } + + this.options.update.call(this, data); + if (this.$ && typeof Prototype === 'undefined') { + this.$(this.element).trigger('update.croppie', data); + } else { + const ev = new CustomEvent('update', { detail: data }); + this.element.dispatchEvent(ev); + } + } + + _isVisible() { + return ( + this.elements.preview.offsetHeight > 0 && + this.elements.preview.offsetWidth > 0 + ); + } + + _initializeZoom() { + const wrap = (this.elements.zoomerWrap = document.createElement('div')), + zoomer = (this.elements.zoomer = document.createElement('input')); + + addClass(wrap, 'cr-slider-wrap'); + addClass(zoomer, 'cr-slider'); + zoomer.type = 'range'; + zoomer.step = '0.0001'; + zoomer.value = '1'; + zoomer.style.display = this.options.showZoomer ? '' : 'none'; + zoomer.setAttribute('aria-label', 'zoom'); + + this.element.appendChild(wrap); + wrap.appendChild(zoomer); + + this._currentZoom = 1; + + const change = () => { + this._onZoom({ + value: parseFloat(zoomer.value), + origin: new TransformOrigin(this.elements.preview), + viewportRect: this.elements.viewport.getBoundingClientRect(), + transform: Transform.parse(this.elements.preview) + }); + }; + + const scroll = (ev: WheelEvent) => { + let delta, targetZoom; + + if (this.options.mouseWheelZoom === 'ctrl' && ev.ctrlKey !== true) { + return 0; + } else if (ev.deltaY) { + delta = ev.deltaY / 1060; //deltaY min: -53 max: 53 // max x 10 x 2 + } else if (ev.detail) { + delta = ev.detail / -60; //delta min: -3 max: 3 // max x 10 x 2 + } else { + delta = 0; + } + + targetZoom = this._currentZoom + delta * this._currentZoom; + + ev.preventDefault(); + this._setZoomerVal(targetZoom); + change(); + }; + + this.elements.zoomer.addEventListener('input', change); // this is being fired twice on keypress + this.elements.zoomer.addEventListener('change', change); + + if (this.options.mouseWheelZoom) { + this.elements.boundary.addEventListener('wheel', scroll); + } + } + + _hasExif() { + return this.options.enableExif && !!window.EXIF; + } + + _onZoom(ui: { + value: number; + origin: TransformOrigin; + viewportRect: ClientRect | DOMRect; + transform: Transform; + }) { + const transform = ui + ? ui.transform + : Transform.parse(this.elements.preview), + vpRect = ui + ? ui.viewportRect + : this.elements.viewport.getBoundingClientRect(), + origin = ui ? ui.origin : new TransformOrigin(this.elements.preview); + + const applyCss = () => { + const transCss = {}; + (transCss as any)[CSS_TRANSFORM] = transform.toString(); + (transCss as any)[CSS_TRANS_ORG] = origin.toString(); + css(this.elements.preview, transCss); + }; + + this._currentZoom = ui ? ui.value : this._currentZoom; + transform.scale = this._currentZoom; + this.elements.zoomer.setAttribute( + 'aria-valuenow', + this._currentZoom.toString() + ); + applyCss(); + + if (this.options.enforceBoundary) { + const boundaries = this._getVirtualBoundaries(vpRect), + transBoundaries = boundaries.translate, + oBoundaries = boundaries.origin; + + if (transform.x >= transBoundaries.maxX) { + origin.x = oBoundaries.minX; + transform.x = transBoundaries.maxX; + } + + if (transform.x <= transBoundaries.minX) { + origin.x = oBoundaries.maxX; + transform.x = transBoundaries.minX; + } + + if (transform.y >= transBoundaries.maxY) { + origin.y = oBoundaries.minY; + transform.y = transBoundaries.maxY; + } + + if (transform.y <= transBoundaries.minY) { + origin.y = oBoundaries.maxY; + transform.y = transBoundaries.minY; + } + } + applyCss(); + this._debouncedOverlay(); + this._triggerUpdate(); + } + + _getVirtualBoundaries(viewport: ClientRect | DOMRect) { + const scale = this._currentZoom, + vpWidth = viewport.width, + vpHeight = viewport.height, + centerFromBoundaryX = this.elements.boundary.clientWidth / 2, + centerFromBoundaryY = this.elements.boundary.clientHeight / 2, + imgRect = this.elements.preview.getBoundingClientRect(), + curImgWidth = imgRect.width, + curImgHeight = imgRect.height, + halfWidth = vpWidth / 2, + halfHeight = vpHeight / 2, + maxX = (halfWidth / scale - centerFromBoundaryX) * -1, + minX = maxX - (curImgWidth * (1 / scale) - vpWidth * (1 / scale)), + maxY = (halfHeight / scale - centerFromBoundaryY) * -1, + minY = maxY - (curImgHeight * (1 / scale) - vpHeight * (1 / scale)), + originMinX = (1 / scale) * halfWidth, + originMaxX = curImgWidth * (1 / scale) - originMinX, + originMinY = (1 / scale) * halfHeight, + originMaxY = curImgHeight * (1 / scale) - originMinY; + + return { + translate: { + maxX: maxX, + minX: minX, + maxY: maxY, + minY: minY + }, + origin: { + maxX: originMaxX, + minX: originMinX, + maxY: originMaxY, + minY: originMinY + } + }; + } + + _initializeResize() { + const wrap = document.createElement('div'); + let isDragging = false; + let direction: 'v' | 'h'; + let originalX: number; + let originalY: number; + const minSize = 50; + let maxWidth: number; + let maxHeight: number; + let vr: HTMLDivElement; + let hr: HTMLDivElement; + + addClass(wrap, 'cr-resizer'); + css(wrap, { + width: this.options.viewport.width + 'px', + height: this.options.viewport.height + 'px' + }); + + if (this.options.resizeControls.height) { + vr = document.createElement('div'); + addClass(vr, 'cr-resizer-vertical'); + wrap.appendChild(vr); + } + + if (this.options.resizeControls.width) { + hr = document.createElement('div'); + addClass(hr, 'cr-resizer-horisontal'); + wrap.appendChild(hr); + } + + const mouseDown = (ev: MouseEvent | TouchEvent) => { + if ( + (ev as MouseEvent).button !== undefined && + (ev as MouseEvent).button !== 0 + ) + return; + + ev.preventDefault(); + if (isDragging) { + return; + } + + const overlayRect = this.elements.overlay.getBoundingClientRect(); + + isDragging = true; + originalX = (ev as MouseEvent).pageX; + originalY = (ev as MouseEvent).pageY; + direction = + (ev.currentTarget as HTMLDivElement).className.indexOf('vertical') !== + -1 + ? 'v' + : 'h'; + maxWidth = overlayRect.width; + maxHeight = overlayRect.height; + + if ((ev as TouchEvent).touches) { + const touches = (ev as TouchEvent).touches[0]; + originalX = touches.pageX; + originalY = touches.pageY; + } + + window.addEventListener('mousemove', mouseMove); + window.addEventListener('touchmove', mouseMove); + window.addEventListener('mouseup', mouseUp); + window.addEventListener('touchend', mouseUp); + (document.body.style as any)[CSS_USERSELECT] = 'none'; + }; + + const mouseMove = (ev: MouseEvent | TouchEvent) => { + let pageX = (ev as MouseEvent).pageX; + let pageY = (ev as MouseEvent).pageY; + + ev.preventDefault(); + + if ((ev as TouchEvent).touches) { + const touches = (ev as TouchEvent).touches[0]; + pageX = touches.pageX; + pageY = touches.pageY; + } + + const deltaX = pageX - originalX; + const deltaY = pageY - originalY; + const newHeight = this.options.viewport.height + deltaY; + const newWidth = this.options.viewport.width + deltaX; + + if (direction === 'v' && newHeight >= minSize && newHeight <= maxHeight) { + css(wrap, { + height: newHeight + 'px' + }); + + this.options.boundary.height += deltaY; + css(this.elements.boundary, { + height: this.options.boundary.height + 'px' + }); + + this.options.viewport.height += deltaY; + css(this.elements.viewport, { + height: this.options.viewport.height + 'px' + }); + } else if ( + direction === 'h' && + newWidth >= minSize && + newWidth <= maxWidth + ) { + css(wrap, { + width: newWidth + 'px' + }); + + this.options.boundary.width += deltaX; + css(this.elements.boundary, { + width: this.options.boundary.width + 'px' + }); + + this.options.viewport.width += deltaX; + css(this.elements.viewport, { + width: this.options.viewport.width + 'px' + }); + } + + this._updateOverlay(); + this._updateZoomLimits(); + this._updateCenterPoint(); + this._triggerUpdate(); + originalY = pageY; + originalX = pageX; + }; + + const mouseUp = () => { + isDragging = false; + window.removeEventListener('mousemove', mouseMove); + window.removeEventListener('touchmove', mouseMove); + window.removeEventListener('mouseup', mouseUp); + window.removeEventListener('touchend', mouseUp); + (document.body.style as any)[CSS_USERSELECT] = ''; + }; + + if (vr) { + vr.addEventListener('mousedown', mouseDown); + vr.addEventListener('touchstart', mouseDown); + } + + if (hr) { + hr.addEventListener('mousedown', mouseDown); + hr.addEventListener('touchstart', mouseDown); + } + + this.elements.boundary.appendChild(wrap); + } + + _updateZoomLimits(initial?: boolean) { + const zoomer = this.elements.zoomer, + scale = parseFloat(zoomer.value), + boundaryData = this.elements.boundary.getBoundingClientRect(), + imgData = naturalImageDimensions( + this.elements.img, + this.data.orientation + ), + vpData = this.elements.viewport.getBoundingClientRect(); + let initialZoom: number, + defaultInitialZoom: number, + minW: number, + minH: number, + minZoom = 0, + maxZoom = this.options.maxZoom || 1.5; + + if (this.options.enforceBoundary) { + minW = vpData.width / imgData.width; + minH = vpData.height / imgData.height; + minZoom = Math.max(minW, minH); + } + + if (minZoom >= maxZoom) { + maxZoom = minZoom + 1; + } + + zoomer.min = fix(minZoom, 4).toString(); + zoomer.max = fix(maxZoom, 4).toString(); + + if (!initial && (scale < minZoom || scale > maxZoom)) { + this._setZoomerVal(scale < minZoom ? minZoom : maxZoom); + } else if (initial) { + defaultInitialZoom = Math.max( + boundaryData.width / imgData.width, + boundaryData.height / imgData.height + ); + initialZoom = + this.data.boundZoom !== null ? this.data.boundZoom : defaultInitialZoom; + this._setZoomerVal(initialZoom); + } + + this.dispatchChange(zoomer); + } + + _replaceImage(img: HTMLImageElement & { exifdata: { Orientation: string } }) { + if (this.elements.img.parentNode) { + this.elements.img.classList.forEach((c) => { + img.classList.add(c); + }); + this.elements.img.parentNode.replaceChild(img, this.elements.img); + this.elements.preview = img; // if the img is attached to the DOM, they're not using the canvas + } + this.elements.img = img; + } + + _transferImageToCanvas(customOrientation?: number) { + const canvas = this.elements.canvas, + img = this.elements.img, + ctx = canvas.getContext('2d'); + + ctx.clearRect(0, 0, canvas.width, canvas.height); + canvas.width = img.width; + canvas.height = img.height; + + const orientation = + (this.options.enableOrientation && customOrientation) || + getExifOrientation(img); + drawCanvas(canvas, img, orientation); + } + + _updatePropertiesFromImage() { + const initialZoom = 1, + cssReset: object = {}, + img = this.elements.preview, + transformReset = new Transform(0, 0, initialZoom), + originReset = new TransformOrigin(), + isVisible = this._isVisible(); + + if (!isVisible || this.data.bound) { + // if the croppie isn't visible or it doesn't need binding + return; + } + + this.data.bound = true; + (cssReset as any)[CSS_TRANSFORM] = transformReset.toString(); + (cssReset as any)[CSS_TRANS_ORG] = originReset.toString(); + (cssReset as any)['opacity'] = 1; + css(img, cssReset); + + const imgData = this.elements.preview.getBoundingClientRect(); + + this._originalImageWidth = imgData.width; + this._originalImageHeight = imgData.height; + this.data.orientation = getExifOrientation(this.elements.img); + + if (this.options.enableZoom) { + this._updateZoomLimits(true); + } else { + this._currentZoom = initialZoom; + } + + transformReset.scale = this._currentZoom; + (cssReset as any)[CSS_TRANSFORM] = transformReset.toString(); + css(img, cssReset); + + if (this.data.points.length) { + this._bindPoints(this.data.points); + } else { + this._centerImage(); + } + + this._updateCenterPoint(); + this._updateOverlay(); + } + + _bindPoints(points: number[]) { + if (points.length !== 4) { + throw 'Croppie - Invalid number of points supplied: ' + points; + } + const pointsWidth = points[2] - points[0], + vpData = this.elements.viewport.getBoundingClientRect(), + boundRect = this.elements.boundary.getBoundingClientRect(), + vpOffset = { + left: vpData.left - boundRect.left, + top: vpData.top - boundRect.top + }, + scale = vpData.width / pointsWidth, + originTop = points[1], + originLeft = points[0], + transformTop = -1 * points[1] + vpOffset.top, + transformLeft = -1 * points[0] + vpOffset.left, + newCss: object = {}; + + (newCss as any)[CSS_TRANS_ORG] = originLeft + 'px ' + originTop + 'px'; + (newCss as any)[CSS_TRANSFORM] = new Transform( + transformLeft, + transformTop, + scale + ).toString(); + css(this.elements.preview, newCss); + + this._setZoomerVal(scale); + this._currentZoom = scale; + } + + _centerImage() { + const imgDim = this.elements.preview.getBoundingClientRect(), + vpDim = this.elements.viewport.getBoundingClientRect(), + boundDim = this.elements.boundary.getBoundingClientRect(), + vpLeft = vpDim.left - boundDim.left, + vpTop = vpDim.top - boundDim.top, + w = vpLeft - (imgDim.width - vpDim.width) / 2, + h = vpTop - (imgDim.height - vpDim.height) / 2, + transform = new Transform(w, h, this._currentZoom); + + css(this.elements.preview, CSS_TRANSFORM, transform.toString()); + } + + _get() { + const imgData = this.elements.preview.getBoundingClientRect(), + vpData = this.elements.viewport.getBoundingClientRect(), + widthDiff = (vpData.width - this.elements.viewport.offsetWidth) / 2, //border + heightDiff = (vpData.height - this.elements.viewport.offsetHeight) / 2; + let scale = this._currentZoom, + x1 = vpData.left - imgData.left, + y1 = vpData.top - imgData.top, + x2 = x1 + this.elements.viewport.offsetWidth + widthDiff, + y2 = y1 + this.elements.viewport.offsetHeight + heightDiff; + + if (scale === Infinity || isNaN(scale)) { + scale = 1; + } + + const max = this.options.enforceBoundary ? 0 : Number.NEGATIVE_INFINITY; + x1 = Math.max(max, x1 / scale); + y1 = Math.max(max, y1 / scale); + x2 = Math.max(max, x2 / scale); + y2 = Math.max(max, y2 / scale); + + return { + points: [fix(x1), fix(y1), fix(x2), fix(y2)], + zoom: scale, + orientation: this.data.orientation + }; + } + + _getCanvas(data: CroppieResultData) { + const points = data.points, + left = points[0], + top = points[1], + right = points[2], + bottom = points[3], + circle = data.circle, + canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + startX = 0, + startY = 0; + let width = right - left, + height = bottom - top; + const canvasWidth = data.outputWidth || width, + canvasHeight = data.outputHeight || height; + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + if (data.backgroundColor) { + ctx.fillStyle = data.backgroundColor; + ctx.fillRect(0, 0, canvasWidth, canvasHeight); + } + + if (this.options.enforceBoundary !== false) { + width = Math.min(width, this._originalImageWidth); + height = Math.min(height, this._originalImageHeight); + } + ctx.drawImage( + this.elements.preview, + left, + top, + width, + height, + startX, + startY, + canvasWidth, + canvasHeight + ); + if (circle) { + ctx.fillStyle = '#fff'; + ctx.globalCompositeOperation = 'destination-in'; + ctx.beginPath(); + ctx.arc( + canvas.width / 2, + canvas.height / 2, + canvas.width / 2, + 0, + Math.PI * 2, + true + ); + ctx.closePath(); + ctx.fill(); + } + return canvas; + } + + _getBase64Result(data: CroppieResultData) { + return this._getCanvas(data).toDataURL(data.format, data.quality); + } + + _getBlobResult(data: CroppieResultData) { + return new Promise((resolve) => { + this._getCanvas(data).toBlob( + (blob) => { + resolve(blob); + }, + data.format, + data.quality + ); + }); + } + + _getHtmlResult(data: CroppieResultData) { + const points = data.points, + div = document.createElement('div'), + img = document.createElement('img'), + width = points[2] - points[0], + height = points[3] - points[1]; + + addClass(div, 'croppie-result'); + div.appendChild(img); + css(img, { + left: -1 * points[0] + 'px', + top: -1 * points[1] + 'px' + }); + img.src = data.url; + css(div, { + width: width + 'px', + height: height + 'px' + }); + + return div; + } + + // _initRotationControls () { + // let wrap, btnLeft, btnRight, iLeft, iRight; + + // wrap = document.createElement('div'); + // this.elements.orientationBtnLeft = btnLeft = document.createElement('button'); + // this.elements.orientationBtnRight = btnRight = document.createElement('button'); + + // wrap.appendChild(btnLeft); + // wrap.appendChild(btnRight); + + // iLeft = document.createElement('i'); + // iRight = document.createElement('i'); + // btnLeft.appendChild(iLeft); + // btnRight.appendChild(iRight); + + // addClass(wrap, 'cr-rotate-controls'); + // addClass(btnLeft, 'cr-rotate-l'); + // addClass(btnRight, 'cr-rotate-r'); + + // this.elements.boundary.appendChild(wrap); + + // btnLeft.addEventListener('click', () => { + // this.rotate(-90); + // }); + // btnRight.addEventListener('click', () => { + // this.rotate(90); + // }); + // } + + _getDebouncedOverlay() { + let timeout: NodeJS.Timeout; + return () => { + const later = () => { + timeout = null; + this._updateOverlay(); + }; + clearTimeout(timeout); + timeout = setTimeout(later, 500); + }; + } + + //#endregion +} +export default Croppie; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..dd3ced01 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,60 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "lib": ["es2015", "dom"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./out-tsc", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + // "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": false, /* Enable strict null checks. */ + "strictFunctionTypes": true, /* Enable strict checking of function types. */ + "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..2ce8d2cb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,61 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "lib": ["es2015", "dom"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./out-tsc", /* Redirect output structure to the directory. */ + "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + // "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": false, /* Enable strict null checks. */ + "strictFunctionTypes": true, /* Enable strict checking of function types. */ + "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./src", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "exclude": ["./build.ts"] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000..40134c42 --- /dev/null +++ b/tslint.json @@ -0,0 +1,10 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended", + "tslint-rules-prettier" + ], + "jsRules": {}, + "rules": {}, + "rulesDirectory": [] +}