big updat:
- update dependencies - add webp support and webp conversion for profile images
1
node_modules/.bin/rollup
generated
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../rollup/dist/bin/rollup
|
||||
147
node_modules/.package-lock.json
generated
vendored
|
|
@ -816,6 +816,32 @@
|
|||
"prettier": ">=2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.62.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
|
||||
"integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.62.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
|
||||
"integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@trapezedev/gradle-parse": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@trapezedev/gradle-parse/-/gradle-parse-7.1.3.tgz",
|
||||
|
|
@ -992,6 +1018,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
||||
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz",
|
||||
|
|
@ -1891,6 +1923,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cropperjs": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz",
|
||||
"integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
|
|
@ -2453,6 +2491,15 @@
|
|||
"pend": "~1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-type": {
|
||||
"version": "10.11.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz",
|
||||
"integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
|
|
@ -2672,6 +2719,30 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/gif-build-worker-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/gif-build-worker-js/-/gif-build-worker-js-1.0.3.tgz",
|
||||
"integrity": "sha512-H5wJsbYNCULRqq7tsh0uom0tKCbTe/erqK+SVirY1yLnMXramHWEGeCO4ruc1ZqxHwqA1hTYIELeXeXeJb8AJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rollup": "^4.24.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gif.js": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gif.js/-/gif.js-0.2.0.tgz",
|
||||
"integrity": "sha512-bYxCoT8OZKmbxY8RN4qDiYuj4nrQDTzgLRcFVovyona1PTWNePzI4nzOmotnlOFIzTk/ZxAHtv+TfVLiBWj/hw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/gifuct-js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gifuct-js/-/gifuct-js-2.1.2.tgz",
|
||||
"integrity": "sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-binary-schema-parser": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/git-raw-commits": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz",
|
||||
|
|
@ -2966,6 +3037,18 @@
|
|||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/image-type": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/image-type/-/image-type-4.1.0.tgz",
|
||||
"integrity": "sha512-CFJMJ8QK8lJvRlTCEgarL4ro6hfDQKif2HjSvYCdQZESaIPV4v9imrf7BQHK+sQeTeNeMpWciR9hyC/g8ybXEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"file-type": "^10.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/indent-string": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||
|
|
@ -3195,6 +3278,12 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/js-binary-schema-parser": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
|
||||
"integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
|
@ -4867,6 +4956,50 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.62.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
|
||||
"integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.9"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.62.2",
|
||||
"@rollup/rollup-android-arm64": "4.62.2",
|
||||
"@rollup/rollup-darwin-arm64": "4.62.2",
|
||||
"@rollup/rollup-darwin-x64": "4.62.2",
|
||||
"@rollup/rollup-freebsd-arm64": "4.62.2",
|
||||
"@rollup/rollup-freebsd-x64": "4.62.2",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.62.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.62.2",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.62.2",
|
||||
"@rollup/rollup-linux-loong64-gnu": "4.62.2",
|
||||
"@rollup/rollup-linux-loong64-musl": "4.62.2",
|
||||
"@rollup/rollup-linux-ppc64-gnu": "4.62.2",
|
||||
"@rollup/rollup-linux-ppc64-musl": "4.62.2",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.62.2",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.62.2",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.62.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.62.2",
|
||||
"@rollup/rollup-linux-x64-musl": "4.62.2",
|
||||
"@rollup/rollup-openbsd-x64": "4.62.2",
|
||||
"@rollup/rollup-openharmony-arm64": "4.62.2",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.62.2",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.62.2",
|
||||
"@rollup/rollup-win32-x64-gnu": "4.62.2",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.62.2",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
|
|
@ -5267,6 +5400,20 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/super-image-cropper": {
|
||||
"version": "1.0.27",
|
||||
"resolved": "https://registry.npmjs.org/super-image-cropper/-/super-image-cropper-1.0.27.tgz",
|
||||
"integrity": "sha512-S5jNy6TmII9WNYu/2fnvOsMSvi4M6ShAFrcf8f0LJjxs7YlBhYNnqiWGY6k68q7dFKEPZtxcakHLJnbWp3WRsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cropperjs": "^1.5.12",
|
||||
"gif-build-worker-js": "1.0.3",
|
||||
"gif.js": "^0.2.0",
|
||||
"gifuct-js": "^2.1.2",
|
||||
"image-type": "^4.1.0",
|
||||
"rollup": "^4.24.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
|
|||
3
node_modules/@rollup/rollup-linux-x64-gnu/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# `@rollup/rollup-linux-x64-gnu`
|
||||
|
||||
This is the **x86_64-unknown-linux-gnu** binary for `rollup`
|
||||
25
node_modules/@rollup/rollup-linux-x64-gnu/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "@rollup/rollup-linux-x64-gnu",
|
||||
"version": "4.62.2",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"files": [
|
||||
"rollup.linux-x64-gnu.node"
|
||||
],
|
||||
"description": "Native bindings for Rollup",
|
||||
"author": "Lukas Taegert-Atkinson",
|
||||
"homepage": "https://rollupjs.org/",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rollup/rollup.git"
|
||||
},
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"main": "./rollup.linux-x64-gnu.node"
|
||||
}
|
||||
BIN
node_modules/@rollup/rollup-linux-x64-gnu/rollup.linux-x64-gnu.node
generated
vendored
Normal file
3
node_modules/@rollup/rollup-linux-x64-musl/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# `@rollup/rollup-linux-x64-musl`
|
||||
|
||||
This is the **x86_64-unknown-linux-musl** binary for `rollup`
|
||||
25
node_modules/@rollup/rollup-linux-x64-musl/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "@rollup/rollup-linux-x64-musl",
|
||||
"version": "4.62.2",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"files": [
|
||||
"rollup.linux-x64-musl.node"
|
||||
],
|
||||
"description": "Native bindings for Rollup",
|
||||
"author": "Lukas Taegert-Atkinson",
|
||||
"homepage": "https://rollupjs.org/",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rollup/rollup.git"
|
||||
},
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"main": "./rollup.linux-x64-musl.node"
|
||||
}
|
||||
BIN
node_modules/@rollup/rollup-linux-x64-musl/rollup.linux-x64-musl.node
generated
vendored
Normal file
21
node_modules/@types/estree/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
15
node_modules/@types/estree/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Installation
|
||||
> `npm install --save @types/estree`
|
||||
|
||||
# Summary
|
||||
This package contains type definitions for estree (https://github.com/estree/estree).
|
||||
|
||||
# Details
|
||||
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree.
|
||||
|
||||
### Additional Details
|
||||
* Last updated: Wed, 06 May 2026 21:01:00 GMT
|
||||
* Dependencies: none
|
||||
|
||||
# Credits
|
||||
These definitions were written by [RReverser](https://github.com/RReverser).
|
||||
167
node_modules/@types/estree/flow.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
declare namespace ESTree {
|
||||
interface FlowTypeAnnotation extends Node {}
|
||||
|
||||
interface FlowBaseTypeAnnotation extends FlowTypeAnnotation {}
|
||||
|
||||
interface FlowLiteralTypeAnnotation extends FlowTypeAnnotation, Literal {}
|
||||
|
||||
interface FlowDeclaration extends Declaration {}
|
||||
|
||||
interface AnyTypeAnnotation extends FlowBaseTypeAnnotation {}
|
||||
|
||||
interface ArrayTypeAnnotation extends FlowTypeAnnotation {
|
||||
elementType: FlowTypeAnnotation;
|
||||
}
|
||||
|
||||
interface BooleanLiteralTypeAnnotation extends FlowLiteralTypeAnnotation {}
|
||||
|
||||
interface BooleanTypeAnnotation extends FlowBaseTypeAnnotation {}
|
||||
|
||||
interface ClassImplements extends Node {
|
||||
id: Identifier;
|
||||
typeParameters?: TypeParameterInstantiation | null;
|
||||
}
|
||||
|
||||
interface ClassProperty {
|
||||
key: Expression;
|
||||
value?: Expression | null;
|
||||
typeAnnotation?: TypeAnnotation | null;
|
||||
computed: boolean;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
interface DeclareClass extends FlowDeclaration {
|
||||
id: Identifier;
|
||||
typeParameters?: TypeParameterDeclaration | null;
|
||||
body: ObjectTypeAnnotation;
|
||||
extends: InterfaceExtends[];
|
||||
}
|
||||
|
||||
interface DeclareFunction extends FlowDeclaration {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
interface DeclareModule extends FlowDeclaration {
|
||||
id: Literal | Identifier;
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
interface DeclareVariable extends FlowDeclaration {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
interface FunctionTypeAnnotation extends FlowTypeAnnotation {
|
||||
params: FunctionTypeParam[];
|
||||
returnType: FlowTypeAnnotation;
|
||||
rest?: FunctionTypeParam | null;
|
||||
typeParameters?: TypeParameterDeclaration | null;
|
||||
}
|
||||
|
||||
interface FunctionTypeParam {
|
||||
name: Identifier;
|
||||
typeAnnotation: FlowTypeAnnotation;
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
interface GenericTypeAnnotation extends FlowTypeAnnotation {
|
||||
id: Identifier | QualifiedTypeIdentifier;
|
||||
typeParameters?: TypeParameterInstantiation | null;
|
||||
}
|
||||
|
||||
interface InterfaceExtends extends Node {
|
||||
id: Identifier | QualifiedTypeIdentifier;
|
||||
typeParameters?: TypeParameterInstantiation | null;
|
||||
}
|
||||
|
||||
interface InterfaceDeclaration extends FlowDeclaration {
|
||||
id: Identifier;
|
||||
typeParameters?: TypeParameterDeclaration | null;
|
||||
extends: InterfaceExtends[];
|
||||
body: ObjectTypeAnnotation;
|
||||
}
|
||||
|
||||
interface IntersectionTypeAnnotation extends FlowTypeAnnotation {
|
||||
types: FlowTypeAnnotation[];
|
||||
}
|
||||
|
||||
interface MixedTypeAnnotation extends FlowBaseTypeAnnotation {}
|
||||
|
||||
interface NullableTypeAnnotation extends FlowTypeAnnotation {
|
||||
typeAnnotation: TypeAnnotation;
|
||||
}
|
||||
|
||||
interface NumberLiteralTypeAnnotation extends FlowLiteralTypeAnnotation {}
|
||||
|
||||
interface NumberTypeAnnotation extends FlowBaseTypeAnnotation {}
|
||||
|
||||
interface StringLiteralTypeAnnotation extends FlowLiteralTypeAnnotation {}
|
||||
|
||||
interface StringTypeAnnotation extends FlowBaseTypeAnnotation {}
|
||||
|
||||
interface TupleTypeAnnotation extends FlowTypeAnnotation {
|
||||
types: FlowTypeAnnotation[];
|
||||
}
|
||||
|
||||
interface TypeofTypeAnnotation extends FlowTypeAnnotation {
|
||||
argument: FlowTypeAnnotation;
|
||||
}
|
||||
|
||||
interface TypeAlias extends FlowDeclaration {
|
||||
id: Identifier;
|
||||
typeParameters?: TypeParameterDeclaration | null;
|
||||
right: FlowTypeAnnotation;
|
||||
}
|
||||
|
||||
interface TypeAnnotation extends Node {
|
||||
typeAnnotation: FlowTypeAnnotation;
|
||||
}
|
||||
|
||||
interface TypeCastExpression extends Expression {
|
||||
expression: Expression;
|
||||
typeAnnotation: TypeAnnotation;
|
||||
}
|
||||
|
||||
interface TypeParameterDeclaration extends Node {
|
||||
params: Identifier[];
|
||||
}
|
||||
|
||||
interface TypeParameterInstantiation extends Node {
|
||||
params: FlowTypeAnnotation[];
|
||||
}
|
||||
|
||||
interface ObjectTypeAnnotation extends FlowTypeAnnotation {
|
||||
properties: ObjectTypeProperty[];
|
||||
indexers: ObjectTypeIndexer[];
|
||||
callProperties: ObjectTypeCallProperty[];
|
||||
}
|
||||
|
||||
interface ObjectTypeCallProperty extends Node {
|
||||
value: FunctionTypeAnnotation;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
interface ObjectTypeIndexer extends Node {
|
||||
id: Identifier;
|
||||
key: FlowTypeAnnotation;
|
||||
value: FlowTypeAnnotation;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
interface ObjectTypeProperty extends Node {
|
||||
key: Expression;
|
||||
value: FlowTypeAnnotation;
|
||||
optional: boolean;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
interface QualifiedTypeIdentifier extends Node {
|
||||
qualification: Identifier | QualifiedTypeIdentifier;
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
interface UnionTypeAnnotation extends FlowTypeAnnotation {
|
||||
types: FlowTypeAnnotation[];
|
||||
}
|
||||
|
||||
interface VoidTypeAnnotation extends FlowBaseTypeAnnotation {}
|
||||
}
|
||||
694
node_modules/@types/estree/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,694 @@
|
|||
// This definition file follows a somewhat unusual format. ESTree allows
|
||||
// runtime type checks based on the `type` parameter. In order to explain this
|
||||
// to typescript we want to use discriminated union types:
|
||||
// https://github.com/Microsoft/TypeScript/pull/9163
|
||||
//
|
||||
// For ESTree this is a bit tricky because the high level interfaces like
|
||||
// Node or Function are pulling double duty. We want to pass common fields down
|
||||
// to the interfaces that extend them (like Identifier or
|
||||
// ArrowFunctionExpression), but you can't extend a type union or enforce
|
||||
// common fields on them. So we've split the high level interfaces into two
|
||||
// types, a base type which passes down inherited fields, and a type union of
|
||||
// all types which extend the base type. Only the type union is exported, and
|
||||
// the union is how other types refer to the collection of inheriting types.
|
||||
//
|
||||
// This makes the definitions file here somewhat more difficult to maintain,
|
||||
// but it has the notable advantage of making ESTree much easier to use as
|
||||
// an end user.
|
||||
|
||||
export interface BaseNodeWithoutComments {
|
||||
// Every leaf interface that extends BaseNode must specify a type property.
|
||||
// The type property should be a string literal. For example, Identifier
|
||||
// has: `type: "Identifier"`
|
||||
type: string;
|
||||
loc?: SourceLocation | null | undefined;
|
||||
range?: [number, number] | undefined;
|
||||
}
|
||||
|
||||
export interface BaseNode extends BaseNodeWithoutComments {
|
||||
leadingComments?: Comment[] | undefined;
|
||||
trailingComments?: Comment[] | undefined;
|
||||
}
|
||||
|
||||
export interface NodeMap {
|
||||
AssignmentProperty: AssignmentProperty;
|
||||
CatchClause: CatchClause;
|
||||
Class: Class;
|
||||
ClassBody: ClassBody;
|
||||
Expression: Expression;
|
||||
Function: Function;
|
||||
Identifier: Identifier;
|
||||
Literal: Literal;
|
||||
MethodDefinition: MethodDefinition;
|
||||
ModuleDeclaration: ModuleDeclaration;
|
||||
ModuleSpecifier: ModuleSpecifier;
|
||||
Pattern: Pattern;
|
||||
PrivateIdentifier: PrivateIdentifier;
|
||||
Program: Program;
|
||||
Property: Property;
|
||||
PropertyDefinition: PropertyDefinition;
|
||||
SpreadElement: SpreadElement;
|
||||
Statement: Statement;
|
||||
Super: Super;
|
||||
SwitchCase: SwitchCase;
|
||||
TemplateElement: TemplateElement;
|
||||
VariableDeclarator: VariableDeclarator;
|
||||
}
|
||||
|
||||
export type Node = NodeMap[keyof NodeMap];
|
||||
|
||||
export interface Comment extends BaseNodeWithoutComments {
|
||||
type: "Line" | "Block";
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface SourceLocation {
|
||||
source?: string | null | undefined;
|
||||
start: Position;
|
||||
end: Position;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
/** >= 1 */
|
||||
line: number;
|
||||
/** >= 0 */
|
||||
column: number;
|
||||
}
|
||||
|
||||
export interface Program extends BaseNode {
|
||||
type: "Program";
|
||||
sourceType: "script" | "module";
|
||||
body: Array<Directive | Statement | ModuleDeclaration>;
|
||||
comments?: Comment[] | undefined;
|
||||
}
|
||||
|
||||
export interface Directive extends BaseNode {
|
||||
type: "ExpressionStatement";
|
||||
expression: Literal;
|
||||
directive: string;
|
||||
}
|
||||
|
||||
export interface BaseFunction extends BaseNode {
|
||||
params: Pattern[];
|
||||
generator?: boolean | undefined;
|
||||
async?: boolean | undefined;
|
||||
// The body is either BlockStatement or Expression because arrow functions
|
||||
// can have a body that's either. FunctionDeclarations and
|
||||
// FunctionExpressions have only BlockStatement bodies.
|
||||
body: BlockStatement | Expression;
|
||||
}
|
||||
|
||||
export type Function = FunctionDeclaration | FunctionExpression | ArrowFunctionExpression;
|
||||
|
||||
export type Statement =
|
||||
| ExpressionStatement
|
||||
| BlockStatement
|
||||
| StaticBlock
|
||||
| EmptyStatement
|
||||
| DebuggerStatement
|
||||
| WithStatement
|
||||
| ReturnStatement
|
||||
| LabeledStatement
|
||||
| BreakStatement
|
||||
| ContinueStatement
|
||||
| IfStatement
|
||||
| SwitchStatement
|
||||
| ThrowStatement
|
||||
| TryStatement
|
||||
| WhileStatement
|
||||
| DoWhileStatement
|
||||
| ForStatement
|
||||
| ForInStatement
|
||||
| ForOfStatement
|
||||
| Declaration;
|
||||
|
||||
export interface BaseStatement extends BaseNode {}
|
||||
|
||||
export interface EmptyStatement extends BaseStatement {
|
||||
type: "EmptyStatement";
|
||||
}
|
||||
|
||||
export interface BlockStatement extends BaseStatement {
|
||||
type: "BlockStatement";
|
||||
body: Statement[];
|
||||
innerComments?: Comment[] | undefined;
|
||||
}
|
||||
|
||||
export interface StaticBlock extends Omit<BlockStatement, "type"> {
|
||||
type: "StaticBlock";
|
||||
}
|
||||
|
||||
export interface ExpressionStatement extends BaseStatement {
|
||||
type: "ExpressionStatement";
|
||||
expression: Expression;
|
||||
}
|
||||
|
||||
export interface IfStatement extends BaseStatement {
|
||||
type: "IfStatement";
|
||||
test: Expression;
|
||||
consequent: Statement;
|
||||
alternate?: Statement | null | undefined;
|
||||
}
|
||||
|
||||
export interface LabeledStatement extends BaseStatement {
|
||||
type: "LabeledStatement";
|
||||
label: Identifier;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
export interface BreakStatement extends BaseStatement {
|
||||
type: "BreakStatement";
|
||||
label?: Identifier | null | undefined;
|
||||
}
|
||||
|
||||
export interface ContinueStatement extends BaseStatement {
|
||||
type: "ContinueStatement";
|
||||
label?: Identifier | null | undefined;
|
||||
}
|
||||
|
||||
export interface WithStatement extends BaseStatement {
|
||||
type: "WithStatement";
|
||||
object: Expression;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
export interface SwitchStatement extends BaseStatement {
|
||||
type: "SwitchStatement";
|
||||
discriminant: Expression;
|
||||
cases: SwitchCase[];
|
||||
}
|
||||
|
||||
export interface ReturnStatement extends BaseStatement {
|
||||
type: "ReturnStatement";
|
||||
argument?: Expression | null | undefined;
|
||||
}
|
||||
|
||||
export interface ThrowStatement extends BaseStatement {
|
||||
type: "ThrowStatement";
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
export interface TryStatement extends BaseStatement {
|
||||
type: "TryStatement";
|
||||
block: BlockStatement;
|
||||
handler?: CatchClause | null | undefined;
|
||||
finalizer?: BlockStatement | null | undefined;
|
||||
}
|
||||
|
||||
export interface WhileStatement extends BaseStatement {
|
||||
type: "WhileStatement";
|
||||
test: Expression;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
export interface DoWhileStatement extends BaseStatement {
|
||||
type: "DoWhileStatement";
|
||||
body: Statement;
|
||||
test: Expression;
|
||||
}
|
||||
|
||||
export interface ForStatement extends BaseStatement {
|
||||
type: "ForStatement";
|
||||
init?: VariableDeclaration | Expression | null | undefined;
|
||||
test?: Expression | null | undefined;
|
||||
update?: Expression | null | undefined;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
export interface BaseForXStatement extends BaseStatement {
|
||||
left: VariableDeclaration | Pattern;
|
||||
right: Expression;
|
||||
body: Statement;
|
||||
}
|
||||
|
||||
export interface ForInStatement extends BaseForXStatement {
|
||||
type: "ForInStatement";
|
||||
}
|
||||
|
||||
export interface DebuggerStatement extends BaseStatement {
|
||||
type: "DebuggerStatement";
|
||||
}
|
||||
|
||||
export type Declaration = FunctionDeclaration | VariableDeclaration | ClassDeclaration;
|
||||
|
||||
export interface BaseDeclaration extends BaseStatement {}
|
||||
|
||||
export interface MaybeNamedFunctionDeclaration extends BaseFunction, BaseDeclaration {
|
||||
type: "FunctionDeclaration";
|
||||
/** It is null when a function declaration is a part of the `export default function` statement */
|
||||
id: Identifier | null;
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
export interface FunctionDeclaration extends MaybeNamedFunctionDeclaration {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
export interface VariableDeclaration extends BaseDeclaration {
|
||||
type: "VariableDeclaration";
|
||||
declarations: VariableDeclarator[];
|
||||
kind: "var" | "let" | "const" | "using" | "await using";
|
||||
}
|
||||
|
||||
export interface VariableDeclarator extends BaseNode {
|
||||
type: "VariableDeclarator";
|
||||
id: Pattern;
|
||||
init?: Expression | null | undefined;
|
||||
}
|
||||
|
||||
export interface ExpressionMap {
|
||||
ArrayExpression: ArrayExpression;
|
||||
ArrowFunctionExpression: ArrowFunctionExpression;
|
||||
AssignmentExpression: AssignmentExpression;
|
||||
AwaitExpression: AwaitExpression;
|
||||
BinaryExpression: BinaryExpression;
|
||||
CallExpression: CallExpression;
|
||||
ChainExpression: ChainExpression;
|
||||
ClassExpression: ClassExpression;
|
||||
ConditionalExpression: ConditionalExpression;
|
||||
FunctionExpression: FunctionExpression;
|
||||
Identifier: Identifier;
|
||||
ImportExpression: ImportExpression;
|
||||
Literal: Literal;
|
||||
LogicalExpression: LogicalExpression;
|
||||
MemberExpression: MemberExpression;
|
||||
MetaProperty: MetaProperty;
|
||||
NewExpression: NewExpression;
|
||||
ObjectExpression: ObjectExpression;
|
||||
SequenceExpression: SequenceExpression;
|
||||
TaggedTemplateExpression: TaggedTemplateExpression;
|
||||
TemplateLiteral: TemplateLiteral;
|
||||
ThisExpression: ThisExpression;
|
||||
UnaryExpression: UnaryExpression;
|
||||
UpdateExpression: UpdateExpression;
|
||||
YieldExpression: YieldExpression;
|
||||
}
|
||||
|
||||
export type Expression = ExpressionMap[keyof ExpressionMap];
|
||||
|
||||
export interface BaseExpression extends BaseNode {}
|
||||
|
||||
export type ChainElement = SimpleCallExpression | MemberExpression;
|
||||
|
||||
export interface ChainExpression extends BaseExpression {
|
||||
type: "ChainExpression";
|
||||
expression: ChainElement;
|
||||
}
|
||||
|
||||
export interface ThisExpression extends BaseExpression {
|
||||
type: "ThisExpression";
|
||||
}
|
||||
|
||||
export interface ArrayExpression extends BaseExpression {
|
||||
type: "ArrayExpression";
|
||||
elements: Array<Expression | SpreadElement | null>;
|
||||
}
|
||||
|
||||
export interface ObjectExpression extends BaseExpression {
|
||||
type: "ObjectExpression";
|
||||
properties: Array<Property | SpreadElement>;
|
||||
}
|
||||
|
||||
export interface PrivateIdentifier extends BaseNode {
|
||||
type: "PrivateIdentifier";
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Property extends BaseNode {
|
||||
type: "Property";
|
||||
key: Expression;
|
||||
value: Expression | Pattern; // Could be an AssignmentProperty
|
||||
kind: "init" | "get" | "set";
|
||||
method: boolean;
|
||||
shorthand: boolean;
|
||||
computed: boolean;
|
||||
}
|
||||
|
||||
export interface PropertyDefinition extends BaseNode {
|
||||
type: "PropertyDefinition";
|
||||
key: Expression | PrivateIdentifier;
|
||||
value?: Expression | null | undefined;
|
||||
computed: boolean;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
export interface FunctionExpression extends BaseFunction, BaseExpression {
|
||||
id?: Identifier | null | undefined;
|
||||
type: "FunctionExpression";
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
export interface SequenceExpression extends BaseExpression {
|
||||
type: "SequenceExpression";
|
||||
expressions: Expression[];
|
||||
}
|
||||
|
||||
export interface UnaryExpression extends BaseExpression {
|
||||
type: "UnaryExpression";
|
||||
operator: UnaryOperator;
|
||||
prefix: true;
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
export interface BinaryExpression extends BaseExpression {
|
||||
type: "BinaryExpression";
|
||||
operator: BinaryOperator;
|
||||
left: Expression | PrivateIdentifier;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
export interface AssignmentExpression extends BaseExpression {
|
||||
type: "AssignmentExpression";
|
||||
operator: AssignmentOperator;
|
||||
left: Pattern | MemberExpression;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
export interface UpdateExpression extends BaseExpression {
|
||||
type: "UpdateExpression";
|
||||
operator: UpdateOperator;
|
||||
argument: Expression;
|
||||
prefix: boolean;
|
||||
}
|
||||
|
||||
export interface LogicalExpression extends BaseExpression {
|
||||
type: "LogicalExpression";
|
||||
operator: LogicalOperator;
|
||||
left: Expression;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
export interface ConditionalExpression extends BaseExpression {
|
||||
type: "ConditionalExpression";
|
||||
test: Expression;
|
||||
alternate: Expression;
|
||||
consequent: Expression;
|
||||
}
|
||||
|
||||
export interface BaseCallExpression extends BaseExpression {
|
||||
callee: Expression | Super;
|
||||
arguments: Array<Expression | SpreadElement>;
|
||||
}
|
||||
export type CallExpression = SimpleCallExpression | NewExpression;
|
||||
|
||||
export interface SimpleCallExpression extends BaseCallExpression {
|
||||
type: "CallExpression";
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
export interface NewExpression extends BaseCallExpression {
|
||||
type: "NewExpression";
|
||||
}
|
||||
|
||||
export interface MemberExpression extends BaseExpression, BasePattern {
|
||||
type: "MemberExpression";
|
||||
object: Expression | Super;
|
||||
property: Expression | PrivateIdentifier;
|
||||
computed: boolean;
|
||||
optional: boolean;
|
||||
}
|
||||
|
||||
export type Pattern = Identifier | ObjectPattern | ArrayPattern | RestElement | AssignmentPattern | MemberExpression;
|
||||
|
||||
export interface BasePattern extends BaseNode {}
|
||||
|
||||
export interface SwitchCase extends BaseNode {
|
||||
type: "SwitchCase";
|
||||
test?: Expression | null | undefined;
|
||||
consequent: Statement[];
|
||||
}
|
||||
|
||||
export interface CatchClause extends BaseNode {
|
||||
type: "CatchClause";
|
||||
param: Pattern | null;
|
||||
body: BlockStatement;
|
||||
}
|
||||
|
||||
export interface Identifier extends BaseNode, BaseExpression, BasePattern {
|
||||
type: "Identifier";
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type Literal = SimpleLiteral | RegExpLiteral | BigIntLiteral;
|
||||
|
||||
export interface SimpleLiteral extends BaseNode, BaseExpression {
|
||||
type: "Literal";
|
||||
value: string | boolean | number | null;
|
||||
raw?: string | undefined;
|
||||
}
|
||||
|
||||
export interface RegExpLiteral extends BaseNode, BaseExpression {
|
||||
type: "Literal";
|
||||
value?: RegExp | null | undefined;
|
||||
regex: {
|
||||
pattern: string;
|
||||
flags: string;
|
||||
};
|
||||
raw?: string | undefined;
|
||||
}
|
||||
|
||||
export interface BigIntLiteral extends BaseNode, BaseExpression {
|
||||
type: "Literal";
|
||||
value?: bigint | null | undefined;
|
||||
bigint: string;
|
||||
raw?: string | undefined;
|
||||
}
|
||||
|
||||
export type UnaryOperator = "-" | "+" | "!" | "~" | "typeof" | "void" | "delete";
|
||||
|
||||
export type BinaryOperator =
|
||||
| "=="
|
||||
| "!="
|
||||
| "==="
|
||||
| "!=="
|
||||
| "<"
|
||||
| "<="
|
||||
| ">"
|
||||
| ">="
|
||||
| "<<"
|
||||
| ">>"
|
||||
| ">>>"
|
||||
| "+"
|
||||
| "-"
|
||||
| "*"
|
||||
| "/"
|
||||
| "%"
|
||||
| "**"
|
||||
| "|"
|
||||
| "^"
|
||||
| "&"
|
||||
| "in"
|
||||
| "instanceof";
|
||||
|
||||
export type LogicalOperator = "||" | "&&" | "??";
|
||||
|
||||
export type AssignmentOperator =
|
||||
| "="
|
||||
| "+="
|
||||
| "-="
|
||||
| "*="
|
||||
| "/="
|
||||
| "%="
|
||||
| "**="
|
||||
| "<<="
|
||||
| ">>="
|
||||
| ">>>="
|
||||
| "|="
|
||||
| "^="
|
||||
| "&="
|
||||
| "||="
|
||||
| "&&="
|
||||
| "??=";
|
||||
|
||||
export type UpdateOperator = "++" | "--";
|
||||
|
||||
export interface ForOfStatement extends BaseForXStatement {
|
||||
type: "ForOfStatement";
|
||||
await: boolean;
|
||||
}
|
||||
|
||||
export interface Super extends BaseNode {
|
||||
type: "Super";
|
||||
}
|
||||
|
||||
export interface SpreadElement extends BaseNode {
|
||||
type: "SpreadElement";
|
||||
argument: Expression;
|
||||
}
|
||||
|
||||
export interface ArrowFunctionExpression extends BaseExpression, BaseFunction {
|
||||
type: "ArrowFunctionExpression";
|
||||
expression: boolean;
|
||||
body: BlockStatement | Expression;
|
||||
}
|
||||
|
||||
export interface YieldExpression extends BaseExpression {
|
||||
type: "YieldExpression";
|
||||
argument?: Expression | null | undefined;
|
||||
delegate: boolean;
|
||||
}
|
||||
|
||||
export interface TemplateLiteral extends BaseExpression {
|
||||
type: "TemplateLiteral";
|
||||
quasis: TemplateElement[];
|
||||
expressions: Expression[];
|
||||
}
|
||||
|
||||
export interface TaggedTemplateExpression extends BaseExpression {
|
||||
type: "TaggedTemplateExpression";
|
||||
tag: Expression;
|
||||
quasi: TemplateLiteral;
|
||||
}
|
||||
|
||||
export interface TemplateElement extends BaseNode {
|
||||
type: "TemplateElement";
|
||||
tail: boolean;
|
||||
value: {
|
||||
/** It is null when the template literal is tagged and the text has an invalid escape (e.g. - tag`\unicode and \u{55}`) */
|
||||
cooked?: string | null | undefined;
|
||||
raw: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AssignmentProperty extends Property {
|
||||
value: Pattern;
|
||||
kind: "init";
|
||||
method: boolean; // false
|
||||
}
|
||||
|
||||
export interface ObjectPattern extends BasePattern {
|
||||
type: "ObjectPattern";
|
||||
properties: Array<AssignmentProperty | RestElement>;
|
||||
}
|
||||
|
||||
export interface ArrayPattern extends BasePattern {
|
||||
type: "ArrayPattern";
|
||||
elements: Array<Pattern | null>;
|
||||
}
|
||||
|
||||
export interface RestElement extends BasePattern {
|
||||
type: "RestElement";
|
||||
argument: Pattern;
|
||||
}
|
||||
|
||||
export interface AssignmentPattern extends BasePattern {
|
||||
type: "AssignmentPattern";
|
||||
left: Pattern;
|
||||
right: Expression;
|
||||
}
|
||||
|
||||
export type Class = ClassDeclaration | ClassExpression;
|
||||
export interface BaseClass extends BaseNode {
|
||||
superClass?: Expression | null | undefined;
|
||||
body: ClassBody;
|
||||
}
|
||||
|
||||
export interface ClassBody extends BaseNode {
|
||||
type: "ClassBody";
|
||||
body: Array<MethodDefinition | PropertyDefinition | StaticBlock>;
|
||||
}
|
||||
|
||||
export interface MethodDefinition extends BaseNode {
|
||||
type: "MethodDefinition";
|
||||
key: Expression | PrivateIdentifier;
|
||||
value: FunctionExpression;
|
||||
kind: "constructor" | "method" | "get" | "set";
|
||||
computed: boolean;
|
||||
static: boolean;
|
||||
}
|
||||
|
||||
export interface MaybeNamedClassDeclaration extends BaseClass, BaseDeclaration {
|
||||
type: "ClassDeclaration";
|
||||
/** It is null when a class declaration is a part of the `export default class` statement */
|
||||
id: Identifier | null;
|
||||
}
|
||||
|
||||
export interface ClassDeclaration extends MaybeNamedClassDeclaration {
|
||||
id: Identifier;
|
||||
}
|
||||
|
||||
export interface ClassExpression extends BaseClass, BaseExpression {
|
||||
type: "ClassExpression";
|
||||
id?: Identifier | null | undefined;
|
||||
}
|
||||
|
||||
export interface MetaProperty extends BaseExpression {
|
||||
type: "MetaProperty";
|
||||
meta: Identifier;
|
||||
property: Identifier;
|
||||
}
|
||||
|
||||
export type ModuleDeclaration =
|
||||
| ImportDeclaration
|
||||
| ExportNamedDeclaration
|
||||
| ExportDefaultDeclaration
|
||||
| ExportAllDeclaration;
|
||||
export interface BaseModuleDeclaration extends BaseNode {}
|
||||
|
||||
export type ModuleSpecifier = ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ExportSpecifier;
|
||||
export interface BaseModuleSpecifier extends BaseNode {
|
||||
local: Identifier;
|
||||
}
|
||||
|
||||
export interface ImportDeclaration extends BaseModuleDeclaration {
|
||||
type: "ImportDeclaration";
|
||||
specifiers: Array<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier>;
|
||||
attributes: ImportAttribute[];
|
||||
source: Literal;
|
||||
}
|
||||
|
||||
export interface ImportSpecifier extends BaseModuleSpecifier {
|
||||
type: "ImportSpecifier";
|
||||
imported: Identifier | Literal;
|
||||
}
|
||||
|
||||
export interface ImportAttribute extends BaseNode {
|
||||
type: "ImportAttribute";
|
||||
key: Identifier | Literal;
|
||||
value: Literal;
|
||||
}
|
||||
|
||||
export interface ImportExpression extends BaseExpression {
|
||||
type: "ImportExpression";
|
||||
source: Expression;
|
||||
options?: Expression | null | undefined;
|
||||
}
|
||||
|
||||
export interface ImportDefaultSpecifier extends BaseModuleSpecifier {
|
||||
type: "ImportDefaultSpecifier";
|
||||
}
|
||||
|
||||
export interface ImportNamespaceSpecifier extends BaseModuleSpecifier {
|
||||
type: "ImportNamespaceSpecifier";
|
||||
}
|
||||
|
||||
export interface ExportNamedDeclaration extends BaseModuleDeclaration {
|
||||
type: "ExportNamedDeclaration";
|
||||
declaration?: Declaration | null | undefined;
|
||||
specifiers: ExportSpecifier[];
|
||||
attributes: ImportAttribute[];
|
||||
source?: Literal | null | undefined;
|
||||
}
|
||||
|
||||
export interface ExportSpecifier extends Omit<BaseModuleSpecifier, "local"> {
|
||||
type: "ExportSpecifier";
|
||||
local: Identifier | Literal;
|
||||
exported: Identifier | Literal;
|
||||
}
|
||||
|
||||
export interface ExportDefaultDeclaration extends BaseModuleDeclaration {
|
||||
type: "ExportDefaultDeclaration";
|
||||
declaration: MaybeNamedFunctionDeclaration | MaybeNamedClassDeclaration | Expression;
|
||||
}
|
||||
|
||||
export interface ExportAllDeclaration extends BaseModuleDeclaration {
|
||||
type: "ExportAllDeclaration";
|
||||
exported: Identifier | Literal | null;
|
||||
attributes: ImportAttribute[];
|
||||
source: Literal;
|
||||
}
|
||||
|
||||
export interface AwaitExpression extends BaseExpression {
|
||||
type: "AwaitExpression";
|
||||
argument: Expression;
|
||||
}
|
||||
27
node_modules/@types/estree/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "@types/estree",
|
||||
"version": "1.0.9",
|
||||
"description": "TypeScript definitions for estree",
|
||||
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree",
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "RReverser",
|
||||
"githubUsername": "RReverser",
|
||||
"url": "https://github.com/RReverser"
|
||||
}
|
||||
],
|
||||
"main": "",
|
||||
"types": "index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||
"directory": "types/estree"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {},
|
||||
"peerDependencies": {},
|
||||
"typesPublisherContentHash": "db16da859cb0bee641414117047a4becba2e9f39d3e14a6745f887c47ef68482",
|
||||
"typeScriptVersion": "5.3",
|
||||
"nonNpm": true
|
||||
}
|
||||
21
node_modules/cropperjs/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright 2015-present Chen Fengyuan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
1076
node_modules/cropperjs/README.md
generated
vendored
Normal file
3267
node_modules/cropperjs/dist/cropper.common.js
generated
vendored
Normal file
309
node_modules/cropperjs/dist/cropper.css
generated
vendored
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/*!
|
||||
* Cropper.js v1.6.2
|
||||
* https://fengyuanchen.github.io/cropperjs
|
||||
*
|
||||
* Copyright 2015-present Chen Fengyuan
|
||||
* Released under the MIT license
|
||||
*
|
||||
* Date: 2024-04-21T07:43:02.731Z
|
||||
*/
|
||||
|
||||
.cropper-container {
|
||||
direction: ltr;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.cropper-container img {
|
||||
backface-visibility: hidden;
|
||||
display: block;
|
||||
height: 100%;
|
||||
image-orientation: 0deg;
|
||||
max-height: none !important;
|
||||
max-width: none !important;
|
||||
min-height: 0 !important;
|
||||
min-width: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-wrap-box,
|
||||
.cropper-canvas,
|
||||
.cropper-drag-box,
|
||||
.cropper-crop-box,
|
||||
.cropper-modal {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.cropper-wrap-box,
|
||||
.cropper-canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cropper-drag-box {
|
||||
background-color: #fff;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cropper-modal {
|
||||
background-color: #000;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cropper-view-box {
|
||||
display: block;
|
||||
height: 100%;
|
||||
outline: 1px solid #39f;
|
||||
outline-color: rgba(51, 153, 255, 0.75);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-dashed {
|
||||
border: 0 dashed #eee;
|
||||
display: block;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.cropper-dashed.dashed-h {
|
||||
border-bottom-width: 1px;
|
||||
border-top-width: 1px;
|
||||
height: calc(100% / 3);
|
||||
left: 0;
|
||||
top: calc(100% / 3);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-dashed.dashed-v {
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
height: 100%;
|
||||
left: calc(100% / 3);
|
||||
top: 0;
|
||||
width: calc(100% / 3);
|
||||
}
|
||||
|
||||
.cropper-center {
|
||||
display: block;
|
||||
height: 0;
|
||||
left: 50%;
|
||||
opacity: 0.75;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.cropper-center::before,
|
||||
.cropper-center::after {
|
||||
background-color: #eee;
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.cropper-center::before {
|
||||
height: 1px;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.cropper-center::after {
|
||||
height: 7px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.cropper-face,
|
||||
.cropper-line,
|
||||
.cropper-point {
|
||||
display: block;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-face {
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.cropper-line {
|
||||
background-color: #39f;
|
||||
}
|
||||
|
||||
.cropper-line.line-e {
|
||||
cursor: ew-resize;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.cropper-line.line-n {
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-line.line-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.cropper-line.line-s {
|
||||
bottom: -3px;
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.cropper-point {
|
||||
background-color: #39f;
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.cropper-point.point-e {
|
||||
cursor: ew-resize;
|
||||
margin-top: -3px;
|
||||
right: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.cropper-point.point-n {
|
||||
cursor: ns-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
margin-top: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.cropper-point.point-s {
|
||||
bottom: -3px;
|
||||
cursor: s-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-ne {
|
||||
cursor: nesw-resize;
|
||||
right: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-nw {
|
||||
cursor: nwse-resize;
|
||||
left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-sw {
|
||||
bottom: -3px;
|
||||
cursor: nesw-resize;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
.cropper-point.point-se {
|
||||
bottom: -3px;
|
||||
cursor: nwse-resize;
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
right: -3px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
.cropper-point.point-se {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
|
||||
.cropper-point.point-se {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
.cropper-point.point-se {
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-point.point-se::before {
|
||||
background-color: #39f;
|
||||
bottom: -50%;
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 200%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
}
|
||||
|
||||
.cropper-invisible {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cropper-bg {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
.cropper-hide {
|
||||
display: block;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.cropper-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cropper-move {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.cropper-crop {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.cropper-disabled .cropper-drag-box,
|
||||
.cropper-disabled .cropper-face,
|
||||
.cropper-disabled .cropper-line,
|
||||
.cropper-disabled .cropper-point {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
3265
node_modules/cropperjs/dist/cropper.esm.js
generated
vendored
Normal file
3273
node_modules/cropperjs/dist/cropper.js
generated
vendored
Normal file
9
node_modules/cropperjs/dist/cropper.min.css
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/*!
|
||||
* Cropper.js v1.6.2
|
||||
* https://fengyuanchen.github.io/cropperjs
|
||||
*
|
||||
* Copyright 2015-present Chen Fengyuan
|
||||
* Released under the MIT license
|
||||
*
|
||||
* Date: 2024-04-21T07:43:02.731Z
|
||||
*/.cropper-container{-webkit-touch-callout:none;direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
|
||||
10
node_modules/cropperjs/dist/cropper.min.js
generated
vendored
Normal file
106
node_modules/cropperjs/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"name": "cropperjs",
|
||||
"description": "JavaScript image cropper.",
|
||||
"version": "1.6.2",
|
||||
"main": "dist/cropper.common.js",
|
||||
"module": "dist/cropper.esm.js",
|
||||
"browser": "dist/cropper.js",
|
||||
"types": "types/index.d.ts",
|
||||
"style": "dist/cropper.css",
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"types"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build:css && npm run build:js",
|
||||
"build:css": "postcss src/index.css -o dist/cropper.css --no-map",
|
||||
"build:js": "rollup -c",
|
||||
"clean": "del-cli dist",
|
||||
"compress": "npm run compress:css && npm run compress:js",
|
||||
"compress:css": "postcss dist/cropper.css -u cssnano -o dist/cropper.min.css --no-map",
|
||||
"compress:js": "uglifyjs dist/cropper.js -o dist/cropper.min.js -c -m --comments /^!/",
|
||||
"copy": "cpy dist/cropper.css docs/css",
|
||||
"lint": "npm run lint:js && npm run lint:css",
|
||||
"lint:css": "stylelint **/*.{css,scss} --fix",
|
||||
"lint:js": "eslint . --fix",
|
||||
"prepare": "husky install",
|
||||
"release": "npm run clean && npm run lint && npm run build && npm run compress && npm run copy && npm test",
|
||||
"start": "npm-run-all --parallel watch:*",
|
||||
"test": "karma start",
|
||||
"test:coverage": "cat coverage/lcov.info | codecov",
|
||||
"watch:css": "postcss src/index.css -o docs/css/cropper.css -m -w",
|
||||
"watch:js": "rollup -c -m -w"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fengyuanchen/cropperjs.git"
|
||||
},
|
||||
"keywords": [
|
||||
"image",
|
||||
"crop",
|
||||
"move",
|
||||
"zoom",
|
||||
"rotate",
|
||||
"scale",
|
||||
"cropper",
|
||||
"cropper.js",
|
||||
"cropping",
|
||||
"processing",
|
||||
"html",
|
||||
"css",
|
||||
"javascript",
|
||||
"front-end",
|
||||
"web"
|
||||
],
|
||||
"author": {
|
||||
"name": "Chen Fengyuan",
|
||||
"url": "https://chenfengyuan.com/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fengyuanchen/cropperjs/issues"
|
||||
},
|
||||
"homepage": "https://fengyuanchen.github.io/cropperjs",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/preset-env": "^7.24.4",
|
||||
"@commitlint/cli": "^17.8.1",
|
||||
"@commitlint/config-conventional": "^17.8.1",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"chai": "^4.4.1",
|
||||
"change-case": "^4.1.2",
|
||||
"codecov": "^3.8.2",
|
||||
"cpy-cli": "^3.1.1",
|
||||
"create-banner": "^2.0.0",
|
||||
"cssnano": "^5.1.15",
|
||||
"del-cli": "^5.1.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"husky": "^8.0.3",
|
||||
"karma": "^6.4.3",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-coverage-istanbul-reporter": "^3.0.3",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-rollup-preprocessor": "^7.0.8",
|
||||
"lint-staged": "^13.3.0",
|
||||
"mocha": "^10.4.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-cli": "^10.1.0",
|
||||
"postcss-header": "^3.0.3",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-preset-env": "^7.8.3",
|
||||
"postcss-url": "^10.1.3",
|
||||
"puppeteer": "^19.11.1",
|
||||
"rollup": "^3.29.4",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"uglify-js": "^3.17.4"
|
||||
}
|
||||
}
|
||||
286
node_modules/cropperjs/src/css/cropper.css
generated
vendored
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
.cropper-container {
|
||||
direction: ltr;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
|
||||
& img {
|
||||
backface-visibility: hidden;
|
||||
display: block;
|
||||
height: 100%;
|
||||
image-orientation: 0deg;
|
||||
max-height: none !important;
|
||||
max-width: none !important;
|
||||
min-height: 0 !important;
|
||||
min-width: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-wrap-box,
|
||||
.cropper-canvas,
|
||||
.cropper-drag-box,
|
||||
.cropper-crop-box,
|
||||
.cropper-modal {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.cropper-wrap-box,
|
||||
.cropper-canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cropper-drag-box {
|
||||
background-color: #fff;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cropper-modal {
|
||||
background-color: #000;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.cropper-view-box {
|
||||
display: block;
|
||||
height: 100%;
|
||||
outline: 1px solid #39f;
|
||||
outline-color: rgba(51, 153, 255, 0.75);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-dashed {
|
||||
border: 0 dashed #eee;
|
||||
display: block;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
|
||||
&.dashed-h {
|
||||
border-bottom-width: 1px;
|
||||
border-top-width: 1px;
|
||||
height: calc(100% / 3);
|
||||
left: 0;
|
||||
top: calc(100% / 3);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.dashed-v {
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
height: 100%;
|
||||
left: calc(100% / 3);
|
||||
top: 0;
|
||||
width: calc(100% / 3);
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-center {
|
||||
display: block;
|
||||
height: 0;
|
||||
left: 50%;
|
||||
opacity: 0.75;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
background-color: #eee;
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&::before {
|
||||
height: 1px;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
height: 7px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-face,
|
||||
.cropper-line,
|
||||
.cropper-point {
|
||||
display: block;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cropper-face {
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.cropper-line {
|
||||
background-color: #39f;
|
||||
|
||||
&.line-e {
|
||||
cursor: ew-resize;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&.line-n {
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.line-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&.line-s {
|
||||
bottom: -3px;
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-point {
|
||||
background-color: #39f;
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
|
||||
&.point-e {
|
||||
cursor: ew-resize;
|
||||
margin-top: -3px;
|
||||
right: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&.point-n {
|
||||
cursor: ns-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.point-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
margin-top: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&.point-s {
|
||||
bottom: -3px;
|
||||
cursor: s-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
&.point-ne {
|
||||
cursor: nesw-resize;
|
||||
right: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.point-nw {
|
||||
cursor: nwse-resize;
|
||||
left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.point-sw {
|
||||
bottom: -3px;
|
||||
cursor: nesw-resize;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
&.point-se {
|
||||
bottom: -3px;
|
||||
cursor: nwse-resize;
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
right: -3px;
|
||||
width: 20px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&.point-se::before {
|
||||
background-color: #39f;
|
||||
bottom: -50%;
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 200%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
}
|
||||
}
|
||||
|
||||
.cropper-invisible {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cropper-bg {
|
||||
background-image: url('../images/bg.png');
|
||||
}
|
||||
|
||||
.cropper-hide {
|
||||
display: block;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.cropper-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cropper-move {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.cropper-crop {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.cropper-disabled .cropper-drag-box,
|
||||
.cropper-disabled .cropper-face,
|
||||
.cropper-disabled .cropper-line,
|
||||
.cropper-disabled .cropper-point {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
290
node_modules/cropperjs/src/css/cropper.scss
generated
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
$cropper-image-path: '../images' !default;
|
||||
|
||||
.cropper {
|
||||
&-container {
|
||||
direction: ltr;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
|
||||
img {
|
||||
backface-visibility: hidden;
|
||||
display: block;
|
||||
height: 100%;
|
||||
image-orientation: 0deg;
|
||||
max-height: none !important;
|
||||
max-width: none !important;
|
||||
min-height: 0 !important;
|
||||
min-width: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-wrap-box,
|
||||
&-canvas,
|
||||
&-drag-box,
|
||||
&-crop-box,
|
||||
&-modal {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-wrap-box,
|
||||
&-canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-drag-box {
|
||||
background-color: #fff;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&-modal {
|
||||
background-color: #000;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&-view-box {
|
||||
display: block;
|
||||
height: 100%;
|
||||
outline: 1px solid #39f;
|
||||
outline-color: rgba(51, 153, 255, 0.75);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-dashed {
|
||||
border: 0 dashed #eee;
|
||||
display: block;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
|
||||
&.dashed-h {
|
||||
border-bottom-width: 1px;
|
||||
border-top-width: 1px;
|
||||
height: calc(100% / 3);
|
||||
left: 0;
|
||||
top: calc(100% / 3);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.dashed-v {
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
height: 100%;
|
||||
left: calc(100% / 3);
|
||||
top: 0;
|
||||
width: calc(100% / 3);
|
||||
}
|
||||
}
|
||||
|
||||
&-center {
|
||||
display: block;
|
||||
height: 0;
|
||||
left: 50%;
|
||||
opacity: 0.75;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
background-color: #eee;
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&::before {
|
||||
height: 1px;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
height: 7px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&-face,
|
||||
&-line,
|
||||
&-point {
|
||||
display: block;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-face {
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&-line {
|
||||
background-color: #39f;
|
||||
|
||||
&.line-e {
|
||||
cursor: ew-resize;
|
||||
right: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&.line-n {
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.line-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&.line-s {
|
||||
bottom: -3px;
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-point {
|
||||
background-color: #39f;
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
|
||||
&.point-e {
|
||||
cursor: ew-resize;
|
||||
margin-top: -3px;
|
||||
right: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&.point-n {
|
||||
cursor: ns-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.point-w {
|
||||
cursor: ew-resize;
|
||||
left: -3px;
|
||||
margin-top: -3px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&.point-s {
|
||||
bottom: -3px;
|
||||
cursor: s-resize;
|
||||
left: 50%;
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
&.point-ne {
|
||||
cursor: nesw-resize;
|
||||
right: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.point-nw {
|
||||
cursor: nwse-resize;
|
||||
left: -3px;
|
||||
top: -3px;
|
||||
}
|
||||
|
||||
&.point-sw {
|
||||
bottom: -3px;
|
||||
cursor: nesw-resize;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
&.point-se {
|
||||
bottom: -3px;
|
||||
cursor: nwse-resize;
|
||||
height: 20px;
|
||||
opacity: 1;
|
||||
right: -3px;
|
||||
width: 20px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
height: 5px;
|
||||
opacity: 0.75;
|
||||
width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&.point-se::before {
|
||||
background-color: #39f;
|
||||
bottom: -50%;
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 200%;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
}
|
||||
}
|
||||
|
||||
&-invisible {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&-bg {
|
||||
background-image: url('#{$cropper-image-path}/bg.png');
|
||||
}
|
||||
|
||||
&-hide {
|
||||
display: block;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
&-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&-move {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&-crop {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
&-disabled &-drag-box,
|
||||
&-disabled &-face,
|
||||
&-disabled &-line,
|
||||
&-disabled &-point {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
BIN
node_modules/cropperjs/src/images/bg.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 168 B |
1
node_modules/cropperjs/src/index.css
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import "./css/cropper.css";
|
||||
3
node_modules/cropperjs/src/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import Cropper from './js/cropper';
|
||||
|
||||
export default Cropper;
|
||||
1
node_modules/cropperjs/src/index.scss
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import "./css/cropper";
|
||||
491
node_modules/cropperjs/src/js/change.js
generated
vendored
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
import {
|
||||
ACTION_ALL,
|
||||
ACTION_CROP,
|
||||
ACTION_EAST,
|
||||
ACTION_MOVE,
|
||||
ACTION_NORTH,
|
||||
ACTION_NORTH_EAST,
|
||||
ACTION_NORTH_WEST,
|
||||
ACTION_SOUTH,
|
||||
ACTION_SOUTH_EAST,
|
||||
ACTION_SOUTH_WEST,
|
||||
ACTION_WEST,
|
||||
ACTION_ZOOM,
|
||||
CLASS_HIDDEN,
|
||||
} from './constants';
|
||||
import {
|
||||
forEach,
|
||||
getMaxZoomRatio,
|
||||
getOffset,
|
||||
removeClass,
|
||||
} from './utilities';
|
||||
|
||||
export default {
|
||||
change(event) {
|
||||
const {
|
||||
options,
|
||||
canvasData,
|
||||
containerData,
|
||||
cropBoxData,
|
||||
pointers,
|
||||
} = this;
|
||||
let { action } = this;
|
||||
let { aspectRatio } = options;
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
} = cropBoxData;
|
||||
const right = left + width;
|
||||
const bottom = top + height;
|
||||
let minLeft = 0;
|
||||
let minTop = 0;
|
||||
let maxWidth = containerData.width;
|
||||
let maxHeight = containerData.height;
|
||||
let renderable = true;
|
||||
let offset;
|
||||
|
||||
// Locking aspect ratio in "free mode" by holding shift key
|
||||
if (!aspectRatio && event.shiftKey) {
|
||||
aspectRatio = width && height ? width / height : 1;
|
||||
}
|
||||
|
||||
if (this.limited) {
|
||||
({ minLeft, minTop } = cropBoxData);
|
||||
maxWidth = minLeft + Math.min(
|
||||
containerData.width,
|
||||
canvasData.width,
|
||||
canvasData.left + canvasData.width,
|
||||
);
|
||||
maxHeight = minTop + Math.min(
|
||||
containerData.height,
|
||||
canvasData.height,
|
||||
canvasData.top + canvasData.height,
|
||||
);
|
||||
}
|
||||
|
||||
const pointer = pointers[Object.keys(pointers)[0]];
|
||||
const range = {
|
||||
x: pointer.endX - pointer.startX,
|
||||
y: pointer.endY - pointer.startY,
|
||||
};
|
||||
const check = (side) => {
|
||||
switch (side) {
|
||||
case ACTION_EAST:
|
||||
if (right + range.x > maxWidth) {
|
||||
range.x = maxWidth - right;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_WEST:
|
||||
if (left + range.x < minLeft) {
|
||||
range.x = minLeft - left;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_NORTH:
|
||||
if (top + range.y < minTop) {
|
||||
range.y = minTop - top;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_SOUTH:
|
||||
if (bottom + range.y > maxHeight) {
|
||||
range.y = maxHeight - bottom;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
// Move crop box
|
||||
case ACTION_ALL:
|
||||
left += range.x;
|
||||
top += range.y;
|
||||
break;
|
||||
|
||||
// Resize crop box
|
||||
case ACTION_EAST:
|
||||
if (range.x >= 0 && (right >= maxWidth || (aspectRatio
|
||||
&& (top <= minTop || bottom >= maxHeight)))) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_EAST);
|
||||
width += range.x;
|
||||
|
||||
if (width < 0) {
|
||||
action = ACTION_WEST;
|
||||
width = -width;
|
||||
left -= width;
|
||||
}
|
||||
|
||||
if (aspectRatio) {
|
||||
height = width / aspectRatio;
|
||||
top += (cropBoxData.height - height) / 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_NORTH:
|
||||
if (range.y <= 0 && (top <= minTop || (aspectRatio
|
||||
&& (left <= minLeft || right >= maxWidth)))) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_NORTH);
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
|
||||
if (height < 0) {
|
||||
action = ACTION_SOUTH;
|
||||
height = -height;
|
||||
top -= height;
|
||||
}
|
||||
|
||||
if (aspectRatio) {
|
||||
width = height * aspectRatio;
|
||||
left += (cropBoxData.width - width) / 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_WEST:
|
||||
if (range.x <= 0 && (left <= minLeft || (aspectRatio
|
||||
&& (top <= minTop || bottom >= maxHeight)))) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_WEST);
|
||||
width -= range.x;
|
||||
left += range.x;
|
||||
|
||||
if (width < 0) {
|
||||
action = ACTION_EAST;
|
||||
width = -width;
|
||||
left -= width;
|
||||
}
|
||||
|
||||
if (aspectRatio) {
|
||||
height = width / aspectRatio;
|
||||
top += (cropBoxData.height - height) / 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_SOUTH:
|
||||
if (range.y >= 0 && (bottom >= maxHeight || (aspectRatio
|
||||
&& (left <= minLeft || right >= maxWidth)))) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_SOUTH);
|
||||
height += range.y;
|
||||
|
||||
if (height < 0) {
|
||||
action = ACTION_NORTH;
|
||||
height = -height;
|
||||
top -= height;
|
||||
}
|
||||
|
||||
if (aspectRatio) {
|
||||
width = height * aspectRatio;
|
||||
left += (cropBoxData.width - width) / 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_NORTH_EAST:
|
||||
if (aspectRatio) {
|
||||
if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_NORTH);
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
width = height * aspectRatio;
|
||||
} else {
|
||||
check(ACTION_NORTH);
|
||||
check(ACTION_EAST);
|
||||
|
||||
if (range.x >= 0) {
|
||||
if (right < maxWidth) {
|
||||
width += range.x;
|
||||
} else if (range.y <= 0 && top <= minTop) {
|
||||
renderable = false;
|
||||
}
|
||||
} else {
|
||||
width += range.x;
|
||||
}
|
||||
|
||||
if (range.y <= 0) {
|
||||
if (top > minTop) {
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
}
|
||||
} else {
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (width < 0 && height < 0) {
|
||||
action = ACTION_SOUTH_WEST;
|
||||
height = -height;
|
||||
width = -width;
|
||||
top -= height;
|
||||
left -= width;
|
||||
} else if (width < 0) {
|
||||
action = ACTION_NORTH_WEST;
|
||||
width = -width;
|
||||
left -= width;
|
||||
} else if (height < 0) {
|
||||
action = ACTION_SOUTH_EAST;
|
||||
height = -height;
|
||||
top -= height;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_NORTH_WEST:
|
||||
if (aspectRatio) {
|
||||
if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_NORTH);
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
width = height * aspectRatio;
|
||||
left += cropBoxData.width - width;
|
||||
} else {
|
||||
check(ACTION_NORTH);
|
||||
check(ACTION_WEST);
|
||||
|
||||
if (range.x <= 0) {
|
||||
if (left > minLeft) {
|
||||
width -= range.x;
|
||||
left += range.x;
|
||||
} else if (range.y <= 0 && top <= minTop) {
|
||||
renderable = false;
|
||||
}
|
||||
} else {
|
||||
width -= range.x;
|
||||
left += range.x;
|
||||
}
|
||||
|
||||
if (range.y <= 0) {
|
||||
if (top > minTop) {
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
}
|
||||
} else {
|
||||
height -= range.y;
|
||||
top += range.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (width < 0 && height < 0) {
|
||||
action = ACTION_SOUTH_EAST;
|
||||
height = -height;
|
||||
width = -width;
|
||||
top -= height;
|
||||
left -= width;
|
||||
} else if (width < 0) {
|
||||
action = ACTION_NORTH_EAST;
|
||||
width = -width;
|
||||
left -= width;
|
||||
} else if (height < 0) {
|
||||
action = ACTION_SOUTH_WEST;
|
||||
height = -height;
|
||||
top -= height;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_SOUTH_WEST:
|
||||
if (aspectRatio) {
|
||||
if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_WEST);
|
||||
width -= range.x;
|
||||
left += range.x;
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
check(ACTION_SOUTH);
|
||||
check(ACTION_WEST);
|
||||
|
||||
if (range.x <= 0) {
|
||||
if (left > minLeft) {
|
||||
width -= range.x;
|
||||
left += range.x;
|
||||
} else if (range.y >= 0 && bottom >= maxHeight) {
|
||||
renderable = false;
|
||||
}
|
||||
} else {
|
||||
width -= range.x;
|
||||
left += range.x;
|
||||
}
|
||||
|
||||
if (range.y >= 0) {
|
||||
if (bottom < maxHeight) {
|
||||
height += range.y;
|
||||
}
|
||||
} else {
|
||||
height += range.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (width < 0 && height < 0) {
|
||||
action = ACTION_NORTH_EAST;
|
||||
height = -height;
|
||||
width = -width;
|
||||
top -= height;
|
||||
left -= width;
|
||||
} else if (width < 0) {
|
||||
action = ACTION_SOUTH_EAST;
|
||||
width = -width;
|
||||
left -= width;
|
||||
} else if (height < 0) {
|
||||
action = ACTION_NORTH_WEST;
|
||||
height = -height;
|
||||
top -= height;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ACTION_SOUTH_EAST:
|
||||
if (aspectRatio) {
|
||||
if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
check(ACTION_EAST);
|
||||
width += range.x;
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
check(ACTION_SOUTH);
|
||||
check(ACTION_EAST);
|
||||
|
||||
if (range.x >= 0) {
|
||||
if (right < maxWidth) {
|
||||
width += range.x;
|
||||
} else if (range.y >= 0 && bottom >= maxHeight) {
|
||||
renderable = false;
|
||||
}
|
||||
} else {
|
||||
width += range.x;
|
||||
}
|
||||
|
||||
if (range.y >= 0) {
|
||||
if (bottom < maxHeight) {
|
||||
height += range.y;
|
||||
}
|
||||
} else {
|
||||
height += range.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (width < 0 && height < 0) {
|
||||
action = ACTION_NORTH_WEST;
|
||||
height = -height;
|
||||
width = -width;
|
||||
top -= height;
|
||||
left -= width;
|
||||
} else if (width < 0) {
|
||||
action = ACTION_SOUTH_WEST;
|
||||
width = -width;
|
||||
left -= width;
|
||||
} else if (height < 0) {
|
||||
action = ACTION_NORTH_EAST;
|
||||
height = -height;
|
||||
top -= height;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// Move canvas
|
||||
case ACTION_MOVE:
|
||||
this.move(range.x, range.y);
|
||||
renderable = false;
|
||||
break;
|
||||
|
||||
// Zoom canvas
|
||||
case ACTION_ZOOM:
|
||||
this.zoom(getMaxZoomRatio(pointers), event);
|
||||
renderable = false;
|
||||
break;
|
||||
|
||||
// Create crop box
|
||||
case ACTION_CROP:
|
||||
if (!range.x || !range.y) {
|
||||
renderable = false;
|
||||
break;
|
||||
}
|
||||
|
||||
offset = getOffset(this.cropper);
|
||||
left = pointer.startX - offset.left;
|
||||
top = pointer.startY - offset.top;
|
||||
width = cropBoxData.minWidth;
|
||||
height = cropBoxData.minHeight;
|
||||
|
||||
if (range.x > 0) {
|
||||
action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
|
||||
} else if (range.x < 0) {
|
||||
left -= width;
|
||||
action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
|
||||
}
|
||||
|
||||
if (range.y < 0) {
|
||||
top -= height;
|
||||
}
|
||||
|
||||
// Show the crop box if is hidden
|
||||
if (!this.cropped) {
|
||||
removeClass(this.cropBox, CLASS_HIDDEN);
|
||||
this.cropped = true;
|
||||
|
||||
if (this.limited) {
|
||||
this.limitCropBox(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
if (renderable) {
|
||||
cropBoxData.width = width;
|
||||
cropBoxData.height = height;
|
||||
cropBoxData.left = left;
|
||||
cropBoxData.top = top;
|
||||
this.action = action;
|
||||
this.renderCropBox();
|
||||
}
|
||||
|
||||
// Override
|
||||
forEach(pointers, (p) => {
|
||||
p.startX = p.endX;
|
||||
p.startY = p.endY;
|
||||
});
|
||||
},
|
||||
};
|
||||
68
node_modules/cropperjs/src/js/constants.js
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
export const IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
||||
export const WINDOW = IS_BROWSER ? window : {};
|
||||
export const IS_TOUCH_DEVICE = IS_BROWSER && WINDOW.document.documentElement ? 'ontouchstart' in WINDOW.document.documentElement : false;
|
||||
export const HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;
|
||||
export const NAMESPACE = 'cropper';
|
||||
|
||||
// Actions
|
||||
export const ACTION_ALL = 'all';
|
||||
export const ACTION_CROP = 'crop';
|
||||
export const ACTION_MOVE = 'move';
|
||||
export const ACTION_ZOOM = 'zoom';
|
||||
export const ACTION_EAST = 'e';
|
||||
export const ACTION_WEST = 'w';
|
||||
export const ACTION_SOUTH = 's';
|
||||
export const ACTION_NORTH = 'n';
|
||||
export const ACTION_NORTH_EAST = 'ne';
|
||||
export const ACTION_NORTH_WEST = 'nw';
|
||||
export const ACTION_SOUTH_EAST = 'se';
|
||||
export const ACTION_SOUTH_WEST = 'sw';
|
||||
|
||||
// Classes
|
||||
export const CLASS_CROP = `${NAMESPACE}-crop`;
|
||||
export const CLASS_DISABLED = `${NAMESPACE}-disabled`;
|
||||
export const CLASS_HIDDEN = `${NAMESPACE}-hidden`;
|
||||
export const CLASS_HIDE = `${NAMESPACE}-hide`;
|
||||
export const CLASS_INVISIBLE = `${NAMESPACE}-invisible`;
|
||||
export const CLASS_MODAL = `${NAMESPACE}-modal`;
|
||||
export const CLASS_MOVE = `${NAMESPACE}-move`;
|
||||
|
||||
// Data keys
|
||||
export const DATA_ACTION = `${NAMESPACE}Action`;
|
||||
export const DATA_PREVIEW = `${NAMESPACE}Preview`;
|
||||
|
||||
// Drag modes
|
||||
export const DRAG_MODE_CROP = 'crop';
|
||||
export const DRAG_MODE_MOVE = 'move';
|
||||
export const DRAG_MODE_NONE = 'none';
|
||||
|
||||
// Events
|
||||
export const EVENT_CROP = 'crop';
|
||||
export const EVENT_CROP_END = 'cropend';
|
||||
export const EVENT_CROP_MOVE = 'cropmove';
|
||||
export const EVENT_CROP_START = 'cropstart';
|
||||
export const EVENT_DBLCLICK = 'dblclick';
|
||||
export const EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
|
||||
export const EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
|
||||
export const EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';
|
||||
export const EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
|
||||
export const EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
|
||||
export const EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
|
||||
export const EVENT_READY = 'ready';
|
||||
export const EVENT_RESIZE = 'resize';
|
||||
export const EVENT_WHEEL = 'wheel';
|
||||
export const EVENT_ZOOM = 'zoom';
|
||||
|
||||
// Mime types
|
||||
export const MIME_TYPE_JPEG = 'image/jpeg';
|
||||
|
||||
// RegExps
|
||||
export const REGEXP_ACTIONS = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/;
|
||||
export const REGEXP_DATA_URL = /^data:/;
|
||||
export const REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;
|
||||
export const REGEXP_TAG_NAME = /^img|canvas$/i;
|
||||
|
||||
// Misc
|
||||
// Inspired by the default width and height of a canvas element.
|
||||
export const MIN_CONTAINER_WIDTH = 200;
|
||||
export const MIN_CONTAINER_HEIGHT = 100;
|
||||
454
node_modules/cropperjs/src/js/cropper.js
generated
vendored
Normal file
|
|
@ -0,0 +1,454 @@
|
|||
import DEFAULTS from './defaults';
|
||||
import TEMPLATE from './template';
|
||||
import render from './render';
|
||||
import preview from './preview';
|
||||
import events from './events';
|
||||
import handlers from './handlers';
|
||||
import change from './change';
|
||||
import methods from './methods';
|
||||
import {
|
||||
ACTION_ALL,
|
||||
CLASS_HIDDEN,
|
||||
CLASS_HIDE,
|
||||
CLASS_INVISIBLE,
|
||||
CLASS_MOVE,
|
||||
DATA_ACTION,
|
||||
EVENT_READY,
|
||||
MIME_TYPE_JPEG,
|
||||
NAMESPACE,
|
||||
REGEXP_DATA_URL,
|
||||
REGEXP_DATA_URL_JPEG,
|
||||
REGEXP_TAG_NAME,
|
||||
WINDOW,
|
||||
} from './constants';
|
||||
import {
|
||||
addClass,
|
||||
addListener,
|
||||
addTimestamp,
|
||||
arrayBufferToDataURL,
|
||||
assign,
|
||||
dataURLToArrayBuffer,
|
||||
dispatchEvent,
|
||||
isCrossOriginURL,
|
||||
isFunction,
|
||||
isPlainObject,
|
||||
parseOrientation,
|
||||
removeClass,
|
||||
resetAndGetOrientation,
|
||||
setData,
|
||||
} from './utilities';
|
||||
|
||||
const AnotherCropper = WINDOW.Cropper;
|
||||
|
||||
class Cropper {
|
||||
/**
|
||||
* Create a new Cropper.
|
||||
* @param {Element} element - The target element for cropping.
|
||||
* @param {Object} [options={}] - The configuration options.
|
||||
*/
|
||||
constructor(element, options = {}) {
|
||||
if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {
|
||||
throw new Error('The first argument is required and must be an <img> or <canvas> element.');
|
||||
}
|
||||
|
||||
this.element = element;
|
||||
this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
|
||||
this.cropped = false;
|
||||
this.disabled = false;
|
||||
this.pointers = {};
|
||||
this.ready = false;
|
||||
this.reloading = false;
|
||||
this.replaced = false;
|
||||
this.sized = false;
|
||||
this.sizing = false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const { element } = this;
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
let url;
|
||||
|
||||
if (element[NAMESPACE]) {
|
||||
return;
|
||||
}
|
||||
|
||||
element[NAMESPACE] = this;
|
||||
|
||||
if (tagName === 'img') {
|
||||
this.isImg = true;
|
||||
|
||||
// e.g.: "img/picture.jpg"
|
||||
url = element.getAttribute('src') || '';
|
||||
this.originalUrl = url;
|
||||
|
||||
// Stop when it's a blank image
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
// e.g.: "https://example.com/img/picture.jpg"
|
||||
url = element.src;
|
||||
} else if (tagName === 'canvas' && window.HTMLCanvasElement) {
|
||||
url = element.toDataURL();
|
||||
}
|
||||
|
||||
this.load(url);
|
||||
}
|
||||
|
||||
load(url) {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.url = url;
|
||||
this.imageData = {};
|
||||
|
||||
const { element, options } = this;
|
||||
|
||||
if (!options.rotatable && !options.scalable) {
|
||||
options.checkOrientation = false;
|
||||
}
|
||||
|
||||
// Only IE10+ supports Typed Arrays
|
||||
if (!options.checkOrientation || !window.ArrayBuffer) {
|
||||
this.clone();
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect the mime type of the image directly if it is a Data URL
|
||||
if (REGEXP_DATA_URL.test(url)) {
|
||||
// Read ArrayBuffer from Data URL of JPEG images directly for better performance
|
||||
if (REGEXP_DATA_URL_JPEG.test(url)) {
|
||||
this.read(dataURLToArrayBuffer(url));
|
||||
} else {
|
||||
// Only a JPEG image may contains Exif Orientation information,
|
||||
// the rest types of Data URLs are not necessary to check orientation at all.
|
||||
this.clone();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Detect the mime type of the image by a XMLHttpRequest.
|
||||
// 2. Load the image as ArrayBuffer for reading orientation if its a JPEG image.
|
||||
const xhr = new XMLHttpRequest();
|
||||
const clone = this.clone.bind(this);
|
||||
|
||||
this.reloading = true;
|
||||
this.xhr = xhr;
|
||||
|
||||
// 1. Cross origin requests are only supported for protocol schemes:
|
||||
// http, https, data, chrome, chrome-extension.
|
||||
// 2. Access to XMLHttpRequest from a Data URL will be blocked by CORS policy
|
||||
// in some browsers as IE11 and Safari.
|
||||
xhr.onabort = clone;
|
||||
xhr.onerror = clone;
|
||||
xhr.ontimeout = clone;
|
||||
|
||||
xhr.onprogress = () => {
|
||||
// Abort the request directly if it not a JPEG image for better performance
|
||||
if (xhr.getResponseHeader('content-type') !== MIME_TYPE_JPEG) {
|
||||
xhr.abort();
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onload = () => {
|
||||
this.read(xhr.response);
|
||||
};
|
||||
|
||||
xhr.onloadend = () => {
|
||||
this.reloading = false;
|
||||
this.xhr = null;
|
||||
};
|
||||
|
||||
// Bust cache when there is a "crossOrigin" property to avoid browser cache error
|
||||
if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {
|
||||
url = addTimestamp(url);
|
||||
}
|
||||
|
||||
// The third parameter is required for avoiding side-effect (#682)
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.withCredentials = element.crossOrigin === 'use-credentials';
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
read(arrayBuffer) {
|
||||
const { options, imageData } = this;
|
||||
|
||||
// Reset the orientation value to its default value 1
|
||||
// as some iOS browsers will render image with its orientation
|
||||
const orientation = resetAndGetOrientation(arrayBuffer);
|
||||
let rotate = 0;
|
||||
let scaleX = 1;
|
||||
let scaleY = 1;
|
||||
|
||||
if (orientation > 1) {
|
||||
// Generate a new URL which has the default orientation value
|
||||
this.url = arrayBufferToDataURL(arrayBuffer, MIME_TYPE_JPEG);
|
||||
({ rotate, scaleX, scaleY } = parseOrientation(orientation));
|
||||
}
|
||||
|
||||
if (options.rotatable) {
|
||||
imageData.rotate = rotate;
|
||||
}
|
||||
|
||||
if (options.scalable) {
|
||||
imageData.scaleX = scaleX;
|
||||
imageData.scaleY = scaleY;
|
||||
}
|
||||
|
||||
this.clone();
|
||||
}
|
||||
|
||||
clone() {
|
||||
const { element, url } = this;
|
||||
let { crossOrigin } = element;
|
||||
let crossOriginUrl = url;
|
||||
|
||||
if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {
|
||||
if (!crossOrigin) {
|
||||
crossOrigin = 'anonymous';
|
||||
}
|
||||
|
||||
// Bust cache when there is not a "crossOrigin" property (#519)
|
||||
crossOriginUrl = addTimestamp(url);
|
||||
}
|
||||
|
||||
this.crossOrigin = crossOrigin;
|
||||
this.crossOriginUrl = crossOriginUrl;
|
||||
|
||||
const image = document.createElement('img');
|
||||
|
||||
if (crossOrigin) {
|
||||
image.crossOrigin = crossOrigin;
|
||||
}
|
||||
|
||||
image.src = crossOriginUrl || url;
|
||||
image.alt = element.alt || 'The image to crop';
|
||||
this.image = image;
|
||||
image.onload = this.start.bind(this);
|
||||
image.onerror = this.stop.bind(this);
|
||||
addClass(image, CLASS_HIDE);
|
||||
element.parentNode.insertBefore(image, element.nextSibling);
|
||||
}
|
||||
|
||||
start() {
|
||||
const { image } = this;
|
||||
|
||||
image.onload = null;
|
||||
image.onerror = null;
|
||||
this.sizing = true;
|
||||
|
||||
// Match all browsers that use WebKit as the layout engine in iOS devices,
|
||||
// such as Safari for iOS, Chrome for iOS, and in-app browsers.
|
||||
const isIOSWebKit = WINDOW.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(WINDOW.navigator.userAgent);
|
||||
const done = (naturalWidth, naturalHeight) => {
|
||||
assign(this.imageData, {
|
||||
naturalWidth,
|
||||
naturalHeight,
|
||||
aspectRatio: naturalWidth / naturalHeight,
|
||||
});
|
||||
this.initialImageData = assign({}, this.imageData);
|
||||
this.sizing = false;
|
||||
this.sized = true;
|
||||
this.build();
|
||||
};
|
||||
|
||||
// Most modern browsers (excepts iOS WebKit)
|
||||
if (image.naturalWidth && !isIOSWebKit) {
|
||||
done(image.naturalWidth, image.naturalHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
const sizingImage = document.createElement('img');
|
||||
const body = document.body || document.documentElement;
|
||||
|
||||
this.sizingImage = sizingImage;
|
||||
|
||||
sizingImage.onload = () => {
|
||||
done(sizingImage.width, sizingImage.height);
|
||||
|
||||
if (!isIOSWebKit) {
|
||||
body.removeChild(sizingImage);
|
||||
}
|
||||
};
|
||||
|
||||
sizingImage.src = image.src;
|
||||
|
||||
// iOS WebKit will convert the image automatically
|
||||
// with its orientation once append it into DOM (#279)
|
||||
if (!isIOSWebKit) {
|
||||
sizingImage.style.cssText = (
|
||||
'left:0;'
|
||||
+ 'max-height:none!important;'
|
||||
+ 'max-width:none!important;'
|
||||
+ 'min-height:0!important;'
|
||||
+ 'min-width:0!important;'
|
||||
+ 'opacity:0;'
|
||||
+ 'position:absolute;'
|
||||
+ 'top:0;'
|
||||
+ 'z-index:-1;'
|
||||
);
|
||||
body.appendChild(sizingImage);
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
const { image } = this;
|
||||
|
||||
image.onload = null;
|
||||
image.onerror = null;
|
||||
image.parentNode.removeChild(image);
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
build() {
|
||||
if (!this.sized || this.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { element, options, image } = this;
|
||||
|
||||
// Create cropper elements
|
||||
const container = element.parentNode;
|
||||
const template = document.createElement('div');
|
||||
|
||||
template.innerHTML = TEMPLATE;
|
||||
|
||||
const cropper = template.querySelector(`.${NAMESPACE}-container`);
|
||||
const canvas = cropper.querySelector(`.${NAMESPACE}-canvas`);
|
||||
const dragBox = cropper.querySelector(`.${NAMESPACE}-drag-box`);
|
||||
const cropBox = cropper.querySelector(`.${NAMESPACE}-crop-box`);
|
||||
const face = cropBox.querySelector(`.${NAMESPACE}-face`);
|
||||
|
||||
this.container = container;
|
||||
this.cropper = cropper;
|
||||
this.canvas = canvas;
|
||||
this.dragBox = dragBox;
|
||||
this.cropBox = cropBox;
|
||||
this.viewBox = cropper.querySelector(`.${NAMESPACE}-view-box`);
|
||||
this.face = face;
|
||||
|
||||
canvas.appendChild(image);
|
||||
|
||||
// Hide the original image
|
||||
addClass(element, CLASS_HIDDEN);
|
||||
|
||||
// Inserts the cropper after to the current image
|
||||
container.insertBefore(cropper, element.nextSibling);
|
||||
|
||||
// Show the hidden image
|
||||
removeClass(image, CLASS_HIDE);
|
||||
|
||||
this.initPreview();
|
||||
this.bind();
|
||||
|
||||
options.initialAspectRatio = Math.max(0, options.initialAspectRatio) || NaN;
|
||||
options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
|
||||
options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;
|
||||
|
||||
addClass(cropBox, CLASS_HIDDEN);
|
||||
|
||||
if (!options.guides) {
|
||||
addClass(cropBox.getElementsByClassName(`${NAMESPACE}-dashed`), CLASS_HIDDEN);
|
||||
}
|
||||
|
||||
if (!options.center) {
|
||||
addClass(cropBox.getElementsByClassName(`${NAMESPACE}-center`), CLASS_HIDDEN);
|
||||
}
|
||||
|
||||
if (options.background) {
|
||||
addClass(cropper, `${NAMESPACE}-bg`);
|
||||
}
|
||||
|
||||
if (!options.highlight) {
|
||||
addClass(face, CLASS_INVISIBLE);
|
||||
}
|
||||
|
||||
if (options.cropBoxMovable) {
|
||||
addClass(face, CLASS_MOVE);
|
||||
setData(face, DATA_ACTION, ACTION_ALL);
|
||||
}
|
||||
|
||||
if (!options.cropBoxResizable) {
|
||||
addClass(cropBox.getElementsByClassName(`${NAMESPACE}-line`), CLASS_HIDDEN);
|
||||
addClass(cropBox.getElementsByClassName(`${NAMESPACE}-point`), CLASS_HIDDEN);
|
||||
}
|
||||
|
||||
this.render();
|
||||
this.ready = true;
|
||||
this.setDragMode(options.dragMode);
|
||||
|
||||
if (options.autoCrop) {
|
||||
this.crop();
|
||||
}
|
||||
|
||||
this.setData(options.data);
|
||||
|
||||
if (isFunction(options.ready)) {
|
||||
addListener(element, EVENT_READY, options.ready, {
|
||||
once: true,
|
||||
});
|
||||
}
|
||||
|
||||
dispatchEvent(element, EVENT_READY);
|
||||
}
|
||||
|
||||
unbuild() {
|
||||
if (!this.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ready = false;
|
||||
this.unbind();
|
||||
this.resetPreview();
|
||||
|
||||
const { parentNode } = this.cropper;
|
||||
|
||||
if (parentNode) {
|
||||
parentNode.removeChild(this.cropper);
|
||||
}
|
||||
|
||||
removeClass(this.element, CLASS_HIDDEN);
|
||||
}
|
||||
|
||||
uncreate() {
|
||||
if (this.ready) {
|
||||
this.unbuild();
|
||||
this.ready = false;
|
||||
this.cropped = false;
|
||||
} else if (this.sizing) {
|
||||
this.sizingImage.onload = null;
|
||||
this.sizing = false;
|
||||
this.sized = false;
|
||||
} else if (this.reloading) {
|
||||
this.xhr.onabort = null;
|
||||
this.xhr.abort();
|
||||
} else if (this.image) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the no conflict cropper class.
|
||||
* @returns {Cropper} The cropper class.
|
||||
*/
|
||||
static noConflict() {
|
||||
window.Cropper = AnotherCropper;
|
||||
return Cropper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the default options.
|
||||
* @param {Object} options - The new default options.
|
||||
*/
|
||||
static setDefaults(options) {
|
||||
assign(DEFAULTS, isPlainObject(options) && options);
|
||||
}
|
||||
}
|
||||
|
||||
assign(Cropper.prototype, render, preview, events, handlers, change, methods);
|
||||
|
||||
export default Cropper;
|
||||
104
node_modules/cropperjs/src/js/defaults.js
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import {
|
||||
DRAG_MODE_CROP,
|
||||
MIN_CONTAINER_HEIGHT,
|
||||
MIN_CONTAINER_WIDTH,
|
||||
} from './constants';
|
||||
|
||||
export default {
|
||||
// Define the view mode of the cropper
|
||||
viewMode: 0, // 0, 1, 2, 3
|
||||
|
||||
// Define the dragging mode of the cropper
|
||||
dragMode: DRAG_MODE_CROP, // 'crop', 'move' or 'none'
|
||||
|
||||
// Define the initial aspect ratio of the crop box
|
||||
initialAspectRatio: NaN,
|
||||
|
||||
// Define the aspect ratio of the crop box
|
||||
aspectRatio: NaN,
|
||||
|
||||
// An object with the previous cropping result data
|
||||
data: null,
|
||||
|
||||
// A selector for adding extra containers to preview
|
||||
preview: '',
|
||||
|
||||
// Re-render the cropper when resize the window
|
||||
responsive: true,
|
||||
|
||||
// Restore the cropped area after resize the window
|
||||
restore: true,
|
||||
|
||||
// Check if the current image is a cross-origin image
|
||||
checkCrossOrigin: true,
|
||||
|
||||
// Check the current image's Exif Orientation information
|
||||
checkOrientation: true,
|
||||
|
||||
// Show the black modal
|
||||
modal: true,
|
||||
|
||||
// Show the dashed lines for guiding
|
||||
guides: true,
|
||||
|
||||
// Show the center indicator for guiding
|
||||
center: true,
|
||||
|
||||
// Show the white modal to highlight the crop box
|
||||
highlight: true,
|
||||
|
||||
// Show the grid background
|
||||
background: true,
|
||||
|
||||
// Enable to crop the image automatically when initialize
|
||||
autoCrop: true,
|
||||
|
||||
// Define the percentage of automatic cropping area when initializes
|
||||
autoCropArea: 0.8,
|
||||
|
||||
// Enable to move the image
|
||||
movable: true,
|
||||
|
||||
// Enable to rotate the image
|
||||
rotatable: true,
|
||||
|
||||
// Enable to scale the image
|
||||
scalable: true,
|
||||
|
||||
// Enable to zoom the image
|
||||
zoomable: true,
|
||||
|
||||
// Enable to zoom the image by dragging touch
|
||||
zoomOnTouch: true,
|
||||
|
||||
// Enable to zoom the image by wheeling mouse
|
||||
zoomOnWheel: true,
|
||||
|
||||
// Define zoom ratio when zoom the image by wheeling mouse
|
||||
wheelZoomRatio: 0.1,
|
||||
|
||||
// Enable to move the crop box
|
||||
cropBoxMovable: true,
|
||||
|
||||
// Enable to resize the crop box
|
||||
cropBoxResizable: true,
|
||||
|
||||
// Toggle drag mode between "crop" and "move" when click twice on the cropper
|
||||
toggleDragModeOnDblclick: true,
|
||||
|
||||
// Size limitation
|
||||
minCanvasWidth: 0,
|
||||
minCanvasHeight: 0,
|
||||
minCropBoxWidth: 0,
|
||||
minCropBoxHeight: 0,
|
||||
minContainerWidth: MIN_CONTAINER_WIDTH,
|
||||
minContainerHeight: MIN_CONTAINER_HEIGHT,
|
||||
|
||||
// Shortcuts of events
|
||||
ready: null,
|
||||
cropstart: null,
|
||||
cropmove: null,
|
||||
cropend: null,
|
||||
crop: null,
|
||||
zoom: null,
|
||||
};
|
||||
116
node_modules/cropperjs/src/js/events.js
generated
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import {
|
||||
EVENT_CROP,
|
||||
EVENT_CROP_END,
|
||||
EVENT_CROP_MOVE,
|
||||
EVENT_CROP_START,
|
||||
EVENT_DBLCLICK,
|
||||
EVENT_POINTER_DOWN,
|
||||
EVENT_POINTER_MOVE,
|
||||
EVENT_POINTER_UP,
|
||||
EVENT_RESIZE,
|
||||
EVENT_WHEEL,
|
||||
EVENT_ZOOM,
|
||||
} from './constants';
|
||||
import {
|
||||
addListener,
|
||||
isFunction,
|
||||
removeListener,
|
||||
} from './utilities';
|
||||
|
||||
export default {
|
||||
bind() {
|
||||
const { element, options, cropper } = this;
|
||||
|
||||
if (isFunction(options.cropstart)) {
|
||||
addListener(element, EVENT_CROP_START, options.cropstart);
|
||||
}
|
||||
|
||||
if (isFunction(options.cropmove)) {
|
||||
addListener(element, EVENT_CROP_MOVE, options.cropmove);
|
||||
}
|
||||
|
||||
if (isFunction(options.cropend)) {
|
||||
addListener(element, EVENT_CROP_END, options.cropend);
|
||||
}
|
||||
|
||||
if (isFunction(options.crop)) {
|
||||
addListener(element, EVENT_CROP, options.crop);
|
||||
}
|
||||
|
||||
if (isFunction(options.zoom)) {
|
||||
addListener(element, EVENT_ZOOM, options.zoom);
|
||||
}
|
||||
|
||||
addListener(cropper, EVENT_POINTER_DOWN, (this.onCropStart = this.cropStart.bind(this)));
|
||||
|
||||
if (options.zoomable && options.zoomOnWheel) {
|
||||
addListener(cropper, EVENT_WHEEL, (this.onWheel = this.wheel.bind(this)), {
|
||||
passive: false,
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (options.toggleDragModeOnDblclick) {
|
||||
addListener(cropper, EVENT_DBLCLICK, (this.onDblclick = this.dblclick.bind(this)));
|
||||
}
|
||||
|
||||
addListener(
|
||||
element.ownerDocument,
|
||||
EVENT_POINTER_MOVE,
|
||||
(this.onCropMove = this.cropMove.bind(this)),
|
||||
);
|
||||
addListener(
|
||||
element.ownerDocument,
|
||||
EVENT_POINTER_UP,
|
||||
(this.onCropEnd = this.cropEnd.bind(this)),
|
||||
);
|
||||
|
||||
if (options.responsive) {
|
||||
addListener(window, EVENT_RESIZE, (this.onResize = this.resize.bind(this)));
|
||||
}
|
||||
},
|
||||
|
||||
unbind() {
|
||||
const { element, options, cropper } = this;
|
||||
|
||||
if (isFunction(options.cropstart)) {
|
||||
removeListener(element, EVENT_CROP_START, options.cropstart);
|
||||
}
|
||||
|
||||
if (isFunction(options.cropmove)) {
|
||||
removeListener(element, EVENT_CROP_MOVE, options.cropmove);
|
||||
}
|
||||
|
||||
if (isFunction(options.cropend)) {
|
||||
removeListener(element, EVENT_CROP_END, options.cropend);
|
||||
}
|
||||
|
||||
if (isFunction(options.crop)) {
|
||||
removeListener(element, EVENT_CROP, options.crop);
|
||||
}
|
||||
|
||||
if (isFunction(options.zoom)) {
|
||||
removeListener(element, EVENT_ZOOM, options.zoom);
|
||||
}
|
||||
|
||||
removeListener(cropper, EVENT_POINTER_DOWN, this.onCropStart);
|
||||
|
||||
if (options.zoomable && options.zoomOnWheel) {
|
||||
removeListener(cropper, EVENT_WHEEL, this.onWheel, {
|
||||
passive: false,
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (options.toggleDragModeOnDblclick) {
|
||||
removeListener(cropper, EVENT_DBLCLICK, this.onDblclick);
|
||||
}
|
||||
|
||||
removeListener(element.ownerDocument, EVENT_POINTER_MOVE, this.onCropMove);
|
||||
removeListener(element.ownerDocument, EVENT_POINTER_UP, this.onCropEnd);
|
||||
|
||||
if (options.responsive) {
|
||||
removeListener(window, EVENT_RESIZE, this.onResize);
|
||||
}
|
||||
},
|
||||
};
|
||||
230
node_modules/cropperjs/src/js/handlers.js
generated
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
import {
|
||||
ACTION_CROP,
|
||||
ACTION_ZOOM,
|
||||
CLASS_CROP,
|
||||
CLASS_MODAL,
|
||||
DATA_ACTION,
|
||||
DRAG_MODE_CROP,
|
||||
DRAG_MODE_MOVE,
|
||||
DRAG_MODE_NONE,
|
||||
EVENT_CROP_END,
|
||||
EVENT_CROP_MOVE,
|
||||
EVENT_CROP_START,
|
||||
REGEXP_ACTIONS,
|
||||
} from './constants';
|
||||
import {
|
||||
addClass,
|
||||
assign,
|
||||
dispatchEvent,
|
||||
forEach,
|
||||
getData,
|
||||
getPointer,
|
||||
hasClass,
|
||||
isNumber,
|
||||
toggleClass,
|
||||
} from './utilities';
|
||||
|
||||
export default {
|
||||
resize() {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { options, container, containerData } = this;
|
||||
const ratioX = container.offsetWidth / containerData.width;
|
||||
const ratioY = container.offsetHeight / containerData.height;
|
||||
const ratio = Math.abs(ratioX - 1) > Math.abs(ratioY - 1) ? ratioX : ratioY;
|
||||
|
||||
// Resize when width changed or height changed
|
||||
if (ratio !== 1) {
|
||||
let canvasData;
|
||||
let cropBoxData;
|
||||
|
||||
if (options.restore) {
|
||||
canvasData = this.getCanvasData();
|
||||
cropBoxData = this.getCropBoxData();
|
||||
}
|
||||
|
||||
this.render();
|
||||
|
||||
if (options.restore) {
|
||||
this.setCanvasData(forEach(canvasData, (n, i) => {
|
||||
canvasData[i] = n * ratio;
|
||||
}));
|
||||
this.setCropBoxData(forEach(cropBoxData, (n, i) => {
|
||||
cropBoxData[i] = n * ratio;
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
dblclick() {
|
||||
if (this.disabled || this.options.dragMode === DRAG_MODE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setDragMode(hasClass(this.dragBox, CLASS_CROP) ? DRAG_MODE_MOVE : DRAG_MODE_CROP);
|
||||
},
|
||||
|
||||
wheel(event) {
|
||||
const ratio = Number(this.options.wheelZoomRatio) || 0.1;
|
||||
let delta = 1;
|
||||
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// Limit wheel speed to prevent zoom too fast (#21)
|
||||
if (this.wheeling) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.wheeling = true;
|
||||
|
||||
setTimeout(() => {
|
||||
this.wheeling = false;
|
||||
}, 50);
|
||||
|
||||
if (event.deltaY) {
|
||||
delta = event.deltaY > 0 ? 1 : -1;
|
||||
} else if (event.wheelDelta) {
|
||||
delta = -event.wheelDelta / 120;
|
||||
} else if (event.detail) {
|
||||
delta = event.detail > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
this.zoom(-delta * ratio, event);
|
||||
},
|
||||
|
||||
cropStart(event) {
|
||||
const { buttons, button } = event;
|
||||
|
||||
if (
|
||||
this.disabled
|
||||
|
||||
// Handle mouse event and pointer event and ignore touch event
|
||||
|| ((
|
||||
event.type === 'mousedown'
|
||||
|| (event.type === 'pointerdown' && event.pointerType === 'mouse')
|
||||
) && (
|
||||
// No primary button (Usually the left button)
|
||||
(isNumber(buttons) && buttons !== 1)
|
||||
|| (isNumber(button) && button !== 0)
|
||||
|
||||
// Open context menu
|
||||
|| event.ctrlKey
|
||||
))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { options, pointers } = this;
|
||||
let action;
|
||||
|
||||
if (event.changedTouches) {
|
||||
// Handle touch event
|
||||
forEach(event.changedTouches, (touch) => {
|
||||
pointers[touch.identifier] = getPointer(touch);
|
||||
});
|
||||
} else {
|
||||
// Handle mouse event and pointer event
|
||||
pointers[event.pointerId || 0] = getPointer(event);
|
||||
}
|
||||
|
||||
if (Object.keys(pointers).length > 1 && options.zoomable && options.zoomOnTouch) {
|
||||
action = ACTION_ZOOM;
|
||||
} else {
|
||||
action = getData(event.target, DATA_ACTION);
|
||||
}
|
||||
|
||||
if (!REGEXP_ACTIONS.test(action)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispatchEvent(this.element, EVENT_CROP_START, {
|
||||
originalEvent: event,
|
||||
action,
|
||||
}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This line is required for preventing page zooming in iOS browsers
|
||||
event.preventDefault();
|
||||
|
||||
this.action = action;
|
||||
this.cropping = false;
|
||||
|
||||
if (action === ACTION_CROP) {
|
||||
this.cropping = true;
|
||||
addClass(this.dragBox, CLASS_MODAL);
|
||||
}
|
||||
},
|
||||
|
||||
cropMove(event) {
|
||||
const { action } = this;
|
||||
|
||||
if (this.disabled || !action) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { pointers } = this;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (dispatchEvent(this.element, EVENT_CROP_MOVE, {
|
||||
originalEvent: event,
|
||||
action,
|
||||
}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.changedTouches) {
|
||||
forEach(event.changedTouches, (touch) => {
|
||||
// The first parameter should not be undefined (#432)
|
||||
assign(pointers[touch.identifier] || {}, getPointer(touch, true));
|
||||
});
|
||||
} else {
|
||||
assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
|
||||
}
|
||||
|
||||
this.change(event);
|
||||
},
|
||||
|
||||
cropEnd(event) {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { action, pointers } = this;
|
||||
|
||||
if (event.changedTouches) {
|
||||
forEach(event.changedTouches, (touch) => {
|
||||
delete pointers[touch.identifier];
|
||||
});
|
||||
} else {
|
||||
delete pointers[event.pointerId || 0];
|
||||
}
|
||||
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (!Object.keys(pointers).length) {
|
||||
this.action = '';
|
||||
}
|
||||
|
||||
if (this.cropping) {
|
||||
this.cropping = false;
|
||||
toggleClass(this.dragBox, CLASS_MODAL, this.cropped && this.options.modal);
|
||||
}
|
||||
|
||||
dispatchEvent(this.element, EVENT_CROP_END, {
|
||||
originalEvent: event,
|
||||
action,
|
||||
});
|
||||
},
|
||||
};
|
||||
834
node_modules/cropperjs/src/js/methods.js
generated
vendored
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
import {
|
||||
CLASS_CROP,
|
||||
CLASS_DISABLED,
|
||||
CLASS_HIDDEN,
|
||||
CLASS_MODAL,
|
||||
CLASS_MOVE,
|
||||
DATA_ACTION,
|
||||
DRAG_MODE_CROP,
|
||||
DRAG_MODE_MOVE,
|
||||
DRAG_MODE_NONE,
|
||||
EVENT_ZOOM,
|
||||
NAMESPACE,
|
||||
} from './constants';
|
||||
import {
|
||||
addClass,
|
||||
assign,
|
||||
dispatchEvent,
|
||||
forEach,
|
||||
getAdjustedSizes,
|
||||
getOffset,
|
||||
getPointersCenter,
|
||||
getSourceCanvas,
|
||||
isNumber,
|
||||
isPlainObject,
|
||||
isUndefined,
|
||||
normalizeDecimalNumber,
|
||||
removeClass,
|
||||
setData,
|
||||
toggleClass,
|
||||
} from './utilities';
|
||||
|
||||
export default {
|
||||
// Show the crop box manually
|
||||
crop() {
|
||||
if (this.ready && !this.cropped && !this.disabled) {
|
||||
this.cropped = true;
|
||||
this.limitCropBox(true, true);
|
||||
|
||||
if (this.options.modal) {
|
||||
addClass(this.dragBox, CLASS_MODAL);
|
||||
}
|
||||
|
||||
removeClass(this.cropBox, CLASS_HIDDEN);
|
||||
this.setCropBoxData(this.initialCropBoxData);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Reset the image and crop box to their initial states
|
||||
reset() {
|
||||
if (this.ready && !this.disabled) {
|
||||
this.imageData = assign({}, this.initialImageData);
|
||||
this.canvasData = assign({}, this.initialCanvasData);
|
||||
this.cropBoxData = assign({}, this.initialCropBoxData);
|
||||
this.renderCanvas();
|
||||
|
||||
if (this.cropped) {
|
||||
this.renderCropBox();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Clear the crop box
|
||||
clear() {
|
||||
if (this.cropped && !this.disabled) {
|
||||
assign(this.cropBoxData, {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
this.cropped = false;
|
||||
this.renderCropBox();
|
||||
this.limitCanvas(true, true);
|
||||
|
||||
// Render canvas after crop box rendered
|
||||
this.renderCanvas();
|
||||
removeClass(this.dragBox, CLASS_MODAL);
|
||||
addClass(this.cropBox, CLASS_HIDDEN);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace the image's src and rebuild the cropper
|
||||
* @param {string} url - The new URL.
|
||||
* @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
replace(url, hasSameSize = false) {
|
||||
if (!this.disabled && url) {
|
||||
if (this.isImg) {
|
||||
this.element.src = url;
|
||||
}
|
||||
|
||||
if (hasSameSize) {
|
||||
this.url = url;
|
||||
this.image.src = url;
|
||||
|
||||
if (this.ready) {
|
||||
this.viewBoxImage.src = url;
|
||||
|
||||
forEach(this.previews, (element) => {
|
||||
element.getElementsByTagName('img')[0].src = url;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this.isImg) {
|
||||
this.replaced = true;
|
||||
}
|
||||
|
||||
this.options.data = null;
|
||||
this.uncreate();
|
||||
this.load(url);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Enable (unfreeze) the cropper
|
||||
enable() {
|
||||
if (this.ready && this.disabled) {
|
||||
this.disabled = false;
|
||||
removeClass(this.cropper, CLASS_DISABLED);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Disable (freeze) the cropper
|
||||
disable() {
|
||||
if (this.ready && !this.disabled) {
|
||||
this.disabled = true;
|
||||
addClass(this.cropper, CLASS_DISABLED);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the cropper and remove the instance from the image
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
destroy() {
|
||||
const { element } = this;
|
||||
|
||||
if (!element[NAMESPACE]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
element[NAMESPACE] = undefined;
|
||||
|
||||
if (this.isImg && this.replaced) {
|
||||
element.src = this.originalUrl;
|
||||
}
|
||||
|
||||
this.uncreate();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Move the canvas with relative offsets
|
||||
* @param {number} offsetX - The relative offset distance on the x-axis.
|
||||
* @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
move(offsetX, offsetY = offsetX) {
|
||||
const { left, top } = this.canvasData;
|
||||
|
||||
return this.moveTo(
|
||||
isUndefined(offsetX) ? offsetX : (left + Number(offsetX)),
|
||||
isUndefined(offsetY) ? offsetY : (top + Number(offsetY)),
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Move the canvas to an absolute point
|
||||
* @param {number} x - The x-axis coordinate.
|
||||
* @param {number} [y=x] - The y-axis coordinate.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
moveTo(x, y = x) {
|
||||
const { canvasData } = this;
|
||||
let changed = false;
|
||||
|
||||
x = Number(x);
|
||||
y = Number(y);
|
||||
|
||||
if (this.ready && !this.disabled && this.options.movable) {
|
||||
if (isNumber(x)) {
|
||||
canvasData.left = x;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (isNumber(y)) {
|
||||
canvasData.top = y;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this.renderCanvas(true);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Zoom the canvas with a relative ratio
|
||||
* @param {number} ratio - The target ratio.
|
||||
* @param {Event} _originalEvent - The original event if any.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
zoom(ratio, _originalEvent) {
|
||||
const { canvasData } = this;
|
||||
|
||||
ratio = Number(ratio);
|
||||
|
||||
if (ratio < 0) {
|
||||
ratio = 1 / (1 - ratio);
|
||||
} else {
|
||||
ratio = 1 + ratio;
|
||||
}
|
||||
|
||||
return this.zoomTo((canvasData.width * ratio) / canvasData.naturalWidth, null, _originalEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Zoom the canvas to an absolute ratio
|
||||
* @param {number} ratio - The target ratio.
|
||||
* @param {Object} pivot - The zoom pivot point coordinate.
|
||||
* @param {Event} _originalEvent - The original event if any.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
zoomTo(ratio, pivot, _originalEvent) {
|
||||
const { options, canvasData } = this;
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
naturalWidth,
|
||||
naturalHeight,
|
||||
} = canvasData;
|
||||
|
||||
ratio = Number(ratio);
|
||||
|
||||
if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) {
|
||||
const newWidth = naturalWidth * ratio;
|
||||
const newHeight = naturalHeight * ratio;
|
||||
|
||||
if (dispatchEvent(this.element, EVENT_ZOOM, {
|
||||
ratio,
|
||||
oldRatio: width / naturalWidth,
|
||||
originalEvent: _originalEvent,
|
||||
}) === false) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (_originalEvent) {
|
||||
const { pointers } = this;
|
||||
const offset = getOffset(this.cropper);
|
||||
const center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {
|
||||
pageX: _originalEvent.pageX,
|
||||
pageY: _originalEvent.pageY,
|
||||
};
|
||||
|
||||
// Zoom from the triggering point of the event
|
||||
canvasData.left -= (newWidth - width) * (
|
||||
((center.pageX - offset.left) - canvasData.left) / width
|
||||
);
|
||||
canvasData.top -= (newHeight - height) * (
|
||||
((center.pageY - offset.top) - canvasData.top) / height
|
||||
);
|
||||
} else if (isPlainObject(pivot) && isNumber(pivot.x) && isNumber(pivot.y)) {
|
||||
canvasData.left -= (newWidth - width) * (
|
||||
(pivot.x - canvasData.left) / width
|
||||
);
|
||||
canvasData.top -= (newHeight - height) * (
|
||||
(pivot.y - canvasData.top) / height
|
||||
);
|
||||
} else {
|
||||
// Zoom from the center of the canvas
|
||||
canvasData.left -= (newWidth - width) / 2;
|
||||
canvasData.top -= (newHeight - height) / 2;
|
||||
}
|
||||
|
||||
canvasData.width = newWidth;
|
||||
canvasData.height = newHeight;
|
||||
this.renderCanvas(true);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotate the canvas with a relative degree
|
||||
* @param {number} degree - The rotate degree.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
rotate(degree) {
|
||||
return this.rotateTo((this.imageData.rotate || 0) + Number(degree));
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotate the canvas to an absolute degree
|
||||
* @param {number} degree - The rotate degree.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
rotateTo(degree) {
|
||||
degree = Number(degree);
|
||||
|
||||
if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {
|
||||
this.imageData.rotate = degree % 360;
|
||||
this.renderCanvas(true, true);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the image on the x-axis.
|
||||
* @param {number} scaleX - The scale ratio on the x-axis.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
scaleX(scaleX) {
|
||||
const { scaleY } = this.imageData;
|
||||
|
||||
return this.scale(scaleX, isNumber(scaleY) ? scaleY : 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the image on the y-axis.
|
||||
* @param {number} scaleY - The scale ratio on the y-axis.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
scaleY(scaleY) {
|
||||
const { scaleX } = this.imageData;
|
||||
|
||||
return this.scale(isNumber(scaleX) ? scaleX : 1, scaleY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the image
|
||||
* @param {number} scaleX - The scale ratio on the x-axis.
|
||||
* @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
scale(scaleX, scaleY = scaleX) {
|
||||
const { imageData } = this;
|
||||
let transformed = false;
|
||||
|
||||
scaleX = Number(scaleX);
|
||||
scaleY = Number(scaleY);
|
||||
|
||||
if (this.ready && !this.disabled && this.options.scalable) {
|
||||
if (isNumber(scaleX)) {
|
||||
imageData.scaleX = scaleX;
|
||||
transformed = true;
|
||||
}
|
||||
|
||||
if (isNumber(scaleY)) {
|
||||
imageData.scaleY = scaleY;
|
||||
transformed = true;
|
||||
}
|
||||
|
||||
if (transformed) {
|
||||
this.renderCanvas(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the cropped area position and size data (base on the original image)
|
||||
* @param {boolean} [rounded=false] - Indicate if round the data values or not.
|
||||
* @returns {Object} The result cropped data.
|
||||
*/
|
||||
getData(rounded = false) {
|
||||
const {
|
||||
options,
|
||||
imageData,
|
||||
canvasData,
|
||||
cropBoxData,
|
||||
} = this;
|
||||
let data;
|
||||
|
||||
if (this.ready && this.cropped) {
|
||||
data = {
|
||||
x: cropBoxData.left - canvasData.left,
|
||||
y: cropBoxData.top - canvasData.top,
|
||||
width: cropBoxData.width,
|
||||
height: cropBoxData.height,
|
||||
};
|
||||
|
||||
const ratio = imageData.width / imageData.naturalWidth;
|
||||
|
||||
forEach(data, (n, i) => {
|
||||
data[i] = n / ratio;
|
||||
});
|
||||
|
||||
if (rounded) {
|
||||
// In case rounding off leads to extra 1px in right or bottom border
|
||||
// we should round the top-left corner and the dimension (#343).
|
||||
const bottom = Math.round(data.y + data.height);
|
||||
const right = Math.round(data.x + data.width);
|
||||
|
||||
data.x = Math.round(data.x);
|
||||
data.y = Math.round(data.y);
|
||||
data.width = right - data.x;
|
||||
data.height = bottom - data.y;
|
||||
}
|
||||
} else {
|
||||
data = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (options.rotatable) {
|
||||
data.rotate = imageData.rotate || 0;
|
||||
}
|
||||
|
||||
if (options.scalable) {
|
||||
data.scaleX = imageData.scaleX || 1;
|
||||
data.scaleY = imageData.scaleY || 1;
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the cropped area position and size with new data
|
||||
* @param {Object} data - The new data.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
setData(data) {
|
||||
const { options, imageData, canvasData } = this;
|
||||
const cropBoxData = {};
|
||||
|
||||
if (this.ready && !this.disabled && isPlainObject(data)) {
|
||||
let transformed = false;
|
||||
|
||||
if (options.rotatable) {
|
||||
if (isNumber(data.rotate) && data.rotate !== imageData.rotate) {
|
||||
imageData.rotate = data.rotate;
|
||||
transformed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.scalable) {
|
||||
if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) {
|
||||
imageData.scaleX = data.scaleX;
|
||||
transformed = true;
|
||||
}
|
||||
|
||||
if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) {
|
||||
imageData.scaleY = data.scaleY;
|
||||
transformed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (transformed) {
|
||||
this.renderCanvas(true, true);
|
||||
}
|
||||
|
||||
const ratio = imageData.width / imageData.naturalWidth;
|
||||
|
||||
if (isNumber(data.x)) {
|
||||
cropBoxData.left = (data.x * ratio) + canvasData.left;
|
||||
}
|
||||
|
||||
if (isNumber(data.y)) {
|
||||
cropBoxData.top = (data.y * ratio) + canvasData.top;
|
||||
}
|
||||
|
||||
if (isNumber(data.width)) {
|
||||
cropBoxData.width = data.width * ratio;
|
||||
}
|
||||
|
||||
if (isNumber(data.height)) {
|
||||
cropBoxData.height = data.height * ratio;
|
||||
}
|
||||
|
||||
this.setCropBoxData(cropBoxData);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the container size data.
|
||||
* @returns {Object} The result container data.
|
||||
*/
|
||||
getContainerData() {
|
||||
return this.ready ? assign({}, this.containerData) : {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the image position and size data.
|
||||
* @returns {Object} The result image data.
|
||||
*/
|
||||
getImageData() {
|
||||
return this.sized ? assign({}, this.imageData) : {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the canvas position and size data.
|
||||
* @returns {Object} The result canvas data.
|
||||
*/
|
||||
getCanvasData() {
|
||||
const { canvasData } = this;
|
||||
const data = {};
|
||||
|
||||
if (this.ready) {
|
||||
forEach([
|
||||
'left',
|
||||
'top',
|
||||
'width',
|
||||
'height',
|
||||
'naturalWidth',
|
||||
'naturalHeight',
|
||||
], (n) => {
|
||||
data[n] = canvasData[n];
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the canvas position and size with new data.
|
||||
* @param {Object} data - The new canvas data.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
setCanvasData(data) {
|
||||
const { canvasData } = this;
|
||||
const { aspectRatio } = canvasData;
|
||||
|
||||
if (this.ready && !this.disabled && isPlainObject(data)) {
|
||||
if (isNumber(data.left)) {
|
||||
canvasData.left = data.left;
|
||||
}
|
||||
|
||||
if (isNumber(data.top)) {
|
||||
canvasData.top = data.top;
|
||||
}
|
||||
|
||||
if (isNumber(data.width)) {
|
||||
canvasData.width = data.width;
|
||||
canvasData.height = data.width / aspectRatio;
|
||||
} else if (isNumber(data.height)) {
|
||||
canvasData.height = data.height;
|
||||
canvasData.width = data.height * aspectRatio;
|
||||
}
|
||||
|
||||
this.renderCanvas(true);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the crop box position and size data.
|
||||
* @returns {Object} The result crop box data.
|
||||
*/
|
||||
getCropBoxData() {
|
||||
const { cropBoxData } = this;
|
||||
let data;
|
||||
|
||||
if (this.ready && this.cropped) {
|
||||
data = {
|
||||
left: cropBoxData.left,
|
||||
top: cropBoxData.top,
|
||||
width: cropBoxData.width,
|
||||
height: cropBoxData.height,
|
||||
};
|
||||
}
|
||||
|
||||
return data || {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the crop box position and size with new data.
|
||||
* @param {Object} data - The new crop box data.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
setCropBoxData(data) {
|
||||
const { cropBoxData } = this;
|
||||
const { aspectRatio } = this.options;
|
||||
let widthChanged;
|
||||
let heightChanged;
|
||||
|
||||
if (this.ready && this.cropped && !this.disabled && isPlainObject(data)) {
|
||||
if (isNumber(data.left)) {
|
||||
cropBoxData.left = data.left;
|
||||
}
|
||||
|
||||
if (isNumber(data.top)) {
|
||||
cropBoxData.top = data.top;
|
||||
}
|
||||
|
||||
if (isNumber(data.width) && data.width !== cropBoxData.width) {
|
||||
widthChanged = true;
|
||||
cropBoxData.width = data.width;
|
||||
}
|
||||
|
||||
if (isNumber(data.height) && data.height !== cropBoxData.height) {
|
||||
heightChanged = true;
|
||||
cropBoxData.height = data.height;
|
||||
}
|
||||
|
||||
if (aspectRatio) {
|
||||
if (widthChanged) {
|
||||
cropBoxData.height = cropBoxData.width / aspectRatio;
|
||||
} else if (heightChanged) {
|
||||
cropBoxData.width = cropBoxData.height * aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
this.renderCropBox();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a canvas drawn the cropped image.
|
||||
* @param {Object} [options={}] - The config options.
|
||||
* @returns {HTMLCanvasElement} - The result canvas.
|
||||
*/
|
||||
getCroppedCanvas(options = {}) {
|
||||
if (!this.ready || !window.HTMLCanvasElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { canvasData } = this;
|
||||
const source = getSourceCanvas(this.image, this.imageData, canvasData, options);
|
||||
|
||||
// Returns the source canvas if it is not cropped.
|
||||
if (!this.cropped) {
|
||||
return source;
|
||||
}
|
||||
|
||||
let {
|
||||
x: initialX,
|
||||
y: initialY,
|
||||
width: initialWidth,
|
||||
height: initialHeight,
|
||||
} = this.getData(options.rounded);
|
||||
const ratio = source.width / Math.floor(canvasData.naturalWidth);
|
||||
|
||||
if (ratio !== 1) {
|
||||
initialX *= ratio;
|
||||
initialY *= ratio;
|
||||
initialWidth *= ratio;
|
||||
initialHeight *= ratio;
|
||||
}
|
||||
|
||||
const aspectRatio = initialWidth / initialHeight;
|
||||
const maxSizes = getAdjustedSizes({
|
||||
aspectRatio,
|
||||
width: options.maxWidth || Infinity,
|
||||
height: options.maxHeight || Infinity,
|
||||
});
|
||||
const minSizes = getAdjustedSizes({
|
||||
aspectRatio,
|
||||
width: options.minWidth || 0,
|
||||
height: options.minHeight || 0,
|
||||
}, 'cover');
|
||||
let {
|
||||
width,
|
||||
height,
|
||||
} = getAdjustedSizes({
|
||||
aspectRatio,
|
||||
width: options.width || (ratio !== 1 ? source.width : initialWidth),
|
||||
height: options.height || (ratio !== 1 ? source.height : initialHeight),
|
||||
});
|
||||
|
||||
width = Math.min(maxSizes.width, Math.max(minSizes.width, width));
|
||||
height = Math.min(maxSizes.height, Math.max(minSizes.height, height));
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
canvas.width = normalizeDecimalNumber(width);
|
||||
canvas.height = normalizeDecimalNumber(height);
|
||||
|
||||
context.fillStyle = options.fillColor || 'transparent';
|
||||
context.fillRect(0, 0, width, height);
|
||||
|
||||
const { imageSmoothingEnabled = true, imageSmoothingQuality } = options;
|
||||
|
||||
context.imageSmoothingEnabled = imageSmoothingEnabled;
|
||||
|
||||
if (imageSmoothingQuality) {
|
||||
context.imageSmoothingQuality = imageSmoothingQuality;
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
|
||||
const sourceWidth = source.width;
|
||||
const sourceHeight = source.height;
|
||||
|
||||
// Source canvas parameters
|
||||
let srcX = initialX;
|
||||
let srcY = initialY;
|
||||
let srcWidth;
|
||||
let srcHeight;
|
||||
|
||||
// Destination canvas parameters
|
||||
let dstX;
|
||||
let dstY;
|
||||
let dstWidth;
|
||||
let dstHeight;
|
||||
|
||||
if (srcX <= -initialWidth || srcX > sourceWidth) {
|
||||
srcX = 0;
|
||||
srcWidth = 0;
|
||||
dstX = 0;
|
||||
dstWidth = 0;
|
||||
} else if (srcX <= 0) {
|
||||
dstX = -srcX;
|
||||
srcX = 0;
|
||||
srcWidth = Math.min(sourceWidth, initialWidth + srcX);
|
||||
dstWidth = srcWidth;
|
||||
} else if (srcX <= sourceWidth) {
|
||||
dstX = 0;
|
||||
srcWidth = Math.min(initialWidth, sourceWidth - srcX);
|
||||
dstWidth = srcWidth;
|
||||
}
|
||||
|
||||
if (srcWidth <= 0 || srcY <= -initialHeight || srcY > sourceHeight) {
|
||||
srcY = 0;
|
||||
srcHeight = 0;
|
||||
dstY = 0;
|
||||
dstHeight = 0;
|
||||
} else if (srcY <= 0) {
|
||||
dstY = -srcY;
|
||||
srcY = 0;
|
||||
srcHeight = Math.min(sourceHeight, initialHeight + srcY);
|
||||
dstHeight = srcHeight;
|
||||
} else if (srcY <= sourceHeight) {
|
||||
dstY = 0;
|
||||
srcHeight = Math.min(initialHeight, sourceHeight - srcY);
|
||||
dstHeight = srcHeight;
|
||||
}
|
||||
|
||||
const params = [
|
||||
srcX,
|
||||
srcY,
|
||||
srcWidth,
|
||||
srcHeight,
|
||||
];
|
||||
|
||||
// Avoid "IndexSizeError"
|
||||
if (dstWidth > 0 && dstHeight > 0) {
|
||||
const scale = width / initialWidth;
|
||||
|
||||
params.push(
|
||||
dstX * scale,
|
||||
dstY * scale,
|
||||
dstWidth * scale,
|
||||
dstHeight * scale,
|
||||
);
|
||||
}
|
||||
|
||||
// All the numerical parameters should be integer for `drawImage`
|
||||
// https://github.com/fengyuanchen/cropper/issues/476
|
||||
context.drawImage(source, ...params.map((param) => Math.floor(normalizeDecimalNumber(param))));
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the aspect ratio of the crop box.
|
||||
* @param {number} aspectRatio - The new aspect ratio.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
setAspectRatio(aspectRatio) {
|
||||
const { options } = this;
|
||||
|
||||
if (!this.disabled && !isUndefined(aspectRatio)) {
|
||||
// 0 -> NaN
|
||||
options.aspectRatio = Math.max(0, aspectRatio) || NaN;
|
||||
|
||||
if (this.ready) {
|
||||
this.initCropBox();
|
||||
|
||||
if (this.cropped) {
|
||||
this.renderCropBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the drag mode.
|
||||
* @param {string} mode - The new drag mode.
|
||||
* @returns {Cropper} this
|
||||
*/
|
||||
setDragMode(mode) {
|
||||
const { options, dragBox, face } = this;
|
||||
|
||||
if (this.ready && !this.disabled) {
|
||||
const croppable = mode === DRAG_MODE_CROP;
|
||||
const movable = options.movable && mode === DRAG_MODE_MOVE;
|
||||
|
||||
mode = (croppable || movable) ? mode : DRAG_MODE_NONE;
|
||||
|
||||
options.dragMode = mode;
|
||||
setData(dragBox, DATA_ACTION, mode);
|
||||
toggleClass(dragBox, CLASS_CROP, croppable);
|
||||
toggleClass(dragBox, CLASS_MOVE, movable);
|
||||
|
||||
if (!options.cropBoxMovable) {
|
||||
// Sync drag mode to crop box when it is not movable
|
||||
setData(face, DATA_ACTION, mode);
|
||||
toggleClass(face, CLASS_CROP, croppable);
|
||||
toggleClass(face, CLASS_MOVE, movable);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
};
|
||||
148
node_modules/cropperjs/src/js/preview.js
generated
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
import { DATA_PREVIEW } from './constants';
|
||||
import {
|
||||
assign,
|
||||
forEach,
|
||||
getData,
|
||||
getTransforms,
|
||||
removeData,
|
||||
setData,
|
||||
setStyle,
|
||||
} from './utilities';
|
||||
|
||||
export default {
|
||||
initPreview() {
|
||||
const { element, crossOrigin } = this;
|
||||
const { preview } = this.options;
|
||||
const url = crossOrigin ? this.crossOriginUrl : this.url;
|
||||
const alt = element.alt || 'The image to preview';
|
||||
const image = document.createElement('img');
|
||||
|
||||
if (crossOrigin) {
|
||||
image.crossOrigin = crossOrigin;
|
||||
}
|
||||
|
||||
image.src = url;
|
||||
image.alt = alt;
|
||||
this.viewBox.appendChild(image);
|
||||
this.viewBoxImage = image;
|
||||
|
||||
if (!preview) {
|
||||
return;
|
||||
}
|
||||
|
||||
let previews = preview;
|
||||
|
||||
if (typeof preview === 'string') {
|
||||
previews = element.ownerDocument.querySelectorAll(preview);
|
||||
} else if (preview.querySelector) {
|
||||
previews = [preview];
|
||||
}
|
||||
|
||||
this.previews = previews;
|
||||
|
||||
forEach(previews, (el) => {
|
||||
const img = document.createElement('img');
|
||||
|
||||
// Save the original size for recover
|
||||
setData(el, DATA_PREVIEW, {
|
||||
width: el.offsetWidth,
|
||||
height: el.offsetHeight,
|
||||
html: el.innerHTML,
|
||||
});
|
||||
|
||||
if (crossOrigin) {
|
||||
img.crossOrigin = crossOrigin;
|
||||
}
|
||||
|
||||
img.src = url;
|
||||
img.alt = alt;
|
||||
|
||||
/**
|
||||
* Override img element styles
|
||||
* Add `display:block` to avoid margin top issue
|
||||
* Add `height:auto` to override `height` attribute on IE8
|
||||
* (Occur only when margin-top <= -height)
|
||||
*/
|
||||
img.style.cssText = (
|
||||
'display:block;'
|
||||
+ 'width:100%;'
|
||||
+ 'height:auto;'
|
||||
+ 'min-width:0!important;'
|
||||
+ 'min-height:0!important;'
|
||||
+ 'max-width:none!important;'
|
||||
+ 'max-height:none!important;'
|
||||
+ 'image-orientation:0deg!important;"'
|
||||
);
|
||||
|
||||
el.innerHTML = '';
|
||||
el.appendChild(img);
|
||||
});
|
||||
},
|
||||
|
||||
resetPreview() {
|
||||
forEach(this.previews, (element) => {
|
||||
const data = getData(element, DATA_PREVIEW);
|
||||
|
||||
setStyle(element, {
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
});
|
||||
|
||||
element.innerHTML = data.html;
|
||||
removeData(element, DATA_PREVIEW);
|
||||
});
|
||||
},
|
||||
|
||||
preview() {
|
||||
const { imageData, canvasData, cropBoxData } = this;
|
||||
const { width: cropBoxWidth, height: cropBoxHeight } = cropBoxData;
|
||||
const { width, height } = imageData;
|
||||
const left = cropBoxData.left - canvasData.left - imageData.left;
|
||||
const top = cropBoxData.top - canvasData.top - imageData.top;
|
||||
|
||||
if (!this.cropped || this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setStyle(this.viewBoxImage, assign({
|
||||
width,
|
||||
height,
|
||||
}, getTransforms(assign({
|
||||
translateX: -left,
|
||||
translateY: -top,
|
||||
}, imageData))));
|
||||
|
||||
forEach(this.previews, (element) => {
|
||||
const data = getData(element, DATA_PREVIEW);
|
||||
const originalWidth = data.width;
|
||||
const originalHeight = data.height;
|
||||
let newWidth = originalWidth;
|
||||
let newHeight = originalHeight;
|
||||
let ratio = 1;
|
||||
|
||||
if (cropBoxWidth) {
|
||||
ratio = originalWidth / cropBoxWidth;
|
||||
newHeight = cropBoxHeight * ratio;
|
||||
}
|
||||
|
||||
if (cropBoxHeight && newHeight > originalHeight) {
|
||||
ratio = originalHeight / cropBoxHeight;
|
||||
newWidth = cropBoxWidth * ratio;
|
||||
newHeight = originalHeight;
|
||||
}
|
||||
|
||||
setStyle(element, {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
});
|
||||
|
||||
setStyle(element.getElementsByTagName('img')[0], assign({
|
||||
width: width * ratio,
|
||||
height: height * ratio,
|
||||
}, getTransforms(assign({
|
||||
translateX: -left * ratio,
|
||||
translateY: -top * ratio,
|
||||
}, imageData))));
|
||||
});
|
||||
},
|
||||
};
|
||||
506
node_modules/cropperjs/src/js/render.js
generated
vendored
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
import {
|
||||
ACTION_ALL,
|
||||
ACTION_MOVE,
|
||||
CLASS_HIDDEN,
|
||||
DATA_ACTION,
|
||||
EVENT_CROP,
|
||||
MIN_CONTAINER_HEIGHT,
|
||||
MIN_CONTAINER_WIDTH,
|
||||
} from './constants';
|
||||
import {
|
||||
addClass,
|
||||
assign,
|
||||
dispatchEvent,
|
||||
getAdjustedSizes,
|
||||
getRotatedSizes,
|
||||
getTransforms,
|
||||
removeClass,
|
||||
setData,
|
||||
setStyle,
|
||||
} from './utilities';
|
||||
|
||||
export default {
|
||||
render() {
|
||||
this.initContainer();
|
||||
this.initCanvas();
|
||||
this.initCropBox();
|
||||
this.renderCanvas();
|
||||
|
||||
if (this.cropped) {
|
||||
this.renderCropBox();
|
||||
}
|
||||
},
|
||||
|
||||
initContainer() {
|
||||
const {
|
||||
element,
|
||||
options,
|
||||
container,
|
||||
cropper,
|
||||
} = this;
|
||||
const minWidth = Number(options.minContainerWidth);
|
||||
const minHeight = Number(options.minContainerHeight);
|
||||
|
||||
addClass(cropper, CLASS_HIDDEN);
|
||||
removeClass(element, CLASS_HIDDEN);
|
||||
|
||||
const containerData = {
|
||||
width: Math.max(
|
||||
container.offsetWidth,
|
||||
minWidth >= 0 ? minWidth : MIN_CONTAINER_WIDTH,
|
||||
),
|
||||
height: Math.max(
|
||||
container.offsetHeight,
|
||||
minHeight >= 0 ? minHeight : MIN_CONTAINER_HEIGHT,
|
||||
),
|
||||
};
|
||||
|
||||
this.containerData = containerData;
|
||||
|
||||
setStyle(cropper, {
|
||||
width: containerData.width,
|
||||
height: containerData.height,
|
||||
});
|
||||
|
||||
addClass(element, CLASS_HIDDEN);
|
||||
removeClass(cropper, CLASS_HIDDEN);
|
||||
},
|
||||
|
||||
// Canvas (image wrapper)
|
||||
initCanvas() {
|
||||
const { containerData, imageData } = this;
|
||||
const { viewMode } = this.options;
|
||||
const rotated = Math.abs(imageData.rotate) % 180 === 90;
|
||||
const naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth;
|
||||
const naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;
|
||||
const aspectRatio = naturalWidth / naturalHeight;
|
||||
let canvasWidth = containerData.width;
|
||||
let canvasHeight = containerData.height;
|
||||
|
||||
if (containerData.height * aspectRatio > containerData.width) {
|
||||
if (viewMode === 3) {
|
||||
canvasWidth = containerData.height * aspectRatio;
|
||||
} else {
|
||||
canvasHeight = containerData.width / aspectRatio;
|
||||
}
|
||||
} else if (viewMode === 3) {
|
||||
canvasHeight = containerData.width / aspectRatio;
|
||||
} else {
|
||||
canvasWidth = containerData.height * aspectRatio;
|
||||
}
|
||||
|
||||
const canvasData = {
|
||||
aspectRatio,
|
||||
naturalWidth,
|
||||
naturalHeight,
|
||||
width: canvasWidth,
|
||||
height: canvasHeight,
|
||||
};
|
||||
|
||||
this.canvasData = canvasData;
|
||||
this.limited = (viewMode === 1 || viewMode === 2);
|
||||
this.limitCanvas(true, true);
|
||||
canvasData.width = Math.min(
|
||||
Math.max(canvasData.width, canvasData.minWidth),
|
||||
canvasData.maxWidth,
|
||||
);
|
||||
canvasData.height = Math.min(
|
||||
Math.max(canvasData.height, canvasData.minHeight),
|
||||
canvasData.maxHeight,
|
||||
);
|
||||
canvasData.left = (containerData.width - canvasData.width) / 2;
|
||||
canvasData.top = (containerData.height - canvasData.height) / 2;
|
||||
canvasData.oldLeft = canvasData.left;
|
||||
canvasData.oldTop = canvasData.top;
|
||||
this.initialCanvasData = assign({}, canvasData);
|
||||
},
|
||||
|
||||
limitCanvas(sizeLimited, positionLimited) {
|
||||
const {
|
||||
options,
|
||||
containerData,
|
||||
canvasData,
|
||||
cropBoxData,
|
||||
} = this;
|
||||
const { viewMode } = options;
|
||||
const { aspectRatio } = canvasData;
|
||||
const cropped = this.cropped && cropBoxData;
|
||||
|
||||
if (sizeLimited) {
|
||||
let minCanvasWidth = Number(options.minCanvasWidth) || 0;
|
||||
let minCanvasHeight = Number(options.minCanvasHeight) || 0;
|
||||
|
||||
if (viewMode > 1) {
|
||||
minCanvasWidth = Math.max(minCanvasWidth, containerData.width);
|
||||
minCanvasHeight = Math.max(minCanvasHeight, containerData.height);
|
||||
|
||||
if (viewMode === 3) {
|
||||
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
|
||||
minCanvasWidth = minCanvasHeight * aspectRatio;
|
||||
} else {
|
||||
minCanvasHeight = minCanvasWidth / aspectRatio;
|
||||
}
|
||||
}
|
||||
} else if (viewMode > 0) {
|
||||
if (minCanvasWidth) {
|
||||
minCanvasWidth = Math.max(
|
||||
minCanvasWidth,
|
||||
cropped ? cropBoxData.width : 0,
|
||||
);
|
||||
} else if (minCanvasHeight) {
|
||||
minCanvasHeight = Math.max(
|
||||
minCanvasHeight,
|
||||
cropped ? cropBoxData.height : 0,
|
||||
);
|
||||
} else if (cropped) {
|
||||
minCanvasWidth = cropBoxData.width;
|
||||
minCanvasHeight = cropBoxData.height;
|
||||
|
||||
if (minCanvasHeight * aspectRatio > minCanvasWidth) {
|
||||
minCanvasWidth = minCanvasHeight * aspectRatio;
|
||||
} else {
|
||||
minCanvasHeight = minCanvasWidth / aspectRatio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
({ width: minCanvasWidth, height: minCanvasHeight } = getAdjustedSizes({
|
||||
aspectRatio,
|
||||
width: minCanvasWidth,
|
||||
height: minCanvasHeight,
|
||||
}));
|
||||
|
||||
canvasData.minWidth = minCanvasWidth;
|
||||
canvasData.minHeight = minCanvasHeight;
|
||||
canvasData.maxWidth = Infinity;
|
||||
canvasData.maxHeight = Infinity;
|
||||
}
|
||||
|
||||
if (positionLimited) {
|
||||
if (viewMode > (cropped ? 0 : 1)) {
|
||||
const newCanvasLeft = containerData.width - canvasData.width;
|
||||
const newCanvasTop = containerData.height - canvasData.height;
|
||||
|
||||
canvasData.minLeft = Math.min(0, newCanvasLeft);
|
||||
canvasData.minTop = Math.min(0, newCanvasTop);
|
||||
canvasData.maxLeft = Math.max(0, newCanvasLeft);
|
||||
canvasData.maxTop = Math.max(0, newCanvasTop);
|
||||
|
||||
if (cropped && this.limited) {
|
||||
canvasData.minLeft = Math.min(
|
||||
cropBoxData.left,
|
||||
cropBoxData.left + (cropBoxData.width - canvasData.width),
|
||||
);
|
||||
canvasData.minTop = Math.min(
|
||||
cropBoxData.top,
|
||||
cropBoxData.top + (cropBoxData.height - canvasData.height),
|
||||
);
|
||||
canvasData.maxLeft = cropBoxData.left;
|
||||
canvasData.maxTop = cropBoxData.top;
|
||||
|
||||
if (viewMode === 2) {
|
||||
if (canvasData.width >= containerData.width) {
|
||||
canvasData.minLeft = Math.min(0, newCanvasLeft);
|
||||
canvasData.maxLeft = Math.max(0, newCanvasLeft);
|
||||
}
|
||||
|
||||
if (canvasData.height >= containerData.height) {
|
||||
canvasData.minTop = Math.min(0, newCanvasTop);
|
||||
canvasData.maxTop = Math.max(0, newCanvasTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canvasData.minLeft = -canvasData.width;
|
||||
canvasData.minTop = -canvasData.height;
|
||||
canvasData.maxLeft = containerData.width;
|
||||
canvasData.maxTop = containerData.height;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderCanvas(changed, transformed) {
|
||||
const { canvasData, imageData } = this;
|
||||
|
||||
if (transformed) {
|
||||
const { width: naturalWidth, height: naturalHeight } = getRotatedSizes({
|
||||
width: imageData.naturalWidth * Math.abs(imageData.scaleX || 1),
|
||||
height: imageData.naturalHeight * Math.abs(imageData.scaleY || 1),
|
||||
degree: imageData.rotate || 0,
|
||||
});
|
||||
const width = canvasData.width * (naturalWidth / canvasData.naturalWidth);
|
||||
const height = canvasData.height * (naturalHeight / canvasData.naturalHeight);
|
||||
|
||||
canvasData.left -= (width - canvasData.width) / 2;
|
||||
canvasData.top -= (height - canvasData.height) / 2;
|
||||
canvasData.width = width;
|
||||
canvasData.height = height;
|
||||
canvasData.aspectRatio = naturalWidth / naturalHeight;
|
||||
canvasData.naturalWidth = naturalWidth;
|
||||
canvasData.naturalHeight = naturalHeight;
|
||||
this.limitCanvas(true, false);
|
||||
}
|
||||
|
||||
if (canvasData.width > canvasData.maxWidth
|
||||
|| canvasData.width < canvasData.minWidth) {
|
||||
canvasData.left = canvasData.oldLeft;
|
||||
}
|
||||
|
||||
if (canvasData.height > canvasData.maxHeight
|
||||
|| canvasData.height < canvasData.minHeight) {
|
||||
canvasData.top = canvasData.oldTop;
|
||||
}
|
||||
|
||||
canvasData.width = Math.min(
|
||||
Math.max(canvasData.width, canvasData.minWidth),
|
||||
canvasData.maxWidth,
|
||||
);
|
||||
canvasData.height = Math.min(
|
||||
Math.max(canvasData.height, canvasData.minHeight),
|
||||
canvasData.maxHeight,
|
||||
);
|
||||
|
||||
this.limitCanvas(false, true);
|
||||
|
||||
canvasData.left = Math.min(
|
||||
Math.max(canvasData.left, canvasData.minLeft),
|
||||
canvasData.maxLeft,
|
||||
);
|
||||
canvasData.top = Math.min(
|
||||
Math.max(canvasData.top, canvasData.minTop),
|
||||
canvasData.maxTop,
|
||||
);
|
||||
canvasData.oldLeft = canvasData.left;
|
||||
canvasData.oldTop = canvasData.top;
|
||||
|
||||
setStyle(this.canvas, assign({
|
||||
width: canvasData.width,
|
||||
height: canvasData.height,
|
||||
}, getTransforms({
|
||||
translateX: canvasData.left,
|
||||
translateY: canvasData.top,
|
||||
})));
|
||||
|
||||
this.renderImage(changed);
|
||||
|
||||
if (this.cropped && this.limited) {
|
||||
this.limitCropBox(true, true);
|
||||
}
|
||||
},
|
||||
|
||||
renderImage(changed) {
|
||||
const { canvasData, imageData } = this;
|
||||
const width = imageData.naturalWidth * (canvasData.width / canvasData.naturalWidth);
|
||||
const height = imageData.naturalHeight * (canvasData.height / canvasData.naturalHeight);
|
||||
|
||||
assign(imageData, {
|
||||
width,
|
||||
height,
|
||||
left: (canvasData.width - width) / 2,
|
||||
top: (canvasData.height - height) / 2,
|
||||
});
|
||||
setStyle(this.image, assign({
|
||||
width: imageData.width,
|
||||
height: imageData.height,
|
||||
}, getTransforms(assign({
|
||||
translateX: imageData.left,
|
||||
translateY: imageData.top,
|
||||
}, imageData))));
|
||||
|
||||
if (changed) {
|
||||
this.output();
|
||||
}
|
||||
},
|
||||
|
||||
initCropBox() {
|
||||
const { options, canvasData } = this;
|
||||
const aspectRatio = options.aspectRatio || options.initialAspectRatio;
|
||||
const autoCropArea = Number(options.autoCropArea) || 0.8;
|
||||
const cropBoxData = {
|
||||
width: canvasData.width,
|
||||
height: canvasData.height,
|
||||
};
|
||||
|
||||
if (aspectRatio) {
|
||||
if (canvasData.height * aspectRatio > canvasData.width) {
|
||||
cropBoxData.height = cropBoxData.width / aspectRatio;
|
||||
} else {
|
||||
cropBoxData.width = cropBoxData.height * aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
this.cropBoxData = cropBoxData;
|
||||
this.limitCropBox(true, true);
|
||||
|
||||
// Initialize auto crop area
|
||||
cropBoxData.width = Math.min(
|
||||
Math.max(cropBoxData.width, cropBoxData.minWidth),
|
||||
cropBoxData.maxWidth,
|
||||
);
|
||||
cropBoxData.height = Math.min(
|
||||
Math.max(cropBoxData.height, cropBoxData.minHeight),
|
||||
cropBoxData.maxHeight,
|
||||
);
|
||||
|
||||
// The width/height of auto crop area must large than "minWidth/Height"
|
||||
cropBoxData.width = Math.max(
|
||||
cropBoxData.minWidth,
|
||||
cropBoxData.width * autoCropArea,
|
||||
);
|
||||
cropBoxData.height = Math.max(
|
||||
cropBoxData.minHeight,
|
||||
cropBoxData.height * autoCropArea,
|
||||
);
|
||||
cropBoxData.left = (
|
||||
canvasData.left + ((canvasData.width - cropBoxData.width) / 2)
|
||||
);
|
||||
cropBoxData.top = (
|
||||
canvasData.top + ((canvasData.height - cropBoxData.height) / 2)
|
||||
);
|
||||
cropBoxData.oldLeft = cropBoxData.left;
|
||||
cropBoxData.oldTop = cropBoxData.top;
|
||||
|
||||
this.initialCropBoxData = assign({}, cropBoxData);
|
||||
},
|
||||
|
||||
limitCropBox(sizeLimited, positionLimited) {
|
||||
const {
|
||||
options,
|
||||
containerData,
|
||||
canvasData,
|
||||
cropBoxData,
|
||||
limited,
|
||||
} = this;
|
||||
const { aspectRatio } = options;
|
||||
|
||||
if (sizeLimited) {
|
||||
let minCropBoxWidth = Number(options.minCropBoxWidth) || 0;
|
||||
let minCropBoxHeight = Number(options.minCropBoxHeight) || 0;
|
||||
let maxCropBoxWidth = limited ? Math.min(
|
||||
containerData.width,
|
||||
canvasData.width,
|
||||
canvasData.width + canvasData.left,
|
||||
containerData.width - canvasData.left,
|
||||
) : containerData.width;
|
||||
let maxCropBoxHeight = limited ? Math.min(
|
||||
containerData.height,
|
||||
canvasData.height,
|
||||
canvasData.height + canvasData.top,
|
||||
containerData.height - canvasData.top,
|
||||
) : containerData.height;
|
||||
|
||||
// The min/maxCropBoxWidth/Height must be less than container's width/height
|
||||
minCropBoxWidth = Math.min(minCropBoxWidth, containerData.width);
|
||||
minCropBoxHeight = Math.min(minCropBoxHeight, containerData.height);
|
||||
|
||||
if (aspectRatio) {
|
||||
if (minCropBoxWidth && minCropBoxHeight) {
|
||||
if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
|
||||
minCropBoxHeight = minCropBoxWidth / aspectRatio;
|
||||
} else {
|
||||
minCropBoxWidth = minCropBoxHeight * aspectRatio;
|
||||
}
|
||||
} else if (minCropBoxWidth) {
|
||||
minCropBoxHeight = minCropBoxWidth / aspectRatio;
|
||||
} else if (minCropBoxHeight) {
|
||||
minCropBoxWidth = minCropBoxHeight * aspectRatio;
|
||||
}
|
||||
|
||||
if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
|
||||
maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
|
||||
} else {
|
||||
maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
|
||||
}
|
||||
}
|
||||
|
||||
// The minWidth/Height must be less than maxWidth/Height
|
||||
cropBoxData.minWidth = Math.min(minCropBoxWidth, maxCropBoxWidth);
|
||||
cropBoxData.minHeight = Math.min(minCropBoxHeight, maxCropBoxHeight);
|
||||
cropBoxData.maxWidth = maxCropBoxWidth;
|
||||
cropBoxData.maxHeight = maxCropBoxHeight;
|
||||
}
|
||||
|
||||
if (positionLimited) {
|
||||
if (limited) {
|
||||
cropBoxData.minLeft = Math.max(0, canvasData.left);
|
||||
cropBoxData.minTop = Math.max(0, canvasData.top);
|
||||
cropBoxData.maxLeft = Math.min(
|
||||
containerData.width,
|
||||
canvasData.left + canvasData.width,
|
||||
) - cropBoxData.width;
|
||||
cropBoxData.maxTop = Math.min(
|
||||
containerData.height,
|
||||
canvasData.top + canvasData.height,
|
||||
) - cropBoxData.height;
|
||||
} else {
|
||||
cropBoxData.minLeft = 0;
|
||||
cropBoxData.minTop = 0;
|
||||
cropBoxData.maxLeft = containerData.width - cropBoxData.width;
|
||||
cropBoxData.maxTop = containerData.height - cropBoxData.height;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderCropBox() {
|
||||
const { options, containerData, cropBoxData } = this;
|
||||
|
||||
if (cropBoxData.width > cropBoxData.maxWidth
|
||||
|| cropBoxData.width < cropBoxData.minWidth) {
|
||||
cropBoxData.left = cropBoxData.oldLeft;
|
||||
}
|
||||
|
||||
if (cropBoxData.height > cropBoxData.maxHeight
|
||||
|| cropBoxData.height < cropBoxData.minHeight) {
|
||||
cropBoxData.top = cropBoxData.oldTop;
|
||||
}
|
||||
|
||||
cropBoxData.width = Math.min(
|
||||
Math.max(cropBoxData.width, cropBoxData.minWidth),
|
||||
cropBoxData.maxWidth,
|
||||
);
|
||||
cropBoxData.height = Math.min(
|
||||
Math.max(cropBoxData.height, cropBoxData.minHeight),
|
||||
cropBoxData.maxHeight,
|
||||
);
|
||||
|
||||
this.limitCropBox(false, true);
|
||||
|
||||
cropBoxData.left = Math.min(
|
||||
Math.max(cropBoxData.left, cropBoxData.minLeft),
|
||||
cropBoxData.maxLeft,
|
||||
);
|
||||
cropBoxData.top = Math.min(
|
||||
Math.max(cropBoxData.top, cropBoxData.minTop),
|
||||
cropBoxData.maxTop,
|
||||
);
|
||||
cropBoxData.oldLeft = cropBoxData.left;
|
||||
cropBoxData.oldTop = cropBoxData.top;
|
||||
|
||||
if (options.movable && options.cropBoxMovable) {
|
||||
// Turn to move the canvas when the crop box is equal to the container
|
||||
setData(this.face, DATA_ACTION, cropBoxData.width >= containerData.width
|
||||
&& cropBoxData.height >= containerData.height ? ACTION_MOVE : ACTION_ALL);
|
||||
}
|
||||
|
||||
setStyle(this.cropBox, assign({
|
||||
width: cropBoxData.width,
|
||||
height: cropBoxData.height,
|
||||
}, getTransforms({
|
||||
translateX: cropBoxData.left,
|
||||
translateY: cropBoxData.top,
|
||||
})));
|
||||
|
||||
if (this.cropped && this.limited) {
|
||||
this.limitCanvas(true, true);
|
||||
}
|
||||
|
||||
if (!this.disabled) {
|
||||
this.output();
|
||||
}
|
||||
},
|
||||
|
||||
output() {
|
||||
this.preview();
|
||||
dispatchEvent(this.element, EVENT_CROP, this.getData());
|
||||
},
|
||||
};
|
||||
27
node_modules/cropperjs/src/js/template.js
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
export default (
|
||||
'<div class="cropper-container" touch-action="none">'
|
||||
+ '<div class="cropper-wrap-box">'
|
||||
+ '<div class="cropper-canvas"></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="cropper-drag-box"></div>'
|
||||
+ '<div class="cropper-crop-box">'
|
||||
+ '<span class="cropper-view-box"></span>'
|
||||
+ '<span class="cropper-dashed dashed-h"></span>'
|
||||
+ '<span class="cropper-dashed dashed-v"></span>'
|
||||
+ '<span class="cropper-center"></span>'
|
||||
+ '<span class="cropper-face"></span>'
|
||||
+ '<span class="cropper-line line-e" data-cropper-action="e"></span>'
|
||||
+ '<span class="cropper-line line-n" data-cropper-action="n"></span>'
|
||||
+ '<span class="cropper-line line-w" data-cropper-action="w"></span>'
|
||||
+ '<span class="cropper-line line-s" data-cropper-action="s"></span>'
|
||||
+ '<span class="cropper-point point-e" data-cropper-action="e"></span>'
|
||||
+ '<span class="cropper-point point-n" data-cropper-action="n"></span>'
|
||||
+ '<span class="cropper-point point-w" data-cropper-action="w"></span>'
|
||||
+ '<span class="cropper-point point-s" data-cropper-action="s"></span>'
|
||||
+ '<span class="cropper-point point-ne" data-cropper-action="ne"></span>'
|
||||
+ '<span class="cropper-point point-nw" data-cropper-action="nw"></span>'
|
||||
+ '<span class="cropper-point point-sw" data-cropper-action="sw"></span>'
|
||||
+ '<span class="cropper-point point-se" data-cropper-action="se"></span>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
);
|
||||
963
node_modules/cropperjs/src/js/utilities.js
generated
vendored
Normal file
|
|
@ -0,0 +1,963 @@
|
|||
import { IS_BROWSER, WINDOW } from './constants';
|
||||
|
||||
/**
|
||||
* Check if the given value is not a number.
|
||||
*/
|
||||
export const isNaN = Number.isNaN || WINDOW.isNaN;
|
||||
|
||||
/**
|
||||
* Check if the given value is a number.
|
||||
* @param {*} value - The value to check.
|
||||
* @returns {boolean} Returns `true` if the given value is a number, else `false`.
|
||||
*/
|
||||
export function isNumber(value) {
|
||||
return typeof value === 'number' && !isNaN(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given value is a positive number.
|
||||
* @param {*} value - The value to check.
|
||||
* @returns {boolean} Returns `true` if the given value is a positive number, else `false`.
|
||||
*/
|
||||
export const isPositiveNumber = (value) => value > 0 && value < Infinity;
|
||||
|
||||
/**
|
||||
* Check if the given value is undefined.
|
||||
* @param {*} value - The value to check.
|
||||
* @returns {boolean} Returns `true` if the given value is undefined, else `false`.
|
||||
*/
|
||||
export function isUndefined(value) {
|
||||
return typeof value === 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given value is an object.
|
||||
* @param {*} value - The value to check.
|
||||
* @returns {boolean} Returns `true` if the given value is an object, else `false`.
|
||||
*/
|
||||
export function isObject(value) {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
/**
|
||||
* Check if the given value is a plain object.
|
||||
* @param {*} value - The value to check.
|
||||
* @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
|
||||
*/
|
||||
export function isPlainObject(value) {
|
||||
if (!isObject(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const { constructor } = value;
|
||||
const { prototype } = constructor;
|
||||
|
||||
return constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given value is a function.
|
||||
* @param {*} value - The value to check.
|
||||
* @returns {boolean} Returns `true` if the given value is a function, else `false`.
|
||||
*/
|
||||
export function isFunction(value) {
|
||||
return typeof value === 'function';
|
||||
}
|
||||
|
||||
const { slice } = Array.prototype;
|
||||
|
||||
/**
|
||||
* Convert array-like or iterable object to an array.
|
||||
* @param {*} value - The value to convert.
|
||||
* @returns {Array} Returns a new array.
|
||||
*/
|
||||
export function toArray(value) {
|
||||
return Array.from ? Array.from(value) : slice.call(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate the given data.
|
||||
* @param {*} data - The data to iterate.
|
||||
* @param {Function} callback - The process function for each element.
|
||||
* @returns {*} The original data.
|
||||
*/
|
||||
export function forEach(data, callback) {
|
||||
if (data && isFunction(callback)) {
|
||||
if (Array.isArray(data) || isNumber(data.length)/* array-like */) {
|
||||
toArray(data).forEach((value, key) => {
|
||||
callback.call(data, value, key, data);
|
||||
});
|
||||
} else if (isObject(data)) {
|
||||
Object.keys(data).forEach((key) => {
|
||||
callback.call(data, data[key], key, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the given object.
|
||||
* @param {*} target - The target object to extend.
|
||||
* @param {*} args - The rest objects for merging to the target object.
|
||||
* @returns {Object} The extended object.
|
||||
*/
|
||||
export const assign = Object.assign || function assign(target, ...args) {
|
||||
if (isObject(target) && args.length > 0) {
|
||||
args.forEach((arg) => {
|
||||
if (isObject(arg)) {
|
||||
Object.keys(arg).forEach((key) => {
|
||||
target[key] = arg[key];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
const REGEXP_DECIMALS = /\.\d*(?:0|9){12}\d*$/;
|
||||
|
||||
/**
|
||||
* Normalize decimal number.
|
||||
* Check out {@link https://0.30000000000000004.com/}
|
||||
* @param {number} value - The value to normalize.
|
||||
* @param {number} [times=100000000000] - The times for normalizing.
|
||||
* @returns {number} Returns the normalized number.
|
||||
*/
|
||||
export function normalizeDecimalNumber(value, times = 100000000000) {
|
||||
return REGEXP_DECIMALS.test(value) ? (Math.round(value * times) / times) : value;
|
||||
}
|
||||
|
||||
const REGEXP_SUFFIX = /^width|height|left|top|marginLeft|marginTop$/;
|
||||
|
||||
/**
|
||||
* Apply styles to the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {Object} styles - The styles for applying.
|
||||
*/
|
||||
export function setStyle(element, styles) {
|
||||
const { style } = element;
|
||||
|
||||
forEach(styles, (value, property) => {
|
||||
if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
|
||||
value = `${value}px`;
|
||||
}
|
||||
|
||||
style[property] = value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given element has a special class.
|
||||
* @param {Element} element - The element to check.
|
||||
* @param {string} value - The class to search.
|
||||
* @returns {boolean} Returns `true` if the special class was found.
|
||||
*/
|
||||
export function hasClass(element, value) {
|
||||
return element.classList
|
||||
? element.classList.contains(value)
|
||||
: element.className.indexOf(value) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add classes to the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {string} value - The classes to be added.
|
||||
*/
|
||||
export function addClass(element, value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNumber(element.length)) {
|
||||
forEach(element, (elem) => {
|
||||
addClass(elem, value);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.classList) {
|
||||
element.classList.add(value);
|
||||
return;
|
||||
}
|
||||
|
||||
const className = element.className.trim();
|
||||
|
||||
if (!className) {
|
||||
element.className = value;
|
||||
} else if (className.indexOf(value) < 0) {
|
||||
element.className = `${className} ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove classes from the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {string} value - The classes to be removed.
|
||||
*/
|
||||
export function removeClass(element, value) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNumber(element.length)) {
|
||||
forEach(element, (elem) => {
|
||||
removeClass(elem, value);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.classList) {
|
||||
element.classList.remove(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.className.indexOf(value) >= 0) {
|
||||
element.className = element.className.replace(value, '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove classes from the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {string} value - The classes to be toggled.
|
||||
* @param {boolean} added - Add only.
|
||||
*/
|
||||
export function toggleClass(element, value, added) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNumber(element.length)) {
|
||||
forEach(element, (elem) => {
|
||||
toggleClass(elem, value, added);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// IE10-11 doesn't support the second parameter of `classList.toggle`
|
||||
if (added) {
|
||||
addClass(element, value);
|
||||
} else {
|
||||
removeClass(element, value);
|
||||
}
|
||||
}
|
||||
|
||||
const REGEXP_CAMEL_CASE = /([a-z\d])([A-Z])/g;
|
||||
|
||||
/**
|
||||
* Transform the given string from camelCase to kebab-case
|
||||
* @param {string} value - The value to transform.
|
||||
* @returns {string} The transformed value.
|
||||
*/
|
||||
export function toParamCase(value) {
|
||||
return value.replace(REGEXP_CAMEL_CASE, '$1-$2').toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data from the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {string} name - The data key to get.
|
||||
* @returns {string} The data value.
|
||||
*/
|
||||
export function getData(element, name) {
|
||||
if (isObject(element[name])) {
|
||||
return element[name];
|
||||
}
|
||||
|
||||
if (element.dataset) {
|
||||
return element.dataset[name];
|
||||
}
|
||||
|
||||
return element.getAttribute(`data-${toParamCase(name)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data to the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {string} name - The data key to set.
|
||||
* @param {string} data - The data value.
|
||||
*/
|
||||
export function setData(element, name, data) {
|
||||
if (isObject(data)) {
|
||||
element[name] = data;
|
||||
} else if (element.dataset) {
|
||||
element.dataset[name] = data;
|
||||
} else {
|
||||
element.setAttribute(`data-${toParamCase(name)}`, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove data from the given element.
|
||||
* @param {Element} element - The target element.
|
||||
* @param {string} name - The data key to remove.
|
||||
*/
|
||||
export function removeData(element, name) {
|
||||
if (isObject(element[name])) {
|
||||
try {
|
||||
delete element[name];
|
||||
} catch (error) {
|
||||
element[name] = undefined;
|
||||
}
|
||||
} else if (element.dataset) {
|
||||
// #128 Safari not allows to delete dataset property
|
||||
try {
|
||||
delete element.dataset[name];
|
||||
} catch (error) {
|
||||
element.dataset[name] = undefined;
|
||||
}
|
||||
} else {
|
||||
element.removeAttribute(`data-${toParamCase(name)}`);
|
||||
}
|
||||
}
|
||||
|
||||
const REGEXP_SPACES = /\s\s*/;
|
||||
const onceSupported = (() => {
|
||||
let supported = false;
|
||||
|
||||
if (IS_BROWSER) {
|
||||
let once = false;
|
||||
const listener = () => {};
|
||||
const options = Object.defineProperty({}, 'once', {
|
||||
get() {
|
||||
supported = true;
|
||||
return once;
|
||||
},
|
||||
|
||||
/**
|
||||
* This setter can fix a `TypeError` in strict mode
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
|
||||
* @param {boolean} value - The value to set
|
||||
*/
|
||||
set(value) {
|
||||
once = value;
|
||||
},
|
||||
});
|
||||
|
||||
WINDOW.addEventListener('test', listener, options);
|
||||
WINDOW.removeEventListener('test', listener, options);
|
||||
}
|
||||
|
||||
return supported;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Remove event listener from the target element.
|
||||
* @param {Element} element - The event target.
|
||||
* @param {string} type - The event type(s).
|
||||
* @param {Function} listener - The event listener.
|
||||
* @param {Object} options - The event options.
|
||||
*/
|
||||
export function removeListener(element, type, listener, options = {}) {
|
||||
let handler = listener;
|
||||
|
||||
type.trim().split(REGEXP_SPACES).forEach((event) => {
|
||||
if (!onceSupported) {
|
||||
const { listeners } = element;
|
||||
|
||||
if (listeners && listeners[event] && listeners[event][listener]) {
|
||||
handler = listeners[event][listener];
|
||||
delete listeners[event][listener];
|
||||
|
||||
if (Object.keys(listeners[event]).length === 0) {
|
||||
delete listeners[event];
|
||||
}
|
||||
|
||||
if (Object.keys(listeners).length === 0) {
|
||||
delete element.listeners;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
element.removeEventListener(event, handler, options);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listener to the target element.
|
||||
* @param {Element} element - The event target.
|
||||
* @param {string} type - The event type(s).
|
||||
* @param {Function} listener - The event listener.
|
||||
* @param {Object} options - The event options.
|
||||
*/
|
||||
export function addListener(element, type, listener, options = {}) {
|
||||
let handler = listener;
|
||||
|
||||
type.trim().split(REGEXP_SPACES).forEach((event) => {
|
||||
if (options.once && !onceSupported) {
|
||||
const { listeners = {} } = element;
|
||||
|
||||
handler = (...args) => {
|
||||
delete listeners[event][listener];
|
||||
element.removeEventListener(event, handler, options);
|
||||
listener.apply(element, args);
|
||||
};
|
||||
|
||||
if (!listeners[event]) {
|
||||
listeners[event] = {};
|
||||
}
|
||||
|
||||
if (listeners[event][listener]) {
|
||||
element.removeEventListener(event, listeners[event][listener], options);
|
||||
}
|
||||
|
||||
listeners[event][listener] = handler;
|
||||
element.listeners = listeners;
|
||||
}
|
||||
|
||||
element.addEventListener(event, handler, options);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch event on the target element.
|
||||
* @param {Element} element - The event target.
|
||||
* @param {string} type - The event type(s).
|
||||
* @param {Object} data - The additional event data.
|
||||
* @returns {boolean} Indicate if the event is default prevented or not.
|
||||
*/
|
||||
export function dispatchEvent(element, type, data) {
|
||||
let event;
|
||||
|
||||
// Event and CustomEvent on IE9-11 are global objects, not constructors
|
||||
if (isFunction(Event) && isFunction(CustomEvent)) {
|
||||
event = new CustomEvent(type, {
|
||||
detail: data,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
} else {
|
||||
event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent(type, true, true, data);
|
||||
}
|
||||
|
||||
return element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the offset base on the document.
|
||||
* @param {Element} element - The target element.
|
||||
* @returns {Object} The offset data.
|
||||
*/
|
||||
export function getOffset(element) {
|
||||
const box = element.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
|
||||
top: box.top + (window.pageYOffset - document.documentElement.clientTop),
|
||||
};
|
||||
}
|
||||
|
||||
const { location } = WINDOW;
|
||||
const REGEXP_ORIGINS = /^(\w+:)\/\/([^:/?#]*):?(\d*)/i;
|
||||
|
||||
/**
|
||||
* Check if the given URL is a cross origin URL.
|
||||
* @param {string} url - The target URL.
|
||||
* @returns {boolean} Returns `true` if the given URL is a cross origin URL, else `false`.
|
||||
*/
|
||||
export function isCrossOriginURL(url) {
|
||||
const parts = url.match(REGEXP_ORIGINS);
|
||||
|
||||
return parts !== null && (
|
||||
parts[1] !== location.protocol
|
||||
|| parts[2] !== location.hostname
|
||||
|| parts[3] !== location.port
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add timestamp to the given URL.
|
||||
* @param {string} url - The target URL.
|
||||
* @returns {string} The result URL.
|
||||
*/
|
||||
export function addTimestamp(url) {
|
||||
const timestamp = `timestamp=${(new Date()).getTime()}`;
|
||||
|
||||
return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transforms base on the given object.
|
||||
* @param {Object} obj - The target object.
|
||||
* @returns {string} A string contains transform values.
|
||||
*/
|
||||
export function getTransforms({
|
||||
rotate,
|
||||
scaleX,
|
||||
scaleY,
|
||||
translateX,
|
||||
translateY,
|
||||
}) {
|
||||
const values = [];
|
||||
|
||||
if (isNumber(translateX) && translateX !== 0) {
|
||||
values.push(`translateX(${translateX}px)`);
|
||||
}
|
||||
|
||||
if (isNumber(translateY) && translateY !== 0) {
|
||||
values.push(`translateY(${translateY}px)`);
|
||||
}
|
||||
|
||||
// Rotate should come first before scale to match orientation transform
|
||||
if (isNumber(rotate) && rotate !== 0) {
|
||||
values.push(`rotate(${rotate}deg)`);
|
||||
}
|
||||
|
||||
if (isNumber(scaleX) && scaleX !== 1) {
|
||||
values.push(`scaleX(${scaleX})`);
|
||||
}
|
||||
|
||||
if (isNumber(scaleY) && scaleY !== 1) {
|
||||
values.push(`scaleY(${scaleY})`);
|
||||
}
|
||||
|
||||
const transform = values.length ? values.join(' ') : 'none';
|
||||
|
||||
return {
|
||||
WebkitTransform: transform,
|
||||
msTransform: transform,
|
||||
transform,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the max ratio of a group of pointers.
|
||||
* @param {string} pointers - The target pointers.
|
||||
* @returns {number} The result ratio.
|
||||
*/
|
||||
export function getMaxZoomRatio(pointers) {
|
||||
const pointers2 = { ...pointers };
|
||||
let maxRatio = 0;
|
||||
|
||||
forEach(pointers, (pointer, pointerId) => {
|
||||
delete pointers2[pointerId];
|
||||
|
||||
forEach(pointers2, (pointer2) => {
|
||||
const x1 = Math.abs(pointer.startX - pointer2.startX);
|
||||
const y1 = Math.abs(pointer.startY - pointer2.startY);
|
||||
const x2 = Math.abs(pointer.endX - pointer2.endX);
|
||||
const y2 = Math.abs(pointer.endY - pointer2.endY);
|
||||
const z1 = Math.sqrt((x1 * x1) + (y1 * y1));
|
||||
const z2 = Math.sqrt((x2 * x2) + (y2 * y2));
|
||||
const ratio = (z2 - z1) / z1;
|
||||
|
||||
if (Math.abs(ratio) > Math.abs(maxRatio)) {
|
||||
maxRatio = ratio;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return maxRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pointer from an event object.
|
||||
* @param {Object} event - The target event object.
|
||||
* @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
|
||||
* @returns {Object} The result pointer contains start and/or end point coordinates.
|
||||
*/
|
||||
export function getPointer({ pageX, pageY }, endOnly) {
|
||||
const end = {
|
||||
endX: pageX,
|
||||
endY: pageY,
|
||||
};
|
||||
|
||||
return endOnly ? end : ({
|
||||
startX: pageX,
|
||||
startY: pageY,
|
||||
...end,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the center point coordinate of a group of pointers.
|
||||
* @param {Object} pointers - The target pointers.
|
||||
* @returns {Object} The center point coordinate.
|
||||
*/
|
||||
export function getPointersCenter(pointers) {
|
||||
let pageX = 0;
|
||||
let pageY = 0;
|
||||
let count = 0;
|
||||
|
||||
forEach(pointers, ({ startX, startY }) => {
|
||||
pageX += startX;
|
||||
pageY += startY;
|
||||
count += 1;
|
||||
});
|
||||
|
||||
pageX /= count;
|
||||
pageY /= count;
|
||||
|
||||
return {
|
||||
pageX,
|
||||
pageY,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the max sizes in a rectangle under the given aspect ratio.
|
||||
* @param {Object} data - The original sizes.
|
||||
* @param {string} [type='contain'] - The adjust type.
|
||||
* @returns {Object} The result sizes.
|
||||
*/
|
||||
export function getAdjustedSizes(
|
||||
{
|
||||
aspectRatio,
|
||||
height,
|
||||
width,
|
||||
},
|
||||
type = 'contain', // or 'cover'
|
||||
) {
|
||||
const isValidWidth = isPositiveNumber(width);
|
||||
const isValidHeight = isPositiveNumber(height);
|
||||
|
||||
if (isValidWidth && isValidHeight) {
|
||||
const adjustedWidth = height * aspectRatio;
|
||||
|
||||
if ((type === 'contain' && adjustedWidth > width) || (type === 'cover' && adjustedWidth < width)) {
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
} else if (isValidWidth) {
|
||||
height = width / aspectRatio;
|
||||
} else if (isValidHeight) {
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new sizes of a rectangle after rotated.
|
||||
* @param {Object} data - The original sizes.
|
||||
* @returns {Object} The result sizes.
|
||||
*/
|
||||
export function getRotatedSizes({ width, height, degree }) {
|
||||
degree = Math.abs(degree) % 180;
|
||||
|
||||
if (degree === 90) {
|
||||
return {
|
||||
width: height,
|
||||
height: width,
|
||||
};
|
||||
}
|
||||
|
||||
const arc = ((degree % 90) * Math.PI) / 180;
|
||||
const sinArc = Math.sin(arc);
|
||||
const cosArc = Math.cos(arc);
|
||||
const newWidth = (width * cosArc) + (height * sinArc);
|
||||
const newHeight = (width * sinArc) + (height * cosArc);
|
||||
|
||||
return degree > 90 ? {
|
||||
width: newHeight,
|
||||
height: newWidth,
|
||||
} : {
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a canvas which drew the given image.
|
||||
* @param {HTMLImageElement} image - The image for drawing.
|
||||
* @param {Object} imageData - The image data.
|
||||
* @param {Object} canvasData - The canvas data.
|
||||
* @param {Object} options - The options.
|
||||
* @returns {HTMLCanvasElement} The result canvas.
|
||||
*/
|
||||
export function getSourceCanvas(
|
||||
image,
|
||||
{
|
||||
aspectRatio: imageAspectRatio,
|
||||
naturalWidth: imageNaturalWidth,
|
||||
naturalHeight: imageNaturalHeight,
|
||||
rotate = 0,
|
||||
scaleX = 1,
|
||||
scaleY = 1,
|
||||
},
|
||||
{
|
||||
aspectRatio,
|
||||
naturalWidth,
|
||||
naturalHeight,
|
||||
},
|
||||
{
|
||||
fillColor = 'transparent',
|
||||
imageSmoothingEnabled = true,
|
||||
imageSmoothingQuality = 'low',
|
||||
maxWidth = Infinity,
|
||||
maxHeight = Infinity,
|
||||
minWidth = 0,
|
||||
minHeight = 0,
|
||||
},
|
||||
) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
const maxSizes = getAdjustedSizes({
|
||||
aspectRatio,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
});
|
||||
const minSizes = getAdjustedSizes({
|
||||
aspectRatio,
|
||||
width: minWidth,
|
||||
height: minHeight,
|
||||
}, 'cover');
|
||||
const width = Math.min(maxSizes.width, Math.max(minSizes.width, naturalWidth));
|
||||
const height = Math.min(maxSizes.height, Math.max(minSizes.height, naturalHeight));
|
||||
|
||||
// Note: should always use image's natural sizes for drawing as
|
||||
// imageData.naturalWidth === canvasData.naturalHeight when rotate % 180 === 90
|
||||
const destMaxSizes = getAdjustedSizes({
|
||||
aspectRatio: imageAspectRatio,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
});
|
||||
const destMinSizes = getAdjustedSizes({
|
||||
aspectRatio: imageAspectRatio,
|
||||
width: minWidth,
|
||||
height: minHeight,
|
||||
}, 'cover');
|
||||
const destWidth = Math.min(
|
||||
destMaxSizes.width,
|
||||
Math.max(destMinSizes.width, imageNaturalWidth),
|
||||
);
|
||||
const destHeight = Math.min(
|
||||
destMaxSizes.height,
|
||||
Math.max(destMinSizes.height, imageNaturalHeight),
|
||||
);
|
||||
const params = [
|
||||
-destWidth / 2,
|
||||
-destHeight / 2,
|
||||
destWidth,
|
||||
destHeight,
|
||||
];
|
||||
|
||||
canvas.width = normalizeDecimalNumber(width);
|
||||
canvas.height = normalizeDecimalNumber(height);
|
||||
context.fillStyle = fillColor;
|
||||
context.fillRect(0, 0, width, height);
|
||||
context.save();
|
||||
context.translate(width / 2, height / 2);
|
||||
context.rotate((rotate * Math.PI) / 180);
|
||||
context.scale(scaleX, scaleY);
|
||||
context.imageSmoothingEnabled = imageSmoothingEnabled;
|
||||
context.imageSmoothingQuality = imageSmoothingQuality;
|
||||
context.drawImage(image, ...params.map((param) => Math.floor(normalizeDecimalNumber(param))));
|
||||
context.restore();
|
||||
return canvas;
|
||||
}
|
||||
|
||||
const { fromCharCode } = String;
|
||||
|
||||
/**
|
||||
* Get string from char code in data view.
|
||||
* @param {DataView} dataView - The data view for read.
|
||||
* @param {number} start - The start index.
|
||||
* @param {number} length - The read length.
|
||||
* @returns {string} The read result.
|
||||
*/
|
||||
export function getStringFromCharCode(dataView, start, length) {
|
||||
let str = '';
|
||||
|
||||
length += start;
|
||||
|
||||
for (let i = start; i < length; i += 1) {
|
||||
str += fromCharCode(dataView.getUint8(i));
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
const REGEXP_DATA_URL_HEAD = /^data:.*,/;
|
||||
|
||||
/**
|
||||
* Transform Data URL to array buffer.
|
||||
* @param {string} dataURL - The Data URL to transform.
|
||||
* @returns {ArrayBuffer} The result array buffer.
|
||||
*/
|
||||
export function dataURLToArrayBuffer(dataURL) {
|
||||
const base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
|
||||
const binary = atob(base64);
|
||||
const arrayBuffer = new ArrayBuffer(binary.length);
|
||||
const uint8 = new Uint8Array(arrayBuffer);
|
||||
|
||||
forEach(uint8, (value, i) => {
|
||||
uint8[i] = binary.charCodeAt(i);
|
||||
});
|
||||
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform array buffer to Data URL.
|
||||
* @param {ArrayBuffer} arrayBuffer - The array buffer to transform.
|
||||
* @param {string} mimeType - The mime type of the Data URL.
|
||||
* @returns {string} The result Data URL.
|
||||
*/
|
||||
export function arrayBufferToDataURL(arrayBuffer, mimeType) {
|
||||
const chunks = [];
|
||||
|
||||
// Chunk Typed Array for better performance (#435)
|
||||
const chunkSize = 8192;
|
||||
let uint8 = new Uint8Array(arrayBuffer);
|
||||
|
||||
while (uint8.length > 0) {
|
||||
// XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9
|
||||
// eslint-disable-next-line prefer-spread
|
||||
chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));
|
||||
uint8 = uint8.subarray(chunkSize);
|
||||
}
|
||||
|
||||
return `data:${mimeType};base64,${btoa(chunks.join(''))}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get orientation value from given array buffer.
|
||||
* @param {ArrayBuffer} arrayBuffer - The array buffer to read.
|
||||
* @returns {number} The read orientation value.
|
||||
*/
|
||||
export function resetAndGetOrientation(arrayBuffer) {
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
let orientation;
|
||||
|
||||
// Ignores range error when the image does not have correct Exif information
|
||||
try {
|
||||
let littleEndian;
|
||||
let app1Start;
|
||||
let ifdStart;
|
||||
|
||||
// Only handle JPEG image (start by 0xFFD8)
|
||||
if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
|
||||
const length = dataView.byteLength;
|
||||
let offset = 2;
|
||||
|
||||
while (offset + 1 < length) {
|
||||
if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
|
||||
app1Start = offset;
|
||||
break;
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (app1Start) {
|
||||
const exifIDCode = app1Start + 4;
|
||||
const tiffOffset = app1Start + 10;
|
||||
|
||||
if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
|
||||
const endianness = dataView.getUint16(tiffOffset);
|
||||
|
||||
littleEndian = endianness === 0x4949;
|
||||
|
||||
if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
|
||||
if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
|
||||
const firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
|
||||
|
||||
if (firstIFDOffset >= 0x00000008) {
|
||||
ifdStart = tiffOffset + firstIFDOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ifdStart) {
|
||||
const length = dataView.getUint16(ifdStart, littleEndian);
|
||||
let offset;
|
||||
let i;
|
||||
|
||||
for (i = 0; i < length; i += 1) {
|
||||
offset = ifdStart + (i * 12) + 2;
|
||||
|
||||
if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
|
||||
// 8 is the offset of the current tag's value
|
||||
offset += 8;
|
||||
|
||||
// Get the original orientation value
|
||||
orientation = dataView.getUint16(offset, littleEndian);
|
||||
|
||||
// Override the orientation with its default value
|
||||
dataView.setUint16(offset, 1, littleEndian);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
orientation = 1;
|
||||
}
|
||||
|
||||
return orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Exif Orientation value.
|
||||
* @param {number} orientation - The orientation to parse.
|
||||
* @returns {Object} The parsed result.
|
||||
*/
|
||||
export function parseOrientation(orientation) {
|
||||
let rotate = 0;
|
||||
let scaleX = 1;
|
||||
let scaleY = 1;
|
||||
|
||||
switch (orientation) {
|
||||
// Flip horizontal
|
||||
case 2:
|
||||
scaleX = -1;
|
||||
break;
|
||||
|
||||
// Rotate left 180°
|
||||
case 3:
|
||||
rotate = -180;
|
||||
break;
|
||||
|
||||
// Flip vertical
|
||||
case 4:
|
||||
scaleY = -1;
|
||||
break;
|
||||
|
||||
// Flip vertical and rotate right 90°
|
||||
case 5:
|
||||
rotate = 90;
|
||||
scaleY = -1;
|
||||
break;
|
||||
|
||||
// Rotate right 90°
|
||||
case 6:
|
||||
rotate = 90;
|
||||
break;
|
||||
|
||||
// Flip horizontal and rotate right 90°
|
||||
case 7:
|
||||
rotate = 90;
|
||||
scaleX = -1;
|
||||
break;
|
||||
|
||||
// Rotate left 90°
|
||||
case 8:
|
||||
rotate = -90;
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return {
|
||||
rotate,
|
||||
scaleX,
|
||||
scaleY,
|
||||
};
|
||||
}
|
||||
204
node_modules/cropperjs/types/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
declare namespace Cropper {
|
||||
export type Action = 'crop' | 'move' | 'zoom' | 'e' | 's' | 'w' | 'n' | 'ne' | 'nw' | 'se' | 'sw' | 'all';
|
||||
export type DragMode = 'crop' | 'move' | 'none';
|
||||
export type ImageSmoothingQuality = 'low' | 'medium' | 'high';
|
||||
export type ViewMode = 0 | 1 | 2 | 3;
|
||||
|
||||
export interface Data {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotate: number;
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
}
|
||||
|
||||
export interface ContainerData {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface ImageData {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotate: number;
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
naturalWidth: number;
|
||||
naturalHeight: number;
|
||||
aspectRatio: number;
|
||||
}
|
||||
|
||||
export interface CanvasData {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
naturalWidth: number;
|
||||
naturalHeight: number;
|
||||
}
|
||||
|
||||
export interface CropBoxData {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface GetCroppedCanvasOptions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
rounded?: boolean;
|
||||
fillColor?: string;
|
||||
imageSmoothingEnabled?: boolean;
|
||||
imageSmoothingQuality?: ImageSmoothingQuality;
|
||||
}
|
||||
|
||||
export interface SetDataOptions {
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
rotate?: number;
|
||||
scaleX?: number;
|
||||
scaleY?: number;
|
||||
}
|
||||
|
||||
export interface SetCanvasDataOptions {
|
||||
left?: number;
|
||||
top?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface SetCropBoxDataOptions {
|
||||
left?: number;
|
||||
top?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
interface CropperEvent<T extends EventTarget = EventTarget> extends CustomEvent {
|
||||
currentTarget: T & { cropper: Cropper };
|
||||
}
|
||||
|
||||
export type ReadyEvent<T extends EventTarget> = CropperEvent<T>
|
||||
|
||||
export interface CropEvent<T extends EventTarget = EventTarget> extends CropperEvent<T> {
|
||||
detail: Data;
|
||||
}
|
||||
|
||||
export interface CropEventData {
|
||||
originalEvent: PointerEvent | TouchEvent | MouseEvent;
|
||||
action: Action;
|
||||
}
|
||||
|
||||
export interface CropStartEvent<T extends EventTarget = EventTarget> extends CropperEvent<T> {
|
||||
detail: CropEventData;
|
||||
}
|
||||
|
||||
export interface CropMoveEvent<T extends EventTarget = EventTarget> extends CropperEvent<T> {
|
||||
detail: CropEventData;
|
||||
}
|
||||
|
||||
export interface CropEndEvent<T extends EventTarget = EventTarget> extends CropperEvent<T> {
|
||||
detail: CropEventData;
|
||||
}
|
||||
|
||||
export interface ZoomEventData {
|
||||
originalEvent: WheelEvent | PointerEvent | TouchEvent | MouseEvent;
|
||||
oldRatio: number;
|
||||
ratio: number;
|
||||
}
|
||||
|
||||
export interface ZoomEvent<T extends EventTarget = EventTarget> extends CropperEvent<T> {
|
||||
detail: ZoomEventData;
|
||||
}
|
||||
|
||||
export interface Options<T extends EventTarget = EventTarget> {
|
||||
aspectRatio?: number;
|
||||
autoCrop?: boolean;
|
||||
autoCropArea?: number;
|
||||
background?: boolean;
|
||||
center?: boolean;
|
||||
checkCrossOrigin?: boolean;
|
||||
checkOrientation?: boolean;
|
||||
cropBoxMovable?: boolean;
|
||||
cropBoxResizable?: boolean;
|
||||
data?: SetDataOptions;
|
||||
dragMode?: DragMode;
|
||||
guides?: boolean;
|
||||
highlight?: boolean;
|
||||
initialAspectRatio?: number;
|
||||
minCanvasHeight?: number;
|
||||
minCanvasWidth?: number;
|
||||
minContainerHeight?: number;
|
||||
minContainerWidth?: number;
|
||||
minCropBoxHeight?: number;
|
||||
minCropBoxWidth?: number;
|
||||
modal?: boolean;
|
||||
movable?: boolean;
|
||||
preview?: HTMLElement | HTMLElement[] | NodeListOf<HTMLElement> | string;
|
||||
responsive?: boolean;
|
||||
restore?: boolean;
|
||||
rotatable?: boolean;
|
||||
scalable?: boolean;
|
||||
toggleDragModeOnDblclick?: boolean;
|
||||
viewMode?: ViewMode;
|
||||
wheelZoomRatio?: number;
|
||||
zoomOnTouch?: boolean;
|
||||
zoomOnWheel?: boolean;
|
||||
zoomable?: boolean;
|
||||
ready?(event: ReadyEvent<T>): void;
|
||||
crop?(event: CropEvent<T>): void;
|
||||
cropend?(event: CropEndEvent<T>): void;
|
||||
cropmove?(event: CropMoveEvent<T>): void;
|
||||
cropstart?(event: CropStartEvent<T>): void;
|
||||
zoom?(event: ZoomEvent<T>): void;
|
||||
}
|
||||
}
|
||||
|
||||
declare class Cropper {
|
||||
constructor(element: HTMLImageElement, options?: Cropper.Options<HTMLImageElement>);
|
||||
constructor(element: HTMLCanvasElement, options?: Cropper.Options<HTMLCanvasElement>);
|
||||
clear(): Cropper;
|
||||
crop(): Cropper;
|
||||
destroy(): Cropper;
|
||||
disable(): Cropper;
|
||||
enable(): Cropper;
|
||||
getCanvasData(): Cropper.CanvasData;
|
||||
getContainerData(): Cropper.ContainerData;
|
||||
getCropBoxData(): Cropper.CropBoxData;
|
||||
getCroppedCanvas(options?: Cropper.GetCroppedCanvasOptions): HTMLCanvasElement;
|
||||
getData(rounded?: boolean): Cropper.Data;
|
||||
getImageData(): Cropper.ImageData;
|
||||
move(offsetX: number, offsetY?: number): Cropper;
|
||||
moveTo(x: number, y?: number): Cropper;
|
||||
replace(url: string, onlyColorChanged?: boolean): Cropper;
|
||||
reset(): Cropper;
|
||||
rotate(degree: number): Cropper;
|
||||
rotateTo(degree: number): Cropper;
|
||||
scale(scaleX: number, scaleY?: number): Cropper;
|
||||
scaleX(scaleX: number): Cropper;
|
||||
scaleY(scaleY: number): Cropper;
|
||||
setAspectRatio(aspectRatio: number): Cropper;
|
||||
setCanvasData(data: Cropper.SetCanvasDataOptions): Cropper;
|
||||
setCropBoxData(data: Cropper.SetCropBoxDataOptions): Cropper;
|
||||
setData(data: Cropper.SetDataOptions): Cropper;
|
||||
setDragMode(dragMode: Cropper.DragMode): Cropper;
|
||||
zoom(ratio: number): Cropper;
|
||||
zoomTo(ratio: number, pivot?: { x: number; y: number }): Cropper;
|
||||
static noConflict(): Cropper;
|
||||
static setDefaults(options: Cropper.Options<EventTarget>): void;
|
||||
}
|
||||
|
||||
declare module 'cropperjs' {
|
||||
export default Cropper;
|
||||
}
|
||||
191
node_modules/file-type/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/// <reference types="node"/>
|
||||
import {Readable as ReadableStream} from 'stream';
|
||||
|
||||
declare namespace fileType {
|
||||
type FileType =
|
||||
| 'jpg'
|
||||
| 'png'
|
||||
| 'gif'
|
||||
| 'webp'
|
||||
| 'flif'
|
||||
| 'cr2'
|
||||
| 'tif'
|
||||
| 'bmp'
|
||||
| 'jxr'
|
||||
| 'psd'
|
||||
| 'zip'
|
||||
| 'tar'
|
||||
| 'rar'
|
||||
| 'gz'
|
||||
| 'bz2'
|
||||
| '7z'
|
||||
| 'dmg'
|
||||
| 'mp4'
|
||||
| 'm4v'
|
||||
| 'mid'
|
||||
| 'mkv'
|
||||
| 'webm'
|
||||
| 'mov'
|
||||
| 'avi'
|
||||
| 'wmv'
|
||||
| 'mpg'
|
||||
| 'mp2'
|
||||
| 'mp3'
|
||||
| 'm4a'
|
||||
| 'ogg'
|
||||
| 'opus'
|
||||
| 'flac'
|
||||
| 'wav'
|
||||
| 'qcp'
|
||||
| 'amr'
|
||||
| 'pdf'
|
||||
| 'epub'
|
||||
| 'mobi'
|
||||
| 'exe'
|
||||
| 'swf'
|
||||
| 'rtf'
|
||||
| 'woff'
|
||||
| 'woff2'
|
||||
| 'eot'
|
||||
| 'ttf'
|
||||
| 'otf'
|
||||
| 'ico'
|
||||
| 'flv'
|
||||
| 'ps'
|
||||
| 'xz'
|
||||
| 'sqlite'
|
||||
| 'nes'
|
||||
| 'crx'
|
||||
| 'xpi'
|
||||
| 'cab'
|
||||
| 'deb'
|
||||
| 'ar'
|
||||
| 'rpm'
|
||||
| 'Z'
|
||||
| 'lz'
|
||||
| 'msi'
|
||||
| 'mxf'
|
||||
| 'mts'
|
||||
| 'wasm'
|
||||
| 'blend'
|
||||
| 'bpg'
|
||||
| 'docx'
|
||||
| 'pptx'
|
||||
| 'xlsx'
|
||||
| '3gp'
|
||||
| 'jp2'
|
||||
| 'jpm'
|
||||
| 'jpx'
|
||||
| 'mj2'
|
||||
| 'aif'
|
||||
| 'odt'
|
||||
| 'ods'
|
||||
| 'odp'
|
||||
| 'xml'
|
||||
| 'heic'
|
||||
| 'cur'
|
||||
| 'ktx'
|
||||
| 'ape'
|
||||
| 'wv'
|
||||
| 'asf'
|
||||
| 'wma'
|
||||
| 'wmv'
|
||||
| 'dcm'
|
||||
| 'mpc'
|
||||
| 'ics'
|
||||
| 'glb'
|
||||
| 'pcap';
|
||||
|
||||
interface FileTypeResult {
|
||||
/**
|
||||
One of the supported [file types](https://github.com/sindresorhus/file-type#supported-file-types).
|
||||
*/
|
||||
ext: FileType;
|
||||
|
||||
/**
|
||||
The detected [MIME type](https://en.wikipedia.org/wiki/Internet_media_type).
|
||||
*/
|
||||
mime: string;
|
||||
}
|
||||
|
||||
type ReadableStreamWithFileType = ReadableStream & {
|
||||
readonly fileType: FileTypeResult | null;
|
||||
};
|
||||
}
|
||||
|
||||
declare const fileType: {
|
||||
/**
|
||||
Detect the file type of a `Buffer`/`Uint8Array`/`ArrayBuffer`. The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
|
||||
|
||||
@param buffer - It only needs the first `.minimumBytes` bytes. The exception is detection of `docx`, `pptx`, and `xlsx` which potentially requires reading the whole file.
|
||||
@returns An object with the detected file type and MIME type or `null` when there was no match.
|
||||
|
||||
@example
|
||||
```
|
||||
import readChunk = require('read-chunk');
|
||||
import fileType = require('file-type');
|
||||
|
||||
const buffer = readChunk.sync('unicorn.png', 0, fileType.minimumBytes);
|
||||
|
||||
fileType(buffer);
|
||||
//=> {ext: 'png', mime: 'image/png'}
|
||||
|
||||
|
||||
// Or from a remote location:
|
||||
|
||||
import * as http from 'http';
|
||||
|
||||
const url = 'https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif';
|
||||
|
||||
http.get(url, response => {
|
||||
response.on('readable', () => {
|
||||
const chunk = response.read(fileType.minimumBytes);
|
||||
response.destroy();
|
||||
console.log(fileType(chunk));
|
||||
//=> {ext: 'gif', mime: 'image/gif'}
|
||||
});
|
||||
});
|
||||
```
|
||||
*/
|
||||
(buffer: Buffer | Uint8Array | ArrayBuffer): fileType.FileTypeResult | null;
|
||||
|
||||
/**
|
||||
The minimum amount of bytes needed to detect a file type. Currently, it's 4100 bytes, but it can change, so don't hard-code it.
|
||||
*/
|
||||
readonly minimumBytes: number;
|
||||
|
||||
/**
|
||||
Detect the file type of a readable stream.
|
||||
|
||||
@param readableStream - A readable stream containing a file to examine, see: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable).
|
||||
@returns A `Promise` which resolves to the original readable stream argument, but with an added `fileType` property, which is an object like the one returned from `fileType()`.
|
||||
|
||||
@example
|
||||
```
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
import fileType = require('file-type');
|
||||
|
||||
(async () => {
|
||||
const read = fs.createReadStream('encrypted.enc');
|
||||
const decipher = crypto.createDecipheriv(alg, key, iv);
|
||||
|
||||
const stream = await fileType.stream(read.pipe(decipher));
|
||||
|
||||
console.log(stream.fileType);
|
||||
//=> {ext: 'mov', mime: 'video/quicktime'}
|
||||
|
||||
const write = fs.createWriteStream(`decrypted.${stream.fileType.ext}`);
|
||||
stream.pipe(write);
|
||||
})();
|
||||
```
|
||||
*/
|
||||
readonly stream: (
|
||||
readableStream: ReadableStream
|
||||
) => Promise<fileType.ReadableStreamWithFileType>;
|
||||
|
||||
// TODO: Remove this for the next major release
|
||||
readonly default: typeof fileType;
|
||||
};
|
||||
|
||||
export = fileType;
|
||||
953
node_modules/file-type/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,953 @@
|
|||
'use strict';
|
||||
const toBytes = s => [...s].map(c => c.charCodeAt(0));
|
||||
const xpiZipFilename = toBytes('META-INF/mozilla.rsa');
|
||||
const oxmlContentTypes = toBytes('[Content_Types].xml');
|
||||
const oxmlRels = toBytes('_rels/.rels');
|
||||
|
||||
function readUInt64LE(buf, offset = 0) {
|
||||
let n = buf[offset];
|
||||
let mul = 1;
|
||||
let i = 0;
|
||||
while (++i < 8) {
|
||||
mul *= 0x100;
|
||||
n += buf[offset + i] * mul;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
const fileType = input => {
|
||||
if (!(input instanceof Uint8Array || input instanceof ArrayBuffer || Buffer.isBuffer(input))) {
|
||||
throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof input}\``);
|
||||
}
|
||||
|
||||
const buf = input instanceof Uint8Array ? input : new Uint8Array(input);
|
||||
|
||||
if (!(buf && buf.length > 1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const check = (header, options) => {
|
||||
options = Object.assign({
|
||||
offset: 0
|
||||
}, options);
|
||||
|
||||
for (let i = 0; i < header.length; i++) {
|
||||
// If a bitmask is set
|
||||
if (options.mask) {
|
||||
// If header doesn't equal `buf` with bits masked off
|
||||
if (header[i] !== (options.mask[i] & buf[i + options.offset])) {
|
||||
return false;
|
||||
}
|
||||
} else if (header[i] !== buf[i + options.offset]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const checkString = (header, options) => check(toBytes(header), options);
|
||||
|
||||
if (check([0xFF, 0xD8, 0xFF])) {
|
||||
return {
|
||||
ext: 'jpg',
|
||||
mime: 'image/jpeg'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])) {
|
||||
return {
|
||||
ext: 'png',
|
||||
mime: 'image/png'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x47, 0x49, 0x46])) {
|
||||
return {
|
||||
ext: 'gif',
|
||||
mime: 'image/gif'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x57, 0x45, 0x42, 0x50], {offset: 8})) {
|
||||
return {
|
||||
ext: 'webp',
|
||||
mime: 'image/webp'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x46, 0x4C, 0x49, 0x46])) {
|
||||
return {
|
||||
ext: 'flif',
|
||||
mime: 'image/flif'
|
||||
};
|
||||
}
|
||||
|
||||
// Needs to be before `tif` check
|
||||
if (
|
||||
(check([0x49, 0x49, 0x2A, 0x0]) || check([0x4D, 0x4D, 0x0, 0x2A])) &&
|
||||
check([0x43, 0x52], {offset: 8})
|
||||
) {
|
||||
return {
|
||||
ext: 'cr2',
|
||||
mime: 'image/x-canon-cr2'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x49, 0x49, 0x2A, 0x0]) ||
|
||||
check([0x4D, 0x4D, 0x0, 0x2A])
|
||||
) {
|
||||
return {
|
||||
ext: 'tif',
|
||||
mime: 'image/tiff'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x42, 0x4D])) {
|
||||
return {
|
||||
ext: 'bmp',
|
||||
mime: 'image/bmp'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x49, 0x49, 0xBC])) {
|
||||
return {
|
||||
ext: 'jxr',
|
||||
mime: 'image/vnd.ms-photo'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x38, 0x42, 0x50, 0x53])) {
|
||||
return {
|
||||
ext: 'psd',
|
||||
mime: 'image/vnd.adobe.photoshop'
|
||||
};
|
||||
}
|
||||
|
||||
// Zip-based file formats
|
||||
// Need to be before the `zip` check
|
||||
if (check([0x50, 0x4B, 0x3, 0x4])) {
|
||||
if (
|
||||
check([0x6D, 0x69, 0x6D, 0x65, 0x74, 0x79, 0x70, 0x65, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x65, 0x70, 0x75, 0x62, 0x2B, 0x7A, 0x69, 0x70], {offset: 30})
|
||||
) {
|
||||
return {
|
||||
ext: 'epub',
|
||||
mime: 'application/epub+zip'
|
||||
};
|
||||
}
|
||||
|
||||
// Assumes signed `.xpi` from addons.mozilla.org
|
||||
if (check(xpiZipFilename, {offset: 30})) {
|
||||
return {
|
||||
ext: 'xpi',
|
||||
mime: 'application/x-xpinstall'
|
||||
};
|
||||
}
|
||||
|
||||
if (checkString('mimetypeapplication/vnd.oasis.opendocument.text', {offset: 30})) {
|
||||
return {
|
||||
ext: 'odt',
|
||||
mime: 'application/vnd.oasis.opendocument.text'
|
||||
};
|
||||
}
|
||||
|
||||
if (checkString('mimetypeapplication/vnd.oasis.opendocument.spreadsheet', {offset: 30})) {
|
||||
return {
|
||||
ext: 'ods',
|
||||
mime: 'application/vnd.oasis.opendocument.spreadsheet'
|
||||
};
|
||||
}
|
||||
|
||||
if (checkString('mimetypeapplication/vnd.oasis.opendocument.presentation', {offset: 30})) {
|
||||
return {
|
||||
ext: 'odp',
|
||||
mime: 'application/vnd.oasis.opendocument.presentation'
|
||||
};
|
||||
}
|
||||
|
||||
// The docx, xlsx and pptx file types extend the Office Open XML file format:
|
||||
// https://en.wikipedia.org/wiki/Office_Open_XML_file_formats
|
||||
// We look for:
|
||||
// - one entry named '[Content_Types].xml' or '_rels/.rels',
|
||||
// - one entry indicating specific type of file.
|
||||
// MS Office, OpenOffice and LibreOffice may put the parts in different order, so the check should not rely on it.
|
||||
const findNextZipHeaderIndex = (arr, startAt = 0) => arr.findIndex((el, i, arr) => i >= startAt && arr[i] === 0x50 && arr[i + 1] === 0x4B && arr[i + 2] === 0x3 && arr[i + 3] === 0x4);
|
||||
|
||||
let zipHeaderIndex = 0; // The first zip header was already found at index 0
|
||||
let oxmlFound = false;
|
||||
let type = null;
|
||||
|
||||
do {
|
||||
const offset = zipHeaderIndex + 30;
|
||||
|
||||
if (!oxmlFound) {
|
||||
oxmlFound = (check(oxmlContentTypes, {offset}) || check(oxmlRels, {offset}));
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
if (checkString('word/', {offset})) {
|
||||
type = {
|
||||
ext: 'docx',
|
||||
mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
};
|
||||
} else if (checkString('ppt/', {offset})) {
|
||||
type = {
|
||||
ext: 'pptx',
|
||||
mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
||||
};
|
||||
} else if (checkString('xl/', {offset})) {
|
||||
type = {
|
||||
ext: 'xlsx',
|
||||
mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (oxmlFound && type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
zipHeaderIndex = findNextZipHeaderIndex(buf, offset);
|
||||
} while (zipHeaderIndex >= 0);
|
||||
|
||||
// No more zip parts available in the buffer, but maybe we are almost certain about the type?
|
||||
if (type) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x50, 0x4B]) &&
|
||||
(buf[2] === 0x3 || buf[2] === 0x5 || buf[2] === 0x7) &&
|
||||
(buf[3] === 0x4 || buf[3] === 0x6 || buf[3] === 0x8)
|
||||
) {
|
||||
return {
|
||||
ext: 'zip',
|
||||
mime: 'application/zip'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x75, 0x73, 0x74, 0x61, 0x72], {offset: 257})) {
|
||||
return {
|
||||
ext: 'tar',
|
||||
mime: 'application/x-tar'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x52, 0x61, 0x72, 0x21, 0x1A, 0x7]) &&
|
||||
(buf[6] === 0x0 || buf[6] === 0x1)
|
||||
) {
|
||||
return {
|
||||
ext: 'rar',
|
||||
mime: 'application/x-rar-compressed'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x1F, 0x8B, 0x8])) {
|
||||
return {
|
||||
ext: 'gz',
|
||||
mime: 'application/gzip'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x42, 0x5A, 0x68])) {
|
||||
return {
|
||||
ext: 'bz2',
|
||||
mime: 'application/x-bzip2'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C])) {
|
||||
return {
|
||||
ext: '7z',
|
||||
mime: 'application/x-7z-compressed'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x78, 0x01])) {
|
||||
return {
|
||||
ext: 'dmg',
|
||||
mime: 'application/x-apple-diskimage'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x33, 0x67, 0x70, 0x35]) || // 3gp5
|
||||
(
|
||||
check([0x0, 0x0, 0x0]) && check([0x66, 0x74, 0x79, 0x70], {offset: 4}) &&
|
||||
(
|
||||
check([0x6D, 0x70, 0x34, 0x31], {offset: 8}) || // MP41
|
||||
check([0x6D, 0x70, 0x34, 0x32], {offset: 8}) || // MP42
|
||||
check([0x69, 0x73, 0x6F, 0x6D], {offset: 8}) || // ISOM
|
||||
check([0x69, 0x73, 0x6F, 0x32], {offset: 8}) || // ISO2
|
||||
check([0x6D, 0x6D, 0x70, 0x34], {offset: 8}) || // MMP4
|
||||
check([0x4D, 0x34, 0x56], {offset: 8}) || // M4V
|
||||
check([0x64, 0x61, 0x73, 0x68], {offset: 8}) // DASH
|
||||
)
|
||||
)) {
|
||||
return {
|
||||
ext: 'mp4',
|
||||
mime: 'video/mp4'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x4D, 0x54, 0x68, 0x64])) {
|
||||
return {
|
||||
ext: 'mid',
|
||||
mime: 'audio/midi'
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/threatstack/libmagic/blob/master/magic/Magdir/matroska
|
||||
if (check([0x1A, 0x45, 0xDF, 0xA3])) {
|
||||
const sliced = buf.subarray(4, 4 + 4096);
|
||||
const idPos = sliced.findIndex((el, i, arr) => arr[i] === 0x42 && arr[i + 1] === 0x82);
|
||||
|
||||
if (idPos !== -1) {
|
||||
const docTypePos = idPos + 3;
|
||||
const findDocType = type => [...type].every((c, i) => sliced[docTypePos + i] === c.charCodeAt(0));
|
||||
|
||||
if (findDocType('matroska')) {
|
||||
return {
|
||||
ext: 'mkv',
|
||||
mime: 'video/x-matroska'
|
||||
};
|
||||
}
|
||||
|
||||
if (findDocType('webm')) {
|
||||
return {
|
||||
ext: 'webm',
|
||||
mime: 'video/webm'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (check([0x0, 0x0, 0x0, 0x14, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20]) ||
|
||||
check([0x66, 0x72, 0x65, 0x65], {offset: 4}) || // Type: `free`
|
||||
check([0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20], {offset: 4}) ||
|
||||
check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) || // MJPEG
|
||||
check([0x6D, 0x6F, 0x6F, 0x76], {offset: 4}) || // Type: `moov`
|
||||
check([0x77, 0x69, 0x64, 0x65], {offset: 4})) {
|
||||
return {
|
||||
ext: 'mov',
|
||||
mime: 'video/quicktime'
|
||||
};
|
||||
}
|
||||
|
||||
// RIFF file format which might be AVI, WAV, QCP, etc
|
||||
if (check([0x52, 0x49, 0x46, 0x46])) {
|
||||
if (check([0x41, 0x56, 0x49], {offset: 8})) {
|
||||
return {
|
||||
ext: 'avi',
|
||||
mime: 'video/vnd.avi'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x57, 0x41, 0x56, 0x45], {offset: 8})) {
|
||||
return {
|
||||
ext: 'wav',
|
||||
mime: 'audio/vnd.wave'
|
||||
};
|
||||
}
|
||||
|
||||
// QLCM, QCP file
|
||||
if (check([0x51, 0x4C, 0x43, 0x4D], {offset: 8})) {
|
||||
return {
|
||||
ext: 'qcp',
|
||||
mime: 'audio/qcelp'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ASF_Header_Object first 80 bytes
|
||||
if (check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) {
|
||||
// Search for header should be in first 1KB of file.
|
||||
|
||||
let offset = 30;
|
||||
do {
|
||||
const objectSize = readUInt64LE(buf, offset + 16);
|
||||
if (check([0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65], {offset})) {
|
||||
// Sync on Stream-Properties-Object (B7DC0791-A9B7-11CF-8EE6-00C00C205365)
|
||||
if (check([0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B], {offset: offset + 24})) {
|
||||
// Found audio:
|
||||
return {
|
||||
ext: 'wma',
|
||||
mime: 'audio/x-ms-wma'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0xC0, 0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B], {offset: offset + 24})) {
|
||||
// Found video:
|
||||
return {
|
||||
ext: 'wmv',
|
||||
mime: 'video/x-ms-asf'
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
offset += objectSize;
|
||||
} while (offset + 24 <= buf.length);
|
||||
|
||||
// Default to ASF generic extension
|
||||
return {
|
||||
ext: 'asf',
|
||||
mime: 'application/vnd.ms-asf'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x0, 0x0, 0x1, 0xBA]) ||
|
||||
check([0x0, 0x0, 0x1, 0xB3])
|
||||
) {
|
||||
return {
|
||||
ext: 'mpg',
|
||||
mime: 'video/mpeg'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x66, 0x74, 0x79, 0x70, 0x33, 0x67], {offset: 4})) {
|
||||
return {
|
||||
ext: '3gp',
|
||||
mime: 'video/3gpp'
|
||||
};
|
||||
}
|
||||
|
||||
// Check for MPEG header at different starting offsets
|
||||
for (let start = 0; start < 2 && start < (buf.length - 16); start++) {
|
||||
if (
|
||||
check([0x49, 0x44, 0x33], {offset: start}) || // ID3 header
|
||||
check([0xFF, 0xE2], {offset: start, mask: [0xFF, 0xE2]}) // MPEG 1 or 2 Layer 3 header
|
||||
) {
|
||||
return {
|
||||
ext: 'mp3',
|
||||
mime: 'audio/mpeg'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0xFF, 0xE4], {offset: start, mask: [0xFF, 0xE4]}) // MPEG 1 or 2 Layer 2 header
|
||||
) {
|
||||
return {
|
||||
ext: 'mp2',
|
||||
mime: 'audio/mpeg'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0xFF, 0xF8], {offset: start, mask: [0xFF, 0xFC]}) // MPEG 2 layer 0 using ADTS
|
||||
) {
|
||||
return {
|
||||
ext: 'mp2',
|
||||
mime: 'audio/mpeg'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0xFF, 0xF0], {offset: start, mask: [0xFF, 0xFC]}) // MPEG 4 layer 0 using ADTS
|
||||
) {
|
||||
return {
|
||||
ext: 'mp4',
|
||||
mime: 'audio/mpeg'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41], {offset: 4})
|
||||
) {
|
||||
return { // MPEG-4 layer 3 (audio)
|
||||
ext: 'm4a',
|
||||
mime: 'audio/mp4' // RFC 4337
|
||||
};
|
||||
}
|
||||
|
||||
// Needs to be before `ogg` check
|
||||
if (check([0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64], {offset: 28})) {
|
||||
return {
|
||||
ext: 'opus',
|
||||
mime: 'audio/opus'
|
||||
};
|
||||
}
|
||||
|
||||
// If 'OggS' in first bytes, then OGG container
|
||||
if (check([0x4F, 0x67, 0x67, 0x53])) {
|
||||
// This is a OGG container
|
||||
|
||||
// If ' theora' in header.
|
||||
if (check([0x80, 0x74, 0x68, 0x65, 0x6F, 0x72, 0x61], {offset: 28})) {
|
||||
return {
|
||||
ext: 'ogv',
|
||||
mime: 'video/ogg'
|
||||
};
|
||||
}
|
||||
|
||||
// If '\x01video' in header.
|
||||
if (check([0x01, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x00], {offset: 28})) {
|
||||
return {
|
||||
ext: 'ogm',
|
||||
mime: 'video/ogg'
|
||||
};
|
||||
}
|
||||
|
||||
// If ' FLAC' in header https://xiph.org/flac/faq.html
|
||||
if (check([0x7F, 0x46, 0x4C, 0x41, 0x43], {offset: 28})) {
|
||||
return {
|
||||
ext: 'oga',
|
||||
mime: 'audio/ogg'
|
||||
};
|
||||
}
|
||||
|
||||
// 'Speex ' in header https://en.wikipedia.org/wiki/Speex
|
||||
if (check([0x53, 0x70, 0x65, 0x65, 0x78, 0x20, 0x20], {offset: 28})) {
|
||||
return {
|
||||
ext: 'spx',
|
||||
mime: 'audio/ogg'
|
||||
};
|
||||
}
|
||||
|
||||
// If '\x01vorbis' in header
|
||||
if (check([0x01, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73], {offset: 28})) {
|
||||
return {
|
||||
ext: 'ogg',
|
||||
mime: 'audio/ogg'
|
||||
};
|
||||
}
|
||||
|
||||
// Default OGG container https://www.iana.org/assignments/media-types/application/ogg
|
||||
return {
|
||||
ext: 'ogx',
|
||||
mime: 'application/ogg'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x66, 0x4C, 0x61, 0x43])) {
|
||||
return {
|
||||
ext: 'flac',
|
||||
mime: 'audio/x-flac'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x4D, 0x41, 0x43, 0x20])) { // 'MAC '
|
||||
return {
|
||||
ext: 'ape',
|
||||
mime: 'audio/ape'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x77, 0x76, 0x70, 0x6B])) { // 'wvpk'
|
||||
return {
|
||||
ext: 'wv',
|
||||
mime: 'audio/wavpack'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A])) {
|
||||
return {
|
||||
ext: 'amr',
|
||||
mime: 'audio/amr'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x25, 0x50, 0x44, 0x46])) {
|
||||
return {
|
||||
ext: 'pdf',
|
||||
mime: 'application/pdf'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x4D, 0x5A])) {
|
||||
return {
|
||||
ext: 'exe',
|
||||
mime: 'application/x-msdownload'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
(buf[0] === 0x43 || buf[0] === 0x46) &&
|
||||
check([0x57, 0x53], {offset: 1})
|
||||
) {
|
||||
return {
|
||||
ext: 'swf',
|
||||
mime: 'application/x-shockwave-flash'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x7B, 0x5C, 0x72, 0x74, 0x66])) {
|
||||
return {
|
||||
ext: 'rtf',
|
||||
mime: 'application/rtf'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x00, 0x61, 0x73, 0x6D])) {
|
||||
return {
|
||||
ext: 'wasm',
|
||||
mime: 'application/wasm'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x77, 0x4F, 0x46, 0x46]) &&
|
||||
(
|
||||
check([0x00, 0x01, 0x00, 0x00], {offset: 4}) ||
|
||||
check([0x4F, 0x54, 0x54, 0x4F], {offset: 4})
|
||||
)
|
||||
) {
|
||||
return {
|
||||
ext: 'woff',
|
||||
mime: 'font/woff'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x77, 0x4F, 0x46, 0x32]) &&
|
||||
(
|
||||
check([0x00, 0x01, 0x00, 0x00], {offset: 4}) ||
|
||||
check([0x4F, 0x54, 0x54, 0x4F], {offset: 4})
|
||||
)
|
||||
) {
|
||||
return {
|
||||
ext: 'woff2',
|
||||
mime: 'font/woff2'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x4C, 0x50], {offset: 34}) &&
|
||||
(
|
||||
check([0x00, 0x00, 0x01], {offset: 8}) ||
|
||||
check([0x01, 0x00, 0x02], {offset: 8}) ||
|
||||
check([0x02, 0x00, 0x02], {offset: 8})
|
||||
)
|
||||
) {
|
||||
return {
|
||||
ext: 'eot',
|
||||
mime: 'application/vnd.ms-fontobject'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x00, 0x01, 0x00, 0x00, 0x00])) {
|
||||
return {
|
||||
ext: 'ttf',
|
||||
mime: 'font/ttf'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x4F, 0x54, 0x54, 0x4F, 0x00])) {
|
||||
return {
|
||||
ext: 'otf',
|
||||
mime: 'font/otf'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x00, 0x00, 0x01, 0x00])) {
|
||||
return {
|
||||
ext: 'ico',
|
||||
mime: 'image/x-icon'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x00, 0x00, 0x02, 0x00])) {
|
||||
return {
|
||||
ext: 'cur',
|
||||
mime: 'image/x-icon'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x46, 0x4C, 0x56, 0x01])) {
|
||||
return {
|
||||
ext: 'flv',
|
||||
mime: 'video/x-flv'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x25, 0x21])) {
|
||||
return {
|
||||
ext: 'ps',
|
||||
mime: 'application/postscript'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00])) {
|
||||
return {
|
||||
ext: 'xz',
|
||||
mime: 'application/x-xz'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x53, 0x51, 0x4C, 0x69])) {
|
||||
return {
|
||||
ext: 'sqlite',
|
||||
mime: 'application/x-sqlite3'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x4E, 0x45, 0x53, 0x1A])) {
|
||||
return {
|
||||
ext: 'nes',
|
||||
mime: 'application/x-nintendo-nes-rom'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x43, 0x72, 0x32, 0x34])) {
|
||||
return {
|
||||
ext: 'crx',
|
||||
mime: 'application/x-google-chrome-extension'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x4D, 0x53, 0x43, 0x46]) ||
|
||||
check([0x49, 0x53, 0x63, 0x28])
|
||||
) {
|
||||
return {
|
||||
ext: 'cab',
|
||||
mime: 'application/vnd.ms-cab-compressed'
|
||||
};
|
||||
}
|
||||
|
||||
// Needs to be before `ar` check
|
||||
if (check([0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A, 0x64, 0x65, 0x62, 0x69, 0x61, 0x6E, 0x2D, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79])) {
|
||||
return {
|
||||
ext: 'deb',
|
||||
mime: 'application/x-deb'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E])) {
|
||||
return {
|
||||
ext: 'ar',
|
||||
mime: 'application/x-unix-archive'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0xED, 0xAB, 0xEE, 0xDB])) {
|
||||
return {
|
||||
ext: 'rpm',
|
||||
mime: 'application/x-rpm'
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
check([0x1F, 0xA0]) ||
|
||||
check([0x1F, 0x9D])
|
||||
) {
|
||||
return {
|
||||
ext: 'Z',
|
||||
mime: 'application/x-compress'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x4C, 0x5A, 0x49, 0x50])) {
|
||||
return {
|
||||
ext: 'lz',
|
||||
mime: 'application/x-lzip'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1])) {
|
||||
return {
|
||||
ext: 'msi',
|
||||
mime: 'application/x-msi'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x02])) {
|
||||
return {
|
||||
ext: 'mxf',
|
||||
mime: 'application/mxf'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x47], {offset: 4}) && (check([0x47], {offset: 192}) || check([0x47], {offset: 196}))) {
|
||||
return {
|
||||
ext: 'mts',
|
||||
mime: 'video/mp2t'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x42, 0x4C, 0x45, 0x4E, 0x44, 0x45, 0x52])) {
|
||||
return {
|
||||
ext: 'blend',
|
||||
mime: 'application/x-blender'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x42, 0x50, 0x47, 0xFB])) {
|
||||
return {
|
||||
ext: 'bpg',
|
||||
mime: 'image/bpg'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A])) {
|
||||
// JPEG-2000 family
|
||||
|
||||
if (check([0x6A, 0x70, 0x32, 0x20], {offset: 20})) {
|
||||
return {
|
||||
ext: 'jp2',
|
||||
mime: 'image/jp2'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x6A, 0x70, 0x78, 0x20], {offset: 20})) {
|
||||
return {
|
||||
ext: 'jpx',
|
||||
mime: 'image/jpx'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x6A, 0x70, 0x6D, 0x20], {offset: 20})) {
|
||||
return {
|
||||
ext: 'jpm',
|
||||
mime: 'image/jpm'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x6D, 0x6A, 0x70, 0x32], {offset: 20})) {
|
||||
return {
|
||||
ext: 'mj2',
|
||||
mime: 'image/mj2'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (check([0x46, 0x4F, 0x52, 0x4D])) {
|
||||
return {
|
||||
ext: 'aif',
|
||||
mime: 'audio/aiff'
|
||||
};
|
||||
}
|
||||
|
||||
if (checkString('<?xml ')) {
|
||||
return {
|
||||
ext: 'xml',
|
||||
mime: 'application/xml'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x42, 0x4F, 0x4F, 0x4B, 0x4D, 0x4F, 0x42, 0x49], {offset: 60})) {
|
||||
return {
|
||||
ext: 'mobi',
|
||||
mime: 'application/x-mobipocket-ebook'
|
||||
};
|
||||
}
|
||||
|
||||
// File Type Box (https://en.wikipedia.org/wiki/ISO_base_media_file_format)
|
||||
if (check([0x66, 0x74, 0x79, 0x70], {offset: 4})) {
|
||||
if (check([0x6D, 0x69, 0x66, 0x31], {offset: 8})) {
|
||||
return {
|
||||
ext: 'heic',
|
||||
mime: 'image/heif'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x6D, 0x73, 0x66, 0x31], {offset: 8})) {
|
||||
return {
|
||||
ext: 'heic',
|
||||
mime: 'image/heif-sequence'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x68, 0x65, 0x69, 0x63], {offset: 8}) || check([0x68, 0x65, 0x69, 0x78], {offset: 8})) {
|
||||
return {
|
||||
ext: 'heic',
|
||||
mime: 'image/heic'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x68, 0x65, 0x76, 0x63], {offset: 8}) || check([0x68, 0x65, 0x76, 0x78], {offset: 8})) {
|
||||
return {
|
||||
ext: 'heic',
|
||||
mime: 'image/heic-sequence'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (check([0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A])) {
|
||||
return {
|
||||
ext: 'ktx',
|
||||
mime: 'image/ktx'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x44, 0x49, 0x43, 0x4D], {offset: 128})) {
|
||||
return {
|
||||
ext: 'dcm',
|
||||
mime: 'application/dicom'
|
||||
};
|
||||
}
|
||||
|
||||
// Musepack, SV7
|
||||
if (check([0x4D, 0x50, 0x2B])) {
|
||||
return {
|
||||
ext: 'mpc',
|
||||
mime: 'audio/x-musepack'
|
||||
};
|
||||
}
|
||||
|
||||
// Musepack, SV8
|
||||
if (check([0x4D, 0x50, 0x43, 0x4B])) {
|
||||
return {
|
||||
ext: 'mpc',
|
||||
mime: 'audio/x-musepack'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x42, 0x45, 0x47, 0x49, 0x4E, 0x3A])) {
|
||||
return {
|
||||
ext: 'ics',
|
||||
mime: 'text/calendar'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0x67, 0x6C, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00])) {
|
||||
return {
|
||||
ext: 'glb',
|
||||
mime: 'model/gltf-binary'
|
||||
};
|
||||
}
|
||||
|
||||
if (check([0xD4, 0xC3, 0xB2, 0xA1]) || check([0xA1, 0xB2, 0xC3, 0xD4])) {
|
||||
return {
|
||||
ext: 'pcap',
|
||||
mime: 'application/vnd.tcpdump.pcap'
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = fileType;
|
||||
// TODO: Remove this for the next major release
|
||||
module.exports.default = fileType;
|
||||
|
||||
Object.defineProperty(fileType, 'minimumBytes', {value: 4100});
|
||||
|
||||
module.exports.stream = readableStream => new Promise((resolve, reject) => {
|
||||
// Using `eval` to work around issues when bundling with Webpack
|
||||
const stream = eval('require')('stream'); // eslint-disable-line no-eval
|
||||
|
||||
readableStream.once('readable', () => {
|
||||
const pass = new stream.PassThrough();
|
||||
const chunk = readableStream.read(module.exports.minimumBytes) || readableStream.read();
|
||||
try {
|
||||
pass.fileType = fileType(chunk);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
readableStream.unshift(chunk);
|
||||
|
||||
if (stream.pipeline) {
|
||||
resolve(stream.pipeline(readableStream, pass, () => {}));
|
||||
} else {
|
||||
resolve(readableStream.pipe(pass));
|
||||
}
|
||||
});
|
||||
});
|
||||
9
node_modules/file-type/license
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
131
node_modules/file-type/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"name": "file-type",
|
||||
"version": "10.11.0",
|
||||
"description": "Detect the file type of a Buffer/Uint8Array/ArrayBuffer",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/file-type",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "sindresorhus.com"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && ava && tsd"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"keywords": [
|
||||
"mime",
|
||||
"file",
|
||||
"type",
|
||||
"archive",
|
||||
"image",
|
||||
"img",
|
||||
"pic",
|
||||
"picture",
|
||||
"flash",
|
||||
"photo",
|
||||
"video",
|
||||
"detect",
|
||||
"check",
|
||||
"is",
|
||||
"exif",
|
||||
"exe",
|
||||
"binary",
|
||||
"buffer",
|
||||
"uint8array",
|
||||
"jpg",
|
||||
"png",
|
||||
"gif",
|
||||
"webp",
|
||||
"flif",
|
||||
"cr2",
|
||||
"tif",
|
||||
"bmp",
|
||||
"jxr",
|
||||
"psd",
|
||||
"zip",
|
||||
"tar",
|
||||
"rar",
|
||||
"gz",
|
||||
"bz2",
|
||||
"7z",
|
||||
"dmg",
|
||||
"mp4",
|
||||
"m4v",
|
||||
"mid",
|
||||
"mkv",
|
||||
"webm",
|
||||
"mov",
|
||||
"avi",
|
||||
"mpg",
|
||||
"mp2",
|
||||
"mp3",
|
||||
"m4a",
|
||||
"ogg",
|
||||
"opus",
|
||||
"flac",
|
||||
"wav",
|
||||
"amr",
|
||||
"pdf",
|
||||
"epub",
|
||||
"mobi",
|
||||
"swf",
|
||||
"rtf",
|
||||
"woff",
|
||||
"woff2",
|
||||
"eot",
|
||||
"ttf",
|
||||
"otf",
|
||||
"ico",
|
||||
"flv",
|
||||
"ps",
|
||||
"xz",
|
||||
"sqlite",
|
||||
"xpi",
|
||||
"cab",
|
||||
"deb",
|
||||
"ar",
|
||||
"rpm",
|
||||
"Z",
|
||||
"lz",
|
||||
"msi",
|
||||
"mxf",
|
||||
"mts",
|
||||
"wasm",
|
||||
"webassembly",
|
||||
"blend",
|
||||
"bpg",
|
||||
"docx",
|
||||
"pptx",
|
||||
"xlsx",
|
||||
"3gp",
|
||||
"jp2",
|
||||
"jpm",
|
||||
"jpx",
|
||||
"mj2",
|
||||
"aif",
|
||||
"odt",
|
||||
"ods",
|
||||
"odp",
|
||||
"xml",
|
||||
"heic",
|
||||
"wma",
|
||||
"ics",
|
||||
"glb",
|
||||
"pcap"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^11.12.2",
|
||||
"ava": "^1.4.1",
|
||||
"pify": "^4.0.1",
|
||||
"read-chunk": "^3.2.0",
|
||||
"tsd": "^0.7.1",
|
||||
"xo": "^0.24.0"
|
||||
}
|
||||
}
|
||||
238
node_modules/file-type/readme.md
generated
vendored
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
# file-type [](https://travis-ci.org/sindresorhus/file-type)
|
||||
|
||||
> Detect the file type of a Buffer/Uint8Array/ArrayBuffer
|
||||
|
||||
The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install file-type
|
||||
```
|
||||
|
||||
<a href="https://www.patreon.com/sindresorhus">
|
||||
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
|
||||
</a>
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
##### Node.js
|
||||
|
||||
```js
|
||||
const readChunk = require('read-chunk');
|
||||
const fileType = require('file-type');
|
||||
|
||||
const buffer = readChunk.sync('unicorn.png', 0, fileType.minimumBytes);
|
||||
|
||||
fileType(buffer);
|
||||
//=> {ext: 'png', mime: 'image/png'}
|
||||
```
|
||||
|
||||
Or from a remote location:
|
||||
|
||||
```js
|
||||
const http = require('http');
|
||||
const fileType = require('file-type');
|
||||
|
||||
const url = 'https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif';
|
||||
|
||||
http.get(url, response => {
|
||||
response.on('readable', () => {
|
||||
const chunk = response.read(fileType.minimumBytes);
|
||||
response.destroy();
|
||||
console.log(fileType(chunk));
|
||||
//=> {ext: 'gif', mime: 'image/gif'}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Or from a stream:
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
const crypto = require('crypto');
|
||||
const fileType = require('file-type');
|
||||
|
||||
(async () => {
|
||||
const read = fs.createReadStream('encrypted.enc');
|
||||
const decipher = crypto.createDecipheriv(alg, key, iv);
|
||||
|
||||
const stream = await fileType.stream(read.pipe(decipher));
|
||||
|
||||
console.log(stream.fileType);
|
||||
//=> {ext: 'mov', mime: 'video/quicktime'}
|
||||
|
||||
const write = fs.createWriteStream(`decrypted.${stream.fileType.ext}`);
|
||||
stream.pipe(write);
|
||||
})();
|
||||
```
|
||||
|
||||
|
||||
##### Browser
|
||||
|
||||
```js
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'unicorn.png');
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
xhr.onload = () => {
|
||||
fileType(new Uint8Array(this.response));
|
||||
//=> {ext: 'png', mime: 'image/png'}
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### fileType(input)
|
||||
|
||||
Returns an `Object` with:
|
||||
|
||||
- `ext` - One of the [supported file types](#supported-file-types)
|
||||
- `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
|
||||
|
||||
Or `null` when there is no match.
|
||||
|
||||
#### input
|
||||
|
||||
Type: `Buffer | Uint8Array | ArrayBuffer`
|
||||
|
||||
It only needs the first `.minimumBytes` bytes. The exception is detection of `docx`, `pptx`, and `xlsx` which potentially requires reading the whole file.
|
||||
|
||||
### fileType.minimumBytes
|
||||
|
||||
Type: `number`
|
||||
|
||||
The minimum amount of bytes needed to detect a file type. Currently, it's 4100 bytes, but it can change, so don't hardcode it.
|
||||
|
||||
### fileType.stream(readableStream)
|
||||
|
||||
Detect the file type of a readable stream.
|
||||
|
||||
Returns a `Promise` which resolves to the original readable stream argument, but with an added `fileType` property, which is an object like the one returned from `fileType()`.
|
||||
|
||||
*Note:* This method is only for Node.js.
|
||||
|
||||
#### readableStream
|
||||
|
||||
Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable)
|
||||
|
||||
|
||||
## Supported file types
|
||||
|
||||
- [`jpg`](https://en.wikipedia.org/wiki/JPEG)
|
||||
- [`png`](https://en.wikipedia.org/wiki/Portable_Network_Graphics)
|
||||
- [`gif`](https://en.wikipedia.org/wiki/GIF)
|
||||
- [`webp`](https://en.wikipedia.org/wiki/WebP)
|
||||
- [`flif`](https://en.wikipedia.org/wiki/Free_Lossless_Image_Format)
|
||||
- [`cr2`](https://fileinfo.com/extension/cr2)
|
||||
- [`tif`](https://en.wikipedia.org/wiki/Tagged_Image_File_Format)
|
||||
- [`bmp`](https://en.wikipedia.org/wiki/BMP_file_format)
|
||||
- [`jxr`](https://en.wikipedia.org/wiki/JPEG_XR)
|
||||
- [`psd`](https://en.wikipedia.org/wiki/Adobe_Photoshop#File_format)
|
||||
- [`zip`](https://en.wikipedia.org/wiki/Zip_(file_format))
|
||||
- [`tar`](https://en.wikipedia.org/wiki/Tar_(computing)#File_format)
|
||||
- [`rar`](https://en.wikipedia.org/wiki/RAR_(file_format))
|
||||
- [`gz`](https://en.wikipedia.org/wiki/Gzip)
|
||||
- [`bz2`](https://en.wikipedia.org/wiki/Bzip2)
|
||||
- [`7z`](https://en.wikipedia.org/wiki/7z)
|
||||
- [`dmg`](https://en.wikipedia.org/wiki/Apple_Disk_Image)
|
||||
- [`mp4`](https://en.wikipedia.org/wiki/MPEG-4_Part_14#Filename_extensions)
|
||||
- [`m4v`](https://en.wikipedia.org/wiki/M4V)
|
||||
- [`mid`](https://en.wikipedia.org/wiki/MIDI)
|
||||
- [`mkv`](https://en.wikipedia.org/wiki/Matroska)
|
||||
- [`webm`](https://en.wikipedia.org/wiki/WebM)
|
||||
- [`mov`](https://en.wikipedia.org/wiki/QuickTime_File_Format)
|
||||
- [`avi`](https://en.wikipedia.org/wiki/Audio_Video_Interleave)
|
||||
- [`wmv`](https://en.wikipedia.org/wiki/Windows_Media_Video)
|
||||
- [`mpg`](https://en.wikipedia.org/wiki/MPEG-1)
|
||||
- [`mp2`](https://en.wikipedia.org/wiki/MPEG-1_Audio_Layer_II)
|
||||
- [`mp3`](https://en.wikipedia.org/wiki/MP3)
|
||||
- [`m4a`](https://en.wikipedia.org/wiki/MPEG-4_Part_14#.MP4_versus_.M4A)
|
||||
- [`ogg`](https://en.wikipedia.org/wiki/Ogg)
|
||||
- [`opus`](https://en.wikipedia.org/wiki/Opus_(audio_format))
|
||||
- [`flac`](https://en.wikipedia.org/wiki/FLAC)
|
||||
- [`wav`](https://en.wikipedia.org/wiki/WAV)
|
||||
- [`qcp`](https://en.wikipedia.org/wiki/QCP)
|
||||
- [`amr`](https://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec)
|
||||
- [`pdf`](https://en.wikipedia.org/wiki/Portable_Document_Format)
|
||||
- [`epub`](https://en.wikipedia.org/wiki/EPUB)
|
||||
- [`mobi`](https://en.wikipedia.org/wiki/Mobipocket) - Mobipocket
|
||||
- [`exe`](https://en.wikipedia.org/wiki/.exe)
|
||||
- [`swf`](https://en.wikipedia.org/wiki/SWF)
|
||||
- [`rtf`](https://en.wikipedia.org/wiki/Rich_Text_Format)
|
||||
- [`woff`](https://en.wikipedia.org/wiki/Web_Open_Font_Format)
|
||||
- [`woff2`](https://en.wikipedia.org/wiki/Web_Open_Font_Format)
|
||||
- [`eot`](https://en.wikipedia.org/wiki/Embedded_OpenType)
|
||||
- [`ttf`](https://en.wikipedia.org/wiki/TrueType)
|
||||
- [`otf`](https://en.wikipedia.org/wiki/OpenType)
|
||||
- [`ico`](https://en.wikipedia.org/wiki/ICO_(file_format))
|
||||
- [`flv`](https://en.wikipedia.org/wiki/Flash_Video)
|
||||
- [`ps`](https://en.wikipedia.org/wiki/Postscript)
|
||||
- [`xz`](https://en.wikipedia.org/wiki/Xz)
|
||||
- [`sqlite`](https://www.sqlite.org/fileformat2.html)
|
||||
- [`nes`](https://fileinfo.com/extension/nes)
|
||||
- [`crx`](https://developer.chrome.com/extensions/crx)
|
||||
- [`xpi`](https://en.wikipedia.org/wiki/XPInstall)
|
||||
- [`cab`](https://en.wikipedia.org/wiki/Cabinet_(file_format))
|
||||
- [`deb`](https://en.wikipedia.org/wiki/Deb_(file_format))
|
||||
- [`ar`](https://en.wikipedia.org/wiki/Ar_(Unix))
|
||||
- [`rpm`](https://fileinfo.com/extension/rpm)
|
||||
- [`Z`](https://fileinfo.com/extension/z)
|
||||
- [`lz`](https://en.wikipedia.org/wiki/Lzip)
|
||||
- [`msi`](https://en.wikipedia.org/wiki/Windows_Installer)
|
||||
- [`mxf`](https://en.wikipedia.org/wiki/Material_Exchange_Format)
|
||||
- [`mts`](https://en.wikipedia.org/wiki/.m2ts)
|
||||
- [`wasm`](https://en.wikipedia.org/wiki/WebAssembly)
|
||||
- [`blend`](https://wiki.blender.org/index.php/Dev:Source/Architecture/File_Format)
|
||||
- [`bpg`](https://bellard.org/bpg/)
|
||||
- [`docx`](https://en.wikipedia.org/wiki/Office_Open_XML)
|
||||
- [`pptx`](https://en.wikipedia.org/wiki/Office_Open_XML)
|
||||
- [`xlsx`](https://en.wikipedia.org/wiki/Office_Open_XML)
|
||||
- [`3gp`](https://en.wikipedia.org/wiki/3GP_and_3G2)
|
||||
- [`jp2`](https://en.wikipedia.org/wiki/JPEG_2000) - JPEG 2000
|
||||
- [`jpm`](https://en.wikipedia.org/wiki/JPEG_2000) - JPEG 2000
|
||||
- [`jpx`](https://en.wikipedia.org/wiki/JPEG_2000) - JPEG 2000
|
||||
- [`mj2`](https://en.wikipedia.org/wiki/Motion_JPEG_2000) - Motion JPEG 2000
|
||||
- [`aif`](https://en.wikipedia.org/wiki/Audio_Interchange_File_Format)
|
||||
- [`odt`](https://en.wikipedia.org/wiki/OpenDocument) - OpenDocument for word processing
|
||||
- [`ods`](https://en.wikipedia.org/wiki/OpenDocument) - OpenDocument for spreadsheets
|
||||
- [`odp`](https://en.wikipedia.org/wiki/OpenDocument) - OpenDocument for presentations
|
||||
- [`xml`](https://en.wikipedia.org/wiki/XML)
|
||||
- [`heic`](https://nokiatech.github.io/heif/technical.html)
|
||||
- [`cur`](https://en.wikipedia.org/wiki/ICO_(file_format))
|
||||
- [`ktx`](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/)
|
||||
- [`ape`](https://en.wikipedia.org/wiki/Monkey%27s_Audio) - Monkey's Audio
|
||||
- [`wv`](https://en.wikipedia.org/wiki/WavPack) - WavPack
|
||||
- [`asf`](https://en.wikipedia.org/wiki/Advanced_Systems_Format) - Advanced Systems Format
|
||||
- [`wma`](https://en.wikipedia.org/wiki/Windows_Media_Audio) - Windows Media Audio
|
||||
- [`wmv`](https://en.wikipedia.org/wiki/Windows_Media_Video) - Windows Media Video
|
||||
- [`dcm`](https://en.wikipedia.org/wiki/DICOM#Data_format) - DICOM Image File
|
||||
- [`mpc`](https://en.wikipedia.org/wiki/Musepack) - Musepack (SV7 & SV8)
|
||||
- [`ics`](https://en.wikipedia.org/wiki/ICalendar#Data_format) - iCalendar
|
||||
- [`glb`](https://github.com/KhronosGroup/glTF) - GL Transmission Format
|
||||
- [`pcap`](https://wiki.wireshark.org/Development/LibpcapFileFormat) - Libpcap File Format
|
||||
|
||||
*SVG isn't included as it requires the whole file to be read, but you can get it [here](https://github.com/sindresorhus/is-svg).*
|
||||
|
||||
*Pull request welcome for additional commonly used file types.*
|
||||
|
||||
|
||||
## Related
|
||||
|
||||
- [file-type-cli](https://github.com/sindresorhus/file-type-cli) - CLI for this module
|
||||
|
||||
|
||||
## Created by
|
||||
|
||||
- [Sindre Sorhus](https://github.com/sindresorhus)
|
||||
- [Mikael Finstad](https://github.com/mifi)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
21
node_modules/gif-build-worker-js/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 陌小路
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2
node_modules/gif-build-worker-js/dist/index.d.ts
generated
vendored
Normal file
2
node_modules/gif-build-worker-js/dist/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
"use strict";const r=require("./lib/gif.worker.js");exports.GifWorker=r,exports.transformToUrl=()=>{const e=new Blob([r],{type:"application/javascript"});return URL.createObjectURL(e)};
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/gif-build-worker-js/dist/index.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
||||
2
node_modules/gif-build-worker-js/dist/lib/gif.worker.js
generated
vendored
Normal file
1
node_modules/gif-build-worker-js/dist/lib/gif.worker.js.map
generated
vendored
Normal file
2
node_modules/gif-build-worker-js/esm/index.d.ts
generated
vendored
Normal file
2
node_modules/gif-build-worker-js/esm/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import t from"./lib/gif.worker.js";const r=()=>{const t=new Blob([e],{type:"application/javascript"});return URL.createObjectURL(t)},e=t;export{e as GifWorker,r as transformToUrl};
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/gif-build-worker-js/esm/index.js.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
||||
2
node_modules/gif-build-worker-js/esm/lib/gif.worker.js
generated
vendored
Normal file
1
node_modules/gif-build-worker-js/esm/lib/gif.worker.js.map
generated
vendored
Normal file
38
node_modules/gif-build-worker-js/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "gif-build-worker-js",
|
||||
"version": "1.0.3",
|
||||
"main": "dist/index.js",
|
||||
"module": "esm/index.js",
|
||||
"type": "module",
|
||||
"types": "esm/index.d.ts",
|
||||
"exports": {
|
||||
"import": {
|
||||
"types": "./esm/index.d.ts",
|
||||
"default": "./esm/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"esm",
|
||||
"package.json"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"rollup": "^4.24.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"clean": "rimraf dist",
|
||||
"dev": "tsc --watch",
|
||||
"prebuild": "npm run clean",
|
||||
"prepublish": "npm run build"
|
||||
}
|
||||
}
|
||||
2
node_modules/gif.js/.npmignore
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
site/build/
|
||||
109
node_modules/gif.js/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
# gif.js
|
||||
|
||||
JavaScript GIF encoder that runs in your browser.
|
||||
|
||||
Uses typed arrays and web workers to render each frame in the background, it's really fast!
|
||||
|
||||
**Demo** - http://jnordberg.github.io/gif.js/
|
||||
|
||||
Works in browsers supporting: [Web Workers](http://www.w3.org/TR/workers/), [File API](http://www.w3.org/TR/FileAPI/) and [Typed Arrays](https://www.khronos.org/registry/typedarray/specs/latest/)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Include `gif.js` found in `dist/` in your page. Also make sure to have `gif.worker.js` in the same location.
|
||||
|
||||
```javascript
|
||||
var gif = new GIF({
|
||||
workers: 2,
|
||||
quality: 10
|
||||
});
|
||||
|
||||
// add an image element
|
||||
gif.addFrame(imageElement);
|
||||
|
||||
// or a canvas element
|
||||
gif.addFrame(canvasElement, {delay: 200});
|
||||
|
||||
// or copy the pixels from a canvas context
|
||||
gif.addFrame(ctx, {copy: true});
|
||||
|
||||
gif.on('finished', function(blob) {
|
||||
window.open(URL.createObjectURL(blob));
|
||||
});
|
||||
|
||||
gif.render();
|
||||
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
Options can be passed to the constructor or using the `setOptions` method.
|
||||
|
||||
| Name | Default | Description |
|
||||
| -------------|-----------------|----------------------------------------------------|
|
||||
| repeat | `0` | repeat count, `-1` = no repeat, `0` = forever |
|
||||
| quality | `10` | pixel sample interval, lower is better |
|
||||
| workers | `2` | number of web workers to spawn |
|
||||
| workerScript | `gif.worker.js` | url to load worker script from |
|
||||
| background | `#fff` | background color where source image is transparent |
|
||||
| width | `null` | output image width |
|
||||
| height | `null` | output image height |
|
||||
| transparent | `null` | transparent hex color, `0x00FF00` = green |
|
||||
| dither | `false` | dithering method, e.g. `FloydSteinberg-serpentine` |
|
||||
| debug | `false` | whether to print debug information to console |
|
||||
|
||||
If width or height is `null` image size will be deteremined by first frame added.
|
||||
|
||||
Available dithering methods are:
|
||||
|
||||
* `FloydSteinberg`
|
||||
* `FalseFloydSteinberg`
|
||||
* `Stucki`
|
||||
* `Atkinson`
|
||||
|
||||
You can add `-serpentine` to use serpentine scanning, e.g. `Stucki-serpentine`.
|
||||
|
||||
### addFrame options
|
||||
|
||||
| Name | Default | Description |
|
||||
| -------------|-----------------|----------------------------------------------------|
|
||||
| delay | `500` | frame delay |
|
||||
| copy | `false` | copy the pixel data |
|
||||
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
gif.js is based on:
|
||||
|
||||
* [Kevin Weiner's Animated gif encoder classes](http://www.fmsware.com/stuff/gif.html)
|
||||
* [Neural-Net color quantization algorithm by Anthony Dekker](http://members.ozemail.com.au/~dekker/NEUQUANT.HTML)
|
||||
* [Thibault Imbert's as3gif](https://code.google.com/p/as3gif/)
|
||||
|
||||
Dithering code contributed by @PAEz and @panrafal
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Johan Nordberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
28
node_modules/gif.js/bin/build
generated
vendored
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
URL="https://github.com/jnordberg/gif.js"
|
||||
TMP="./dist/tmp"
|
||||
|
||||
if [ -d ./node_modules ]; then
|
||||
PATH="./node_modules/.bin/:$PATH"
|
||||
VERSION=`./bin/version`
|
||||
echo "Packaging gif.js $VERSION"
|
||||
browserify -d -s GIF -t coffeeify src/gif.coffee | exorcist dist/_gif.js.map > dist/_gif.js
|
||||
uglifyjs dist/_gif.js \
|
||||
--preamble "// gif.js $VERSION - $URL" \
|
||||
--in-source-map dist/_gif.js.map \
|
||||
--source-map dist/gif.js.map \
|
||||
--source-map-url gif.js.map \
|
||||
> dist/gif.js
|
||||
browserify -d -t coffeeify --bare src/gif.worker.coffee | exorcist dist/_gif.worker.js.map > dist/_gif.worker.js
|
||||
uglifyjs dist/_gif.worker.js \
|
||||
--preamble "// gif.worker.js $VERSION - $URL" \
|
||||
--in-source-map dist/_gif.worker.js.map \
|
||||
--source-map dist/gif.worker.js.map \
|
||||
--source-map-url gif.worker.js.map \
|
||||
> dist/gif.worker.js
|
||||
rm dist/_*
|
||||
else
|
||||
echo "Build dependencies missing. Run npm install"
|
||||
exit 1
|
||||
fi
|
||||
4
node_modules/gif.js/bin/version
generated
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env node
|
||||
var fs = require('fs');
|
||||
var pkg = JSON.parse(fs.readFileSync('package.json'));
|
||||
process.stdout.write(pkg.version);
|
||||
3
node_modules/gif.js/dist/gif.js
generated
vendored
Normal file
1
node_modules/gif.js/dist/gif.js.map
generated
vendored
Normal file
3
node_modules/gif.js/dist/gif.worker.js
generated
vendored
Normal file
1
node_modules/gif.js/dist/gif.worker.js.map
generated
vendored
Normal file
14
node_modules/gif.js/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* _ ___ _
|
||||
(_) / __)(_)
|
||||
____ _ _| |__ _ ___
|
||||
/ _ | (_ __)| |/___)
|
||||
( (_| | | | | _ | |___ |
|
||||
\___ |_| |_|(_)| (___/
|
||||
(_____| (_*/
|
||||
|
||||
module.exports = {
|
||||
NeuQuant: require('./src/NeuQuant.js'),
|
||||
TypedNeuQuant: require('./src/TypedNeuQuant.js'),
|
||||
GIFEncoder: require('./src/GIFEncoder.js'),
|
||||
LZWEncoder: require('./src/LZWEncoder.js')
|
||||
};
|
||||
25
node_modules/gif.js/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "gif.js",
|
||||
"version": "0.2.0",
|
||||
"description": "JavaScript GIF encoding library",
|
||||
"author": "Johan Nordberg <code@johan-nordberg.com>",
|
||||
"main": "index.js",
|
||||
"repository": "https://github.com/jnordberg/gif.js.git",
|
||||
"devDependencies": {
|
||||
"browserify": "^13.1.1",
|
||||
"coffeeify": "^2.1.0",
|
||||
"exorcist": "^0.4.0",
|
||||
"uglify-js": "^2.7.5"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "./bin/build"
|
||||
},
|
||||
"browser": "./dist/gif.js",
|
||||
"keywords": [
|
||||
"gif",
|
||||
"animation",
|
||||
"encoder"
|
||||
],
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md"
|
||||
}
|
||||
22
node_modules/gif.js/site/config.json
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"locals": {
|
||||
"name": "gif.js",
|
||||
"url": "http://jnordberg.github.io/gif.js",
|
||||
"description": "Full-featured JavaScript GIF encoder that runs in your browser.",
|
||||
"keywords": "gif, encoder, animation, browser, unicorn",
|
||||
"version": "3"
|
||||
},
|
||||
"baseUrl": "/gif.js/",
|
||||
"require": {
|
||||
"hljs": "highlight.js"
|
||||
},
|
||||
"nunjucks": {
|
||||
"autoescape": false
|
||||
},
|
||||
"plugins": [
|
||||
"wintersmith-browserify",
|
||||
"wintersmith-less",
|
||||
"wintersmith-nunjucks",
|
||||
"./plugins/jsignore.coffee"
|
||||
]
|
||||
}
|
||||
23
node_modules/gif.js/site/contents/code.md
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
```javascript
|
||||
var gif = new GIF({
|
||||
workers: 2,
|
||||
quality: 10
|
||||
});
|
||||
|
||||
// add a image element
|
||||
gif.addFrame(imageElement);
|
||||
|
||||
// or a canvas element
|
||||
gif.addFrame(canvasElement, {delay: 200});
|
||||
|
||||
// or copy the pixels from a canvas context
|
||||
gif.addFrame(ctx, {copy: true});
|
||||
|
||||
gif.on('finished', function(blob) {
|
||||
window.open(URL.createObjectURL(blob));
|
||||
});
|
||||
|
||||
gif.render();
|
||||
|
||||
```
|
||||
BIN
node_modules/gif.js/site/contents/images/loading.gif
generated
vendored
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
node_modules/gif.js/site/contents/images/logo.gif
generated
vendored
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
node_modules/gif.js/site/contents/images/test/anim-ref.gif
generated
vendored
Normal file
|
After Width: | Height: | Size: 468 KiB |
BIN
node_modules/gif.js/site/contents/images/test/anim1.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
node_modules/gif.js/site/contents/images/test/anim2.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
node_modules/gif.js/site/contents/images/test/anim3.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
node_modules/gif.js/site/contents/images/test/anim4.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test1-orig.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test1-ref.gif
generated
vendored
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test2-orig.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test2-ref.gif
generated
vendored
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test3-orig.jpg
generated
vendored
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test3-orig.png
generated
vendored
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
node_modules/gif.js/site/contents/images/test/test3-ref.gif
generated
vendored
Normal file
|
After Width: | Height: | Size: 74 KiB |
3
node_modules/gif.js/site/contents/index.json
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"template": "index.html"
|
||||
}
|
||||
2
node_modules/gif.js/site/contents/robots.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow:
|
||||
109
node_modules/gif.js/site/contents/scripts/main.coffee
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
require 'browsernizr/test/css/rgba'
|
||||
require 'browsernizr/test/css/transforms3d'
|
||||
Modernizr = require 'browsernizr'
|
||||
|
||||
require './vendor/mootools.js'
|
||||
|
||||
async = require 'async'
|
||||
ready = require './vendor/ready.js'
|
||||
|
||||
now = window.performance?.now?.bind(window.performance) or Date.now
|
||||
|
||||
# fallback for browsers not supporting createObjectURL
|
||||
blobURLSupport = window.URL?.createObjectURL?
|
||||
buildDataURL = do ->
|
||||
charMap = {}
|
||||
charMap[i] = String.fromCharCode(i) for i in [0...256]
|
||||
return (data) ->
|
||||
str = ''
|
||||
for i in [0...data.length]
|
||||
str += charMap[data[i]]
|
||||
return 'data:image/gif;base64,' + btoa(str)
|
||||
|
||||
loadImage = (src, callback) ->
|
||||
img = new Image()
|
||||
img.onload = ->
|
||||
callback null, img
|
||||
img.onerror = ->
|
||||
callback new Error "Could load #{ src }"
|
||||
img.src = src
|
||||
|
||||
setupDemo = (element) ->
|
||||
element.getElements('.hover-buttons li').addEvents
|
||||
mouseenter: -> element.addClass @className
|
||||
mouseleave: -> element.removeClass @className
|
||||
|
||||
qslider = element.getElement '.quality input'
|
||||
qvalue = element.getElement '.quality span'
|
||||
renderimg = element.getElement 'img.render'
|
||||
logel = element.getElement 'pre'
|
||||
|
||||
gif = new GIF
|
||||
debug: true
|
||||
quality: 10
|
||||
workers: 2
|
||||
|
||||
startTime = null
|
||||
gif.on 'start', ->
|
||||
startTime = now()
|
||||
|
||||
gif.on 'finished', (blob, data) ->
|
||||
if blobURLSupport
|
||||
renderimg.src = URL.createObjectURL(blob)
|
||||
else
|
||||
renderimg.src = buildDataURL(data)
|
||||
delta = now() - startTime
|
||||
logel.set 'text', "Rendered #{ images.length } frame(s) at q#{ gif.options.quality } in #{ delta.toFixed(2) }ms"
|
||||
|
||||
gif.on 'progress', (p) ->
|
||||
logel.set 'text', "Rendering #{ images.length } frame(s) at q#{ gif.options.quality }... #{ Math.round(p * 100) }%"
|
||||
|
||||
images = element.getElements('img.original').map (img) -> img.src
|
||||
async.map images, loadImage, (error, images) ->
|
||||
throw error if error?
|
||||
gif.addFrame image, {delay: 500, copy: true} for image in images
|
||||
gif.render()
|
||||
|
||||
qslider.addEvent 'change', ->
|
||||
val = 31 - parseInt qslider.value
|
||||
qvalue.set 'text', val
|
||||
gif.setOption 'quality', val
|
||||
gif.abort()
|
||||
gif.render()
|
||||
|
||||
(element.getElement '.dither select')?.addEvent 'change', ->
|
||||
gif.setOption 'dither', if @value is 'None' then false else @value
|
||||
gif.abort()
|
||||
gif.render()
|
||||
|
||||
delay = element.getElement '.delay'
|
||||
if delay?
|
||||
delay.getElement('input').addEvent 'change', ->
|
||||
value = parseInt this.value
|
||||
delay.getElement('.value').set 'text', value + 'ms'
|
||||
for frame in gif.frames
|
||||
frame.delay = value
|
||||
gif.abort()
|
||||
gif.render()
|
||||
|
||||
repeat = element.getElement '.repeat'
|
||||
if repeat?
|
||||
repeat.getElement('input').addEvent 'change', ->
|
||||
value = parseInt this.value
|
||||
value = -1 if value is 0
|
||||
value = 0 if value is 21
|
||||
switch value
|
||||
when 0
|
||||
txt = 'forever'
|
||||
when -1
|
||||
txt = 'none'
|
||||
else
|
||||
txt = value
|
||||
repeat.getElement('.value').set 'text', txt
|
||||
gif.setOption 'repeat', value
|
||||
gif.abort()
|
||||
gif.render()
|
||||
|
||||
ready ->
|
||||
for demo in document.body.querySelectorAll '.demo'
|
||||
setupDemo demo
|
||||
4060
node_modules/gif.js/site/contents/scripts/vendor/mootools.js
generated
vendored
Normal file
54
node_modules/gif.js/site/contents/scripts/vendor/ready.js
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*!
|
||||
* domready (c) Dustin Diaz 2012 - License MIT
|
||||
*/
|
||||
!function (name, context, definition) {
|
||||
if (typeof module != 'undefined') module.exports = definition()
|
||||
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition)
|
||||
else context[name] = definition()
|
||||
}('domready', this, function (ready) {
|
||||
|
||||
var fns = [], fn, f = false
|
||||
, doc = document
|
||||
, testEl = doc.documentElement
|
||||
, hack = testEl.doScroll
|
||||
, domContentLoaded = 'DOMContentLoaded'
|
||||
, addEventListener = 'addEventListener'
|
||||
, onreadystatechange = 'onreadystatechange'
|
||||
, readyState = 'readyState'
|
||||
, loaded = /^loade|c/.test(doc[readyState])
|
||||
|
||||
function flush(f) {
|
||||
loaded = 1
|
||||
while (f = fns.shift()) f()
|
||||
}
|
||||
|
||||
doc[addEventListener] && doc[addEventListener](domContentLoaded, fn = function () {
|
||||
doc.removeEventListener(domContentLoaded, fn, f)
|
||||
flush()
|
||||
}, f)
|
||||
|
||||
|
||||
hack && doc.attachEvent(onreadystatechange, fn = function () {
|
||||
if (/^c/.test(doc[readyState])) {
|
||||
doc.detachEvent(onreadystatechange, fn)
|
||||
flush()
|
||||
}
|
||||
})
|
||||
|
||||
return (ready = hack ?
|
||||
function (fn) {
|
||||
self != top ?
|
||||
loaded ? fn() : fns.push(fn) :
|
||||
function () {
|
||||
try {
|
||||
testEl.doScroll('left')
|
||||
} catch (e) {
|
||||
return setTimeout(function() { ready(fn) }, 50)
|
||||
}
|
||||
fn()
|
||||
}()
|
||||
} :
|
||||
function (fn) {
|
||||
loaded ? fn() : fns.push(fn)
|
||||
})
|
||||
})
|
||||
188
node_modules/gif.js/site/contents/styles/main.less
generated
vendored
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
@import 'vendor/reset';
|
||||
@import 'vendor/elements';
|
||||
|
||||
@background: #d4d4d4;
|
||||
@text: #131313;
|
||||
|
||||
html, body {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Helvetica, sans-serif;
|
||||
font-size: 21px;
|
||||
color: @text;
|
||||
margin: 0;
|
||||
.gradient(@background, @background, lighten(@background, 20%));
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.6em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.6em;
|
||||
margin-bottom: 0.8em;
|
||||
margin-top: 1.2em;
|
||||
}
|
||||
|
||||
header {
|
||||
padding-top: 1em;
|
||||
h1 {
|
||||
overflow: hidden;
|
||||
text-indent: -100%;
|
||||
background: url(../images/logo.gif);
|
||||
width: 600px;
|
||||
height: 288px;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
padding: 10em 1em 1em;
|
||||
font-style: italic;
|
||||
&, a {
|
||||
text-decoration: none;
|
||||
color: #505050;
|
||||
}
|
||||
}
|
||||
|
||||
header, footer, .wrap {
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.demo, .demo pre {
|
||||
font-family: Menlo, monospace;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.demo {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
width: 600px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||
margin-bottom: 2em;
|
||||
.hover-buttons {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
li {
|
||||
font-size: 14px;
|
||||
opacity: 0.6;
|
||||
background: #000;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
color: #fff;
|
||||
float: left;
|
||||
padding: 10px 12px 9px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
margin-right: 10px;
|
||||
cursor: default;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.images {
|
||||
height: 392px;
|
||||
img {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 600px;
|
||||
height: 392px;
|
||||
&.render {
|
||||
display: block;
|
||||
background: #fff url(../images/loading.gif) no-repeat center center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.original .images img.original {
|
||||
display: block;
|
||||
}
|
||||
&.reference .images img.reference {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 10px;
|
||||
p {
|
||||
line-height: 1.3;
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
display: inline-block;
|
||||
min-width: 80px;
|
||||
}
|
||||
pre {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
.tomorrow-comment, pre .comment, pre .title {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
.tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
|
||||
color: #c82829;
|
||||
}
|
||||
|
||||
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant {
|
||||
color: #f5871f;
|
||||
}
|
||||
|
||||
.tomorrow-yellow, pre .ruby .class .title, pre .css .rules .attribute {
|
||||
color: #eab700;
|
||||
}
|
||||
|
||||
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
.tomorrow-aqua, pre .css .hexcolor {
|
||||
color: #3e999f;
|
||||
}
|
||||
|
||||
.tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
|
||||
color: #4271ae;
|
||||
}
|
||||
|
||||
.tomorrow-purple, pre .keyword, pre .javascript .function {
|
||||
color: #8959a8;
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
background: white;
|
||||
color: #4d4d4c;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
pre.src code {
|
||||
font-size: 0.7em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
pre .coffeescript .javascript,
|
||||
pre .javascript .xml,
|
||||
pre .tex .formula,
|
||||
pre .xml .javascript,
|
||||
pre .xml .vbscript,
|
||||
pre .xml .css,
|
||||
pre .xml .cdata {
|
||||
opacity: 0.5;
|
||||
}
|
||||
156
node_modules/gif.js/site/contents/styles/vendor/elements.less
generated
vendored
Executable file
|
|
@ -0,0 +1,156 @@
|
|||
//---------------------------------------------------
|
||||
// LESS Elements 0.9
|
||||
//---------------------------------------------------
|
||||
// A set of useful LESS mixins
|
||||
// More info at: http://lesselements.com
|
||||
//---------------------------------------------------
|
||||
|
||||
.gradient(@color: #F5F5F5, @start: #EEE, @stop: #FFF) {
|
||||
background: @color;
|
||||
background: -webkit-gradient(linear,
|
||||
left bottom,
|
||||
left top,
|
||||
color-stop(0, @start),
|
||||
color-stop(1, @stop));
|
||||
background: -ms-linear-gradient(bottom,
|
||||
@start,
|
||||
@stop);
|
||||
background: -moz-linear-gradient(center bottom,
|
||||
@start 0%,
|
||||
@stop 100%);
|
||||
background: -o-linear-gradient(@stop,
|
||||
@start);
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@stop,@start));
|
||||
}
|
||||
.bw-gradient(@color: #F5F5F5, @start: 0, @stop: 255) {
|
||||
background: @color;
|
||||
background: -webkit-gradient(linear,
|
||||
left bottom,
|
||||
left top,
|
||||
color-stop(0, rgb(@start,@start,@start)),
|
||||
color-stop(1, rgb(@stop,@stop,@stop)));
|
||||
background: -ms-linear-gradient(bottom,
|
||||
rgb(@start,@start,@start) 0%,
|
||||
rgb(@stop,@stop,@stop) 100%);
|
||||
background: -moz-linear-gradient(center bottom,
|
||||
rgb(@start,@start,@start) 0%,
|
||||
rgb(@stop,@stop,@stop) 100%);
|
||||
background: -o-linear-gradient(rgb(@stop,@stop,@stop),
|
||||
rgb(@start,@start,@start));
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",rgb(@stop,@stop,@stop),rgb(@start,@start,@start)));
|
||||
}
|
||||
.bordered(@top-color: #EEE, @right-color: #EEE, @bottom-color: #EEE, @left-color: #EEE) {
|
||||
border-top: solid 1px @top-color;
|
||||
border-left: solid 1px @left-color;
|
||||
border-right: solid 1px @right-color;
|
||||
border-bottom: solid 1px @bottom-color;
|
||||
}
|
||||
.drop-shadow(@x-axis: 0, @y-axis: 1px, @blur: 2px, @alpha: 0.1) {
|
||||
-webkit-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha);
|
||||
-moz-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha);
|
||||
box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha);
|
||||
}
|
||||
.rounded(@radius: 2px) {
|
||||
-webkit-border-radius: @radius;
|
||||
-moz-border-radius: @radius;
|
||||
border-radius: @radius;
|
||||
}
|
||||
.border-radius(@topright: 0, @bottomright: 0, @bottomleft: 0, @topleft: 0) {
|
||||
-webkit-border-top-right-radius: @topright;
|
||||
-webkit-border-bottom-right-radius: @bottomright;
|
||||
-webkit-border-bottom-left-radius: @bottomleft;
|
||||
-webkit-border-top-left-radius: @topleft;
|
||||
-moz-border-radius-topright: @topright;
|
||||
-moz-border-radius-bottomright: @bottomright;
|
||||
-moz-border-radius-bottomleft: @bottomleft;
|
||||
-moz-border-radius-topleft: @topleft;
|
||||
border-top-right-radius: @topright;
|
||||
border-bottom-right-radius: @bottomright;
|
||||
border-bottom-left-radius: @bottomleft;
|
||||
border-top-left-radius: @topleft;
|
||||
.background-clip(padding-box);
|
||||
}
|
||||
.opacity(@opacity: 0.5) {
|
||||
-moz-opacity: @opacity;
|
||||
-khtml-opacity: @opacity;
|
||||
-webkit-opacity: @opacity;
|
||||
opacity: @opacity;
|
||||
@opperc: @opacity * 100;
|
||||
-ms-filter: ~"progid:DXImageTransform.Microsoft.Alpha(opacity=@{opperc})";
|
||||
filter: ~"alpha(opacity=@{opperc})";
|
||||
}
|
||||
.transition-duration(@duration: 0.2s) {
|
||||
-moz-transition-duration: @duration;
|
||||
-webkit-transition-duration: @duration;
|
||||
-o-transition-duration: @duration;
|
||||
transition-duration: @duration;
|
||||
}
|
||||
.transform(...) {
|
||||
-webkit-transform: @arguments;
|
||||
-moz-transform: @arguments;
|
||||
-o-transform: @arguments;
|
||||
-ms-transform: @arguments;
|
||||
transform: @arguments;
|
||||
}
|
||||
.rotation(@deg:5deg){
|
||||
.transform(rotate(@deg));
|
||||
}
|
||||
.scale(@ratio:1.5){
|
||||
.transform(scale(@ratio));
|
||||
}
|
||||
.transition(@duration:0.2s, @ease:ease-out) {
|
||||
-webkit-transition: all @duration @ease;
|
||||
-moz-transition: all @duration @ease;
|
||||
-o-transition: all @duration @ease;
|
||||
transition: all @duration @ease;
|
||||
}
|
||||
.inner-shadow(@horizontal:0, @vertical:1px, @blur:2px, @alpha: 0.4) {
|
||||
-webkit-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha);
|
||||
-moz-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha);
|
||||
box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha);
|
||||
}
|
||||
.box-shadow(@arguments) {
|
||||
-webkit-box-shadow: @arguments;
|
||||
-moz-box-shadow: @arguments;
|
||||
box-shadow: @arguments;
|
||||
}
|
||||
.box-sizing(@sizing: border-box) {
|
||||
-ms-box-sizing: @sizing;
|
||||
-moz-box-sizing: @sizing;
|
||||
-webkit-box-sizing: @sizing;
|
||||
box-sizing: @sizing;
|
||||
}
|
||||
.user-select(@argument: none) {
|
||||
-webkit-user-select: @argument;
|
||||
-moz-user-select: @argument;
|
||||
-ms-user-select: @argument;
|
||||
user-select: @argument;
|
||||
}
|
||||
.columns(@colwidth: 250px, @colcount: 0, @colgap: 50px, @columnRuleColor: #EEE, @columnRuleStyle: solid, @columnRuleWidth: 1px) {
|
||||
-moz-column-width: @colwidth;
|
||||
-moz-column-count: @colcount;
|
||||
-moz-column-gap: @colgap;
|
||||
-moz-column-rule-color: @columnRuleColor;
|
||||
-moz-column-rule-style: @columnRuleStyle;
|
||||
-moz-column-rule-width: @columnRuleWidth;
|
||||
-webkit-column-width: @colwidth;
|
||||
-webkit-column-count: @colcount;
|
||||
-webkit-column-gap: @colgap;
|
||||
-webkit-column-rule-color: @columnRuleColor;
|
||||
-webkit-column-rule-style: @columnRuleStyle;
|
||||
-webkit-column-rule-width: @columnRuleWidth;
|
||||
column-width: @colwidth;
|
||||
column-count: @colcount;
|
||||
column-gap: @colgap;
|
||||
column-rule-color: @columnRuleColor;
|
||||
column-rule-style: @columnRuleStyle;
|
||||
column-rule-width: @columnRuleWidth;
|
||||
}
|
||||
.translate(@x:0, @y:0) {
|
||||
.transform(translate(@x, @y));
|
||||
}
|
||||
.background-clip(@argument: padding-box) {
|
||||
-moz-background-clip: @argument;
|
||||
-webkit-background-clip: @argument;
|
||||
background-clip: @argument;
|
||||
}
|
||||
45
node_modules/gif.js/site/contents/styles/vendor/reset.less
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// http://meyerweb.com/eric/tools/css/reset/
|
||||
// v2.0 | 20110126
|
||||
// License: none (public domain)
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
// HTML5 display-role reset for older browsers
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
65
node_modules/gif.js/site/contents/tests/canvas.coffee
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
require '../scripts/vendor/mootools.js'
|
||||
ready = require '../scripts/vendor/ready.js'
|
||||
|
||||
num_frames = 20
|
||||
width = 600
|
||||
height = 300
|
||||
text = 'HYPNO TOAD'
|
||||
textSize = 70
|
||||
|
||||
now = window.performance?.now?.bind(window.performance) or Date.now
|
||||
|
||||
rgb = (rgb...) -> "rgb(#{ rgb.map((v) -> Math.floor(v * 255)).join(',') })"
|
||||
hsl = (hsl...) ->
|
||||
hsl = hsl.map (v, i) -> if i is 0 then v * 360 else "#{ v * 100 }%"
|
||||
return "hsl(#{ hsl.join(',') })"
|
||||
|
||||
ready ->
|
||||
canvas = document.createElement 'canvas'
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
|
||||
startTime = null
|
||||
ctx = canvas.getContext '2d'
|
||||
info = document.id 'info'
|
||||
|
||||
gif = new GIF
|
||||
workers: 4
|
||||
workerScript: '/gif.js/gif.worker.js'
|
||||
width: width
|
||||
height: height
|
||||
|
||||
gif.on 'start', -> startTime = now()
|
||||
|
||||
gif.on 'progress', (p) -> info.set 'text', Math.round(p * 100)+'%'
|
||||
|
||||
gif.on 'finished', (blob) ->
|
||||
img = document.id 'result'
|
||||
img.src = URL.createObjectURL(blob)
|
||||
delta = now() - startTime
|
||||
info.set 'text', """
|
||||
100%
|
||||
#{ (delta / 1000).toFixed 2 }sec
|
||||
#{ (blob.size / 1000).toFixed 2 }kb
|
||||
"""
|
||||
|
||||
ctx.font = "bold #{ textSize }px Helvetica"
|
||||
ctx.textAlign = 'center'
|
||||
ctx.textBaseline = 'middle'
|
||||
ctx.lineWidth = 3
|
||||
w2 = width / 2
|
||||
h2 = height / 2
|
||||
for i in [0...num_frames]
|
||||
p = i / (num_frames - 1)
|
||||
grad = ctx.createRadialGradient w2, h2, 0, w2, h2, w2
|
||||
grad.addColorStop 0, hsl p, 1, 0.5
|
||||
grad.addColorStop 1, hsl (p + 0.2) % 1, 1, 0.4
|
||||
ctx.fillStyle = grad
|
||||
ctx.fillRect 0, 0, width, height
|
||||
ctx.fillStyle = hsl (p + 0.5) % 1, 1, 0.7
|
||||
ctx.strokeStyle = hsl (p + 0.8) % 1, 1, 0.9
|
||||
ctx.fillText text, w2, h2
|
||||
ctx.strokeText text, w2, h2
|
||||
gif.addFrame ctx, {copy: true, delay: 20}
|
||||
|
||||
gif.render()
|
||||
10
node_modules/gif.js/site/contents/tests/canvas.md
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: Canvas rendering
|
||||
script: canvas.coffee
|
||||
template: test.html
|
||||
---
|
||||
|
||||
<p id="info">Loading...</p>
|
||||
|
||||
<img id="result">
|
||||
|
||||
BIN
node_modules/gif.js/site/contents/tests/clip.mp4
generated
vendored
Normal file
BIN
node_modules/gif.js/site/contents/tests/clip.ogv
generated
vendored
Normal file
70
node_modules/gif.js/site/contents/tests/video.coffee
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
require '../scripts/vendor/mootools.js'
|
||||
ready = require '../scripts/vendor/ready.js'
|
||||
now = window.performance?.now?.bind(window.performance) or Date.now
|
||||
|
||||
ready ->
|
||||
info = document.id 'info'
|
||||
video = document.id 'video'
|
||||
button = document.id 'go'
|
||||
sample = document.id 'sample'
|
||||
|
||||
gif = new GIF
|
||||
workers: 4
|
||||
workerScript: '/gif.js/gif.worker.js'
|
||||
width: 600
|
||||
height: 337
|
||||
|
||||
startTime = null
|
||||
sampleInterval = null
|
||||
|
||||
sampleUpdate = ->
|
||||
sampleInterval = parseInt sample.value
|
||||
gif.abort()
|
||||
document.id('info').set 'text', """
|
||||
ready to start with a sample interval of #{ sampleInterval }ms
|
||||
"""
|
||||
|
||||
video.addEventListener 'canplay', ->
|
||||
button.disabled = false
|
||||
sample.disabled = false
|
||||
sampleUpdate()
|
||||
|
||||
sample.addEvent 'change', sampleUpdate
|
||||
|
||||
button.addEvent 'click', ->
|
||||
video.pause()
|
||||
video.currentTime = 0
|
||||
gif.abort()
|
||||
gif.frames = []
|
||||
video.play()
|
||||
|
||||
gif.on 'start', -> startTime = now()
|
||||
|
||||
gif.on 'progress', (p) ->
|
||||
info.set 'text', "rendering: #{ Math.round(p * 100) }%"
|
||||
|
||||
gif.on 'finished', (blob) ->
|
||||
img = document.id 'result'
|
||||
img.src = URL.createObjectURL(blob)
|
||||
delta = now() - startTime
|
||||
info.set 'text', """
|
||||
done in
|
||||
#{ (delta / 1000).toFixed 2 }sec,
|
||||
size #{ (blob.size / 1000).toFixed 2 }kb
|
||||
"""
|
||||
|
||||
# this might not be the best approach to capturing
|
||||
# html video, but i but i can't seek since my dev server
|
||||
# doesn't support http byte requests
|
||||
timer = null
|
||||
capture = ->
|
||||
info.set 'html', "capturing at #{ video.currentTime }"
|
||||
gif.addFrame video, {copy: true, delay: sampleInterval}
|
||||
|
||||
video.addEventListener 'play', ->
|
||||
clearInterval timer
|
||||
timer = setInterval capture, sampleInterval
|
||||
|
||||
video.addEventListener 'ended', ->
|
||||
clearInterval timer
|
||||
gif.render()
|
||||
20
node_modules/gif.js/site/contents/tests/video.md
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Video to GIF
|
||||
script: video.coffee
|
||||
template: test.html
|
||||
---
|
||||
|
||||
<p>
|
||||
<input disabled id="sample" type="range" step="1" min="20" max="500" value="100">
|
||||
<button id="go" disabled>Do it!</button>
|
||||
</p>
|
||||
|
||||
<p id="info">Loading...</p>
|
||||
|
||||
<video id="video" width="600">
|
||||
<source src="clip.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.'>
|
||||
<source src="clip.ogv" type='video/ogg; codecs="theora, vorbis"'>
|
||||
</video>
|
||||
|
||||
<img id="result">
|
||||
|
||||
11
node_modules/gif.js/site/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"wintersmith-browserify": "*",
|
||||
"wintersmith-less": "*",
|
||||
"wintersmith-nunjucks": "*",
|
||||
"browsernizr": "*",
|
||||
"async": "~0.2.8",
|
||||
"highlight.js": "~7.3.0"
|
||||
}
|
||||
}
|
||||
4
node_modules/gif.js/site/plugins/jsignore.coffee
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = (env, callback) ->
|
||||
# hack to have browserify ignore the symlinked gif.js
|
||||
env.registerContentPlugin 'files', 'gif.*js', env.plugins.StaticFile
|
||||
callback()
|
||||
152
node_modules/gif.js/site/templates/index.html
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
{% extends 'layout.html' %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="gif.js?v={{ version }}"></script>
|
||||
<script src="scripts/main.js?v={{ version }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<h2>Usage</h2>
|
||||
{{ contents['code.md'].html }}
|
||||
|
||||
<h2>Demo</h2>
|
||||
|
||||
<p>
|
||||
GIF images are generated in the background using web workers. Hover the original and reference
|
||||
tabs to see respective image.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Reference images are rendered with photoshop cs6 using the perceptual setting with no dither.
|
||||
</p>
|
||||
|
||||
<div class="demo">
|
||||
<div class="images">
|
||||
<img class="render">
|
||||
<img class="original" src="images/test/anim1.jpg">
|
||||
<img class="original" src="images/test/anim2.jpg">
|
||||
<img class="original" src="images/test/anim3.jpg">
|
||||
<img class="original" src="images/test/anim4.jpg">
|
||||
<img class="reference" src="images/test/anim-ref.gif">
|
||||
</div>
|
||||
<ul class="hover-buttons">
|
||||
<li class="original">Original</li>
|
||||
<li class="reference">Reference</li>
|
||||
</ul>
|
||||
<div class="controls">
|
||||
<p class="quality">
|
||||
<label>Quality</label>
|
||||
<input type="range" step="1" min="1" max="30" value="20">
|
||||
<span class="value">10</span>
|
||||
</p>
|
||||
<p class="delay">
|
||||
<label>Delay</label>
|
||||
<input type="range" step="1" min="0" max="1000" value="500">
|
||||
<span class="value">500ms</span>
|
||||
</p>
|
||||
<p class="repeat">
|
||||
<label>Repeat</label>
|
||||
<input type="range" step="1" min="0" max="21" value="21">
|
||||
<span class="value">forever</span>
|
||||
</p>
|
||||
<p class="dither">
|
||||
<label>Dither</label>
|
||||
<select>
|
||||
<option selected>None</option>
|
||||
<option>Atkinson</option>
|
||||
<option>Atkinson-serpentine </option>
|
||||
<option>FalseFloydSteinberg</option>
|
||||
<option>FalseFloydSteinberg-serpentine</option>
|
||||
<option>FloydSteinberg</option>
|
||||
<option>FloydSteinberg-serpentine</option>
|
||||
<option>Stucki</option>
|
||||
<option>Stucki-serpentine</option>
|
||||
</select>
|
||||
</p>
|
||||
<p class="info">
|
||||
<pre>Loading...</pre>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="images">
|
||||
<img class="render">
|
||||
<img class="original" src="images/test/test1-orig.jpg">
|
||||
<img class="reference" src="images/test/test1-ref.gif">
|
||||
</div>
|
||||
<ul class="hover-buttons">
|
||||
<li class="original">Original</li>
|
||||
<li class="reference">Reference</li>
|
||||
</ul>
|
||||
<div class="controls">
|
||||
<p class="quality">
|
||||
<label>Quality</label>
|
||||
<input type="range" step="1" min="1" max="30" value="20">
|
||||
<span class="value">10</span>
|
||||
</p>
|
||||
<p class="dither">
|
||||
<label>Dither</label>
|
||||
<select>
|
||||
<option selected>None</option>
|
||||
<option>Atkinson</option>
|
||||
<option>Atkinson-serpentine </option>
|
||||
<option>FalseFloydSteinberg</option>
|
||||
<option>FalseFloydSteinberg-serpentine</option>
|
||||
<option>FloydSteinberg</option>
|
||||
<option>FloydSteinberg-serpentine</option>
|
||||
<option>Stucki</option>
|
||||
<option>Stucki-serpentine</option>
|
||||
</select>
|
||||
</p>
|
||||
<p class="info">
|
||||
<pre>Loading...</pre>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="images">
|
||||
<img class="render">
|
||||
<img class="original" src="images/test/test3-orig.png">
|
||||
<img class="reference" src="images/test/test3-ref.gif">
|
||||
</div>
|
||||
<ul class="hover-buttons">
|
||||
<li class="original">Original</li>
|
||||
<li class="reference">Reference</li>
|
||||
</ul>
|
||||
<div class="controls">
|
||||
<p class="quality">
|
||||
<label>Quality</label>
|
||||
<input type="range" step="1" min="1" max="30" value="20">
|
||||
<span class="value">10</span>
|
||||
</p>
|
||||
<p class="dither">
|
||||
<label>Dither</label>
|
||||
<select>
|
||||
<option selected>None</option>
|
||||
<option>Atkinson</option>
|
||||
<option>Atkinson-serpentine </option>
|
||||
<option>FalseFloydSteinberg</option>
|
||||
<option>FalseFloydSteinberg-serpentine</option>
|
||||
<option>FloydSteinberg</option>
|
||||
<option>FloydSteinberg-serpentine</option>
|
||||
<option>Stucki</option>
|
||||
<option>Stucki-serpentine</option>
|
||||
</select>
|
||||
</p>
|
||||
<p class="info">
|
||||
<pre>Loading...</pre>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Other tests</h2>
|
||||
<ul>
|
||||
{% for test in contents.tests._.pages %}
|
||||
<li><a href="{{ test.url }}">{{ test.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||