From aaf7d424e6450bff54a55cdcd0ef899c52e0b8a1 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 26 Mar 2020 20:18:16 +0900 Subject: [PATCH 001/188] Update dev dependencies. --- package-lock.json | 474 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 277 insertions(+), 199 deletions(-) diff --git a/package-lock.json b/package-lock.json index c78302d..359f535 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,58 +5,84 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" } }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "dev": true + }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.9.0", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true }, "acorn-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "dev": true }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } } }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -112,17 +138,6 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "chardet": { @@ -161,10 +176,16 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "comment-parser": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.6.2.tgz", - "integrity": "sha512-Wdms0Q8d4vvb2Yk72OwZjwNWtMklbC5Re7lD9cjCP/AG1fhocmc0TrxGBBAXPLy8fZQPrfHGgyygwI0lA7pbzA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.7.2.tgz", + "integrity": "sha512-4Rjb1FnxtOcv9qsfuaNuVsmmVn4ooVoBHzYfyKteiXwIU84PClyGA5jASoFMwPV93+FPh9spwueXauxFJZkGAg==", "dev": true }, "concat-map": { @@ -184,6 +205,14 @@ "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "debug": { @@ -265,58 +294,36 @@ "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } } }, "eslint-config-blueimp": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-config-blueimp/-/eslint-config-blueimp-1.4.0.tgz", - "integrity": "sha512-OWTLOSmsjDglgNvKKtb0t3iRPpyzFvXszXJkSS1w5oqkgVmFaKiNeoU6qOyZF2Q78rIog77RgIhzyA387b+7qA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/eslint-config-blueimp/-/eslint-config-blueimp-1.9.0.tgz", + "integrity": "sha512-30uqxKoc3/AAn7H2lzISh77SRAecZ3D52RBwLdrV5G/ZorWcPNX0ZnlTz68AnaXoIXFu6irkMs1Zi/KJaA5ZJQ==", "dev": true }, "eslint-config-prettier": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.7.0.tgz", - "integrity": "sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz", + "integrity": "sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ==", "dev": true, "requires": { "get-stdin": "^6.0.0" } }, "eslint-plugin-jsdoc": { - "version": "15.12.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-15.12.2.tgz", - "integrity": "sha512-QHzPc3VKTEbTn369/HpqDjl/czv3fCei/bZg5NA5tu9Od10MfpTH4kc1xnRDobhQoDs3AMz9wuaI4coHWRzMQw==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-22.1.0.tgz", + "integrity": "sha512-54NdbICM7KrxsGUqQsev9aIMqPXyvyBx2218Qcm0TQ16P9CtBI+YY4hayJR6adrxlq4Ej0JLpgfUXWaQVFqmQg==", "dev": true, "requires": { - "comment-parser": "^0.6.2", + "comment-parser": "^0.7.2", "debug": "^4.1.1", - "jsdoctypeparser": "^5.1.1", + "jsdoctypeparser": "^6.1.0", "lodash": "^4.17.15", - "object.entries-ponyfill": "^1.0.1", - "regextras": "^0.6.1" + "regextras": "^0.7.0", + "semver": "^6.3.0", + "spdx-expression-parse": "^3.0.0" } }, "eslint-plugin-prettier": { @@ -354,13 +361,13 @@ "dev": true }, "espree": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", - "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", "dev": true, "requires": { - "acorn": "^7.1.0", - "acorn-jsx": "^5.1.0", + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", "eslint-visitor-keys": "^1.1.0" } }, @@ -371,12 +378,20 @@ "dev": true }, "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz", + "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "^5.0.0" + }, + "dependencies": { + "estraverse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", + "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", + "dev": true + } } }, "esrecurse": { @@ -412,9 +427,9 @@ } }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, "fast-diff": { @@ -436,9 +451,9 @@ "dev": true }, "figures": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", - "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -503,18 +518,18 @@ } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, "globals": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", - "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", "dev": true, "requires": { "type-fest": "^0.8.1" @@ -574,75 +589,83 @@ "dev": true }, "inquirer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.1.tgz", - "integrity": "sha512-V1FFQ3TIO15det8PijPLFR9M9baSlnRs9nL7zWu1MNVA2T9YVl9ZbrHJhYs7e9X8jeMZ3lr2JH/rdHFgNCBdYw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", - "chalk": "^2.4.2", + "chalk": "^3.0.0", "cli-cursor": "^3.1.0", "cli-width": "^2.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.15", "mute-stream": "0.0.8", - "run-async": "^2.2.0", + "run-async": "^2.4.0", "rxjs": "^6.5.3", "string-width": "^4.1.0", - "strip-ansi": "^5.1.0", + "strip-ansi": "^6.0.0", "through": "^2.3.6" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } }, - "is-fullwidth-code-point": { + "chalk": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } + "color-name": "~1.1.4" } }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } @@ -654,9 +677,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { @@ -697,9 +720,9 @@ } }, "jsdoctypeparser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-5.1.1.tgz", - "integrity": "sha512-APGygIJrT5bbz5lsVt8vyLJC0miEbQf/z9ZBfTr4RYvdia8AhWMRlYgivvwHG5zKD/VW3d6qpChCy64hpQET3A==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz", + "integrity": "sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA==", "dev": true }, "json-schema-traverse": { @@ -746,18 +769,18 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "ms": { @@ -784,12 +807,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "object.entries-ponyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.entries-ponyfill/-/object.entries-ponyfill-1.0.1.tgz", - "integrity": "sha1-Kavfd8v70mVm3RqiTp2I9lQz0lY=", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -889,9 +906,9 @@ "dev": true }, "regextras": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.6.1.tgz", - "integrity": "sha512-EzIHww9xV2Kpqx+corS/I7OBmf2rZ0pKKJPsw5Dc+l6Zq1TslDmtRIP9maVn3UH+72MIXmn8zzDgP07ihQogUA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.0.tgz", + "integrity": "sha512-ds+fL+Vhl918gbAUb0k2gVKbTZLsg84Re3DI6p85Et0U0tYME3hyW4nMK8Px4dtDaBA2qNjvG5uWyW7eK5gfmw==", "dev": true }, "resolve-from": { @@ -920,18 +937,18 @@ } }, "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", "dev": true, "requires": { "is-promise": "^2.1.0" } }, "rxjs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", - "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -944,9 +961,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "shebang-command": { @@ -979,6 +996,14 @@ "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } } }, "source-map": { @@ -987,18 +1012,88 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, "strip-json-comments": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "dev": true }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -1011,18 +1106,18 @@ "string-width": "^3.0.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -1033,15 +1128,6 @@ "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } } } }, @@ -1067,9 +1153,9 @@ } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", "dev": true }, "type-check": { @@ -1088,21 +1174,13 @@ "dev": true }, "uglify-js": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz", - "integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz", + "integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==", "dev": true, "requires": { "commander": "~2.20.3", "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } } }, "uri-js": { diff --git a/package.json b/package.json index 7609e5f..90a44d2 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "eslint": "6", "eslint-config-blueimp": "1", "eslint-config-prettier": "6", - "eslint-plugin-jsdoc": "15", + "eslint-plugin-jsdoc": "22", "eslint-plugin-prettier": "3", "prettier": "1", "uglify-js": "3" From 2cfea2c928808856be6c255a4170130f72315411 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 26 Mar 2020 20:35:40 +0900 Subject: [PATCH 002/188] Update prettier to version 2. --- js/demo/demo.js | 20 +-- js/load-image-exif-map.js | 10 +- js/load-image-exif.js | 32 ++--- js/load-image-fetch.js | 18 +-- js/load-image-iptc-map.js | 10 +- js/load-image-iptc.js | 14 +- js/load-image-meta.js | 16 +-- js/load-image-orientation.js | 12 +- js/load-image-scale.js | 16 +-- js/load-image.js | 28 ++-- package-lock.json | 6 +- package.json | 6 +- test/test.js | 260 +++++++++++++++++------------------ 13 files changed, 225 insertions(+), 223 deletions(-) diff --git a/js/demo/demo.js b/js/demo/demo.js index f1db0c8..0f6d455 100644 --- a/js/demo/demo.js +++ b/js/demo/demo.js @@ -11,7 +11,7 @@ /* global loadImage, $ */ -$(function() { +$(function () { 'use strict' var result = $('#result') @@ -59,7 +59,7 @@ $(function() { thumbNode.empty() loadImage( thumbnail, - function(img) { + function (img) { node.append(img).show() }, options @@ -191,7 +191,7 @@ $(function() { } $(document) - .on('dragover', function(e) { + .on('dragover', function (e) { e.preventDefault() var originalEvent = event.originalEvent if (originalEvent) originalEvent.dataTransfer.dropEffect = 'copy' @@ -202,7 +202,7 @@ $(function() { $('#url').on('change paste input', urlChangeHandler) - $('#edit').on('click', function(event) { + $('#edit').on('click', function (event) { event.preventDefault() var imgNode = result.find('img, canvas') var img = imgNode[0] @@ -217,24 +217,24 @@ $(function() { img.width / pixelRatio - 40, img.height / pixelRatio - 40 ], - onSelect: function(coords) { + onSelect: function (coords) { coordinates = coords }, - onRelease: function() { + onRelease: function () { coordinates = null } }, - function() { + function () { jcropAPI = this } ) .parent() - .on('click', function(event) { + .on('click', function (event) { event.preventDefault() }) }) - $('#crop').on('click', function(event) { + $('#crop').on('click', function (event) { event.preventDefault() var img = result.find('img, canvas')[0] var pixelRatio = window.devicePixelRatio || 1 @@ -255,7 +255,7 @@ $(function() { } }) - $('#cancel').on('click', function(event) { + $('#cancel').on('click', function (event) { event.preventDefault() if (jcropAPI) jcropAPI.release() }) diff --git a/js/load-image-exif-map.js b/js/load-image-exif-map.js index ec23462..68d90d3 100644 --- a/js/load-image-exif-map.js +++ b/js/load-image-exif-map.js @@ -14,7 +14,7 @@ /* global define, module, require */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -25,7 +25,7 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' loadImage.ExifMap.prototype.tags = { @@ -321,7 +321,7 @@ } } - loadImage.ExifMap.prototype.getText = function(id) { + loadImage.ExifMap.prototype.getText = function (id) { var value = this.get(id) switch (id) { case 'LightSource': @@ -359,7 +359,7 @@ } return String(value) } - ;(function(exifMapPrototype) { + ;(function (exifMapPrototype) { var tags = exifMapPrototype.tags var map = exifMapPrototype.map var prop @@ -371,7 +371,7 @@ } })(loadImage.ExifMap.prototype) - loadImage.ExifMap.prototype.getAll = function() { + loadImage.ExifMap.prototype.getAll = function () { var map = {} var prop var id diff --git a/js/load-image-exif.js b/js/load-image-exif.js index 3e9701f..08e3884 100644 --- a/js/load-image-exif.js +++ b/js/load-image-exif.js @@ -13,7 +13,7 @@ /* eslint-disable no-console */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -24,10 +24,10 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' - loadImage.ExifMap = function() { + loadImage.ExifMap = function () { return this } @@ -35,11 +35,11 @@ Orientation: 0x0112 } - loadImage.ExifMap.prototype.get = function(id) { + loadImage.ExifMap.prototype.get = function (id) { return this[id] || this[this.map[id]] } - loadImage.getExifThumbnail = function(dataView, offset, length) { + loadImage.getExifThumbnail = function (dataView, offset, length) { if (!length || offset + length > dataView.byteLength) { console.log('Invalid Exif data: Invalid thumbnail data.') return @@ -52,14 +52,14 @@ loadImage.exifTagTypes = { // byte, 8-bit unsigned int: 1: { - getValue: function(dataView, dataOffset) { + getValue: function (dataView, dataOffset) { return dataView.getUint8(dataOffset) }, size: 1 }, // ascii, 8-bit byte: 2: { - getValue: function(dataView, dataOffset) { + getValue: function (dataView, dataOffset) { return String.fromCharCode(dataView.getUint8(dataOffset)) }, size: 1, @@ -67,21 +67,21 @@ }, // short, 16 bit int: 3: { - getValue: function(dataView, dataOffset, littleEndian) { + getValue: function (dataView, dataOffset, littleEndian) { return dataView.getUint16(dataOffset, littleEndian) }, size: 2 }, // long, 32 bit int: 4: { - getValue: function(dataView, dataOffset, littleEndian) { + getValue: function (dataView, dataOffset, littleEndian) { return dataView.getUint32(dataOffset, littleEndian) }, size: 4 }, // rational = two long values, first is numerator, second is denominator: 5: { - getValue: function(dataView, dataOffset, littleEndian) { + getValue: function (dataView, dataOffset, littleEndian) { return ( dataView.getUint32(dataOffset, littleEndian) / dataView.getUint32(dataOffset + 4, littleEndian) @@ -91,14 +91,14 @@ }, // slong, 32 bit signed int: 9: { - getValue: function(dataView, dataOffset, littleEndian) { + getValue: function (dataView, dataOffset, littleEndian) { return dataView.getInt32(dataOffset, littleEndian) }, size: 4 }, // srational, two slongs, first is numerator, second is denominator: 10: { - getValue: function(dataView, dataOffset, littleEndian) { + getValue: function (dataView, dataOffset, littleEndian) { return ( dataView.getInt32(dataOffset, littleEndian) / dataView.getInt32(dataOffset + 4, littleEndian) @@ -110,7 +110,7 @@ // undefined, 8-bit byte, value depending on field: loadImage.exifTagTypes[7] = loadImage.exifTagTypes[1] - loadImage.getExifValue = function( + loadImage.getExifValue = function ( dataView, tiffOffset, offset, @@ -167,7 +167,7 @@ return values } - loadImage.parseExifTag = function( + loadImage.parseExifTag = function ( dataView, tiffOffset, offset, @@ -185,7 +185,7 @@ ) } - loadImage.parseExifTags = function( + loadImage.parseExifTags = function ( dataView, tiffOffset, dirOffset, @@ -216,7 +216,7 @@ return dataView.getUint32(dirEndOffset, littleEndian) } - loadImage.parseExifData = function(dataView, offset, length, data, options) { + loadImage.parseExifData = function (dataView, offset, length, data, options) { if (options.disableExif) { return } diff --git a/js/load-image-fetch.js b/js/load-image-fetch.js index ab64665..83fbfd5 100644 --- a/js/load-image-fetch.js +++ b/js/load-image-fetch.js @@ -11,7 +11,7 @@ /* global define, module, require */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -22,18 +22,18 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' if (typeof fetch !== 'undefined' && typeof Request !== 'undefined') { - loadImage.fetchBlob = function(url, callback, options) { + loadImage.fetchBlob = function (url, callback, options) { if (loadImage.hasMetaOption(options)) { fetch(new Request(url, options)) - .then(function(response) { + .then(function (response) { return response.blob() }) .then(callback) - .catch(function(err) { + .catch(function (err) { console.log(err) // eslint-disable-line no-console callback() }) @@ -46,23 +46,23 @@ typeof XMLHttpRequest !== 'undefined' && typeof ProgressEvent !== 'undefined' ) { - loadImage.fetchBlob = function(url, callback, options) { + loadImage.fetchBlob = function (url, callback, options) { if (loadImage.hasMetaOption(options)) { // eslint-disable-next-line no-param-reassign options = options || {} var req = new XMLHttpRequest() req.open(options.method || 'GET', url) if (options.headers) { - Object.keys(options.headers).forEach(function(key) { + Object.keys(options.headers).forEach(function (key) { req.setRequestHeader(key, options.headers[key]) }) } req.withCredentials = options.credentials === 'include' req.responseType = 'blob' - req.onload = function() { + req.onload = function () { callback(req.response) } - req.onerror = req.onabort = req.ontimeout = function(e) { + req.onerror = req.onabort = req.ontimeout = function (e) { console.log(e) // eslint-disable-line no-console callback() } diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js index 5ed39de..adffa21 100644 --- a/js/load-image-iptc-map.js +++ b/js/load-image-iptc-map.js @@ -16,7 +16,7 @@ /* global define, module, require */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -27,7 +27,7 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' loadImage.IptcMap.prototype.tags = { @@ -96,11 +96,11 @@ // 0xca: 'PreviewData' } - loadImage.IptcMap.prototype.getText = function(id) { + loadImage.IptcMap.prototype.getText = function (id) { var value = this.get(id) return String(value) } - ;(function(iptcMapPrototype) { + ;(function (iptcMapPrototype) { var tags = iptcMapPrototype.tags var map = iptcMapPrototype.map || {} var prop @@ -112,7 +112,7 @@ } })(loadImage.IptcMap.prototype) - loadImage.IptcMap.prototype.getAll = function() { + loadImage.IptcMap.prototype.getAll = function () { var map = {} var prop var id diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index be2e764..c7ce7c1 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -12,7 +12,7 @@ /* global define, module, require, Buffer */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -23,10 +23,10 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' - loadImage.IptcMap = function() { + loadImage.IptcMap = function () { return this } @@ -34,11 +34,11 @@ ObjectName: 0x5 } - loadImage.IptcMap.prototype.get = function(id) { + loadImage.IptcMap.prototype.get = function (id) { return this[id] || this[this.map[id]] } - loadImage.parseIptcTags = function( + loadImage.parseIptcTags = function ( dataView, startOffset, sectionLength, @@ -89,13 +89,13 @@ } } - loadImage.parseIptcData = function(dataView, offset, length, data, options) { + loadImage.parseIptcData = function (dataView, offset, length, data, options) { if (options.disableIptc) { return } var markerLength = offset + length // Found '8BIM' ? - var isFieldSegmentStart = function(dataView, offset) { + var isFieldSegmentStart = function (dataView, offset) { return ( dataView.getUint32(offset) === 0x3842494d && dataView.getUint16(offset + 4) === 0x0404 diff --git a/js/load-image-meta.js b/js/load-image-meta.js index c110036..023d6be 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -15,7 +15,7 @@ /* global define, module, require, DataView, Uint8Array */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -26,7 +26,7 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' var hasblobSlice = @@ -37,7 +37,7 @@ loadImage.blobSlice = hasblobSlice && - function() { + function () { var slice = this.slice || this.webkitSlice || this.mozSlice return slice.apply(this, arguments) } @@ -56,7 +56,7 @@ // properties: // * maxMetaDataSize: Defines the maximum number of bytes to parse. // * disableImageHead: Disables creating the imageHead property. - loadImage.parseMetaData = function(file, callback, options, data) { + loadImage.parseMetaData = function (file, callback, options, data) { // eslint-disable-next-line no-param-reassign options = options || {} // eslint-disable-next-line no-param-reassign @@ -75,7 +75,7 @@ noMetaData || !loadImage.readFile( loadImage.blobSlice.call(file, 0, maxMetaDataSize), - function(e) { + function (e) { if (e.target.error) { // FileReader error // eslint-disable-next-line no-console @@ -163,16 +163,16 @@ } // Determines if meta data should be loaded automatically: - loadImage.hasMetaOption = function(options) { + loadImage.hasMetaOption = function (options) { return options && options.meta } var originalTransform = loadImage.transform - loadImage.transform = function(img, options, callback, file, data) { + loadImage.transform = function (img, options, callback, file, data) { if (loadImage.hasMetaOption(options)) { loadImage.parseMetaData( file, - function(data) { + function (data) { originalTransform.call(loadImage, img, options, callback, file, data) }, options, diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 11a5ccf..a14c6a4 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -11,7 +11,7 @@ /* global define, module, require */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -26,7 +26,7 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' var originalHasCanvasOption = loadImage.hasCanvasOption @@ -35,14 +35,14 @@ var originalGetTransformedOptions = loadImage.getTransformedOptions // Determines if the target image should be a canvas element: - loadImage.hasCanvasOption = function(options) { + loadImage.hasCanvasOption = function (options) { return ( !!options.orientation || originalHasCanvasOption.call(loadImage, options) ) } // Determines if meta data should be loaded automatically: - loadImage.hasMetaOption = function(options) { + loadImage.hasMetaOption = function (options) { return ( (options && options.orientation === true) || originalHasMetaOption.call(loadImage, options) @@ -51,7 +51,7 @@ // Transform image orientation based on // the given EXIF orientation option: - loadImage.transformCoordinates = function(canvas, options) { + loadImage.transformCoordinates = function (canvas, options) { originalTransformCoordinates.call(loadImage, canvas, options) var ctx = canvas.getContext('2d') var width = canvas.width @@ -110,7 +110,7 @@ // Transforms coordinate and dimension options // based on the given orientation option: - loadImage.getTransformedOptions = function(img, opts, data) { + loadImage.getTransformedOptions = function (img, opts, data) { var options = originalGetTransformedOptions.call(loadImage, img, opts) var orientation = options.orientation var newOptions diff --git a/js/load-image-scale.js b/js/load-image-scale.js index 39be9a5..4de6d02 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -11,7 +11,7 @@ /* global define, module, require */ -;(function(factory) { +;(function (factory) { 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: @@ -22,12 +22,12 @@ // Browser globals: factory(window.loadImage) } -})(function(loadImage) { +})(function (loadImage) { 'use strict' var originalTransform = loadImage.transform - loadImage.transform = function(img, options, callback, file, data) { + loadImage.transform = function (img, options, callback, file, data) { originalTransform.call( loadImage, loadImage.scale(img, options, data), @@ -41,12 +41,12 @@ // Transform image coordinates, allows to override e.g. // the canvas orientation based on the orientation option, // gets canvas, options passed as arguments: - loadImage.transformCoordinates = function() {} + loadImage.transformCoordinates = function () {} // Returns transformed options, allows to override e.g. // maxWidth, maxHeight and crop options based on the aspectRatio. // gets img, options passed as arguments: - loadImage.getTransformedOptions = function(img, options) { + loadImage.getTransformedOptions = function (img, options) { var aspectRatio = options.aspectRatio var newOptions var i @@ -75,7 +75,7 @@ } // Canvas render method, allows to implement a different rendering algorithm: - loadImage.renderImageToCanvas = function( + loadImage.renderImageToCanvas = function ( canvas, img, sourceX, @@ -104,7 +104,7 @@ } // Determines if the target image should be a canvas element: - loadImage.hasCanvasOption = function(options) { + loadImage.hasCanvasOption = function (options) { return options.canvas || options.crop || !!options.aspectRatio } @@ -113,7 +113,7 @@ // Returns a canvas object if the browser supports canvas // and the hasCanvasOption method returns true or a canvas // object is passed as image, else the scaled image: - loadImage.scale = function(img, options, data) { + loadImage.scale = function (img, options, data) { // eslint-disable-next-line no-param-reassign options = options || {} var canvas = document.createElement('canvas') diff --git a/js/load-image.js b/js/load-image.js index 2e158f5..a93ea13 100644 --- a/js/load-image.js +++ b/js/load-image.js @@ -11,7 +11,7 @@ /* global define, webkitURL, module */ -;(function($) { +;(function ($) { 'use strict' /** @@ -27,16 +27,16 @@ function loadImage(file, callback, options) { var img = document.createElement('img') var url - img.onerror = function(event) { + img.onerror = function (event) { return loadImage.onerror(img, event, file, callback, options) } - img.onload = function(event) { + img.onload = function (event) { return loadImage.onload(img, event, file, callback, options) } if (typeof file === 'string') { loadImage.fetchBlob( file, - function(blob) { + function (blob) { if (blob && loadImage.isInstanceOf('Blob', blob)) { // eslint-disable-next-line no-param-reassign file = blob @@ -63,7 +63,7 @@ img.src = url return img } - return loadImage.readFile(file, function(e) { + return loadImage.readFile(file, function (e) { var target = e.target if (target && target.result) { img.src = target.result @@ -96,27 +96,27 @@ // If the callback given to this function returns a blob, it is used as image // source instead of the original url and overrides the file argument used in // the onload and onerror event callbacks: - loadImage.fetchBlob = function(url, callback) { + loadImage.fetchBlob = function (url, callback) { callback() } - loadImage.isInstanceOf = function(type, obj) { + loadImage.isInstanceOf = function (type, obj) { // Cross-frame instanceof check return Object.prototype.toString.call(obj) === '[object ' + type + ']' } - loadImage.transform = function(img, options, callback, file, data) { + loadImage.transform = function (img, options, callback, file, data) { callback(img, data) } - loadImage.onerror = function(img, event, file, callback, options) { + loadImage.onerror = function (img, event, file, callback, options) { revokeHelper(img, options) if (callback) { callback.call(img, event) } } - loadImage.onload = function(img, event, file, callback, options) { + loadImage.onload = function (img, event, file, callback, options) { revokeHelper(img, options) if (callback) { loadImage.transform(img, options, callback, file, { @@ -126,18 +126,18 @@ } } - loadImage.createObjectURL = function(file) { + loadImage.createObjectURL = function (file) { return urlAPI ? urlAPI.createObjectURL(file) : false } - loadImage.revokeObjectURL = function(url) { + loadImage.revokeObjectURL = function (url) { return urlAPI ? urlAPI.revokeObjectURL(url) : false } // Loads a given File object via FileReader interface, // invokes the callback with the event object (load or error). // The result can be read via event.target.result: - loadImage.readFile = function(file, callback, method) { + loadImage.readFile = function (file, callback, method) { if ($.FileReader) { var fileReader = new FileReader() fileReader.onload = fileReader.onerror = callback @@ -152,7 +152,7 @@ } if (typeof define === 'function' && define.amd) { - define(function() { + define(function () { return loadImage }) } else if (typeof module === 'object' && module.exports) { diff --git a/package-lock.json b/package-lock.json index 359f535..bd0aed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -873,9 +873,9 @@ "dev": true }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.2.tgz", + "integrity": "sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==", "dev": true }, "prettier-linter-helpers": { diff --git a/package.json b/package.json index 90a44d2..771faeb 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "eslint-config-prettier": "6", "eslint-plugin-jsdoc": "22", "eslint-plugin-prettier": "3", - "prettier": "1", + "prettier": "2", "uglify-js": "3" }, "eslintConfig": { @@ -56,9 +56,11 @@ "test/vendor" ], "prettier": { + "arrowParens": "avoid", "proseWrap": "always", "semi": false, - "singleQuote": true + "singleQuote": true, + "trailingComma": "none" }, "scripts": { "lint": "eslint .", diff --git a/test/test.js b/test/test.js index fc1d1a7..660761c 100644 --- a/test/test.js +++ b/test/test.js @@ -12,7 +12,7 @@ /* global describe, it */ /* eslint-disable no-unused-expressions */ -;(function(expect, loadImage) { +;(function (expect, loadImage) { 'use strict' var canCreateBlob = !!window.dataURLtoBlob @@ -65,17 +65,17 @@ } } - describe('Loading', function() { - it('Return an object with onload and onerror methods', function() { - var img = loadImage(blobGIF, function() {}) + describe('Loading', function () { + it('Return an object with onload and onerror methods', function () { + var img = loadImage(blobGIF, function () {}) expect(img).to.be.an.instanceOf(Object) expect(img.onload).to.be.a('function') expect(img.onerror).to.be.a('function') }) - it('Load image url', function(done) { + it('Load image url', function (done) { expect( - loadImage(imageUrlGIF, function(img) { + loadImage(imageUrlGIF, function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -83,9 +83,9 @@ ).to.be.ok }) - it('Load image blob', function(done) { + it('Load image blob', function (done) { expect( - loadImage(blobGIF, function(img) { + loadImage(blobGIF, function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -93,9 +93,9 @@ ).to.be.ok }) - it('Return image loading error to callback', function(done) { + it('Return image loading error to callback', function (done) { expect( - loadImage('404', function(img) { + loadImage('404', function (img) { expect(img).to.be.an.instanceOf(window.Event) expect(img.type).to.equal('error') done() @@ -103,12 +103,12 @@ ).to.be.ok }) - it('Keep object URL if noRevoke is true', function(done) { + it('Keep object URL if noRevoke is true', function (done) { expect( loadImage( blobGIF, - function(img) { - loadImage(img.src, function(img2) { + function (img) { + loadImage(img.src, function (img2) { expect(img.width).to.equal(img2.width) expect(img.height).to.equal(img2.height) done() @@ -119,10 +119,10 @@ ).to.be.ok }) - it('Discard object URL if noRevoke is undefined/false', function(done) { + it('Discard object URL if noRevoke is undefined/false', function (done) { expect( - loadImage(blobGIF, function(img) { - loadImage(img.src, function(img2) { + loadImage(blobGIF, function (img) { + loadImage(img.src, function (img2) { if (!window.callPhantom) { // revokeObjectUrl doesn't seem to have an effect in PhantomJS expect(img2).to.be.an.instanceOf(window.Event) @@ -134,9 +134,9 @@ ).to.be.ok }) - it('Provide original image width+height in callback data', function(done) { + it('Provide original image width+height in callback data', function (done) { expect( - loadImage(imageUrlGIF, function(img, data) { + loadImage(imageUrlGIF, function (img, data) { expect(data.originalWidth).to.equal(80) expect(data.originalHeight).to.equal(60) done() @@ -145,13 +145,13 @@ }) }) - describe('Scaling', function() { - describe('max/min', function() { - it('Scale to maxWidth', function(done) { + describe('Scaling', function () { + describe('max/min', function () { + it('Scale to maxWidth', function (done) { expect( loadImage( blobGIF, - function(img, data) { + function (img, data) { expect(img.width).to.equal(40) expect(img.height).to.equal(30) expect(data.originalWidth).to.equal(80) @@ -163,11 +163,11 @@ ).to.be.ok }) - it('Scale to maxHeight', function(done) { + it('Scale to maxHeight', function (done) { expect( loadImage( blobGIF, - function(img, data) { + function (img, data) { expect(img.width).to.equal(20) expect(img.height).to.equal(15) expect(data.originalWidth).to.equal(80) @@ -179,11 +179,11 @@ ).to.be.ok }) - it('Scale to minWidth', function(done) { + it('Scale to minWidth', function (done) { expect( loadImage( blobGIF, - function(img, data) { + function (img, data) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) expect(data.originalWidth).to.equal(80) @@ -195,11 +195,11 @@ ).to.be.ok }) - it('Scale to minHeight', function(done) { + it('Scale to minHeight', function (done) { expect( loadImage( blobGIF, - function(img, data) { + function (img, data) { expect(img.width).to.equal(320) expect(img.height).to.equal(240) expect(data.originalWidth).to.equal(80) @@ -211,11 +211,11 @@ ).to.be.ok }) - it('Scale to minWidth but respect maxWidth', function(done) { + it('Scale to minWidth but respect maxWidth', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) done() @@ -225,11 +225,11 @@ ).to.be.ok }) - it('Scale to minHeight but respect maxHeight', function(done) { + it('Scale to minHeight but respect maxHeight', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) done() @@ -239,11 +239,11 @@ ).to.be.ok }) - it('Scale to minWidth but respect maxHeight', function(done) { + it('Scale to minWidth but respect maxHeight', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) done() @@ -253,11 +253,11 @@ ).to.be.ok }) - it('Scale to minHeight but respect maxWidth', function(done) { + it('Scale to minHeight but respect maxWidth', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) done() @@ -267,11 +267,11 @@ ).to.be.ok }) - it('Scale up with the given pixelRatio', function(done) { + it('Scale up with the given pixelRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(320) expect(img.height).to.equal(240) expect(img.style.width).to.equal('160px') @@ -283,11 +283,11 @@ ).to.be.ok }) - it('Scale down with the given pixelRatio', function(done) { + it('Scale down with the given pixelRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) expect(img.style.width).to.equal('40px') @@ -299,11 +299,11 @@ ).to.be.ok }) - it('Scale down with the given downsamplingRatio', function(done) { + it('Scale down with the given downsamplingRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(20) expect(img.height).to.equal(15) done() @@ -313,11 +313,11 @@ ).to.be.ok }) - it('Ignore max settings for smaller image dimensions', function(done) { + it('Ignore max settings for smaller image dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -327,11 +327,11 @@ ).to.be.ok }) - it('Ignore min settings for larger image dimensions', function(done) { + it('Ignore min settings for larger image dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -342,12 +342,12 @@ }) }) - describe('contain', function() { - it('Scale up to contain image in max dimensions', function(done) { + describe('contain', function () { + it('Scale up to contain image in max dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) done() @@ -357,11 +357,11 @@ ).to.be.ok }) - it('Scale down to contain image in max dimensions', function(done) { + it('Scale down to contain image in max dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(30) done() @@ -372,12 +372,12 @@ }) }) - describe('cover', function() { - it('Scale up to cover max dimensions with image dimensions', function(done) { + describe('cover', function () { + it('Scale up to cover max dimensions with image dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(160) expect(img.height).to.equal(120) done() @@ -387,11 +387,11 @@ ).to.be.ok }) - it('Scale down to cover max dimensions with image dimensions', function(done) { + it('Scale down to cover max dimensions with image dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(30) done() @@ -403,12 +403,12 @@ }) }) - describe('Cropping', function() { - it('Crop to same values for maxWidth and maxHeight', function(done) { + describe('Cropping', function () { + it('Crop to same values for maxWidth and maxHeight', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(40) done() @@ -418,11 +418,11 @@ ).to.be.ok }) - it('Crop to different values for maxWidth and maxHeight', function(done) { + it('Crop to different values for maxWidth and maxHeight', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(60) done() @@ -432,11 +432,11 @@ ).to.be.ok }) - it('Crop using the given sourceWidth/sourceHeight dimensions', function(done) { + it('Crop using the given sourceWidth/sourceHeight dimensions', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(40) done() @@ -446,11 +446,11 @@ ).to.be.ok }) - it('Crop using the given left/top coordinates', function(done) { + it('Crop using the given left/top coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(20) done() @@ -460,11 +460,11 @@ ).to.be.ok }) - it('Crop using the given right/bottom coordinates', function(done) { + it('Crop using the given right/bottom coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(20) done() @@ -474,11 +474,11 @@ ).to.be.ok }) - it('Crop using the given 2:1 aspectRatio', function(done) { + it('Crop using the given 2:1 aspectRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(40) done() @@ -488,11 +488,11 @@ ).to.be.ok }) - it('Crop using the given 2:3 aspectRatio', function(done) { + it('Crop using the given 2:3 aspectRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(40) expect(img.height).to.equal(60) done() @@ -502,11 +502,11 @@ ).to.be.ok }) - it('Crop using maxWidth/maxHeight and pixelRatio', function(done) { + it('Crop using maxWidth/maxHeight and pixelRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(80) expect(img.style.width).to.equal('40px') @@ -518,11 +518,11 @@ ).to.be.ok }) - it('Crop using sourceWidth/sourceHeight and pixelRatio', function(done) { + it('Crop using sourceWidth/sourceHeight and pixelRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(80) expect(img.style.width).to.equal('40px') @@ -534,11 +534,11 @@ ).to.be.ok }) - it('Crop using maxWidth/maxHeight and downsamplingRatio', function(done) { + it('Crop using maxWidth/maxHeight and downsamplingRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(10) expect(img.height).to.equal(10) @@ -558,12 +558,12 @@ }) }) - describe('Orientation', function() { - it('Should keep the orientation', function(done) { + describe('Orientation', function () { + it('Should keep the orientation', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -573,11 +573,11 @@ ).to.be.ok }) - it('Should rotate left', function(done) { + it('Should rotate left', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(60) expect(img.height).to.equal(80) done() @@ -587,11 +587,11 @@ ).to.be.ok }) - it('Should rotate right', function(done) { + it('Should rotate right', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(60) expect(img.height).to.equal(80) done() @@ -601,11 +601,11 @@ ).to.be.ok }) - it('Should adjust constraints to new coordinates', function(done) { + it('Should adjust constraints to new coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(30) expect(img.height).to.equal(40) done() @@ -615,11 +615,11 @@ ).to.be.ok }) - it('Should adjust left and top to new coordinates', function(done) { + it('Should adjust left and top to new coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(30) expect(img.height).to.equal(60) done() @@ -629,11 +629,11 @@ ).to.be.ok }) - it('Should adjust right and bottom to new coordinates', function(done) { + it('Should adjust right and bottom to new coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(30) expect(img.height).to.equal(60) done() @@ -643,11 +643,11 @@ ).to.be.ok }) - it('Should adjust left and bottom to new coordinates', function(done) { + it('Should adjust left and bottom to new coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(30) expect(img.height).to.equal(60) done() @@ -657,11 +657,11 @@ ).to.be.ok }) - it('Should adjust right and top to new coordinates', function(done) { + it('Should adjust right and top to new coordinates', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(30) expect(img.height).to.equal(60) done() @@ -671,11 +671,11 @@ ).to.be.ok }) - it('Should rotate left with the given pixelRatio', function(done) { + it('Should rotate left with the given pixelRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(120) expect(img.height).to.equal(160) expect(img.style.width).to.equal('60px') @@ -687,11 +687,11 @@ ).to.be.ok }) - it('Should rotate right with the given pixelRatio', function(done) { + it('Should rotate right with the given pixelRatio', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(120) expect(img.height).to.equal(160) expect(img.style.width).to.equal('60px') @@ -703,11 +703,11 @@ ).to.be.ok }) - it('Should ignore too small orientation value', function(done) { + it('Should ignore too small orientation value', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -717,11 +717,11 @@ ).to.be.ok }) - it('Should ignore too large orientation value', function(done) { + it('Should ignore too large orientation value', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.width).to.equal(80) expect(img.height).to.equal(60) done() @@ -731,11 +731,11 @@ ).to.be.ok }) - it('Should rotate right based on the exif orientation value', function(done) { + it('Should rotate right based on the exif orientation value', function (done) { expect( loadImage( blobJPEG, - function(img, data) { + function (img, data) { expect(data).to.be.ok expect(data.exif).to.be.ok expect(data.exif.get('Orientation')).to.equal(6) @@ -748,11 +748,11 @@ ).to.be.ok }) - it('Should scale image after exif based orientation', function(done) { + it('Should scale image after exif based orientation', function (done) { expect( loadImage( blobJPEG, - function(img) { + function (img) { expect(img.width).to.equal(10) expect(img.height).to.equal(20) done() @@ -763,10 +763,10 @@ }) }) - describe('Canvas', function() { - it('Return img element to callback if canvas is not true', function(done) { + describe('Canvas', function () { + it('Return img element to callback if canvas is not true', function (done) { expect( - loadImage(blobGIF, function(img) { + loadImage(blobGIF, function (img) { expect(img.getContext).to.be.undefined expect(img.nodeName.toLowerCase()).to.equal('img') done() @@ -774,11 +774,11 @@ ).to.be.ok }) - it('Return canvas element to callback if canvas is true', function(done) { + it('Return canvas element to callback if canvas is true', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.getContext).to.be.ok expect(img.nodeName.toLowerCase()).to.equal('canvas') done() @@ -788,11 +788,11 @@ ).to.be.ok }) - it('Return scaled canvas element to callback', function(done) { + it('Return scaled canvas element to callback', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { expect(img.getContext).to.be.ok expect(img.nodeName.toLowerCase()).to.equal('canvas') expect(img.width).to.equal(40) @@ -804,11 +804,11 @@ ).to.be.ok }) - it('Accept a canvas element as parameter for loadImage.scale', function(done) { + it('Accept a canvas element as parameter for loadImage.scale', function (done) { expect( loadImage( blobGIF, - function(img) { + function (img) { // eslint-disable-next-line no-param-reassign img = loadImage.scale(img, { maxWidth: 40 @@ -825,29 +825,29 @@ }) }) - describe('Metadata', function() { - it('Should parse Exif tags', function(done) { - loadImage.parseMetaData(blobJPEG, function(data) { + describe('Metadata', function () { + it('Should parse Exif tags', function (done) { + loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exif).to.be.ok expect(data.exif.get('Orientation')).to.equal(6) done() }) }) - it('Should parse IPTC tags', function(done) { - loadImage.parseMetaData(blobJPEG, function(data) { + it('Should parse IPTC tags', function (done) { + loadImage.parseMetaData(blobJPEG, function (data) { expect(data.iptc).to.be.ok expect(data.iptc.get('ObjectName')).to.equal('objectname') done() }) }) - it('Should parse the complete image head', function(done) { - loadImage.parseMetaData(blobJPEG, function(data) { + it('Should parse the complete image head', function (done) { + loadImage.parseMetaData(blobJPEG, function (data) { expect(data.imageHead).to.be.ok loadImage.parseMetaData( createBlob(data.imageHead, 'image/jpeg'), - function(data) { + function (data) { expect(data.exif).to.be.ok expect(data.exif.get('Orientation')).to.equal(6) done() @@ -856,11 +856,11 @@ }) }) - it('Should parse meta data automatically', function(done) { + it('Should parse meta data automatically', function (done) { expect( loadImage( blobJPEG, - function(img, data) { + function (img, data) { expect(data).to.be.ok expect(data.imageHead).to.be.ok expect(data.exif).to.be.ok @@ -879,12 +879,12 @@ ('fetch' in window && 'Request' in window) || ('XMLHttpRequest' in window && 'ProgressEvent' in window) ) { - describe('Fetch', function() { - it('Should fetch image URL as blob if meta option is true', function(done) { + describe('Fetch', function () { + it('Should fetch image URL as blob if meta option is true', function (done) { expect( loadImage( imageUrlJPEG, - function(img, data) { + function (img, data) { expect(data).to.be.ok expect(data.imageHead).to.be.ok expect(data.exif).to.be.ok @@ -898,9 +898,9 @@ ).to.be.ok }) - it('Should load image URL as img if meta option is false', function(done) { + it('Should load image URL as img if meta option is false', function (done) { expect( - loadImage(imageUrlJPEG, function(img, data) { + loadImage(imageUrlJPEG, function (img, data) { expect(data.imageHead).to.be.undefined expect(data.exif).to.be.undefined expect(img.width).to.equal(2) From e197a77cddf399b5788342437400212b1dd0806d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 26 Mar 2020 20:41:54 +0900 Subject: [PATCH 003/188] Update vendor dependencies. --- js/vendor/jquery.Jcrop.js | 8 +- js/vendor/jquery.js | 596 ++++++++++++++++------- test/vendor/canvas-to-blob.js | 18 +- test/vendor/mocha.css | 1 - test/vendor/mocha.js | 862 +++++++++++++++++++--------------- 5 files changed, 910 insertions(+), 575 deletions(-) diff --git a/js/vendor/jquery.Jcrop.js b/js/vendor/jquery.Jcrop.js index 3e32f04..41f80e6 100755 --- a/js/vendor/jquery.Jcrop.js +++ b/js/vendor/jquery.Jcrop.js @@ -1,9 +1,9 @@ /** - * jquery.Jcrop.js v0.9.12 + * jquery.Jcrop.js v0.9.15 * jQuery Image Cropping Plugin - released under MIT License * Author: Kelly Hallman * http://github.com/tapmodo/Jcrop - * Copyright (c) 2008-2013 Tapmodo Interactive LLC {{{ + * Copyright (c) 2008-2018 Tapmodo Interactive LLC {{{ * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -228,10 +228,10 @@ function newSelection(e) //{{{ { if (options.disabled) { - return false; + return; } if (!options.allowSelect) { - return false; + return; } btndown = true; docOffset = getPos($img); diff --git a/js/vendor/jquery.js b/js/vendor/jquery.js index 9b5206b..773ad95 100644 --- a/js/vendor/jquery.js +++ b/js/vendor/jquery.js @@ -1,5 +1,5 @@ /*! - * jQuery JavaScript Library v3.3.1 + * jQuery JavaScript Library v3.4.1 * https://jquery.com/ * * Includes Sizzle.js @@ -9,7 +9,7 @@ * Released under the MIT license * https://jquery.org/license * - * Date: 2018-01-20T17:24Z + * Date: 2019-05-01T21:04Z */ ( function( global, factory ) { @@ -91,20 +91,33 @@ var isWindow = function isWindow( obj ) { var preservedScriptAttributes = { type: true, src: true, + nonce: true, noModule: true }; - function DOMEval( code, doc, node ) { + function DOMEval( code, node, doc ) { doc = doc || document; - var i, + var i, val, script = doc.createElement( "script" ); script.text = code; if ( node ) { for ( i in preservedScriptAttributes ) { - if ( node[ i ] ) { - script[ i ] = node[ i ]; + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); } } } @@ -129,7 +142,7 @@ function toType( obj ) { var - version = "3.3.1", + version = "3.4.1", // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -258,25 +271,28 @@ jQuery.extend = jQuery.fn.extend = function() { // Extend the base object for ( name in options ) { - src = target[ name ]; copy = options[ name ]; + // Prevent Object.prototype pollution // Prevent never-ending loop - if ( target === copy ) { + if ( name === "__proto__" || target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; - if ( copyIsArray ) { - copyIsArray = false; - clone = src && Array.isArray( src ) ? src : []; - + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; } else { - clone = src && jQuery.isPlainObject( src ) ? src : {}; + clone = src; } + copyIsArray = false; // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); @@ -329,9 +345,6 @@ jQuery.extend( { }, isEmptyObject: function( obj ) { - - /* eslint-disable no-unused-vars */ - // See https://github.com/eslint/eslint/issues/6125 var name; for ( name in obj ) { @@ -341,8 +354,8 @@ jQuery.extend( { }, // Evaluates a script in a global context - globalEval: function( code ) { - DOMEval( code ); + globalEval: function( code, options ) { + DOMEval( code, { nonce: options && options.nonce } ); }, each: function( obj, callback ) { @@ -498,14 +511,14 @@ function isArrayLike( obj ) { } var Sizzle = /*! - * Sizzle CSS Selector Engine v2.3.3 + * Sizzle CSS Selector Engine v2.3.4 * https://sizzlejs.com/ * - * Copyright jQuery Foundation and other contributors + * Copyright JS Foundation and other contributors * Released under the MIT license - * http://jquery.org/license + * https://js.foundation/ * - * Date: 2016-08-08 + * Date: 2019-04-08 */ (function( window ) { @@ -539,6 +552,7 @@ var i, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), + nonnativeSelectorCache = createCache(), sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; @@ -600,8 +614,7 @@ var i, rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + rdescend = new RegExp( whitespace + "|>" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), @@ -622,6 +635,7 @@ var i, whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, + rhtml = /HTML$/i, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, @@ -676,9 +690,9 @@ var i, setDocument(); }, - disabledAncestor = addCombinator( + inDisabledFieldset = addCombinator( function( elem ) { - return elem.disabled === true && ("form" in elem || "label" in elem); + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; }, { dir: "parentNode", next: "legend" } ); @@ -791,18 +805,22 @@ function Sizzle( selector, context, results, seed ) { // Take advantage of querySelectorAll if ( support.qsa && - !compilerCache[ selector + " " ] && - (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - - if ( nodeType !== 1 ) { - newContext = context; - newSelector = selector; + !nonnativeSelectorCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) && - // qSA looks outside Element context, which is not what we want - // Thanks to Andrew Dupont for this workaround technique - // Support: IE <=8 + // Support: IE 8 only // Exclude object elements - } else if ( context.nodeName.toLowerCase() !== "object" ) { + (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && rdescend.test( selector ) ) { // Capture the context ID, setting it first if necessary if ( (nid = context.getAttribute( "id" )) ) { @@ -824,17 +842,16 @@ function Sizzle( selector, context, results, seed ) { context; } - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); } } } @@ -998,7 +1015,7 @@ function createDisabledPseudo( disabled ) { // Where there is no isDisabled, check manually /* jshint -W018 */ elem.isDisabled !== !disabled && - disabledAncestor( elem ) === disabled; + inDisabledFieldset( elem ) === disabled; } return elem.disabled === disabled; @@ -1055,10 +1072,13 @@ support = Sizzle.support = {}; * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; + var namespace = elem.namespaceURI, + docElem = (elem.ownerDocument || elem).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); }; /** @@ -1480,11 +1500,8 @@ Sizzle.matchesSelector = function( elem, expr ) { setDocument( elem ); } - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - if ( support.matchesSelector && documentIsHTML && - !compilerCache[ expr + " " ] && + !nonnativeSelectorCache[ expr + " " ] && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { @@ -1498,7 +1515,9 @@ Sizzle.matchesSelector = function( elem, expr ) { elem.document && elem.document.nodeType !== 11 ) { return ret; } - } catch (e) {} + } catch (e) { + nonnativeSelectorCache( expr, true ); + } } return Sizzle( expr, document, null, [ elem ] ).length > 0; @@ -1957,7 +1976,7 @@ Expr = Sizzle.selectors = { "contains": markFunction(function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; }; }), @@ -2096,7 +2115,11 @@ Expr = Sizzle.selectors = { }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } @@ -3146,18 +3169,18 @@ jQuery.each( { return siblings( elem.firstChild ); }, contents: function( elem ) { - if ( nodeName( elem, "iframe" ) ) { - return elem.contentDocument; - } + if ( typeof elem.contentDocument !== "undefined" ) { + return elem.contentDocument; + } - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } - return jQuery.merge( [], elem.childNodes ); + return jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { @@ -4466,6 +4489,26 @@ var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } var isHiddenWithinTree = function( elem, el ) { // isHiddenWithinTree might be called from jQuery#filter function; @@ -4480,7 +4523,7 @@ var isHiddenWithinTree = function( elem, el ) { // Support: Firefox <=43 - 45 // Disconnected elements can have computed display: none, so first confirm that elem is // in the document. - jQuery.contains( elem.ownerDocument, elem ) && + isAttached( elem ) && jQuery.css( elem, "display" ) === "none"; }; @@ -4522,7 +4565,8 @@ function adjustCSS( elem, prop, valueParts, tween ) { unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), // Starting value computation is required for potential unit mismatches - initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && rcssNum.exec( jQuery.css( elem, prop ) ); if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { @@ -4669,7 +4713,7 @@ jQuery.fn.extend( { } ); var rcheckableType = ( /^(?:checkbox|radio)$/i ); -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); @@ -4741,7 +4785,7 @@ function setGlobalEval( elems, refElements ) { var rhtml = /<|&#?\w+;/; function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, contains, j, + var elem, tmp, tag, wrap, attached, j, fragment = context.createDocumentFragment(), nodes = [], i = 0, @@ -4805,13 +4849,13 @@ function buildFragment( elems, context, scripts, selection, ignored ) { continue; } - contains = jQuery.contains( elem.ownerDocument, elem ); + attached = isAttached( elem ); // Append to fragment tmp = getAll( fragment.appendChild( elem ), "script" ); // Preserve script evaluation history - if ( contains ) { + if ( attached ) { setGlobalEval( tmp ); } @@ -4854,8 +4898,6 @@ function buildFragment( elems, context, scripts, selection, ignored ) { div.innerHTML = ""; support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; } )(); -var documentElement = document.documentElement; - var @@ -4871,8 +4913,19 @@ function returnFalse() { return false; } +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + // Support: IE <=9 only -// See #13393 for more info +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 function safeActiveElement() { try { return document.activeElement; @@ -5172,9 +5225,10 @@ jQuery.event = { while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { - // Triggered event must either 1) have no namespace, or 2) have namespace(s) - // a subset or equal to those in the bound event (both can have no namespace). - if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; @@ -5298,39 +5352,51 @@ jQuery.event = { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, - focus: { + click: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); } + + // Return false to allow normal processing in the caller + return false; }, - delegateType: "focusout" - }, - click: { + trigger: function( data ) { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { - this.click(); - return false; + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); } + + // Return non-false to allow normal event-path propagation + return true; }, - // For cross-browser consistency, don't fire native .click() on links + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack _default: function( event ) { - return nodeName( event.target, "a" ); + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); } }, @@ -5347,6 +5413,93 @@ jQuery.event = { } }; +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + jQuery.removeEvent = function( elem, type, handle ) { // This "if" is needed for plain objects @@ -5459,6 +5612,7 @@ jQuery.each( { shiftKey: true, view: true, "char": true, + code: true, charCode: true, key: true, keyCode: true, @@ -5505,6 +5659,33 @@ jQuery.each( { } }, jQuery.event.addProp ); +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + // Create mouseenter/leave events using mouseover/out and event-time checks // so that event delegation works in jQuery. // Do the same for pointerenter/pointerleave and pointerover/pointerout @@ -5755,11 +5936,13 @@ function domManip( collection, args, callback, ignored ) { if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + } ); } } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); } } } @@ -5781,7 +5964,7 @@ function remove( elem, selector, keepData ) { } if ( node.parentNode ) { - if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + if ( keepData && isAttached( node ) ) { setGlobalEval( getAll( node, "script" ) ); } node.parentNode.removeChild( node ); @@ -5799,7 +5982,7 @@ jQuery.extend( { clone: function( elem, dataAndEvents, deepDataAndEvents ) { var i, l, srcElements, destElements, clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); + inPage = isAttached( elem ); // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && @@ -6095,8 +6278,10 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); // Support: IE 9 only // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) div.style.position = "absolute"; - scrollboxSizeVal = div.offsetWidth === 36 || "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; documentElement.removeChild( container ); @@ -6167,7 +6352,7 @@ function curCSS( elem, name, computed ) { if ( computed ) { ret = computed.getPropertyValue( name ) || computed[ name ]; - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + if ( ret === "" && !isAttached( elem ) ) { ret = jQuery.style( elem, name ); } @@ -6223,30 +6408,13 @@ function addGetHookIf( conditionFn, hookFn ) { } -var +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rcustomProp = /^--/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }, - - cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style; - -// Return a css property mapped to a potentially vendor prefixed property +// Return a vendor-prefixed property or undefined function vendorPropName( name ) { - // Shortcut for names that are not vendor prefixed - if ( name in emptyStyle ) { - return name; - } - // Check for vendor prefixed names var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), i = cssPrefixes.length; @@ -6259,16 +6427,33 @@ function vendorPropName( name ) { } } -// Return a property mapped along what jQuery.cssProps suggests or to -// a vendor prefixed property. +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property function finalPropName( name ) { - var ret = jQuery.cssProps[ name ]; - if ( !ret ) { - ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; } - return ret; + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; } + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + function setPositiveNumber( elem, value, subtract ) { // Any relative (+/-) values have already been @@ -6340,7 +6525,10 @@ function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computed delta - extra - 0.5 - ) ); + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; } return delta; @@ -6350,9 +6538,16 @@ function getWidthOrHeight( elem, dimension, extra ) { // Start with computed style var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + val = curCSS( elem, dimension, styles ), - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox; + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); // Support: Firefox <=54 // Return a confounding non-pixel value or feign ignorance, as appropriate. @@ -6363,22 +6558,29 @@ function getWidthOrHeight( elem, dimension, extra ) { val = "auto"; } - // Check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = valueIsBorderBox && - ( support.boxSizingReliable() || val === elem.style[ dimension ] ); // Fall back to offsetWidth/offsetHeight when value is "auto" // This happens for inline elements with no explicit setting (gh-3571) // Support: Android <=4.1 - 4.3 only // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - if ( val === "auto" || - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) { + // Support: IE 9-11 only + // Also use offsetWidth/offsetHeight for when box sizing is unreliable + // We use getClientRects() to check for hidden/disconnected. + // In those cases, the computed value can be trusted to be border-box + if ( ( !support.boxSizingReliable() && isBorderBox || + val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + elem.getClientRects().length ) { - val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ]; + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - // offsetWidth/offsetHeight provide border-box values - valueIsBorderBox = true; + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } } // Normalize "" and auto @@ -6424,6 +6626,13 @@ jQuery.extend( { "flexGrow": true, "flexShrink": true, "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, "lineHeight": true, "opacity": true, "order": true, @@ -6479,7 +6688,9 @@ jQuery.extend( { } // If a number was passed in, add the unit (except for certain CSS properties) - if ( type === "number" ) { + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); } @@ -6579,18 +6790,29 @@ jQuery.each( [ "height", "width" ], function( i, dimension ) { set: function( elem, value, extra ) { var matches, styles = getStyles( elem ), - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra && boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ); + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; // Account for unreliable border-box dimensions by comparing offset* to computed and // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && support.scrollboxSize() === styles.position ) { + if ( isBorderBox && scrollboxSizeBuggy ) { subtract -= Math.ceil( elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - parseFloat( styles[ dimension ] ) - @@ -6758,9 +6980,9 @@ Tween.propHooks = { // Use .style if available and use plain properties where available. if ( jQuery.fx.step[ tween.prop ] ) { jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && - ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || - jQuery.cssHooks[ tween.prop ] ) ) { + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); } else { tween.elem[ tween.prop ] = tween.now; @@ -8467,6 +8689,10 @@ jQuery.param = function( a, traditional ) { encodeURIComponent( value == null ? "" : value ); }; + if ( a == null ) { + return ""; + } + // If an array was passed in, assume that it is an array of form elements. if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { @@ -8969,12 +9195,14 @@ jQuery.extend( { if ( !responseHeaders ) { responseHeaders = {}; while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); } } - match = responseHeaders[ key.toLowerCase() ]; + match = responseHeaders[ key.toLowerCase() + " " ]; } - return match == null ? null : match; + return match == null ? null : match.join( ", " ); }, // Raw string @@ -9363,7 +9591,7 @@ jQuery.each( [ "get", "post" ], function( i, method ) { } ); -jQuery._evalUrl = function( url ) { +jQuery._evalUrl = function( url, options ) { return jQuery.ajax( { url: url, @@ -9373,7 +9601,16 @@ jQuery._evalUrl = function( url ) { cache: true, async: false, global: false, - "throws": true + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options ); + } } ); }; @@ -9656,24 +9893,21 @@ jQuery.ajaxPrefilter( "script", function( s ) { // Bind script tag hack transport jQuery.ajaxTransport( "script", function( s ) { - // This transport only deals with cross domain requests - if ( s.crossDomain ) { + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { var script, callback; return { send: function( _, complete ) { - script = jQuery( " diff --git a/js/demo/demo.js b/js/demo/demo.js index ffc664c..cd9b62d 100644 --- a/js/demo/demo.js +++ b/js/demo/demo.js @@ -19,7 +19,6 @@ $(function () { var iptcNode = $('#iptc') var thumbNode = $('#thumbnail') var actionsNode = $('#actions') - var currentFile var coordinates var jcropAPI @@ -94,31 +93,33 @@ $(function () { * @param {object} [data] Meta data object */ function updateResults(img, data) { - var fileName = currentFile.name - var href = currentFile.url || img.src - var dataURLStart - var content if (!(img.src || img instanceof HTMLCanvasElement)) { - content = $('Loading image file failed') - } else { - if (!href) { - href = img.toDataURL(currentFile.type) - // Check if file type is supported for the dataURL export: - dataURLStart = 'data:' + currentFile.type - if (href.slice(0, dataURLStart.length) !== dataURLStart) { - fileName = fileName.replace(/\.\w+$/, '.png') - } - } - content = $('') - .append(img) - .attr('download', fileName) - .attr('href', href) + result.children().replaceWith($('Loading image file failed')) + return } + var content = $('').append(img) result.children().replaceWith(content) - if (img.getContext) { - actionsNode.show() + if (data) { + if (img.getContext) { + actionsNode.show() + } + displayMetaData(data) + result.data(data) + } else { + // eslint-disable-next-line no-param-reassign + data = result.data() + } + if (data.imageHead && data.exif) { + // Reset Exif Orientation data: + loadImage.writeExifData(data.imageHead, data, 'Orientation', 1) + img.toBlob(function (blob) { + loadImage.replaceHead(blob, data.imageHead, function (newBlob) { + content + .attr('href', loadImage.createObjectURL(newBlob)) + .attr('download', 'image.jpg') + }) + }, 'image/jpeg') } - displayMetaData(data) } /** @@ -164,7 +165,6 @@ $(function () { if (!file) { return } - currentFile = file displayImage(file) } @@ -173,12 +173,7 @@ $(function () { */ function urlChangeHandler() { var url = $(this).val() - if (!url || (currentFile && currentFile.url === url)) return - currentFile = { - name: url.replace(/^.*\//g, ''), - url: url - } - displayImage(url) + if (url) displayImage(url) } // Hide URL/FileReader API requirement message in capable browsers: diff --git a/js/load-image-exif.js b/js/load-image-exif.js index 87e4fe1..8f17d8d 100644 --- a/js/load-image-exif.js +++ b/js/load-image-exif.js @@ -9,7 +9,7 @@ * https://opensource.org/licenses/MIT */ -/* global define, module, require */ +/* global define, module, require, DataView */ /* eslint-disable no-console */ @@ -322,6 +322,19 @@ // Registers the Exif parser for the APP1 JPEG meta data segment: loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseExifData) + loadImage.exifWriters = { + // Orientation writer: + 0x0112: function (buffer, data, value) { + var view = new DataView(buffer, data.exifOffsets[0x0112] + 8, 2) + view.setUint16(0, value, data.exifLittleEndian) + return buffer + } + } + + loadImage.writeExifData = function (buffer, data, id, value) { + loadImage.exifWriters[data.exif.map[id]](buffer, data, value) + } + // Adds the following properties to the parseMetaData callback data: // * exif: The parsed Exif tags // * exifOffsets: The parsed Exif tag offsets diff --git a/test/vendor/canvas-to-blob.js b/js/vendor/canvas-to-blob.js similarity index 100% rename from test/vendor/canvas-to-blob.js rename to js/vendor/canvas-to-blob.js diff --git a/test/index.html b/test/index.html index 576c47e..bdf2717 100644 --- a/test/index.html +++ b/test/index.html @@ -26,10 +26,9 @@ - + diff --git a/test/test.js b/test/test.js index d6d6689..e33843c 100644 --- a/test/test.js +++ b/test/test.js @@ -106,6 +106,16 @@ ).to.be.ok }) + it('Provide original image width+height in callback data', function (done) { + expect( + loadImage(imageUrlGIF, function (img, data) { + expect(data.originalWidth).to.equal(60) + expect(data.originalHeight).to.equal(40) + done() + }) + ).to.be.ok + }) + it('Keep object URL if noRevoke is true', function (done) { expect( loadImage( @@ -133,16 +143,6 @@ }) ).to.be.ok }) - - it('Provide original image width+height in callback data', function (done) { - expect( - loadImage(imageUrlGIF, function (img, data) { - expect(data.originalWidth).to.equal(60) - expect(data.originalHeight).to.equal(40) - done() - }) - ).to.be.ok - }) }) describe('Scaling', function () { @@ -989,6 +989,16 @@ }) }) + it('Should parse Exif tag offsets', function (done) { + loadImage.parseMetaData(blobJPEG, function (data) { + expect(data.exifOffsets).to.be.ok + expect(data.exifOffsets.get('Orientation')).to.equal(0x16) + expect(data.exifTiffOffset).to.equal(0xc) + expect(data.exifLittleEndian).to.equal(false) + done() + }) + }) + it('Should parse IPTC tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.iptc).to.be.ok @@ -1028,6 +1038,40 @@ ) ).to.be.ok }) + + it('Should write Exif Orientation tag and replace image head', function (done) { + loadImage( + blobJPEG, + function (img, data) { + expect(data.imageHead).to.be.ok + expect(data.exif).to.be.ok + expect(data.exif.get('Orientation')).to.equal(6) + expect(data.iptc).to.be.ok + expect(data.iptc.get('ObjectName')).to.equal('blueimp.net') + // Reset Exif Orientation data: + loadImage.writeExifData(data.imageHead, data, 'Orientation', 1) + img.toBlob(function (blob) { + loadImage.replaceHead(blob, data.imageHead, function (newBlob) { + loadImage( + newBlob, + function (img, data) { + expect(img.width).to.equal(40) + expect(img.height).to.equal(60) + expect(data.imageHead).to.be.ok + expect(data.exif).to.be.ok + expect(data.exif.get('Orientation')).to.equal(1) + expect(data.iptc).to.be.ok + expect(data.iptc.get('ObjectName')).to.equal('blueimp.net') + done() + }, + { meta: true } + ) + }) + }, 'image/jpeg') + }, + { meta: true, orientation: true, canvas: true, minWidth: 40 } + ) + }) }) describe('Fetch', function () { @@ -1041,7 +1085,9 @@ it('Should fetch image URL as blob if meta option is true', function (done) { expect( loadImage( - imageUrlJPEG, + // IE11 does not allow XMLHttpRequest access to data URLs, + // so we use an ObjectURL instead of imageUrlJPEG directly: + loadImage.createObjectURL(blobJPEG), function (img, data) { expect(data).to.be.ok expect(data.imageHead).to.be.ok From ef42de0cdc529615aff6c501da39049f61bacadd Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Fri, 10 Apr 2020 07:01:52 +0900 Subject: [PATCH 023/188] 2.31.0 --- js/load-image.all.min.js | 2 +- js/load-image.all.min.js.map | 2 +- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/load-image.all.min.js b/js/load-image.all.min.js index d0ef65b..f09182f 100644 --- a/js/load-image.all.min.js +++ b/js/load-image.all.min.js @@ -1,2 +1,2 @@ -!function(a){"use strict";function r(t,i,o){var a,n=document.createElement("img");return n.onerror=function(e){return r.onerror(n,e,t,i,o)},n.onload=function(e){return r.onload(n,e,t,i,o)},"string"==typeof t?(r.fetchBlob(t,function(e){e&&r.isInstanceOf("Blob",e)?a=r.createObjectURL(t=e):(a=t,o&&o.crossOrigin&&(n.crossOrigin=o.crossOrigin)),n.src=a},o),n):r.isInstanceOf("Blob",t)||r.isInstanceOf("File",t)?(a=n._objectURL=r.createObjectURL(t))?(n.src=a,n):r.readFile(t,function(e){var t=e.target;t&&t.result?n.src=t.result:i&&i(e)}):void 0}var t=a.createObjectURL&&a||a.URL&&URL.revokeObjectURL&&URL||a.webkitURL&&webkitURL;function n(e,t){!e._objectURL||t&&t.noRevoke||(r.revokeObjectURL(e._objectURL),delete e._objectURL)}r.fetchBlob=function(e,t){t()},r.isInstanceOf=function(e,t){return Object.prototype.toString.call(t)==="[object "+e+"]"},r.transform=function(e,t,i,o,a){i(e,a)},r.onerror=function(e,t,i,o,a){n(e,a),o&&o.call(e,t)},r.onload=function(e,t,i,o,a){n(e,a),o&&r.transform(e,a,o,i,{originalWidth:e.naturalWidth||e.width,originalHeight:e.naturalHeight||e.height})},r.createObjectURL=function(e){return!!t&&t.createObjectURL(e)},r.revokeObjectURL=function(e){return!!t&&t.revokeObjectURL(e)},r.readFile=function(e,t,i){if(a.FileReader){var o=new FileReader;if(o.onload=o.onerror=t,o[i=i||"readAsDataURL"])return o[i](e),o}return!1},"function"==typeof define&&define.amd?define(function(){return r}):"object"==typeof module&&module.exports?module.exports=r:a.loadImage=r}("undefined"!=typeof window&&window||this),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image"],e):"object"==typeof module&&module.exports?e(require("./load-image")):e(window.loadImage)}(function(x){"use strict";var n=x.transform;x.transform=function(e,t,i,o,a){n.call(x,x.scale(e,t,a),t,i,o,a)},x.transformCoordinates=function(){},x.getTransformedOptions=function(e,t){var i,o,a,n,r=t.aspectRatio;if(!r)return t;for(o in i={},t)Object.prototype.hasOwnProperty.call(t,o)&&(i[o]=t[o]);return i.crop=!0,r<(a=e.naturalWidth||e.width)/(n=e.naturalHeight||e.height)?(i.maxWidth=n*r,i.maxHeight=n):(i.maxWidth=a,i.maxHeight=a/r),i},x.renderImageToCanvas=function(e,t,i,o,a,n,r,s,l,c,d){var u=e.getContext("2d");return!1===d.imageSmoothingEnabled?u.imageSmoothingEnabled=!1:d.imageSmoothingQuality&&(u.imageSmoothingQuality=d.imageSmoothingQuality),u.drawImage(t,i,o,a,n,r,s,l,c),e},x.hasCanvasOption=function(e){return e.canvas||e.crop||!!e.aspectRatio},x.scale=function(e,t,i){t=t||{};var o,a,n,r,s,l,c,d,u,f,g,p=document.createElement("canvas"),h=e.getContext||x.hasCanvasOption(t)&&p.getContext,m=e.naturalWidth||e.width,A=e.naturalHeight||e.height,b=m,y=A;function S(){var e=Math.max((n||b)/b,(r||y)/y);1r.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if(o=p.metaDataParsers.jpeg[t])for(a=0;ae.byteLength))return g.createObjectURL(new Blob([e.buffer.slice(t,t+i)]));console.log("Invalid Exif data: Invalid thumbnail data.")},g.exifTagTypes={1:{getValue:function(e,t){return e.getUint8(t)},size:1},2:{getValue:function(e,t){return String.fromCharCode(e.getUint8(t))},size:1,ascii:!0},3:{getValue:function(e,t,i){return e.getUint16(t,i)},size:2},4:{getValue:function(e,t,i){return e.getUint32(t,i)},size:4},5:{getValue:function(e,t,i){return e.getUint32(t,i)/e.getUint32(t+4,i)},size:8},9:{getValue:function(e,t,i){return e.getInt32(t,i)},size:4},10:{getValue:function(e,t,i){return e.getInt32(t,i)/e.getInt32(t+4,i)},size:8}},g.exifTagTypes[7]=g.exifTagTypes[1],g.getExifValue=function(e,t,i,o,a,n){var r,s,l,c,d,u,f=g.exifTagTypes[o];if(f){if(!((s=4<(r=f.size*a)?t+e.getUint32(i+8,n):i+8)+r>e.byteLength)){if(1===a)return f.getValue(e,s,n);for(l=[],c=0;ce.byteLength)console.log("Invalid Exif data: Invalid directory offset.");else{if(!((r=i+2+12*(n=e.getUint16(i,o)))+4>e.byteLength)){for(s=0;se.byteLength)console.log("Invalid Exif data: Invalid segment size.");else if(0===e.getUint16(t+8)){switch(e.getUint16(l)){case 18761:n=!0;break;case 19789:n=!1;break;default:return void console.log("Invalid Exif data: Invalid byte alignment marker.")}42===e.getUint16(l+2,n)?(r=e.getUint32(l+4,n),o.exif=new g.ExifMap,(r=g.parseExifTags(e,l,l+r,n,o))&&!a.disableExifThumbnail&&(s={exif:{}},r=g.parseExifTags(e,l,l+r,n,s),s.exif[513]&&(o.exif.Thumbnail=g.getExifThumbnail(e,l+s.exif[513],s.exif[514]))),o.exif[34665]&&!a.disableExifSub&&g.parseExifTags(e,l,l+o.exif[34665],n,o),o.exif[34853]&&!a.disableExifGps&&g.parseExifTags(e,l,l+o.exif[34853],n,o)):console.log("Invalid Exif data: Missing TIFF marker.")}else console.log("Invalid Exif data: Missing byte alignment offset.")}},g.metaDataParsers.jpeg[65505].push(g.parseExifData)}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-exif"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-exif")):e(window.loadImage)}(function(e){"use strict";e.ExifMap.prototype.tags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber",0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},e.ExifMap.prototype.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},e.ExifMap.prototype.getText=function(e){var t=this.get(e);switch(e){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[e][t];case"ExifVersion":case"FlashpixVersion":if(!t)return;return String.fromCharCode(t[0],t[1],t[2],t[3]);case"ComponentsConfiguration":if(!t)return;return this.stringValues[e][t[0]]+this.stringValues[e][t[1]]+this.stringValues[e][t[2]]+this.stringValues[e][t[3]];case"GPSVersionID":if(!t)return;return t[0]+"."+t[1]+"."+t[2]+"."+t[3]}return String(t)},function(e){var t,i=e.tags,o=e.map;for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(o[i[t]]=t)}(e.ExifMap.prototype),e.ExifMap.prototype.getAll=function(){var e,t,i={};for(e in this)Object.prototype.hasOwnProperty.call(this,e)&&(t=this.tags[e])&&(i[t]=this.getText(t));return i}}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-meta"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-meta")):e(window.loadImage)}(function(u){"use strict";u.IptcMap=function(){return this},u.IptcMap.prototype.map={ObjectName:5},u.IptcMap.prototype.get=function(e){return this[e]||this[this.map[e]]},u.parseIptcTags=function(e,t,i,o){function a(e,t,i){for(var o="",a=t;ar.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if((a=p.metaDataParsers.jpeg[t])&&!u.disableMetaDataParsers)for(o=0;oe.byteLength))return g.createObjectURL(new Blob([e.buffer.slice(t,t+i)]));console.log("Invalid Exif data: Invalid thumbnail data.")},g.exifTagTypes={1:{getValue:function(e,t){return e.getUint8(t)},size:1},2:{getValue:function(e,t){return String.fromCharCode(e.getUint8(t))},size:1,ascii:!0},3:{getValue:function(e,t,i){return e.getUint16(t,i)},size:2},4:{getValue:function(e,t,i){return e.getUint32(t,i)},size:4},5:{getValue:function(e,t,i){return e.getUint32(t,i)/e.getUint32(t+4,i)},size:8},9:{getValue:function(e,t,i){return e.getInt32(t,i)},size:4},10:{getValue:function(e,t,i){return e.getInt32(t,i)/e.getInt32(t+4,i)},size:8}},g.exifTagTypes[7]=g.exifTagTypes[1],g.getExifValue=function(e,t,i,a,o,n){var r,s,l,c,d,u,f=g.exifTagTypes[a];if(f){if(!((s=4<(r=f.size*o)?t+e.getUint32(i+8,n):i+8)+r>e.byteLength)){if(1===o)return f.getValue(e,s,n);for(l=[],c=0;ce.byteLength)console.log("Invalid Exif data: Invalid directory offset.");else{if(!((r=i+2+12*(n=e.getUint16(i,a)))+4>e.byteLength)){for(s=0;se.byteLength)console.log("Invalid Exif data: Invalid segment size.");else if(0===e.getUint16(t+8)){switch(e.getUint16(l)){case 18761:n=!0;break;case 19789:n=!1;break;default:return void console.log("Invalid Exif data: Invalid byte alignment marker.")}42===e.getUint16(l+2,n)?(r=e.getUint32(l+4,n),a.exif=new g.ExifMap,o.disableExifOffsets||(a.exifOffsets=new g.ExifMap,a.exifTiffOffset=l,a.exifLittleEndian=n),(r=g.parseExifTags(e,l,l+r,n,a))&&!o.disableExifThumbnail&&(s={exif:{}},r=g.parseExifTags(e,l,l+r,n,s),s.exif[513]&&(a.exif.Thumbnail=g.getExifThumbnail(e,l+s.exif[513],s.exif[514]))),a.exif[34665]&&!o.disableExifSub&&g.parseExifTags(e,l,l+a.exif[34665],n,a),a.exif[34853]&&!o.disableExifGps&&g.parseExifTags(e,l,l+a.exif[34853],n,a)):console.log("Invalid Exif data: Missing TIFF marker.")}else console.log("Invalid Exif data: Missing byte alignment offset.")}},g.metaDataParsers.jpeg[65505].push(g.parseExifData),g.exifWriters={274:function(e,t,i){return new DataView(e,t.exifOffsets[274]+8,2).setUint16(0,i,t.exifLittleEndian),e}},g.writeExifData=function(e,t,i,a){g.exifWriters[t.exif.map[i]](e,t,a)}}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-exif"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-exif")):e(window.loadImage)}(function(e){"use strict";e.ExifMap.prototype.tags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber",0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},e.ExifMap.prototype.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},e.ExifMap.prototype.getText=function(e){var t=this.get(e);switch(e){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[e][t];case"ExifVersion":case"FlashpixVersion":if(!t)return;return String.fromCharCode(t[0],t[1],t[2],t[3]);case"ComponentsConfiguration":if(!t)return;return this.stringValues[e][t[0]]+this.stringValues[e][t[1]]+this.stringValues[e][t[2]]+this.stringValues[e][t[3]];case"GPSVersionID":if(!t)return;return t[0]+"."+t[1]+"."+t[2]+"."+t[3]}return String(t)},function(e){var t,i=e.tags,a=e.map;for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(a[i[t]]=t)}(e.ExifMap.prototype),e.ExifMap.prototype.getAll=function(){var e,t,i={};for(e in this)Object.prototype.hasOwnProperty.call(this,e)&&(t=this.tags[e])&&(i[t]=this.getText(t));return i}}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-meta"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-meta")):e(window.loadImage)}(function(u){"use strict";u.IptcMap=function(){return this},u.IptcMap.prototype.map={ObjectName:5},u.IptcMap.prototype.get=function(e){return this[e]||this[this.map[e]]},u.parseIptcTags=function(e,t,i,a){function o(e,t,i){for(var a="",o=t;o Date: Fri, 10 Apr 2020 20:02:59 +0900 Subject: [PATCH 024/188] Store Exif Thumbnail as Blob instead of ObjectURL. --- js/load-image-exif.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/js/load-image-exif.js b/js/load-image-exif.js index 8f17d8d..74c2632 100644 --- a/js/load-image-exif.js +++ b/js/load-image-exif.js @@ -32,7 +32,8 @@ } loadImage.ExifMap.prototype.map = { - Orientation: 0x0112 + Orientation: 0x0112, + Thumbnail: 0x0201 } loadImage.ExifMap.prototype.get = function (id) { @@ -44,9 +45,7 @@ console.log('Invalid Exif data: Invalid thumbnail data.') return } - return loadImage.createObjectURL( - new Blob([dataView.buffer.slice(offset, offset + length)]) - ) + return new Blob([dataView.buffer.slice(offset, offset + length)]) } loadImage.exifTagTypes = { @@ -229,7 +228,6 @@ var tiffOffset = offset + 10 var littleEndian var dirOffset - var thumbnailData // Check for the ASCII code for "Exif" (0x45786966): if (dataView.getUint32(offset + 4) !== 0x45786966) { // No Exif data, might be XMP data instead @@ -280,20 +278,19 @@ data ) if (dirOffset && !options.disableExifThumbnail) { - thumbnailData = { exif: {} } dirOffset = loadImage.parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, - thumbnailData + data ) // Check for JPEG Thumbnail offset: - if (thumbnailData.exif[0x0201]) { - data.exif.Thumbnail = loadImage.getExifThumbnail( + if (data.exif[0x0201]) { + data.exif[0x0201] = loadImage.getExifThumbnail( dataView, - tiffOffset + thumbnailData.exif[0x0201], - thumbnailData.exif[0x0202] // Thumbnail data length + tiffOffset + data.exif[0x0201], + data.exif[0x0202] // Thumbnail data length ) } } From cebde9bbc1c893ab90fbd3d80612199c3a731b7b Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sat, 11 Apr 2020 22:51:13 +0900 Subject: [PATCH 025/188] Refactor EXIF and IPTC parsing. Map EXIF private IFD tags into separate objects (Exif, GPSInfo, Interoperability). Store IPTC tag offsets as data.iptcOffsets. Add `getName` method to tag maps to retrieve the tag name for a given tag code. Add new options to include/exclude tags from parsing (includeExifTags, excludeExifTags, includeIptcTags, excludeIptcTags). Exclude EXIF MakerNote and IPTC ObjectPreviewData by default. Add link to thumbnail image for the demo. --- README.md | 144 +++++++++++++++--- css/demo.css | 2 + index.html | 4 +- js/demo/demo.js | 22 ++- js/load-image-exif-map.js | 298 +++++++++++++++++++++----------------- js/load-image-exif.js | 242 ++++++++++++++++++++++--------- js/load-image-iptc-map.js | 206 +++++++++++++++----------- js/load-image-iptc.js | 246 +++++++++++++++++++++---------- test/test.js | 147 +++++++++++++++++++ 9 files changed, 911 insertions(+), 400 deletions(-) diff --git a/README.md b/README.md index 9581cb8..cc067d4 100644 --- a/README.md +++ b/README.md @@ -429,8 +429,8 @@ is available. ### Exif parser If you include the Load Image Exif Parser extension, the argument passed to the -callback for `parseMetaData` will contain the additional properties if Exif data -could be found in the given image: +callback for `parseMetaData` will contain the following additional properties if +Exif data could be found in the given image: - `exif`: The parsed Exif tags - `exifOffsets`: The parsed Exif tag offsets @@ -451,14 +451,26 @@ var orientation = data.exif.get('Orientation') var orientationOffset = data.exifOffsets.get('Orientation') ``` -By default, the only available mapped names are `Orientation` and `Thumbnail`. +By default, only the following names are mapped: + +- `Orientation` +- `Thumbnail` +- `Exif` +- `GPSInfo` +- `Interoperability` + If you also include the Load Image Exif Map library, additional tag mappings -become available, as well as two additional methods, `exif.getText()` and -`exif.getAll()`: +become available, as well as three additional methods: + +- `exif.getText()` +- `exif.getName()` +- `exif.getAll()` ```js var flashText = data.exif.getText('Flash') // e.g.: 'Flash fired, auto mode', +var name = data.exif.getName(0x0112) // Orientation + // A map of all parsed tags with their mapped names/text as keys/values: var allTags = data.exif.getAll() ``` @@ -466,11 +478,55 @@ var allTags = data.exif.getAll() The Exif parser also adds additional options for the parseMetaData method, to disable certain aspects of the parser: -- `disableExif`: Disables Exif parsing. -- `disableExifThumbnail`: Disables parsing of the Exif Thumbnail. -- `disableExifSub`: Disables parsing of the Exif Sub IFD. -- `disableExifGps`: Disables parsing of the Exif GPS Info IFD. -- `disableExifOffsets`: Disables storing Exif tag offsets +- `disableExif`: Disables Exif parsing when `true`. +- `disableExifThumbnail`: Disables parsing of Thumbnail data when `true`. +- `disableExifOffsets`: Disables storing Exif tag offsets when `true`. +- `includeExifTags`: A map of Exif tags to include for parsing (includes all but + the excluded tags by default). +- `excludeExifTags`: A map of Exif tags to exclude from parsing (defaults to + exclude `Exif` `MakerNote`). + +An example parsing only Orientation, Thumbnail and ExifVersion tags: + +```js +loadImage.parseMetaData( + fileOrBlob, + function (data) { + console.log('Exif data: ', data.exif) + }, + { + includeExifTags: { + 0x0112: true, // Orientation + 0x0201: true, // JPEGInterchangeFormat (Thumbnail data offset) + 0x0202: true, // JPEGInterchangeFormatLength (Thumbnail data length) + 0x8769: { + // ExifIFDPointer + 0x9000: true // ExifVersion + } + } + } +) +``` + +An example excluding `Exif` `MakerNote` and `GPSInfo`: + +```js +loadImage.parseMetaData( + fileOrBlob, + function (data) { + console.log('Exif data: ', data.exif) + }, + { + excludeExifTags: { + 0x8769: { + // ExifIFDPointer + 0x927c: true // MakerNote + }, + 0x8825: true // GPSInfoIFDPointer + } + } +) +``` ### Exif writer @@ -498,29 +554,41 @@ loadImage( ### IPTC parser If you include the Load Image IPTC Parser extension, the argument passed to the -callback for `parseMetaData` will contain the additional property `iptc` if IPTC -data could be found in the given image. +callback for `parseMetaData` will contain the following additional properties if +IPTC data could be found in the given image: + +- `iptc`: The parsed IPTC tags +- `iptcOffsets`: The parsed IPTC tag offsets + The `iptc` object stores the parsed IPTC tags: ```js -var objectname = data.iptc[0x5] +var objectname = data.iptc[5] ``` -It also provides an `iptc.get()` method to retrieve the tag value via the tag's -mapped name: +The `iptc` and `iptcOffsets` objects also provide a `get()` method to retrieve +the tag value/offset via the tag's mapped name: ```js var objectname = data.iptc.get('ObjectName') ``` -By default, the only available mapped names are `ObjectName`. +By default, only the following names are mapped: + +- `ObjectName` + If you also include the Load Image IPTC Map library, additional tag mappings -become available, as well as two additional methods, `iptc.getText()` and -`iptc.getAll()`: +become available, as well as three additional methods: + +- `iptc.getText()` +- `iptc.getName()` +- `iptc.getAll()` ```js var keywords = data.iptc.getText('Keywords') // e.g.: ['Weather','Sky'] +var name = data.iptc.getName(5) // ObjectName + // A map of all parsed tags with their mapped names/text as keys/values: var allTags = data.iptc.getAll() ``` @@ -528,7 +596,45 @@ var allTags = data.iptc.getAll() The IPTC parser also adds additional options for the parseMetaData method, to disable certain aspects of the parser: -- `disableIptc`: Disables IPTC parsing. +- `disableIptc`: Disables IPTC parsing when true. +- `disableIptcOffsets`: Disables storing IPTC tag offsets when `true`. +- `includeIptcTags`: A map of IPTC tags to include for parsing (includes all but + the excluded tags by default). +- `excludeIptcTags`: A map of IPTC tags to exclude from parsing (defaults to + exclude `ObjectPreviewData`). + +An example parsing only the `ObjectName` tag: + +```js +loadImage.parseMetaData( + fileOrBlob, + function (data) { + console.log('IPTC data: ', data.iptc) + }, + { + includeIptcTags: { + 5: true // ObjectName + } + } +) +``` + +An example excluding `ApplicationRecordVersion` and `ObjectPreviewData`: + +```js +loadImage.parseMetaData( + fileOrBlob, + function (data) { + console.log('IPTC data: ', data.iptc) + }, + { + excludeIptcTags: { + 0: true, // ApplicationRecordVersion + 202: true // ObjectPreviewData + } + } +) +``` ## License diff --git a/css/demo.css b/css/demo.css index 2b2a8d5..fb0f6a3 100644 --- a/css/demo.css +++ b/css/demo.css @@ -41,6 +41,7 @@ table { word-wrap: break-word; table-layout: fixed; border-collapse: collapse; + margin-bottom: 20px; } tr { background: #fff; @@ -55,6 +56,7 @@ td { } #result, #thumbnail { + margin-bottom: 20px; padding: 20px; background: #fff; color: #222; diff --git a/index.html b/index.html index 9a13bf9..bc619ec 100644 --- a/index.html +++ b/index.html @@ -96,12 +96,10 @@

Result

diff --git a/js/demo/demo.js b/js/demo/demo.js index cd9b62d..6dc9fa3 100644 --- a/js/demo/demo.js +++ b/js/demo/demo.js @@ -29,12 +29,16 @@ $(function () { * @param {object} tags Tags object */ function displayTagData(node, tags) { - var table = node.find('table').empty() + var table = $('') var row = $('') var cell = $('') var prop for (prop in tags) { if (Object.prototype.hasOwnProperty.call(tags, prop)) { + if (typeof tags[prop] === 'object') { + displayTagData(node, tags[prop]) + continue + } table.append( row .clone() @@ -43,7 +47,7 @@ $(function () { ) } } - node.show() + node.append(table).show() } /** @@ -55,11 +59,15 @@ $(function () { */ function displayThumbnailImage(node, thumbnail, options) { if (thumbnail) { - thumbNode.empty() + var link = $('') + .attr('href', loadImage.createObjectURL(thumbnail)) + .attr('download', 'thumbnail.jpg') + .appendTo(node) loadImage( thumbnail, function (img) { - node.append(img).show() + link.append(img) + node.show() }, options ) @@ -136,9 +144,9 @@ $(function () { orientation: true, meta: true } - exifNode.hide() - iptcNode.hide() - thumbNode.hide() + exifNode.hide().find('table').remove() + iptcNode.hide().find('table').remove() + thumbNode.hide().empty() if (!loadImage(file, updateResults, options)) { result .children() diff --git a/js/load-image-exif-map.js b/js/load-image-exif-map.js index 68d90d3..9684ae8 100644 --- a/js/load-image-exif-map.js +++ b/js/load-image-exif-map.js @@ -28,15 +28,14 @@ })(function (loadImage) { 'use strict' - loadImage.ExifMap.prototype.tags = { + var ExifMapProto = loadImage.ExifMap.prototype + + ExifMapProto.tags = { // ================= // TIFF tags (IFD0): // ================= 0x0100: 'ImageWidth', 0x0101: 'ImageHeight', - 0x8769: 'ExifIFDPointer', - 0x8825: 'GPSInfoIFDPointer', - 0xa005: 'InteroperabilityIFDPointer', 0x0102: 'BitsPerSample', 0x0103: 'Compression', 0x0106: 'PhotometricInterpretation', @@ -65,116 +64,120 @@ 0x0131: 'Software', 0x013b: 'Artist', 0x8298: 'Copyright', - // ================== - // Exif Sub IFD tags: - // ================== - 0x9000: 'ExifVersion', // EXIF version - 0xa000: 'FlashpixVersion', // Flashpix format version - 0xa001: 'ColorSpace', // Color space information tag - 0xa002: 'PixelXDimension', // Valid width of meaningful image - 0xa003: 'PixelYDimension', // Valid height of meaningful image - 0xa500: 'Gamma', - 0x9101: 'ComponentsConfiguration', // Information about channels - 0x9102: 'CompressedBitsPerPixel', // Compressed bits per pixel - 0x927c: 'MakerNote', // Any desired information written by the manufacturer - 0x9286: 'UserComment', // Comments by user - 0xa004: 'RelatedSoundFile', // Name of related sound file - 0x9003: 'DateTimeOriginal', // Date and time when the original image was generated - 0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally - 0x9290: 'SubSecTime', // Fractions of seconds for DateTime - 0x9291: 'SubSecTimeOriginal', // Fractions of seconds for DateTimeOriginal - 0x9292: 'SubSecTimeDigitized', // Fractions of seconds for DateTimeDigitized - 0x829a: 'ExposureTime', // Exposure time (in seconds) - 0x829d: 'FNumber', - 0x8822: 'ExposureProgram', // Exposure program - 0x8824: 'SpectralSensitivity', // Spectral sensitivity - 0x8827: 'PhotographicSensitivity', // EXIF 2.3, ISOSpeedRatings in EXIF 2.2 - 0x8828: 'OECF', // Optoelectric conversion factor - 0x8830: 'SensitivityType', - 0x8831: 'StandardOutputSensitivity', - 0x8832: 'RecommendedExposureIndex', - 0x8833: 'ISOSpeed', - 0x8834: 'ISOSpeedLatitudeyyy', - 0x8835: 'ISOSpeedLatitudezzz', - 0x9201: 'ShutterSpeedValue', // Shutter speed - 0x9202: 'ApertureValue', // Lens aperture - 0x9203: 'BrightnessValue', // Value of brightness - 0x9204: 'ExposureBias', // Exposure bias - 0x9205: 'MaxApertureValue', // Smallest F number of lens - 0x9206: 'SubjectDistance', // Distance to subject in meters - 0x9207: 'MeteringMode', // Metering mode - 0x9208: 'LightSource', // Kind of light source - 0x9209: 'Flash', // Flash status - 0x9214: 'SubjectArea', // Location and area of main subject - 0x920a: 'FocalLength', // Focal length of the lens in mm - 0xa20b: 'FlashEnergy', // Strobe energy in BCPS - 0xa20c: 'SpatialFrequencyResponse', - 0xa20e: 'FocalPlaneXResolution', // Number of pixels in width direction per FPRUnit - 0xa20f: 'FocalPlaneYResolution', // Number of pixels in height direction per FPRUnit - 0xa210: 'FocalPlaneResolutionUnit', // Unit for measuring the focal plane resolution - 0xa214: 'SubjectLocation', // Location of subject in image - 0xa215: 'ExposureIndex', // Exposure index selected on camera - 0xa217: 'SensingMethod', // Image sensor type - 0xa300: 'FileSource', // Image source (3 == DSC) - 0xa301: 'SceneType', // Scene type (1 == directly photographed) - 0xa302: 'CFAPattern', // Color filter array geometric pattern - 0xa401: 'CustomRendered', // Special processing - 0xa402: 'ExposureMode', // Exposure mode - 0xa403: 'WhiteBalance', // 1 = auto white balance, 2 = manual - 0xa404: 'DigitalZoomRatio', // Digital zoom ratio - 0xa405: 'FocalLengthIn35mmFilm', - 0xa406: 'SceneCaptureType', // Type of scene - 0xa407: 'GainControl', // Degree of overall image gain adjustment - 0xa408: 'Contrast', // Direction of contrast processing applied by camera - 0xa409: 'Saturation', // Direction of saturation processing applied by camera - 0xa40a: 'Sharpness', // Direction of sharpness processing applied by camera - 0xa40b: 'DeviceSettingDescription', - 0xa40c: 'SubjectDistanceRange', // Distance to subject - 0xa420: 'ImageUniqueID', // Identifier assigned uniquely to each image - 0xa430: 'CameraOwnerName', - 0xa431: 'BodySerialNumber', - 0xa432: 'LensSpecification', - 0xa433: 'LensMake', - 0xa434: 'LensModel', - 0xa435: 'LensSerialNumber', - // ============== - // GPS Info tags: - // ============== - 0x0000: 'GPSVersionID', - 0x0001: 'GPSLatitudeRef', - 0x0002: 'GPSLatitude', - 0x0003: 'GPSLongitudeRef', - 0x0004: 'GPSLongitude', - 0x0005: 'GPSAltitudeRef', - 0x0006: 'GPSAltitude', - 0x0007: 'GPSTimeStamp', - 0x0008: 'GPSSatellites', - 0x0009: 'GPSStatus', - 0x000a: 'GPSMeasureMode', - 0x000b: 'GPSDOP', - 0x000c: 'GPSSpeedRef', - 0x000d: 'GPSSpeed', - 0x000e: 'GPSTrackRef', - 0x000f: 'GPSTrack', - 0x0010: 'GPSImgDirectionRef', - 0x0011: 'GPSImgDirection', - 0x0012: 'GPSMapDatum', - 0x0013: 'GPSDestLatitudeRef', - 0x0014: 'GPSDestLatitude', - 0x0015: 'GPSDestLongitudeRef', - 0x0016: 'GPSDestLongitude', - 0x0017: 'GPSDestBearingRef', - 0x0018: 'GPSDestBearing', - 0x0019: 'GPSDestDistanceRef', - 0x001a: 'GPSDestDistance', - 0x001b: 'GPSProcessingMethod', - 0x001c: 'GPSAreaInformation', - 0x001d: 'GPSDateStamp', - 0x001e: 'GPSDifferential', - 0x001f: 'GPSHPositioningError' + 0x8769: { + // ExifIFDPointer + 0x9000: 'ExifVersion', // EXIF version + 0xa000: 'FlashpixVersion', // Flashpix format version + 0xa001: 'ColorSpace', // Color space information tag + 0xa002: 'PixelXDimension', // Valid width of meaningful image + 0xa003: 'PixelYDimension', // Valid height of meaningful image + 0xa500: 'Gamma', + 0x9101: 'ComponentsConfiguration', // Information about channels + 0x9102: 'CompressedBitsPerPixel', // Compressed bits per pixel + 0x927c: 'MakerNote', // Any desired information written by the manufacturer + 0x9286: 'UserComment', // Comments by user + 0xa004: 'RelatedSoundFile', // Name of related sound file + 0x9003: 'DateTimeOriginal', // Date and time when the original image was generated + 0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally + 0x9290: 'SubSecTime', // Fractions of seconds for DateTime + 0x9291: 'SubSecTimeOriginal', // Fractions of seconds for DateTimeOriginal + 0x9292: 'SubSecTimeDigitized', // Fractions of seconds for DateTimeDigitized + 0x829a: 'ExposureTime', // Exposure time (in seconds) + 0x829d: 'FNumber', + 0x8822: 'ExposureProgram', // Exposure program + 0x8824: 'SpectralSensitivity', // Spectral sensitivity + 0x8827: 'PhotographicSensitivity', // EXIF 2.3, ISOSpeedRatings in EXIF 2.2 + 0x8828: 'OECF', // Optoelectric conversion factor + 0x8830: 'SensitivityType', + 0x8831: 'StandardOutputSensitivity', + 0x8832: 'RecommendedExposureIndex', + 0x8833: 'ISOSpeed', + 0x8834: 'ISOSpeedLatitudeyyy', + 0x8835: 'ISOSpeedLatitudezzz', + 0x9201: 'ShutterSpeedValue', // Shutter speed + 0x9202: 'ApertureValue', // Lens aperture + 0x9203: 'BrightnessValue', // Value of brightness + 0x9204: 'ExposureBias', // Exposure bias + 0x9205: 'MaxApertureValue', // Smallest F number of lens + 0x9206: 'SubjectDistance', // Distance to subject in meters + 0x9207: 'MeteringMode', // Metering mode + 0x9208: 'LightSource', // Kind of light source + 0x9209: 'Flash', // Flash status + 0x9214: 'SubjectArea', // Location and area of main subject + 0x920a: 'FocalLength', // Focal length of the lens in mm + 0xa20b: 'FlashEnergy', // Strobe energy in BCPS + 0xa20c: 'SpatialFrequencyResponse', + 0xa20e: 'FocalPlaneXResolution', // Number of pixels in width direction per FPRUnit + 0xa20f: 'FocalPlaneYResolution', // Number of pixels in height direction per FPRUnit + 0xa210: 'FocalPlaneResolutionUnit', // Unit for measuring the focal plane resolution + 0xa214: 'SubjectLocation', // Location of subject in image + 0xa215: 'ExposureIndex', // Exposure index selected on camera + 0xa217: 'SensingMethod', // Image sensor type + 0xa300: 'FileSource', // Image source (3 == DSC) + 0xa301: 'SceneType', // Scene type (1 == directly photographed) + 0xa302: 'CFAPattern', // Color filter array geometric pattern + 0xa401: 'CustomRendered', // Special processing + 0xa402: 'ExposureMode', // Exposure mode + 0xa403: 'WhiteBalance', // 1 = auto white balance, 2 = manual + 0xa404: 'DigitalZoomRatio', // Digital zoom ratio + 0xa405: 'FocalLengthIn35mmFilm', + 0xa406: 'SceneCaptureType', // Type of scene + 0xa407: 'GainControl', // Degree of overall image gain adjustment + 0xa408: 'Contrast', // Direction of contrast processing applied by camera + 0xa409: 'Saturation', // Direction of saturation processing applied by camera + 0xa40a: 'Sharpness', // Direction of sharpness processing applied by camera + 0xa40b: 'DeviceSettingDescription', + 0xa40c: 'SubjectDistanceRange', // Distance to subject + 0xa420: 'ImageUniqueID', // Identifier assigned uniquely to each image + 0xa430: 'CameraOwnerName', + 0xa431: 'BodySerialNumber', + 0xa432: 'LensSpecification', + 0xa433: 'LensMake', + 0xa434: 'LensModel', + 0xa435: 'LensSerialNumber' + }, + 0x8825: { + // GPSInfoIFDPointer + 0x0000: 'GPSVersionID', + 0x0001: 'GPSLatitudeRef', + 0x0002: 'GPSLatitude', + 0x0003: 'GPSLongitudeRef', + 0x0004: 'GPSLongitude', + 0x0005: 'GPSAltitudeRef', + 0x0006: 'GPSAltitude', + 0x0007: 'GPSTimeStamp', + 0x0008: 'GPSSatellites', + 0x0009: 'GPSStatus', + 0x000a: 'GPSMeasureMode', + 0x000b: 'GPSDOP', + 0x000c: 'GPSSpeedRef', + 0x000d: 'GPSSpeed', + 0x000e: 'GPSTrackRef', + 0x000f: 'GPSTrack', + 0x0010: 'GPSImgDirectionRef', + 0x0011: 'GPSImgDirection', + 0x0012: 'GPSMapDatum', + 0x0013: 'GPSDestLatitudeRef', + 0x0014: 'GPSDestLatitude', + 0x0015: 'GPSDestLongitudeRef', + 0x0016: 'GPSDestLongitude', + 0x0017: 'GPSDestBearingRef', + 0x0018: 'GPSDestBearing', + 0x0019: 'GPSDestDistanceRef', + 0x001a: 'GPSDestDistance', + 0x001b: 'GPSProcessingMethod', + 0x001c: 'GPSAreaInformation', + 0x001d: 'GPSDateStamp', + 0x001e: 'GPSDifferential', + 0x001f: 'GPSHPositioningError' + }, + 0xa005: { + // InteroperabilityIFDPointer + 0x0001: 'InteroperabilityIndex' + } } - loadImage.ExifMap.prototype.stringValues = { + ExifMapProto.stringValues = { ExposureProgram: { 0: 'Undefined', 1: 'Manual', @@ -321,9 +324,9 @@ } } - loadImage.ExifMap.prototype.getText = function (id) { - var value = this.get(id) - switch (id) { + ExifMapProto.getText = function (name) { + var value = this.get(name) + switch (name) { case 'LightSource': case 'Flash': case 'MeteringMode': @@ -340,7 +343,7 @@ case 'SubjectDistanceRange': case 'FileSource': case 'Orientation': - return this.stringValues[id][value] + return this.stringValues[name][value] case 'ExifVersion': case 'FlashpixVersion': if (!value) return @@ -348,10 +351,10 @@ case 'ComponentsConfiguration': if (!value) return return ( - this.stringValues[id][value[0]] + - this.stringValues[id][value[1]] + - this.stringValues[id][value[2]] + - this.stringValues[id][value[3]] + this.stringValues[name][value[0]] + + this.stringValues[name][value[1]] + + this.stringValues[name][value[2]] + + this.stringValues[name][value[3]] ) case 'GPSVersionID': if (!value) return @@ -359,30 +362,53 @@ } return String(value) } - ;(function (exifMapPrototype) { - var tags = exifMapPrototype.tags - var map = exifMapPrototype.map - var prop - // Map the tag names to tags: - for (prop in tags) { - if (Object.prototype.hasOwnProperty.call(tags, prop)) { - map[tags[prop]] = prop - } - } - })(loadImage.ExifMap.prototype) - loadImage.ExifMap.prototype.getAll = function () { + ExifMapProto.getAll = function () { var map = {} var prop - var id + var obj + var name for (prop in this) { if (Object.prototype.hasOwnProperty.call(this, prop)) { - id = this.tags[prop] - if (id) { - map[id] = this.getText(id) + obj = this[prop] + if (obj && obj.getAll) { + map[this.privateIFDs[prop].name] = obj.getAll() + } else { + name = this.tags[prop] + if (name) map[name] = this.getText(name) } } } return map } + + ExifMapProto.getName = function (tagCode) { + var name = this.tags[tagCode] + if (typeof name === 'object') return this.privateIFDs[tagCode].name + return name + } + + // Extend the map of tag names to tag codes: + ;(function () { + var tags = ExifMapProto.tags + var prop + var privateIFD + var subTags + // Map the tag names to tags: + for (prop in tags) { + if (Object.prototype.hasOwnProperty.call(tags, prop)) { + privateIFD = ExifMapProto.privateIFDs[prop] + if (privateIFD) { + subTags = tags[prop] + for (prop in subTags) { + if (Object.prototype.hasOwnProperty.call(subTags, prop)) { + privateIFD.map[subTags[prop]] = Number(prop) + } + } + } else { + ExifMapProto.map[tags[prop]] = Number(prop) + } + } + } + })() }) diff --git a/js/load-image-exif.js b/js/load-image-exif.js index 74c2632..7dff053 100644 --- a/js/load-image-exif.js +++ b/js/load-image-exif.js @@ -27,28 +27,67 @@ })(function (loadImage) { 'use strict' - loadImage.ExifMap = function () { - return this + /** + * Exif tag map + * + * @name ExifMap + * @class + * @param {number} tagCode Private IFD tag code + */ + function ExifMap(tagCode) { + if (tagCode) { + Object.defineProperty(this, 'map', { + value: this.privateIFDs[tagCode].map + }) + Object.defineProperty(this, 'tags', { + value: (this.tags && this.tags[tagCode]) || {} + }) + } } - loadImage.ExifMap.prototype.map = { + ExifMap.prototype.map = { Orientation: 0x0112, - Thumbnail: 0x0201 + Thumbnail: 0x0201, + Exif: 0x8769, + GPSInfo: 0x8825, + Interoperability: 0xa005 + } + + ExifMap.prototype.privateIFDs = { + 0x8769: { name: 'Exif', map: {} }, + 0x8825: { name: 'GPSInfo', map: {} }, + 0xa005: { name: 'Interoperability', map: {} } } - loadImage.ExifMap.prototype.get = function (id) { + /** + * Retrieves exif tag value + * + * @param {number|string} id Exif tag code or name + * @returns {object} Exif tag value + */ + ExifMap.prototype.get = function (id) { return this[id] || this[this.map[id]] } - loadImage.getExifThumbnail = function (dataView, offset, length) { + /** + * Returns the Exif Thumbnail data as Blob. + * + * @param {DataView} dataView Data view interface + * @param {number} offset Thumbnail data offset + * @param {number} length Thumbnail data length + * @returns {undefined|Blob} Returns the Thumbnail Blob or undefined + */ + function getExifThumbnail(dataView, offset, length) { if (!length || offset + length > dataView.byteLength) { console.log('Invalid Exif data: Invalid thumbnail data.') return } - return new Blob([dataView.buffer.slice(offset, offset + length)]) + return new Blob([dataView.buffer.slice(offset, offset + length)], { + type: 'image/jpeg' + }) } - loadImage.exifTagTypes = { + var ExifTagTypes = { // byte, 8-bit unsigned int: 1: { getValue: function (dataView, dataOffset) { @@ -107,9 +146,20 @@ } } // undefined, 8-bit byte, value depending on field: - loadImage.exifTagTypes[7] = loadImage.exifTagTypes[1] + ExifTagTypes[7] = ExifTagTypes[1] - loadImage.getExifValue = function ( + /** + * Returns Exif tag value. + * + * @param {DataView} dataView Data view interface + * @param {number} tiffOffset TIFF offset + * @param {number} offset Tag offset + * @param {number} type Tag type + * @param {number} length Tag length + * @param {boolean} littleEndian Little endian encoding + * @returns {object} Tag value + */ + function getExifValue( dataView, tiffOffset, offset, @@ -117,7 +167,7 @@ length, littleEndian ) { - var tagType = loadImage.exifTagTypes[type] + var tagType = ExifTagTypes[type] var tagSize var dataOffset var values @@ -166,33 +216,30 @@ return values } - loadImage.parseExifTag = function ( - dataView, - tiffOffset, - offset, - littleEndian - ) { - return { - number: dataView.getUint16(offset, littleEndian), - value: loadImage.getExifValue( - dataView, - tiffOffset, - offset, - dataView.getUint16(offset + 2, littleEndian), // tag type - dataView.getUint32(offset + 4, littleEndian), // tag length - littleEndian - ) - } - } - - loadImage.parseExifTags = function ( + /** + * Parses Exif tags. + * + * @param {DataView} dataView Data view interface + * @param {number} tiffOffset TIFF offset + * @param {number} dirOffset Directory offset + * @param {boolean} littleEndian Little endian encoding + * @param {ExifMap} tags Map to store parsed exif tags + * @param {ExifMap} tagOffsets Map to store parsed exif tag offsets + * @param {object} includeTags Map of tags to include + * @param {object} excludeTags Map of tags to exclude + * @returns {number} Next directory offset + */ + function parseExifTags( dataView, tiffOffset, dirOffset, littleEndian, - data + tags, + tagOffsets, + includeTags, + excludeTags ) { - var tagsNumber, dirEndOffset, i, tagOffset, tag + var tagsNumber, dirEndOffset, i, tagOffset, tagNumber, tagValue if (dirOffset + 6 > dataView.byteLength) { console.log('Invalid Exif data: Invalid directory offset.') return @@ -205,29 +252,80 @@ } for (i = 0; i < tagsNumber; i += 1) { tagOffset = dirOffset + 2 + 12 * i - tag = this.parseExifTag( + tagNumber = dataView.getUint16(tagOffset, littleEndian) + if (includeTags && !includeTags[tagNumber]) continue + if (excludeTags && excludeTags[tagNumber] === true) continue + tagValue = getExifValue( dataView, tiffOffset, tagOffset, - littleEndian, - data + dataView.getUint16(tagOffset + 2, littleEndian), // tag type + dataView.getUint32(tagOffset + 4, littleEndian), // tag length + littleEndian ) - data.exif[tag.number] = tag.value - if (data.exifOffsets) { - data.exifOffsets[tag.number] = tagOffset + tags[tagNumber] = tagValue + if (tagOffsets) { + tagOffsets[tagNumber] = tagOffset } } // Return the offset to the next directory: return dataView.getUint32(dirEndOffset, littleEndian) } + /** + * Parses Private IFD tags. + * + * @param {object} data Data object to store exif tags and offsets + * @param {number} tagCode Private IFD tag code + * @param {DataView} dataView Data view interface + * @param {number} tiffOffset TIFF offset + * @param {boolean} littleEndian Little endian encoding + * @param {object} includeTags Map of tags to include + * @param {object} excludeTags Map of tags to exclude + */ + function parseExifPrivateIFD( + data, + tagCode, + dataView, + tiffOffset, + littleEndian, + includeTags, + excludeTags + ) { + var dirOffset = data.exif[tagCode] + if (dirOffset) { + data.exif[tagCode] = new ExifMap(tagCode) + if (data.exifOffsets) { + data.exifOffsets[tagCode] = new ExifMap(tagCode) + } + parseExifTags( + dataView, + tiffOffset, + tiffOffset + dirOffset, + littleEndian, + data.exif[tagCode], + data.exifOffsets && data.exifOffsets[tagCode], + includeTags && includeTags[tagCode], + excludeTags && excludeTags[tagCode] + ) + } + } + loadImage.parseExifData = function (dataView, offset, length, data, options) { if (options.disableExif) { return } + var includeTags = options.includeExifTags + var excludeTags = options.excludeExifTags || { + 0x8769: { + // ExifIFDPointer + 0x927c: true // MakerNote + } + } var tiffOffset = offset + 10 var littleEndian var dirOffset + var privateIFDs // Check for the ASCII code for "Exif" (0x45786966): if (dataView.getUint32(offset + 4) !== 0x45786966) { // No Exif data, might be XMP data instead @@ -262,58 +360,56 @@ // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal: dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian) // Create the exif object to store the tags: - data.exif = new loadImage.ExifMap() + data.exif = new ExifMap() if (!options.disableExifOffsets) { - data.exifOffsets = new loadImage.ExifMap() + data.exifOffsets = new ExifMap() data.exifTiffOffset = tiffOffset data.exifLittleEndian = littleEndian } // Parse the tags of the main image directory and retrieve the // offset to the next directory, usually the thumbnail directory: - dirOffset = loadImage.parseExifTags( + dirOffset = parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, - data + data.exif, + data.exifOffsets, + includeTags, + excludeTags ) if (dirOffset && !options.disableExifThumbnail) { - dirOffset = loadImage.parseExifTags( + dirOffset = parseExifTags( dataView, tiffOffset, tiffOffset + dirOffset, littleEndian, - data + data.exif, + data.exifOffsets, + includeTags, + excludeTags ) // Check for JPEG Thumbnail offset: - if (data.exif[0x0201]) { - data.exif[0x0201] = loadImage.getExifThumbnail( + if (data.exif[0x0201] && data.exif[0x0202]) { + data.exif[0x0201] = getExifThumbnail( dataView, tiffOffset + data.exif[0x0201], data.exif[0x0202] // Thumbnail data length ) } } - // Check for Exif Sub IFD Pointer: - if (data.exif[0x8769] && !options.disableExifSub) { - loadImage.parseExifTags( - dataView, - tiffOffset, - tiffOffset + data.exif[0x8769], // directory offset - littleEndian, - data - ) - } - // Check for GPS Info IFD Pointer: - if (data.exif[0x8825] && !options.disableExifGps) { - loadImage.parseExifTags( + privateIFDs = Object.keys(data.exif.privateIFDs) + privateIFDs.forEach(function (tagCode) { + parseExifPrivateIFD( + data, + tagCode, dataView, tiffOffset, - tiffOffset + data.exif[0x8825], // directory offset littleEndian, - data + includeTags, + excludeTags ) - } + }) } // Registers the Exif parser for the APP1 JPEG meta data segment: @@ -332,16 +428,18 @@ loadImage.exifWriters[data.exif.map[id]](buffer, data, value) } + loadImage.ExifMap = ExifMap + // Adds the following properties to the parseMetaData callback data: - // * exif: The parsed Exif tags - // * exifOffsets: The parsed Exif tag offsets - // * exifTiffOffset: TIFF header offset (used for offset pointers) - // * exifLittleEndian: little endian order if true, big endian if false + // - exif: The parsed Exif tags + // - exifOffsets: The parsed Exif tag offsets + // - exifTiffOffset: TIFF header offset (used for offset pointers) + // - exifLittleEndian: little endian order if true, big endian if false // Adds the following options to the parseMetaData method: - // * disableExif: Disables Exif parsing. - // * disableExifThumbnail: Disables parsing of the Exif Thumbnail. - // * disableExifSub: Disables parsing of the Exif Sub IFD. - // * disableExifGps: Disables parsing of the Exif GPS Info IFD. - // * disableExifOffsets: Disables storing Exif tag offsets + // - disableExif: Disables Exif parsing when true. + // - disableExifThumbnail: Disables parsing of Thumbnail data when true. + // - disableExifOffsets: Disables storing Exif tag offsets when true. + // - includeExifTags: A map of Exif tags to include for parsing. + // - excludeExifTags: A map of Exif tags to exclude from parsing. }) diff --git a/js/load-image-iptc-map.js b/js/load-image-iptc-map.js index adffa21..165d17d 100644 --- a/js/load-image-iptc-map.js +++ b/js/load-image-iptc-map.js @@ -6,9 +6,8 @@ * Copyright 2018, Dave Bevan * * IPTC tags mapping based on - * https://github.com/jseidelin/exif-js * https://iptc.org/standards/photo-metadata - * http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf + * https://exiftool.org/TagNames/IPTC.html * * Licensed under the MIT license: * https://opensource.org/licenses/MIT @@ -30,100 +29,141 @@ })(function (loadImage) { 'use strict' - loadImage.IptcMap.prototype.tags = { - // ========== - // IPTC tags: - // ========== - 0x03: 'ObjectType', - 0x04: 'ObjectAttribute', - 0x05: 'ObjectName', - 0x07: 'EditStatus', - 0x08: 'EditorialUpdate', - 0x0a: 'Urgency', - 0x0c: 'SubjectRef', - 0x0f: 'Category', - 0x14: 'SupplCategory', - 0x16: 'FixtureID', - 0x19: 'Keywords', - 0x1a: 'ContentLocCode', - 0x1b: 'ContentLocName', - 0x1e: 'ReleaseDate', - 0x23: 'ReleaseTime', - 0x25: 'ExpirationDate', - 0x26: 'ExpirationTime', - 0x28: 'SpecialInstructions', - 0x2a: 'ActionAdvised', - 0x2d: 'RefService', - 0x2f: 'RefDate', - 0x32: 'RefNumber', - 0x37: 'DateCreated', - 0x3c: 'TimeCreated', - 0x3e: 'DigitalCreationDate', - 0x3f: 'DigitalCreationTime', - 0x41: 'OriginatingProgram', - 0x46: 'ProgramVersion', - 0x4b: 'ObjectCycle', - 0x50: 'Byline', - 0x55: 'BylineTitle', - 0x5a: 'City', - 0x5c: 'Sublocation', - 0x5f: 'State', - 0x64: 'CountryCode', - 0x65: 'CountryName', - 0x67: 'OrigTransRef', - 0x69: 'Headline', - 0x6e: 'Credit', - 0x73: 'Source', - 0x74: 'CopyrightNotice', - 0x76: 'Contact', - 0x78: 'Caption', - 0x7a: 'WriterEditor', - 0x82: 'ImageType', - 0x83: 'ImageOrientation', - 0x87: 'LanguageID' + var IptcMapProto = loadImage.IptcMap.prototype - // We don't record these tags: - // - // 0x00: 'RecordVersion', - // 0x7d: 'RasterizedCaption', - // 0x96: 'AudioType', - // 0x97: 'AudioSamplingRate', - // 0x98: 'AudioSamplingRes', - // 0x99: 'AudioDuration', - // 0x9a: 'AudioOutcue', - // 0xc8: 'PreviewFileFormat', - // 0xc9: 'PreviewFileFormatVer', - // 0xca: 'PreviewData' + IptcMapProto.tags = { + 0: 'ApplicationRecordVersion', + 3: 'ObjectTypeReference', + 4: 'ObjectAttributeReference', + 5: 'ObjectName', + 7: 'EditStatus', + 8: 'EditorialUpdate', + 10: 'Urgency', + 12: 'SubjectReference', + 15: 'Category', + 20: 'SupplementalCategories', + 22: 'FixtureIdentifier', + 25: 'Keywords', + 26: 'ContentLocationCode', + 27: 'ContentLocationName', + 30: 'ReleaseDate', + 35: 'ReleaseTime', + 37: 'ExpirationDate', + 38: 'ExpirationTime', + 40: 'SpecialInstructions', + 42: 'ActionAdvised', + 45: 'ReferenceService', + 47: 'ReferenceDate', + 50: 'ReferenceNumber', + 55: 'DateCreated', + 60: 'TimeCreated', + 62: 'DigitalCreationDate', + 63: 'DigitalCreationTime', + 65: 'OriginatingProgram', + 70: 'ProgramVersion', + 75: 'ObjectCycle', + 80: 'Byline', + 85: 'BylineTitle', + 90: 'City', + 92: 'Sublocation', + 95: 'State', + 100: 'CountryCode', + 101: 'Country', + 103: 'OriginalTransmissionReference', + 105: 'Headline', + 110: 'Credit', + 115: 'Source', + 116: 'CopyrightNotice', + 118: 'Contact', + 120: 'Caption', + 121: 'LocalCaption', + 122: 'Writer', + 125: 'RasterizedCaption', + 130: 'ImageType', + 131: 'ImageOrientation', + 135: 'LanguageIdentifier', + 150: 'AudioType', + 151: 'AudioSamplingRate', + 152: 'AudioSamplingResolution', + 153: 'AudioDuration', + 154: 'AudioOutcue', + 184: 'JobID', + 185: 'MasterDocumentID', + 186: 'ShortDocumentID', + 187: 'UniqueDocumentID', + 188: 'OwnerID', + 200: 'ObjectPreviewFileFormat', + 201: 'ObjectPreviewFileVersion', + 202: 'ObjectPreviewData', + 221: 'Prefs', + 225: 'ClassifyState', + 228: 'SimilarityIndex', + 230: 'DocumentNotes', + 231: 'DocumentHistory', + 232: 'ExifCameraInfo', + 255: 'CatalogSets' } - loadImage.IptcMap.prototype.getText = function (id) { + IptcMapProto.stringValues = { + 10: { + 0: '0 (reserved)', + 1: '1 (most urgent)', + 2: '2', + 3: '3', + 4: '4', + 5: '5 (normal urgency)', + 6: '6', + 7: '7', + 8: '8 (least urgent)', + 9: '9 (user-defined priority)' + }, + 75: { + a: 'Morning', + b: 'Both Morning and Evening', + p: 'Evening' + }, + 131: { + L: 'Landscape', + P: 'Portrait', + S: 'Square' + } + } + + IptcMapProto.getText = function (id) { var value = this.get(id) + var tagCode = this.map[id] + var stringValue = this.stringValues[tagCode] + if (stringValue) return stringValue[value] return String(value) } - ;(function (iptcMapPrototype) { - var tags = iptcMapPrototype.tags - var map = iptcMapPrototype.map || {} - var prop - // Map the tag names to tags: - for (prop in tags) { - if (Object.prototype.hasOwnProperty.call(tags, prop)) { - map[tags[prop]] = prop - } - } - })(loadImage.IptcMap.prototype) - loadImage.IptcMap.prototype.getAll = function () { + IptcMapProto.getAll = function () { var map = {} var prop - var id + var name for (prop in this) { if (Object.prototype.hasOwnProperty.call(this, prop)) { - id = this.tags[prop] - if (id) { - map[id] = this.getText(id) - } + name = this.tags[prop] + if (name) map[name] = this.getText(name) } } return map } + + IptcMapProto.getName = function (tagCode) { + return this.tags[tagCode] + } + + // Extend the map of tag names to tag codes: + ;(function () { + var tags = IptcMapProto.tags + var map = IptcMapProto.map || {} + var prop + // Map the tag names to tags: + for (prop in tags) { + if (Object.prototype.hasOwnProperty.call(tags, prop)) { + map[tags[prop]] = Number(prop) + } + } + })() }) diff --git a/js/load-image-iptc.js b/js/load-image-iptc.js index c7ce7c1..9f6a900 100644 --- a/js/load-image-iptc.js +++ b/js/load-image-iptc.js @@ -10,7 +10,7 @@ * https://opensource.org/licenses/MIT */ -/* global define, module, require, Buffer */ +/* global define, module, require, DataView */ ;(function (factory) { 'use strict' @@ -26,128 +26,214 @@ })(function (loadImage) { 'use strict' - loadImage.IptcMap = function () { - return this + /** + * IPTC tag map + * + * @name IptcMap + * @class + */ + function IptcMap() {} + + IptcMap.prototype.map = { + ObjectName: 5 } - loadImage.IptcMap.prototype.map = { - ObjectName: 0x5 + IptcMap.prototype.types = { + 0: 'Uint16', // ApplicationRecordVersion + 200: 'Uint16', // ObjectPreviewFileFormat + 201: 'Uint16', // ObjectPreviewFileVersion + 202: 'binary' // ObjectPreviewData } - loadImage.IptcMap.prototype.get = function (id) { + /** + * Retrieves IPTC tag value + * + * @param {number|string} id IPTC tag code or name + * @returns {object} IPTC tag value + */ + IptcMap.prototype.get = function (id) { return this[id] || this[this.map[id]] } - loadImage.parseIptcTags = function ( + /** + * Retrieves string for the given DataView and range + * + * @param {DataView} dataView Data view interface + * @param {number} offset Offset start + * @param {number} length Offset length + * @returns {string} String value + */ + function getStringValue(dataView, offset, length) { + var outstr = '' + var end = offset + length + for (var n = offset; n < end; n += 1) { + outstr += String.fromCharCode(dataView.getUint8(n)) + } + return outstr + } + + /** + * Retrieves tag value for the given DataView and range + * + * @param {number} tagCode Private IFD tag code + * @param {IptcMap} map IPTC tag map + * @param {DataView} dataView Data view interface + * @param {number} offset Range start + * @param {number} length Range length + * @returns {object} Tag value + */ + function getTagValue(tagCode, map, dataView, offset, length) { + if (map.types[tagCode] === 'binary') { + return new Blob([dataView.buffer.slice(offset, offset + length)]) + } + if (map.types[tagCode] === 'Uint16') { + return dataView.getUint16(offset) + } + return getStringValue(dataView, offset, length) + } + + /** + * Combines IPTC value with existing ones. + * + * @param {object} value Existing IPTC field value + * @param {object} newValue New IPTC field value + * @returns {object} Resulting IPTC field value + */ + function combineTagValues(value, newValue) { + if (value === undefined) return newValue + if (value instanceof Array) { + value.push(newValue) + return value + } + return [value, newValue] + } + + /** + * Parses IPTC tags. + * + * @param {DataView} dataView Data view interface + * @param {number} segmentOffset Segment offset + * @param {number} segmentLength Segment length + * @param {object} data Data export object + * @param {object} includeTags Map of tags to include + * @param {object} excludeTags Map of tags to exclude + */ + function parseIptcTags( dataView, - startOffset, - sectionLength, - data + segmentOffset, + segmentLength, + data, + includeTags, + excludeTags ) { - /** - * Retrieves string for the given Buffer and range - * - * @param {Buffer} buffer IPTC buffer - * @param {number} start Range start - * @param {number} length Range length - * @returns {string} String value - */ - function getStringFromDB(buffer, start, length) { - var outstr = '' - for (var n = start; n < start + length; n++) { - outstr += String.fromCharCode(buffer.getUint8(n)) - } - return outstr - } - var fieldValue, dataSize, segmentType - var segmentStartPos = startOffset - while (segmentStartPos < startOffset + sectionLength) { - // we currently handle the 2: class of iptc tag + var value, tagSize, tagCode + var segmentEnd = segmentOffset + segmentLength + var offset = segmentOffset + while (offset < segmentEnd) { if ( - dataView.getUint8(segmentStartPos) === 0x1c && - dataView.getUint8(segmentStartPos + 1) === 0x02 + dataView.getUint8(offset) === 0x1c && // tag marker + dataView.getUint8(offset + 1) === 0x02 // record number, only handles v2 ) { - segmentType = dataView.getUint8(segmentStartPos + 2) - // only store data for known tags - if (segmentType in data.iptc.tags) { - dataSize = dataView.getInt16(segmentStartPos + 3) - fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize) - // Check if we already stored a value with this name - if (Object.prototype.hasOwnProperty.call(data.iptc, segmentType)) { - // Value already stored with this name, create multivalue field - if (data.iptc[segmentType] instanceof Array) { - data.iptc[segmentType].push(fieldValue) - } else { - data.iptc[segmentType] = [data.iptc[segmentType], fieldValue] - } - } else { - data.iptc[segmentType] = fieldValue + tagCode = dataView.getUint8(offset + 2) + if ( + (!includeTags || includeTags[tagCode]) && + (!excludeTags || !excludeTags[tagCode]) + ) { + tagSize = dataView.getInt16(offset + 3) + value = getTagValue(tagCode, data.iptc, dataView, offset + 5, tagSize) + data.iptc[tagCode] = combineTagValues(data.iptc[tagCode], value) + if (data.iptcOffsets) { + data.iptcOffsets[tagCode] = offset } } } - segmentStartPos++ + offset += 1 } } + /** + * Tests if field segment starts at offset. + * + * @param {DataView} dataView Data view interface + * @param {number} offset Segment offset + * @returns {boolean} True if '8BIM' exists at offset + */ + function isSegmentStart(dataView, offset) { + return ( + dataView.getUint32(offset) === 0x3842494d && // Photoshop segment start + dataView.getUint16(offset + 4) === 0x0404 // IPTC segment start + ) + } + + /** + * Returns header length. + * + * @param {DataView} dataView Data view interface + * @param {number} offset Segment offset + * @returns {number} Header length + */ + function getHeaderLength(dataView, offset) { + var length = dataView.getUint8(offset + 7) + if (length % 2 !== 0) length += 1 + // Check for pre photoshop 6 format + if (length === 0) { + // Always 4 + length = 4 + } + return length + } + loadImage.parseIptcData = function (dataView, offset, length, data, options) { if (options.disableIptc) { return } var markerLength = offset + length - // Found '8BIM' ? - var isFieldSegmentStart = function (dataView, offset) { - return ( - dataView.getUint32(offset) === 0x3842494d && - dataView.getUint16(offset + 4) === 0x0404 - ) - } - // Hunt forward, looking for the correct IPTC block signature: - // Reference: https://metacpan.org/pod/distribution/Image-MetaData-JPEG/lib/Image/MetaData/JPEG/Structures.pod#Structure-of-a-Photoshop-style-APP13-segment - // From https://github.com/exif-js/exif-js/blob/master/exif.js ~ line 474 on while (offset + 8 < markerLength) { - if (isFieldSegmentStart(dataView, offset)) { - var nameHeaderLength = dataView.getUint8(offset + 7) - if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1 - // Check for pre photoshop 6 format - if (nameHeaderLength === 0) { - // Always 4 - nameHeaderLength = 4 - } - var startOffset = offset + 8 + nameHeaderLength - if (startOffset > markerLength) { + if (isSegmentStart(dataView, offset)) { + var headerLength = getHeaderLength(dataView, offset) + var segmentOffset = offset + 8 + headerLength + if (segmentOffset > markerLength) { // eslint-disable-next-line no-console console.log('Invalid IPTC data: Invalid segment offset.') break } - var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength) - if (offset + sectionLength > markerLength) { + var segmentLength = dataView.getUint16(offset + 6 + headerLength) + if (offset + segmentLength > markerLength) { // eslint-disable-next-line no-console console.log('Invalid IPTC data: Invalid segment size.') break } // Create the iptc object to store the tags: - data.iptc = new loadImage.IptcMap() - // Parse the tags - return loadImage.parseIptcTags( + data.iptc = new IptcMap() + if (!options.disableIptcOffsets) { + data.iptcOffsets = new IptcMap() + } + parseIptcTags( dataView, - startOffset, - sectionLength, - data + segmentOffset, + segmentLength, + data, + options.includeIptcTags, + options.excludeIptcTags || { 202: true } // ObjectPreviewData ) + return } // eslint-disable-next-line no-param-reassign - offset++ + offset += 1 } - // eslint-disable-next-line no-console - console.log('No IPTC data at this offset - could be XMP') } // Registers this IPTC parser for the APP13 JPEG meta data segment: loadImage.metaDataParsers.jpeg[0xffed].push(loadImage.parseIptcData) + loadImage.IptcMap = IptcMap + // Adds the following properties to the parseMetaData callback data: - // * iptc: The iptc tags, parsed by the parseIptcData method + // - iptc: The iptc tags, parsed by the parseIptcData method // Adds the following options to the parseMetaData method: - // * disableIptc: Disables IPTC parsing. + // - disableIptc: Disables IPTC parsing when true. + // - disableIptcOffsets: Disables storing IPTC tag offsets when true. + // - includeIptcTags: A map of IPTC tags to include for parsing. + // - excludeIptcTags: A map of IPTC tags to exclude from parsing. }) diff --git a/test/test.js b/test/test.js index e33843c..6f2b039 100644 --- a/test/test.js +++ b/test/test.js @@ -989,6 +989,17 @@ }) }) + it('Should not parse Exif tags if disabled', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.exif).to.be.undefined + done() + }, + { disableExif: true } + ) + }) + it('Should parse Exif tag offsets', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exifOffsets).to.be.ok @@ -999,6 +1010,57 @@ }) }) + it('Should not parse Exif tag offsets if disabled', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.exifOffsets).to.be.undefined + done() + }, + { disableExifOffsets: true } + ) + }) + + it('Should only parse included Exif tags', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.exif).to.be.ok + expect(data.exif.get('Orientation')).to.equal(6) + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.exif).to.be.ok + expect(data.exif.get('Orientation')).to.be.undefined + done() + }, + { includeExifTags: { 0x0132: true } } // DateTime + ) + }, + { includeExifTags: { 0x0112: true } } // Orientation + ) + }) + + it('Should not parse excluded Exif tags', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.exif).to.be.ok + expect(data.exif.get('Orientation')).to.equal(6) + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.exif).to.be.ok + expect(data.exif.get('Orientation')).to.be.undefined + done() + }, + { excludeExifTags: { 0x0112: true } } // Orientation + ) + }, + { excludeExifTags: { 0x0132: true } } // DateTime + ) + }) + it('Should parse IPTC tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.iptc).to.be.ok @@ -1007,6 +1069,80 @@ }) }) + it('Should not parse IPTC tags if disabled', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.iptc).to.be.undefined + done() + }, + { disableIptc: true } + ) + }) + + it('Should parse IPTC tag offsets', function (done) { + loadImage.parseMetaData(blobJPEG, function (data) { + expect(data.iptcOffsets).to.be.ok + expect(data.iptcOffsets.get('ObjectName')).to.equal(0x44) + done() + }) + }) + + it('Should not parse IPTC tag offsets if disabled', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.iptcOffsets).to.be.undefined + done() + }, + { disableIptcOffsets: true } + ) + }) + + it('Should only parse included IPTC tags', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.iptc).to.be.ok + expect(data.iptc.get('ApplicationRecordVersion')).to.be.undefined + expect(data.iptc.get('ObjectName')).to.equal('blueimp.net') + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.iptc).to.be.ok + expect(data.iptc.get('ApplicationRecordVersion')).to.equal(4) + expect(data.iptc.get('ObjectName')).to.be.undefined + done() + }, + { includeIptcTags: { 0: true } } // ApplicationRecordVersion + ) + }, + { includeIptcTags: { 5: true } } // ObjectName + ) + }) + + it('Should not parse excluded IPTC tags', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.iptc).to.be.ok + expect(data.iptc.get('ApplicationRecordVersion')).to.equal(4) + expect(data.iptc.get('ObjectName')).to.be.undefined + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.iptc).to.be.ok + expect(data.iptc.get('ApplicationRecordVersion')).to.be.undefined + expect(data.iptc.get('ObjectName')).to.equal('blueimp.net') + done() + }, + { excludeIptcTags: { 0: true } } // Orientation + ) + }, + { excludeIptcTags: { 5: true } } // DateTime + ) + }) + it('Should parse the complete image head', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.imageHead).to.be.ok @@ -1021,6 +1157,17 @@ }) }) + it('Should not parse the complete image head if disabled', function (done) { + loadImage.parseMetaData( + blobJPEG, + function (data) { + expect(data.imageHead).to.be.undefined + done() + }, + { disableImageHead: true } + ) + }) + it('Should parse meta data automatically', function (done) { expect( loadImage( From 79fdc82d6fe5fdd4ce037d184202708dac87349c Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sat, 11 Apr 2020 23:54:11 +0900 Subject: [PATCH 026/188] Fix ObjectURL revoke tests for Edge Legacy and IE. --- test/test.js | 72 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/test/test.js b/test/test.js index 6f2b039..35f9eab 100644 --- a/test/test.js +++ b/test/test.js @@ -68,6 +68,25 @@ } } + /** + * Request helper function. + * + * @param {string} url URL to request + * @param {Function} callback Request callback + */ + function request(url, callback) { + var xhr = new XMLHttpRequest() + xhr.onload = callback + xhr.onerror = callback + try { + xhr.open('GET', url, true) + } catch (e) { + callback.call(xhr, e) + return + } + xhr.send() + } + describe('Loading', function () { it('Return an object with onload and onerror methods', function () { var img = loadImage(blobGIF, function () {}) @@ -116,32 +135,37 @@ ).to.be.ok }) - it('Keep object URL if noRevoke is true', function (done) { - expect( - loadImage( - blobGIF, - function (img) { - loadImage(img.src, function (img2) { - expect(img.width).to.equal(img2.width) - expect(img.height).to.equal(img2.height) + describe('ObjectURL revoke', function () { + // Using XMLHttpRequest via the request helper function to test Object + // URLs to work around Edge Legacy and IE caching image URLs. + if (!window.XMLHttpRequest) return + + it('Keep object URL if noRevoke is set to true', function (done) { + expect( + loadImage( + blobGIF, + function (img) { + request(img.src, function (event) { + expect(event.type).to.equal('load') + done() + }) + }, + { noRevoke: true } + ) + ).to.be.ok + }) + + it('Discard object URL if noRevoke is not set', function (done) { + expect( + loadImage(blobGIF, function (img) { + request(img.src, function (event) { + // IE throws an error that has no type property: + expect(event.type).to.be.oneOf(['error', undefined]) done() }) - }, - { noRevoke: true } - ) - ).to.be.ok - }) - - it('Discard object URL if noRevoke is undefined/false', function (done) { - expect( - loadImage(blobGIF, function (img) { - loadImage(img.src, function (img2) { - expect(img2).to.be.an.instanceOf(window.Event) - expect(img2.type).to.equal('error') - done() }) - }) - ).to.be.ok + ).to.be.ok + }) }) }) @@ -1232,7 +1256,7 @@ it('Should fetch image URL as blob if meta option is true', function (done) { expect( loadImage( - // IE11 does not allow XMLHttpRequest access to data URLs, + // IE does not allow XMLHttpRequest access to data URLs, // so we use an ObjectURL instead of imageUrlJPEG directly: loadImage.createObjectURL(blobJPEG), function (img, data) { From 95b46948e57a6c719d119db3ac1454320c0f2d02 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sat, 11 Apr 2020 23:57:56 +0900 Subject: [PATCH 027/188] Add url to load/error event handler params. --- js/load-image.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/js/load-image.js b/js/load-image.js index a93ea13..ea633e4 100644 --- a/js/load-image.js +++ b/js/load-image.js @@ -28,10 +28,10 @@ var img = document.createElement('img') var url img.onerror = function (event) { - return loadImage.onerror(img, event, file, callback, options) + return loadImage.onerror(img, event, file, url, callback, options) } img.onload = function (event) { - return loadImage.onload(img, event, file, callback, options) + return loadImage.onload(img, event, file, url, callback, options) } if (typeof file === 'string') { loadImage.fetchBlob( @@ -58,7 +58,7 @@ // (Firefox 3.6) support the File API but not Blobs: loadImage.isInstanceOf('File', file) ) { - url = img._objectURL = loadImage.createObjectURL(file) + url = loadImage.createObjectURL(file) if (url) { img.src = url return img @@ -83,13 +83,12 @@ /** * Helper function to revoke an object URL * - * @param {HTMLImageElement} img Image element + * @param {string} url Blob Object URL * @param {object} [options] Options object */ - function revokeHelper(img, options) { - if (img._objectURL && !(options && options.noRevoke)) { - loadImage.revokeObjectURL(img._objectURL) - delete img._objectURL + function revokeHelper(url, options) { + if (url && url.slice(0, 5) === 'blob:' && !(options && options.noRevoke)) { + loadImage.revokeObjectURL(url) } } @@ -109,15 +108,15 @@ callback(img, data) } - loadImage.onerror = function (img, event, file, callback, options) { - revokeHelper(img, options) + loadImage.onerror = function (img, event, file, url, callback, options) { + revokeHelper(url, options) if (callback) { callback.call(img, event) } } - loadImage.onload = function (img, event, file, callback, options) { - revokeHelper(img, options) + loadImage.onload = function (img, event, file, url, callback, options) { + revokeHelper(url, options) if (callback) { loadImage.transform(img, options, callback, file, { originalWidth: img.naturalWidth || img.width, From 6b38e52d6525415f08785ee571529f63b72cbc51 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 12 Apr 2020 00:00:47 +0900 Subject: [PATCH 028/188] Remove the meta dependency from the fetch plugin. --- js/load-image-fetch.js | 64 ++++++++++++++++++------------------------ js/load-image-meta.js | 5 ---- js/load-image.js | 48 ++++++++++++++++++++----------- 3 files changed, 58 insertions(+), 59 deletions(-) diff --git a/js/load-image-fetch.js b/js/load-image-fetch.js index 83fbfd5..7aabda1 100644 --- a/js/load-image-fetch.js +++ b/js/load-image-fetch.js @@ -15,9 +15,9 @@ 'use strict' if (typeof define === 'function' && define.amd) { // Register as an anonymous AMD module: - define(['./load-image', './load-image-meta'], factory) + define(['./load-image'], factory) } else if (typeof module === 'object' && module.exports) { - factory(require('./load-image'), require('./load-image-meta')) + factory(require('./load-image')) } else { // Browser globals: factory(window.loadImage) @@ -27,19 +27,14 @@ if (typeof fetch !== 'undefined' && typeof Request !== 'undefined') { loadImage.fetchBlob = function (url, callback, options) { - if (loadImage.hasMetaOption(options)) { - fetch(new Request(url, options)) - .then(function (response) { - return response.blob() - }) - .then(callback) - .catch(function (err) { - console.log(err) // eslint-disable-line no-console - callback() - }) - } else { - callback() - } + fetch(new Request(url, options)) + .then(function (response) { + return response.blob() + }) + .then(callback) + .catch(function (err) { + callback(null, err) + }) } } else if ( // Check for XHR2 support: @@ -47,29 +42,24 @@ typeof ProgressEvent !== 'undefined' ) { loadImage.fetchBlob = function (url, callback, options) { - if (loadImage.hasMetaOption(options)) { - // eslint-disable-next-line no-param-reassign - options = options || {} - var req = new XMLHttpRequest() - req.open(options.method || 'GET', url) - if (options.headers) { - Object.keys(options.headers).forEach(function (key) { - req.setRequestHeader(key, options.headers[key]) - }) - } - req.withCredentials = options.credentials === 'include' - req.responseType = 'blob' - req.onload = function () { - callback(req.response) - } - req.onerror = req.onabort = req.ontimeout = function (e) { - console.log(e) // eslint-disable-line no-console - callback() - } - req.send(options.body) - } else { - callback() + // eslint-disable-next-line no-param-reassign + options = options || {} + var req = new XMLHttpRequest() + req.open(options.method || 'GET', url) + if (options.headers) { + Object.keys(options.headers).forEach(function (key) { + req.setRequestHeader(key, options.headers[key]) + }) + } + req.withCredentials = options.credentials === 'include' + req.responseType = 'blob' + req.onload = function () { + callback(req.response) + } + req.onerror = req.onabort = req.ontimeout = function (err) { + callback(null, err) } + req.send(options.body) } } }) diff --git a/js/load-image-meta.js b/js/load-image-meta.js index 2bcb8e6..ecdb077 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -179,11 +179,6 @@ ) } - // Determines if meta data should be loaded automatically: - loadImage.hasMetaOption = function (options) { - return options && options.meta - } - var originalTransform = loadImage.transform loadImage.transform = function (img, options, callback, file, data) { if (loadImage.hasMetaOption(options)) { diff --git a/js/load-image.js b/js/load-image.js index ea633e4..50a36cd 100644 --- a/js/load-image.js +++ b/js/load-image.js @@ -27,6 +27,26 @@ function loadImage(file, callback, options) { var img = document.createElement('img') var url + /** + * Callback for the fetchBlob call. + * + * @param {Blob} blob Blob object + * @param {Error} err Error object + */ + function fetchBlobCallback(blob, err) { + if (err) console.log(err) // eslint-disable-line no-console + if (blob && loadImage.isInstanceOf('Blob', blob)) { + // eslint-disable-next-line no-param-reassign + file = blob + url = loadImage.createObjectURL(file) + } else { + url = file + if (options && options.crossOrigin) { + img.crossOrigin = options.crossOrigin + } + } + img.src = url + } img.onerror = function (event) { return loadImage.onerror(img, event, file, url, callback, options) } @@ -34,23 +54,11 @@ return loadImage.onload(img, event, file, url, callback, options) } if (typeof file === 'string') { - loadImage.fetchBlob( - file, - function (blob) { - if (blob && loadImage.isInstanceOf('Blob', blob)) { - // eslint-disable-next-line no-param-reassign - file = blob - url = loadImage.createObjectURL(file) - } else { - url = file - if (options && options.crossOrigin) { - img.crossOrigin = options.crossOrigin - } - } - img.src = url - }, - options - ) + if (loadImage.hasMetaOption(options)) { + loadImage.fetchBlob(file, fetchBlobCallback, options) + } else { + fetchBlobCallback() + } return img } else if ( loadImage.isInstanceOf('Blob', file) || @@ -92,6 +100,12 @@ } } + // Determines if meta data should be loaded automatically. + // Requires the load image meta extension to load meta data. + loadImage.hasMetaOption = function (options) { + return options && options.meta + } + // If the callback given to this function returns a blob, it is used as image // source instead of the original url and overrides the file argument used in // the onload and onerror event callbacks: From ff22e11daa25d0cff328124abf1cca22ffaa4be5 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 12 Apr 2020 00:09:31 +0900 Subject: [PATCH 029/188] 3.0.0 --- js/load-image.all.min.js | 2 +- js/load-image.all.min.js.map | 2 +- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/load-image.all.min.js b/js/load-image.all.min.js index f09182f..a1522ed 100644 --- a/js/load-image.all.min.js +++ b/js/load-image.all.min.js @@ -1,2 +1,2 @@ -!function(o){"use strict";function r(t,i,a){var o,n=document.createElement("img");return n.onerror=function(e){return r.onerror(n,e,t,i,a)},n.onload=function(e){return r.onload(n,e,t,i,a)},"string"==typeof t?(r.fetchBlob(t,function(e){e&&r.isInstanceOf("Blob",e)?o=r.createObjectURL(t=e):(o=t,a&&a.crossOrigin&&(n.crossOrigin=a.crossOrigin)),n.src=o},a),n):r.isInstanceOf("Blob",t)||r.isInstanceOf("File",t)?(o=n._objectURL=r.createObjectURL(t))?(n.src=o,n):r.readFile(t,function(e){var t=e.target;t&&t.result?n.src=t.result:i&&i(e)}):void 0}var t=o.createObjectURL&&o||o.URL&&URL.revokeObjectURL&&URL||o.webkitURL&&webkitURL;function n(e,t){!e._objectURL||t&&t.noRevoke||(r.revokeObjectURL(e._objectURL),delete e._objectURL)}r.fetchBlob=function(e,t){t()},r.isInstanceOf=function(e,t){return Object.prototype.toString.call(t)==="[object "+e+"]"},r.transform=function(e,t,i,a,o){i(e,o)},r.onerror=function(e,t,i,a,o){n(e,o),a&&a.call(e,t)},r.onload=function(e,t,i,a,o){n(e,o),a&&r.transform(e,o,a,i,{originalWidth:e.naturalWidth||e.width,originalHeight:e.naturalHeight||e.height})},r.createObjectURL=function(e){return!!t&&t.createObjectURL(e)},r.revokeObjectURL=function(e){return!!t&&t.revokeObjectURL(e)},r.readFile=function(e,t,i){if(o.FileReader){var a=new FileReader;if(a.onload=a.onerror=t,a[i=i||"readAsDataURL"])return a[i](e),a}return!1},"function"==typeof define&&define.amd?define(function(){return r}):"object"==typeof module&&module.exports?module.exports=r:o.loadImage=r}("undefined"!=typeof window&&window||this),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image"],e):"object"==typeof module&&module.exports?e(require("./load-image")):e(window.loadImage)}(function(x){"use strict";var n=x.transform;x.transform=function(e,t,i,a,o){n.call(x,x.scale(e,t,o),t,i,a,o)},x.transformCoordinates=function(){},x.getTransformedOptions=function(e,t){var i,a,o,n,r=t.aspectRatio;if(!r)return t;for(a in i={},t)Object.prototype.hasOwnProperty.call(t,a)&&(i[a]=t[a]);return i.crop=!0,r<(o=e.naturalWidth||e.width)/(n=e.naturalHeight||e.height)?(i.maxWidth=n*r,i.maxHeight=n):(i.maxWidth=o,i.maxHeight=o/r),i},x.renderImageToCanvas=function(e,t,i,a,o,n,r,s,l,c,d){var u=e.getContext("2d");return!1===d.imageSmoothingEnabled?u.imageSmoothingEnabled=!1:d.imageSmoothingQuality&&(u.imageSmoothingQuality=d.imageSmoothingQuality),u.drawImage(t,i,a,o,n,r,s,l,c),e},x.hasCanvasOption=function(e){return e.canvas||e.crop||!!e.aspectRatio},x.scale=function(e,t,i){t=t||{};var a,o,n,r,s,l,c,d,u,f,g,p=document.createElement("canvas"),m=e.getContext||x.hasCanvasOption(t)&&p.getContext,h=e.naturalWidth||e.width,A=e.naturalHeight||e.height,b=h,y=A;function S(){var e=Math.max((n||b)/b,(r||y)/y);1r.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if((a=p.metaDataParsers.jpeg[t])&&!u.disableMetaDataParsers)for(o=0;oe.byteLength))return g.createObjectURL(new Blob([e.buffer.slice(t,t+i)]));console.log("Invalid Exif data: Invalid thumbnail data.")},g.exifTagTypes={1:{getValue:function(e,t){return e.getUint8(t)},size:1},2:{getValue:function(e,t){return String.fromCharCode(e.getUint8(t))},size:1,ascii:!0},3:{getValue:function(e,t,i){return e.getUint16(t,i)},size:2},4:{getValue:function(e,t,i){return e.getUint32(t,i)},size:4},5:{getValue:function(e,t,i){return e.getUint32(t,i)/e.getUint32(t+4,i)},size:8},9:{getValue:function(e,t,i){return e.getInt32(t,i)},size:4},10:{getValue:function(e,t,i){return e.getInt32(t,i)/e.getInt32(t+4,i)},size:8}},g.exifTagTypes[7]=g.exifTagTypes[1],g.getExifValue=function(e,t,i,a,o,n){var r,s,l,c,d,u,f=g.exifTagTypes[a];if(f){if(!((s=4<(r=f.size*o)?t+e.getUint32(i+8,n):i+8)+r>e.byteLength)){if(1===o)return f.getValue(e,s,n);for(l=[],c=0;ce.byteLength)console.log("Invalid Exif data: Invalid directory offset.");else{if(!((r=i+2+12*(n=e.getUint16(i,a)))+4>e.byteLength)){for(s=0;se.byteLength)console.log("Invalid Exif data: Invalid segment size.");else if(0===e.getUint16(t+8)){switch(e.getUint16(l)){case 18761:n=!0;break;case 19789:n=!1;break;default:return void console.log("Invalid Exif data: Invalid byte alignment marker.")}42===e.getUint16(l+2,n)?(r=e.getUint32(l+4,n),a.exif=new g.ExifMap,o.disableExifOffsets||(a.exifOffsets=new g.ExifMap,a.exifTiffOffset=l,a.exifLittleEndian=n),(r=g.parseExifTags(e,l,l+r,n,a))&&!o.disableExifThumbnail&&(s={exif:{}},r=g.parseExifTags(e,l,l+r,n,s),s.exif[513]&&(a.exif.Thumbnail=g.getExifThumbnail(e,l+s.exif[513],s.exif[514]))),a.exif[34665]&&!o.disableExifSub&&g.parseExifTags(e,l,l+a.exif[34665],n,a),a.exif[34853]&&!o.disableExifGps&&g.parseExifTags(e,l,l+a.exif[34853],n,a)):console.log("Invalid Exif data: Missing TIFF marker.")}else console.log("Invalid Exif data: Missing byte alignment offset.")}},g.metaDataParsers.jpeg[65505].push(g.parseExifData),g.exifWriters={274:function(e,t,i){return new DataView(e,t.exifOffsets[274]+8,2).setUint16(0,i,t.exifLittleEndian),e}},g.writeExifData=function(e,t,i,a){g.exifWriters[t.exif.map[i]](e,t,a)}}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-exif"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-exif")):e(window.loadImage)}(function(e){"use strict";e.ExifMap.prototype.tags={256:"ImageWidth",257:"ImageHeight",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer",40965:"InteroperabilityIFDPointer",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber",0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},e.ExifMap.prototype.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},e.ExifMap.prototype.getText=function(e){var t=this.get(e);switch(e){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[e][t];case"ExifVersion":case"FlashpixVersion":if(!t)return;return String.fromCharCode(t[0],t[1],t[2],t[3]);case"ComponentsConfiguration":if(!t)return;return this.stringValues[e][t[0]]+this.stringValues[e][t[1]]+this.stringValues[e][t[2]]+this.stringValues[e][t[3]];case"GPSVersionID":if(!t)return;return t[0]+"."+t[1]+"."+t[2]+"."+t[3]}return String(t)},function(e){var t,i=e.tags,a=e.map;for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(a[i[t]]=t)}(e.ExifMap.prototype),e.ExifMap.prototype.getAll=function(){var e,t,i={};for(e in this)Object.prototype.hasOwnProperty.call(this,e)&&(t=this.tags[e])&&(i[t]=this.getText(t));return i}}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-meta"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-meta")):e(window.loadImage)}(function(u){"use strict";u.IptcMap=function(){return this},u.IptcMap.prototype.map={ObjectName:5},u.IptcMap.prototype.get=function(e){return this[e]||this[this.map[e]]},u.parseIptcTags=function(e,t,i,a){function o(e,t,i){for(var a="",o=t;or.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if((n=m.metaDataParsers.jpeg[t])&&!u.disableMetaDataParsers)for(a=0;ae.byteLength)){if(1===a)return f.getValue(e,s,o);for(l=[],c=0;ce.byteLength)console.log("Invalid Exif data: Invalid directory offset.");else{if(!((c=i+2+12*(l=e.getUint16(i,n)))+4>e.byteLength)){for(d=0;dc.byteLength)console.log("Invalid Exif data: Invalid segment size.");else if(0===c.getUint16(e+8)){switch(c.getUint16(m)){case 18761:u=!0;break;case 19789:u=!1;break;default:return void console.log("Invalid Exif data: Invalid byte alignment marker.")}42===c.getUint16(m+2,u)?(n=c.getUint32(m+4,u),d.exif=new h,i.disableExifOffsets||(d.exifOffsets=new h,d.exifTiffOffset=m,d.exifLittleEndian=u),(n=p(c,m,m+n,u,d.exif,d.exifOffsets,f,g))&&!i.disableExifThumbnail&&(n=p(c,m,m+n,u,d.exif,d.exifOffsets,f,g),d.exif[513]&&d.exif[514]&&(d.exif[513]=function(e,t,i){if(i&&!(t+i>e.byteLength))return new Blob([e.buffer.slice(t,t+i)],{type:"image/jpeg"});console.log("Invalid Exif data: Invalid thumbnail data.")}(c,m+d.exif[513],d.exif[514]))),Object.keys(d.exif.privateIFDs).forEach(function(e){var t,i,n,a,o,r,s,l;i=e,n=c,a=m,o=u,r=f,s=g,(l=(t=d).exif[i])&&(t.exif[i]=new h(i),t.exifOffsets&&(t.exifOffsets[i]=new h(i)),p(n,a,a+l,o,t.exif[i],t.exifOffsets&&t.exifOffsets[i],r&&r[i],s&&s[i]))})):console.log("Invalid Exif data: Missing TIFF marker.")}else console.log("Invalid Exif data: Missing byte alignment offset.")}},a.metaDataParsers.jpeg[65505].push(a.parseExifData),a.exifWriters={274:function(e,t,i){return new DataView(e,t.exifOffsets[274]+8,2).setUint16(0,i,t.exifLittleEndian),e}},a.writeExifData=function(e,t,i,n){a.exifWriters[t.exif.map[i]](e,t,n)},a.ExifMap=h}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-exif"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-exif")):e(window.loadImage)}(function(e){"use strict";var a=e.ExifMap.prototype;a.tags={256:"ImageWidth",257:"ImageHeight",258:"BitsPerSample",259:"Compression",262:"PhotometricInterpretation",274:"Orientation",277:"SamplesPerPixel",284:"PlanarConfiguration",530:"YCbCrSubSampling",531:"YCbCrPositioning",282:"XResolution",283:"YResolution",296:"ResolutionUnit",273:"StripOffsets",278:"RowsPerStrip",279:"StripByteCounts",513:"JPEGInterchangeFormat",514:"JPEGInterchangeFormatLength",301:"TransferFunction",318:"WhitePoint",319:"PrimaryChromaticities",529:"YCbCrCoefficients",532:"ReferenceBlackWhite",306:"DateTime",270:"ImageDescription",271:"Make",272:"Model",305:"Software",315:"Artist",33432:"Copyright",34665:{36864:"ExifVersion",40960:"FlashpixVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",42240:"Gamma",37121:"ComponentsConfiguration",37122:"CompressedBitsPerPixel",37500:"MakerNote",37510:"UserComment",40964:"RelatedSoundFile",36867:"DateTimeOriginal",36868:"DateTimeDigitized",37520:"SubSecTime",37521:"SubSecTimeOriginal",37522:"SubSecTimeDigitized",33434:"ExposureTime",33437:"FNumber",34850:"ExposureProgram",34852:"SpectralSensitivity",34855:"PhotographicSensitivity",34856:"OECF",34864:"SensitivityType",34865:"StandardOutputSensitivity",34866:"RecommendedExposureIndex",34867:"ISOSpeed",34868:"ISOSpeedLatitudeyyy",34869:"ISOSpeedLatitudezzz",37377:"ShutterSpeedValue",37378:"ApertureValue",37379:"BrightnessValue",37380:"ExposureBias",37381:"MaxApertureValue",37382:"SubjectDistance",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37396:"SubjectArea",37386:"FocalLength",41483:"FlashEnergy",41484:"SpatialFrequencyResponse",41486:"FocalPlaneXResolution",41487:"FocalPlaneYResolution",41488:"FocalPlaneResolutionUnit",41492:"SubjectLocation",41493:"ExposureIndex",41495:"SensingMethod",41728:"FileSource",41729:"SceneType",41730:"CFAPattern",41985:"CustomRendered",41986:"ExposureMode",41987:"WhiteBalance",41988:"DigitalZoomRatio",41989:"FocalLengthIn35mmFilm",41990:"SceneCaptureType",41991:"GainControl",41992:"Contrast",41993:"Saturation",41994:"Sharpness",41995:"DeviceSettingDescription",41996:"SubjectDistanceRange",42016:"ImageUniqueID",42032:"CameraOwnerName",42033:"BodySerialNumber",42034:"LensSpecification",42035:"LensMake",42036:"LensModel",42037:"LensSerialNumber"},34853:{0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude",5:"GPSAltitudeRef",6:"GPSAltitude",7:"GPSTimeStamp",8:"GPSSatellites",9:"GPSStatus",10:"GPSMeasureMode",11:"GPSDOP",12:"GPSSpeedRef",13:"GPSSpeed",14:"GPSTrackRef",15:"GPSTrack",16:"GPSImgDirectionRef",17:"GPSImgDirection",18:"GPSMapDatum",19:"GPSDestLatitudeRef",20:"GPSDestLatitude",21:"GPSDestLongitudeRef",22:"GPSDestLongitude",23:"GPSDestBearingRef",24:"GPSDestBearing",25:"GPSDestDistanceRef",26:"GPSDestDistance",27:"GPSProcessingMethod",28:"GPSAreaInformation",29:"GPSDateStamp",30:"GPSDifferential",31:"GPSHPositioningError"},40965:{1:"InteroperabilityIndex"}},a.stringValues={ExposureProgram:{0:"Undefined",1:"Manual",2:"Normal program",3:"Aperture priority",4:"Shutter priority",5:"Creative program",6:"Action program",7:"Portrait mode",8:"Landscape mode"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{0:"Unknown",1:"Daylight",2:"Fluorescent",3:"Tungsten (incandescent light)",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 - 5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire",1:"Flash fired",5:"Strobe return light not detected",7:"Strobe return light detected",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},SensingMethod:{1:"Undefined",2:"One-chip color area sensor",3:"Two-chip color area sensor",4:"Three-chip color area sensor",5:"Color sequential area sensor",7:"Trilinear sensor",8:"Color sequential linear sensor"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},SceneType:{1:"Directly photographed"},CustomRendered:{0:"Normal process",1:"Custom process"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},GainControl:{0:"None",1:"Low gain up",2:"High gain up",3:"Low gain down",4:"High gain down"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},SubjectDistanceRange:{0:"Unknown",1:"Macro",2:"Close view",3:"Distant view"},FileSource:{3:"DSC"},ComponentsConfiguration:{0:"",1:"Y",2:"Cb",3:"Cr",4:"R",5:"G",6:"B"},Orientation:{1:"top-left",2:"top-right",3:"bottom-right",4:"bottom-left",5:"left-top",6:"right-top",7:"right-bottom",8:"left-bottom"}},a.getText=function(e){var t=this.get(e);switch(e){case"LightSource":case"Flash":case"MeteringMode":case"ExposureProgram":case"SensingMethod":case"SceneCaptureType":case"SceneType":case"CustomRendered":case"WhiteBalance":case"GainControl":case"Contrast":case"Saturation":case"Sharpness":case"SubjectDistanceRange":case"FileSource":case"Orientation":return this.stringValues[e][t];case"ExifVersion":case"FlashpixVersion":if(!t)return;return String.fromCharCode(t[0],t[1],t[2],t[3]);case"ComponentsConfiguration":if(!t)return;return this.stringValues[e][t[0]]+this.stringValues[e][t[1]]+this.stringValues[e][t[2]]+this.stringValues[e][t[3]];case"GPSVersionID":if(!t)return;return t[0]+"."+t[1]+"."+t[2]+"."+t[3]}return String(t)},a.getAll=function(){var e,t,i,n={};for(e in this)Object.prototype.hasOwnProperty.call(this,e)&&((t=this[e])&&t.getAll?n[this.privateIFDs[e].name]=t.getAll():(i=this.tags[e])&&(n[i]=this.getText(i)));return n},a.getName=function(e){var t=this.tags[e];return"object"==typeof t?this.privateIFDs[e].name:t},function(){var e,t,i,n=a.tags;for(e in n)if(Object.prototype.hasOwnProperty.call(n,e))if(t=a.privateIFDs[e])for(e in i=n[e])Object.prototype.hasOwnProperty.call(i,e)&&(t.map[i[e]]=Number(e));else a.map[n[e]]=Number(e)}()}),function(e){"use strict";"function"==typeof define&&define.amd?define(["./load-image","./load-image-meta"],e):"object"==typeof module&&module.exports?e(require("./load-image"),require("./load-image-meta")):e(window.loadImage)}(function(e){"use strict";function g(){}function m(e,t,i,n,a){return"binary"===t.types[e]?new Blob([i.buffer.slice(n,n+a)]):"Uint16"===t.types[e]?i.getUint16(n):function(e,t,i){for(var n="",a=t+i,o=t;o Date: Mon, 20 Apr 2020 12:36:13 +0600 Subject: [PATCH 030/188] Compatibility with jQuery 3.5.0 --- js/demo/demo.js | 6 +++--- js/vendor/jquery.Jcrop.js | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/js/demo/demo.js b/js/demo/demo.js index 6dc9fa3..b42cdcd 100644 --- a/js/demo/demo.js +++ b/js/demo/demo.js @@ -29,7 +29,7 @@ $(function () { * @param {object} tags Tags object */ function displayTagData(node, tags) { - var table = $('
') + var table = $('
') var row = $('') var cell = $('') var prop @@ -59,7 +59,7 @@ $(function () { */ function displayThumbnailImage(node, thumbnail, options) { if (thumbnail) { - var link = $('') + var link = $('') .attr('href', loadImage.createObjectURL(thumbnail)) .attr('download', 'thumbnail.jpg') .appendTo(node) @@ -105,7 +105,7 @@ $(function () { result.children().replaceWith($('Loading image file failed')) return } - var content = $('').append(img) + var content = $('').append(img) result.children().replaceWith(content) if (data) { if (img.getContext) { diff --git a/js/vendor/jquery.Jcrop.js b/js/vendor/jquery.Jcrop.js index 41f80e6..a879391 100755 --- a/js/vendor/jquery.Jcrop.js +++ b/js/vendor/jquery.Jcrop.js @@ -329,7 +329,7 @@ boundy = $img.height(), - $div = $('
').width(boundx).height(boundy).addClass(cssClass('holder')).css({ + $div = $('
').width(boundx).height(boundy).addClass(cssClass('holder')).css({ position: 'relative', backgroundColor: options.bgColor }).insertAfter($origimg).append($img); @@ -338,19 +338,19 @@ $div.addClass(options.addClass); } - var $img2 = $('
'), + var $img2 = $('
'), - $img_holder = $('
') + $img_holder = $('
') .width('100%').height('100%').css({ zIndex: 310, position: 'absolute', overflow: 'hidden' }), - $hdl_holder = $('
') + $hdl_holder = $('
') .width('100%').height('100%').css('zIndex', 320), - $sel = $('
') + $sel = $('
') .css({ position: 'absolute', zIndex: 600 @@ -737,7 +737,7 @@ // Shade Module {{{ var Shade = (function() { var enabled = false, - holder = $('
').css({ + holder = $('
').css({ position: 'absolute', zIndex: 240, opacity: 0 @@ -779,7 +779,7 @@ }); } function createShade() { - return $('
').css({ + return $('
').css({ position: 'absolute', backgroundColor: options.shadeColor||options.bgColor }).appendTo(holder); @@ -863,7 +863,7 @@ // Private Methods function insertBorder(type) //{{{ { - var jq = $('
').css({ + var jq = $('
').css({ position: 'absolute', opacity: options.borderOpacity }).addClass(cssClass(type)); @@ -873,7 +873,7 @@ //}}} function dragDiv(ord, zi) //{{{ { - var jq = $('
').mousedown(createDragger(ord)).css({ + var jq = $('
').mousedown(createDragger(ord)).css({ cursor: ord + '-resize', position: 'absolute', zIndex: zi @@ -1226,7 +1226,7 @@ width: '12px' }).addClass('jcrop-keymgr'), - $keywrap = $('
').css({ + $keywrap = $('
').css({ position: 'absolute', overflow: 'hidden' }).append($keymgr); From 804fd90a10177d7a9ccdbdfde2a663046cb02a8e Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 20 Apr 2020 22:31:56 +0900 Subject: [PATCH 031/188] Remove unnecessary whitespace. --- README.md | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index cc067d4..3c7a154 100644 --- a/README.md +++ b/README.md @@ -240,100 +240,103 @@ Defines the minimum height of the img/canvas element. The width of the sub-rectangle of the source image to draw into the destination canvas. - Defaults to the source image width and requires `canvas: true`. +Defaults to the source image width and requires `canvas: true`. ### sourceHeight The height of the sub-rectangle of the source image to draw into the destination canvas. - Defaults to the source image height and requires `canvas: true`. +Defaults to the source image height and requires `canvas: true`. ### top The top margin of the sub-rectangle of the source image. - Defaults to `0` and requires `canvas: true`. +Defaults to `0` and requires `canvas: true`. ### right The right margin of the sub-rectangle of the source image. - Defaults to `0` and requires `canvas: true`. +Defaults to `0` and requires `canvas: true`. ### bottom The bottom margin of the sub-rectangle of the source image. - Defaults to `0` and requires `canvas: true`. +Defaults to `0` and requires `canvas: true`. ### left The left margin of the sub-rectangle of the source image. - Defaults to `0` and requires `canvas: true`. +Defaults to `0` and requires `canvas: true`. ### contain Scales the image up/down to contain it in the max dimensions if set to `true`. - This emulates the CSS feature [background-image: contain](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#contain). +This emulates the CSS feature +[background-image: contain](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#contain). ### cover Scales the image up/down to cover the max dimensions with the image dimensions if set to `true`. - This emulates the CSS feature [background-image: cover](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#cover). +This emulates the CSS feature +[background-image: cover](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#cover). ### aspectRatio Crops the image to the given aspect ratio (e.g. `16/9`). - Setting the `aspectRatio` also enables the `crop` option. +Setting the `aspectRatio` also enables the `crop` option. ### pixelRatio Defines the ratio of the canvas pixels to the physical image pixels on the screen. - Should be set to `window.devicePixelRatio` unless the scaled image is not rendered -on screen. - Defaults to `1` and requires `canvas: true`. +Should be set to `window.devicePixelRatio` unless the scaled image is not +rendered on screen. +Defaults to `1` and requires `canvas: true`. ### downsamplingRatio Defines the ratio in which the image is downsampled. - By default, images are downsampled in one step. With a ratio of `0.5`, each step +By default, images are downsampled in one step. With a ratio of `0.5`, each step scales the image to half the size, before reaching the target dimensions. - Requires `canvas: true`. +Requires `canvas: true`. ### imageSmoothingEnabled If set to `false`, [disables image smoothing](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled). - Defaults to `true` and requires `canvas: true`. +Defaults to `true` and requires `canvas: true`. ### imageSmoothingQuality Sets the [quality of image smoothing](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality). - Possible values: `'low'`, `'medium'`, `'high'` - Defaults to `'low'` and requires `canvas: true`. +Possible values: `'low'`, `'medium'`, `'high'` +Defaults to `'low'` and requires `canvas: true`. ### crop Crops the image to the maxWidth/maxHeight constraints if set to `true`. - Enabling the `crop` option also enables the `canvas` option. +Enabling the `crop` option also enables the `canvas` option. ### orientation Transform the canvas according to the specified Exif orientation, which can be an `integer` in the range of `1` to `8` or the boolean value `true`. - When set to `true`, it will set the orientation value based on the EXIF data of +When set to `true`, it will set the orientation value based on the EXIF data of the image, which will be parsed automatically if the exif library is available. Setting `orientation` to an integer in the range of `2` to `8` enables the `canvas` option. - Setting `orientation` to `true` enables the `canvas` and `meta` options, unless -the browser supports automatic image orientation (see [browser support for image-orientation](https://caniuse.com/#feat=css-image-orientation)). +Setting `orientation` to `true` enables the `canvas` and `meta` options, unless +the browser supports automatic image orientation (see +[browser support for image-orientation](https://caniuse.com/#feat=css-image-orientation)). ### meta Automatically parses the image meta data if set to `true`. - The meta data is passed to the callback as part of the second argument. - If the file is given as URL and the browser supports the +The meta data is passed to the callback as part of the second argument. +If the file is given as URL and the browser supports the [fetch API](https://developer.mozilla.org/en/docs/Web/API/Fetch_API), fetches the file as Blob to be able to parse the meta data. From 3f7b50eab933a339b1dac49b284581957b245c85 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 20 Apr 2020 22:43:51 +0900 Subject: [PATCH 032/188] Clarify meta option. --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c7a154..8a1e09a 100644 --- a/README.md +++ b/README.md @@ -335,10 +335,13 @@ the browser supports automatic image orientation (see ### meta Automatically parses the image meta data if set to `true`. -The meta data is passed to the callback as part of the second argument. +If meta data has been found, the data object passed as second argument to the +callback function has additional properties (see +[meta data parsing](#meta-data-parsing)). If the file is given as URL and the browser supports the -[fetch API](https://developer.mozilla.org/en/docs/Web/API/Fetch_API), fetches -the file as Blob to be able to parse the meta data. +[fetch API](https://developer.mozilla.org/en/docs/Web/API/Fetch_API) or the XHR +[responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType) +`blob`, fetches the file as Blob to be able to parse the meta data. ### canvas From addca35c4769526606d303f44a5064557a1f420b Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 20 Apr 2020 22:51:22 +0900 Subject: [PATCH 033/188] Flash tag is not in the base exif map anymore. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a1e09a..0e334be 100644 --- a/README.md +++ b/README.md @@ -473,7 +473,7 @@ become available, as well as three additional methods: - `exif.getAll()` ```js -var flashText = data.exif.getText('Flash') // e.g.: 'Flash fired, auto mode', +var flashText = data.exif.getText('Orientation') // e.g. right-top for value 6 var name = data.exif.getName(0x0112) // Orientation From 2715acb296be9dbbd122fbf4131bb2958becb635 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 20 Apr 2020 22:56:47 +0900 Subject: [PATCH 034/188] Add headlines for the Exif/IPTC parser options. --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0e334be..48c2d33 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,10 @@ - [Meta data parsing](#meta-data-parsing) - [Image head](#image-head) - [Exif parser](#exif-parser) + - [Exif parser options](#exif-parser-options) - [Exif writer](#exif-writer) - [IPTC parser](#iptc-parser) + - [IPTC parser options](#iptc-parser-options) - [License](#license) - [Credits](#credits) @@ -481,8 +483,9 @@ var name = data.exif.getName(0x0112) // Orientation var allTags = data.exif.getAll() ``` -The Exif parser also adds additional options for the parseMetaData method, to -disable certain aspects of the parser: +#### Exif parser options + +The Exif parser adds additional options: - `disableExif`: Disables Exif parsing when `true`. - `disableExifThumbnail`: Disables parsing of Thumbnail data when `true`. @@ -599,8 +602,9 @@ var name = data.iptc.getName(5) // ObjectName var allTags = data.iptc.getAll() ``` -The IPTC parser also adds additional options for the parseMetaData method, to -disable certain aspects of the parser: +#### IPTC parser options + +The IPTC parser adds additional options: - `disableIptc`: Disables IPTC parsing when true. - `disableIptcOffsets`: Disables storing IPTC tag offsets when `true`. From b96a82149f6fb13befc1764caec141647b1324e7 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Mon, 20 Apr 2020 23:35:53 +0900 Subject: [PATCH 035/188] Add examples for parsing Exif Thumbnail and IFDs. --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 48c2d33..865156c 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ - [Meta data parsing](#meta-data-parsing) - [Image head](#image-head) - [Exif parser](#exif-parser) + - [Exif Thumbnail](#exif-thumbnail) + - [Exif IFD](#exif-ifd) + - [GPSInfo IFD](#gpsinfo-ifd) + - [Interoperability IFD](#interoperability-ifd) - [Exif parser options](#exif-parser-options) - [Exif writer](#exif-writer) - [IPTC parser](#iptc-parser) @@ -463,9 +467,9 @@ By default, only the following names are mapped: - `Orientation` - `Thumbnail` -- `Exif` -- `GPSInfo` -- `Interoperability` +- `Exif` (see [Exif IFD](#exif-ifd)) +- `GPSInfo` (see [GPSInfo IFD](#gpsinfo-ifd)) +- `Interoperability` (see [Interoperability IFD](#interoperability-ifd)) If you also include the Load Image Exif Map library, additional tag mappings become available, as well as three additional methods: @@ -483,6 +487,86 @@ var name = data.exif.getName(0x0112) // Orientation var allTags = data.exif.getAll() ``` +#### Exif Thumbnail + +Example code displaying a thumbnail image embedded into the Exif meta data: + +```js +loadImage( + fileOrBlobOrUrl, + function (img, data) { + var thumbBlob = data.exif && data.exif.get('Thumbnail') + if (thumbBlob) { + loadImage(thumbBlob, function (thumbImage) { + document.body.appendChild(thumbImage) + }) + } + }, + { meta: true } +) +``` + +#### Exif IFD + +Example code displaying data from the Exif IFD (Image File Directory) that +contains Exif specified TIFF tags: + +```js +loadImage( + fileOrBlobOrUrl, + function (img, data) { + var exifIFD = data.exif && data.exif.get('Exif') + if (exifIFD) { + // Map of all Exif IFD tags with their mapped names/text as keys/values: + console.log(exifIFD.getAll()) + // A specific Exif IFD tag value: + console.log(exifIFD.get('UserComment')) + } + }, + { meta: true } +) +``` + +#### GPSInfo IFD + +Example code displaying data from the Exif IFD (Image File Directory) that +contains [GPS](https://en.wikipedia.org/wiki/Global_Positioning_System) info: + +```js +loadImage( + fileOrBlobOrUrl, + function (img, data) { + var gpsInfo = data.exif && data.exif.get('GPSInfo') + if (gpsInfo) { + // Map of all GPSInfo tags with their mapped names/text as keys/values: + console.log(gpsInfo.getAll()) + // A specific GPSInfo tag value: + console.log(gpsInfo.get('GPSLatitude')) + } + }, + { meta: true } +) +``` + +#### Interoperability IFD + +Example code displaying data from the Exif IFD (Image File Directory) that +contains Interoperability data: + +```js +loadImage( + fileOrBlobOrUrl, + function (img, data) { + var interoperabilityData = data.exif && data.exif.get('Interoperability') + if (interoperabilityData) { + // The InteroperabilityIndex tag value: + console.log(interoperabilityData.get('InteroperabilityIndex')) + } + }, + { meta: true } +) +``` + #### Exif parser options The Exif parser adds additional options: From 6869b3b537e0e88f7e9cad04932aaca922708e05 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 17:34:37 +0900 Subject: [PATCH 036/188] Remove unnecessary "Should" as test prefix. --- test/test.js | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/test/test.js b/test/test.js index 35f9eab..cb1cd8d 100644 --- a/test/test.js +++ b/test/test.js @@ -682,7 +682,7 @@ }) describe('Orientation', function () { - it('Should keep the orientation', function (done) { + it('Keep the orientation', function (done) { expect( loadImage( blobGIF, @@ -696,7 +696,7 @@ ).to.be.ok }) - it('Should rotate left', function (done) { + it('Rotate left', function (done) { expect( loadImage( blobGIF, @@ -710,7 +710,7 @@ ).to.be.ok }) - it('Should rotate right', function (done) { + it('Rotate right', function (done) { expect( loadImage( blobGIF, @@ -724,7 +724,7 @@ ).to.be.ok }) - it('Should adjust constraints to new coordinates', function (done) { + it('Adjust constraints to new coordinates', function (done) { expect( loadImage( blobGIF, @@ -738,7 +738,7 @@ ).to.be.ok }) - it('Should adjust left and top to new coordinates', function (done) { + it('Adjust left and top to new coordinates', function (done) { expect( loadImage( blobGIF, @@ -752,7 +752,7 @@ ).to.be.ok }) - it('Should adjust right and bottom to new coordinates', function (done) { + it('Adjust right and bottom to new coordinates', function (done) { expect( loadImage( blobGIF, @@ -766,7 +766,7 @@ ).to.be.ok }) - it('Should adjust left and bottom to new coordinates', function (done) { + it('Adjust left and bottom to new coordinates', function (done) { expect( loadImage( blobGIF, @@ -780,7 +780,7 @@ ).to.be.ok }) - it('Should adjust right and top to new coordinates', function (done) { + it('Adjust right and top to new coordinates', function (done) { expect( loadImage( blobGIF, @@ -794,7 +794,7 @@ ).to.be.ok }) - it('Should rotate left with the given pixelRatio', function (done) { + it('Rotate left with the given pixelRatio', function (done) { expect( loadImage( blobGIF, @@ -810,7 +810,7 @@ ).to.be.ok }) - it('Should rotate right with the given pixelRatio', function (done) { + it('Rotate right with the given pixelRatio', function (done) { expect( loadImage( blobGIF, @@ -826,7 +826,7 @@ ).to.be.ok }) - it('Should ignore too small orientation value', function (done) { + it('Ignore too small orientation value', function (done) { expect( loadImage( blobGIF, @@ -840,7 +840,7 @@ ).to.be.ok }) - it('Should ignore too large orientation value', function (done) { + it('Ignore too large orientation value', function (done) { expect( loadImage( blobGIF, @@ -854,7 +854,7 @@ ).to.be.ok }) - it('Should rotate right based on the exif orientation value', function (done) { + it('Rotate right based on the exif orientation value', function (done) { expect( loadImage( blobJPEG, @@ -906,7 +906,7 @@ ).to.be.ok }) - it('Should scale image after exif based orientation', function (done) { + it('Scale image after exif based orientation', function (done) { expect( loadImage( blobJPEG, @@ -923,7 +923,7 @@ describe('from-image', function () { if (!loadImage.orientation) return - it('Should use automatic browser image orientation', function (done) { + it('Use automatic browser image orientation', function (done) { expect( loadImage( blobJPEG, @@ -1005,7 +1005,7 @@ }) describe('Metadata', function () { - it('Should parse Exif tags', function (done) { + it('Parse Exif tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exif).to.be.ok expect(data.exif.get('Orientation')).to.equal(6) @@ -1013,7 +1013,7 @@ }) }) - it('Should not parse Exif tags if disabled', function (done) { + it('Do not parse Exif tags if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1024,7 +1024,7 @@ ) }) - it('Should parse Exif tag offsets', function (done) { + it('Parse Exif tag offsets', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exifOffsets).to.be.ok expect(data.exifOffsets.get('Orientation')).to.equal(0x16) @@ -1034,7 +1034,7 @@ }) }) - it('Should not parse Exif tag offsets if disabled', function (done) { + it('Do not parse Exif tag offsets if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1045,7 +1045,7 @@ ) }) - it('Should only parse included Exif tags', function (done) { + it('Only parse included Exif tags', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1065,7 +1065,7 @@ ) }) - it('Should not parse excluded Exif tags', function (done) { + it('Do not parse excluded Exif tags', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1085,7 +1085,7 @@ ) }) - it('Should parse IPTC tags', function (done) { + it('Parse IPTC tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.iptc).to.be.ok expect(data.iptc.get('ObjectName')).to.equal('blueimp.net') @@ -1093,7 +1093,7 @@ }) }) - it('Should not parse IPTC tags if disabled', function (done) { + it('Do not parse IPTC tags if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1104,7 +1104,7 @@ ) }) - it('Should parse IPTC tag offsets', function (done) { + it('Parse IPTC tag offsets', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.iptcOffsets).to.be.ok expect(data.iptcOffsets.get('ObjectName')).to.equal(0x44) @@ -1112,7 +1112,7 @@ }) }) - it('Should not parse IPTC tag offsets if disabled', function (done) { + it('Do not parse IPTC tag offsets if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1123,7 +1123,7 @@ ) }) - it('Should only parse included IPTC tags', function (done) { + it('Only parse included IPTC tags', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1145,7 +1145,7 @@ ) }) - it('Should not parse excluded IPTC tags', function (done) { + it('Do not parse excluded IPTC tags', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1167,7 +1167,7 @@ ) }) - it('Should parse the complete image head', function (done) { + it('Parse the complete image head', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.imageHead).to.be.ok loadImage.parseMetaData( @@ -1181,7 +1181,7 @@ }) }) - it('Should not parse the complete image head if disabled', function (done) { + it('Do not parse the complete image head if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1192,7 +1192,7 @@ ) }) - it('Should parse meta data automatically', function (done) { + it('Parse meta data automatically', function (done) { expect( loadImage( blobJPEG, @@ -1210,7 +1210,7 @@ ).to.be.ok }) - it('Should write Exif Orientation tag and replace image head', function (done) { + it('Write Exif Orientation tag and replace image head', function (done) { loadImage( blobJPEG, function (img, data) { @@ -1253,7 +1253,7 @@ return } - it('Should fetch image URL as blob if meta option is true', function (done) { + it('Fetch image URL as blob if meta option is true', function (done) { expect( loadImage( // IE does not allow XMLHttpRequest access to data URLs, @@ -1273,7 +1273,7 @@ ).to.be.ok }) - it('Should load image URL as img if meta option is false', function (done) { + it('Load image URL as img if meta option is false', function (done) { expect( loadImage(imageUrlJPEG, function (img, data) { expect(data.imageHead).to.be.undefined From 4931b411b0d1df37850e0c480ee521a10e2e20a6 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:16:45 +0900 Subject: [PATCH 037/188] Always pass a data arg to getTransformedOptions. --- js/load-image-scale.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/load-image-scale.js b/js/load-image-scale.js index 57ac273..dbebf50 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -45,7 +45,7 @@ // Returns transformed options, allows to override e.g. // maxWidth, maxHeight and crop options based on the aspectRatio. - // gets img, options passed as arguments: + // gets img, options, data passed as arguments: loadImage.getTransformedOptions = function (img, options) { var aspectRatio = options.aspectRatio var newOptions @@ -168,7 +168,7 @@ } if (useCanvas) { // eslint-disable-next-line no-param-reassign - options = loadImage.getTransformedOptions(img, options, data) + options = loadImage.getTransformedOptions(img, options, data || {}) sourceX = options.left || 0 sourceY = options.top || 0 if (options.sourceWidth) { From 379922b53b38a601738dfa8e79d3a396d494e82d Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:23:47 +0900 Subject: [PATCH 038/188] Ensure pixelRatio scaling is idempotent. --- js/load-image-scale.js | 16 +++++++++++----- test/test.js | 12 ++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/js/load-image-scale.js b/js/load-image-scale.js index dbebf50..a662dde 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -225,11 +225,17 @@ if (useCanvas) { pixelRatio = options.pixelRatio if (pixelRatio > 1) { - canvas.style.width = destWidth + 'px' - canvas.style.height = destHeight + 'px' - destWidth *= pixelRatio - destHeight *= pixelRatio - canvas.getContext('2d').scale(pixelRatio, pixelRatio) + if (parseInt(img.style.width, 10) === width / pixelRatio) { + // Source image is already scaled according to device pixel ratio + canvas.style.width = destWidth / pixelRatio + 'px' + canvas.style.height = destHeight / pixelRatio + 'px' + } else { + canvas.style.width = destWidth + 'px' + canvas.style.height = destHeight + 'px' + destWidth *= pixelRatio + destHeight *= pixelRatio + canvas.getContext('2d').scale(pixelRatio, pixelRatio) + } } downsamplingRatio = options.downsamplingRatio if ( diff --git a/test/test.js b/test/test.js index cb1cd8d..1e688ae 100644 --- a/test/test.js +++ b/test/test.js @@ -300,6 +300,12 @@ expect(img.height).to.equal(160) expect(img.style.width).to.equal('120px') expect(img.style.height).to.equal('80px') + // Check if pixelRatio scaling is idempotent: + var img2 = loadImage.scale(img, { minWidth: 120, pixelRatio: 2 }) + expect(img2.width).to.equal(240) + expect(img2.height).to.equal(160) + expect(img2.style.width).to.equal('120px') + expect(img2.style.height).to.equal('80px') done() }, { minWidth: 120, canvas: true, pixelRatio: 2 } @@ -316,6 +322,12 @@ expect(img.height).to.equal(40) expect(img.style.width).to.equal('30px') expect(img.style.height).to.equal('20px') + // Check if pixelRatio scaling is idempotent: + var img2 = loadImage.scale(img, { minWidth: 30, pixelRatio: 2 }) + expect(img2.width).to.equal(60) + expect(img2.height).to.equal(40) + expect(img2.style.width).to.equal('30px') + expect(img2.style.height).to.equal('20px') done() }, { maxWidth: 30, canvas: true, pixelRatio: 2 } From 029ac424c3a9bb4c40951a1e12c73424c0808f00 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:39:31 +0900 Subject: [PATCH 039/188] Document orientation values visually in source. --- js/load-image-orientation.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 74a5be1..7527527 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -9,6 +9,20 @@ * https://opensource.org/licenses/MIT */ +/* + +Exif orientation values to correctly display the letter F: + + 1 2 3 4 5 6 7 8 + +██████ ██████ ██ ██ ██████████ ██ ██ ██████████ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +████ ████ ████ ████ ██ ██████████ ██████████ ██ +██ ██ ██ ██ +██ ██ ██████ ██████ + +*/ + /* global define, module, require */ ;(function (factory) { From cda87fc468211987d808be08dc7ab06c059fa3e7 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:41:32 +0900 Subject: [PATCH 040/188] Add missing degree character in source comments. --- js/load-image-orientation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 7527527..e48ebcd 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -119,7 +119,7 @@ Exif orientation values to correctly display the letter F: ctx.scale(1, -1) break case 5: - // vertical flip + 90 rotate right + // vertical flip + 90° rotate right ctx.rotate(0.5 * Math.PI) ctx.scale(1, -1) break @@ -129,7 +129,7 @@ Exif orientation values to correctly display the letter F: ctx.translate(0, -height) break case 7: - // horizontal flip + 90 rotate right + // horizontal flip + 90° rotate right ctx.rotate(0.5 * Math.PI) ctx.translate(width, -height) ctx.scale(-1, 1) @@ -185,7 +185,7 @@ Exif orientation values to correctly display the letter F: newOptions.bottom = options.top break case 5: - // vertical flip + 90 rotate right + // vertical flip + 90° rotate right newOptions.left = options.top newOptions.top = options.left newOptions.right = options.bottom @@ -199,7 +199,7 @@ Exif orientation values to correctly display the letter F: newOptions.bottom = options.left break case 7: - // horizontal flip + 90 rotate right + // horizontal flip + 90° rotate right newOptions.left = options.bottom newOptions.top = options.right newOptions.right = options.top From 61761388e0b9fff19acef3c4d5c1413c11b8c843 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:45:39 +0900 Subject: [PATCH 041/188] Normalize original width+height dimensions. Switch reported original dimensions for browsers with automatic image orientation. --- js/load-image-orientation.js | 28 ++++++++++++++++++++++++++++ test/test.js | 14 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index e48ebcd..41ac1ee 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -43,6 +43,7 @@ Exif orientation values to correctly display the letter F: })(function (loadImage) { 'use strict' + var originalTransform = loadImage.transform var originalHasCanvasOption = loadImage.hasCanvasOption var originalHasMetaOption = loadImage.hasMetaOption var originalTransformCoordinates = loadImage.transformCoordinates @@ -66,6 +67,33 @@ Exif orientation values to correctly display the letter F: img.src = testImageURL })() + loadImage.transform = function (img, options, callback, file, data) { + originalTransform.call( + loadImage, + img, + options, + function (img, data) { + if (data) { + var exifOrientation = data.exif && data.exif.get('Orientation') + if ( + loadImage.orientation && + exifOrientation > 4 && + exifOrientation < 9 + ) { + // Automatic image orientation switched image dimensions + var originalWidth = data.originalWidth + var originalHeight = data.originalHeight + data.originalWidth = originalHeight + data.originalHeight = originalWidth + } + } + callback(img, data) + }, + file, + data + ) + } + // Determines if the target image should be a canvas element: loadImage.hasCanvasOption = function (options) { return ( diff --git a/test/test.js b/test/test.js index 1e688ae..84b4a4b 100644 --- a/test/test.js +++ b/test/test.js @@ -932,6 +932,20 @@ ).to.be.ok }) + it('Provide original image width+height from before orientation', function (done) { + expect( + loadImage( + blobJPEG, + function (img, data) { + expect(data.originalWidth).to.equal(3) + expect(data.originalHeight).to.equal(2) + done() + }, + { meta: true, minWidth: 20, minHeight: 30 } + ) + ).to.be.ok + }) + describe('from-image', function () { if (!loadImage.orientation) return From 2e72d13f9f4de50623d9b7e057d969e2121aa0ea Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:46:48 +0900 Subject: [PATCH 042/188] Return early for invalid orientation values. --- js/load-image-orientation.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 41ac1ee..71faf2f 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -111,19 +111,18 @@ Exif orientation values to correctly display the letter F: ) } - // Transform image orientation based on - // the given EXIF orientation option: + // Transform image orientation based on the given EXIF orientation option: loadImage.transformCoordinates = function (canvas, options) { originalTransformCoordinates.call(loadImage, canvas, options) + var orientation = options.orientation + if (!(orientation > 1 && orientation < 9)) { + return + } var ctx = canvas.getContext('2d') var width = canvas.width var height = canvas.height var styleWidth = canvas.style.width var styleHeight = canvas.style.height - var orientation = options.orientation - if (!(orientation > 1 && orientation < 9)) { - return - } if (orientation > 4) { canvas.width = height canvas.height = width From ae7013d4084a1b82a283be23c0543e92bd8ff2c2 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 21 Apr 2020 23:49:37 +0900 Subject: [PATCH 043/188] Move transformCoordinates declaration to the end. --- js/load-image-orientation.js | 116 +++++++++++++++++------------------ 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 71faf2f..2f28171 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -111,64 +111,6 @@ Exif orientation values to correctly display the letter F: ) } - // Transform image orientation based on the given EXIF orientation option: - loadImage.transformCoordinates = function (canvas, options) { - originalTransformCoordinates.call(loadImage, canvas, options) - var orientation = options.orientation - if (!(orientation > 1 && orientation < 9)) { - return - } - var ctx = canvas.getContext('2d') - var width = canvas.width - var height = canvas.height - var styleWidth = canvas.style.width - var styleHeight = canvas.style.height - if (orientation > 4) { - canvas.width = height - canvas.height = width - canvas.style.width = styleHeight - canvas.style.height = styleWidth - } - switch (orientation) { - case 2: - // horizontal flip - ctx.translate(width, 0) - ctx.scale(-1, 1) - break - case 3: - // 180° rotate left - ctx.translate(width, height) - ctx.rotate(Math.PI) - break - case 4: - // vertical flip - ctx.translate(0, height) - ctx.scale(1, -1) - break - case 5: - // vertical flip + 90° rotate right - ctx.rotate(0.5 * Math.PI) - ctx.scale(1, -1) - break - case 6: - // 90° rotate right - ctx.rotate(0.5 * Math.PI) - ctx.translate(0, -height) - break - case 7: - // horizontal flip + 90° rotate right - ctx.rotate(0.5 * Math.PI) - ctx.translate(width, -height) - ctx.scale(-1, 1) - break - case 8: - // 90° rotate left - ctx.rotate(-0.5 * Math.PI) - ctx.translate(-width, 0) - break - } - } - // Transforms coordinate and dimension options // based on the given orientation option: loadImage.getTransformedOptions = function (img, opts, data) { @@ -250,4 +192,62 @@ Exif orientation values to correctly display the letter F: } return newOptions } + + // Transform image orientation based on the given EXIF orientation option: + loadImage.transformCoordinates = function (canvas, options) { + originalTransformCoordinates.call(loadImage, canvas, options) + var orientation = options.orientation + if (!(orientation > 1 && orientation < 9)) { + return + } + var ctx = canvas.getContext('2d') + var width = canvas.width + var height = canvas.height + var styleWidth = canvas.style.width + var styleHeight = canvas.style.height + if (orientation > 4) { + canvas.width = height + canvas.height = width + canvas.style.width = styleHeight + canvas.style.height = styleWidth + } + switch (orientation) { + case 2: + // horizontal flip + ctx.translate(width, 0) + ctx.scale(-1, 1) + break + case 3: + // 180° rotate left + ctx.translate(width, height) + ctx.rotate(Math.PI) + break + case 4: + // vertical flip + ctx.translate(0, height) + ctx.scale(1, -1) + break + case 5: + // vertical flip + 90° rotate right + ctx.rotate(0.5 * Math.PI) + ctx.scale(1, -1) + break + case 6: + // 90° rotate right + ctx.rotate(0.5 * Math.PI) + ctx.translate(0, -height) + break + case 7: + // horizontal flip + 90° rotate right + ctx.rotate(0.5 * Math.PI) + ctx.translate(width, -height) + ctx.scale(-1, 1) + break + case 8: + // 90° rotate left + ctx.rotate(-0.5 * Math.PI) + ctx.translate(-width, 0) + break + } + } }) From 83ace3efe41ffbf8221431f546365918311656aa Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 00:07:26 +0900 Subject: [PATCH 044/188] Remove unnecessary double negation. --- js/load-image-orientation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 2f28171..312f6ac 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -97,7 +97,7 @@ Exif orientation values to correctly display the letter F: // Determines if the target image should be a canvas element: loadImage.hasCanvasOption = function (options) { return ( - (!!options.orientation === true && !loadImage.orientation) || + (options.orientation === true && !loadImage.orientation) || (options.orientation > 1 && options.orientation < 9) || originalHasCanvasOption.call(loadImage, options) ) From 2abe1704e69886711b4b3b8bafe946822a57f7cc Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 18:31:51 +0900 Subject: [PATCH 045/188] Pass data object to transformCoordinates function. --- js/load-image-scale.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/js/load-image-scale.js b/js/load-image-scale.js index a662dde..2f1fcc7 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -40,7 +40,7 @@ // Transform image coordinates, allows to override e.g. // the canvas orientation based on the orientation option, - // gets canvas, options passed as arguments: + // gets canvas, options and data passed as arguments: loadImage.transformCoordinates = function () {} // Returns transformed options, allows to override e.g. @@ -121,6 +121,8 @@ loadImage.scale = function (img, options, data) { // eslint-disable-next-line no-param-reassign options = options || {} + // eslint-disable-next-line no-param-reassign + data = data || {} var canvas = document.createElement('canvas') var useCanvas = img.getContext || @@ -168,7 +170,7 @@ } if (useCanvas) { // eslint-disable-next-line no-param-reassign - options = loadImage.getTransformedOptions(img, options, data || {}) + options = loadImage.getTransformedOptions(img, options, data) sourceX = options.left || 0 sourceY = options.top || 0 if (options.sourceWidth) { @@ -285,7 +287,7 @@ } canvas.width = destWidth canvas.height = destHeight - loadImage.transformCoordinates(canvas, options) + loadImage.transformCoordinates(canvas, options, data) return loadImage.renderImageToCanvas( canvas, img, From e52c67b9f77c18a9a7a6f0f22b04ea0a80ce63ae Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 21:22:09 +0900 Subject: [PATCH 046/188] Rename functions for canvas and meta requirements. --- js/load-image-meta.js | 2 +- js/load-image-orientation.js | 12 ++++++------ js/load-image-scale.js | 10 +++------- js/load-image.js | 4 ++-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/js/load-image-meta.js b/js/load-image-meta.js index ecdb077..63177d3 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -181,7 +181,7 @@ var originalTransform = loadImage.transform loadImage.transform = function (img, options, callback, file, data) { - if (loadImage.hasMetaOption(options)) { + if (loadImage.requiresMetaData(options)) { loadImage.parseMetaData( file, function (data) { diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 312f6ac..ce0557f 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -44,8 +44,8 @@ Exif orientation values to correctly display the letter F: 'use strict' var originalTransform = loadImage.transform - var originalHasCanvasOption = loadImage.hasCanvasOption - var originalHasMetaOption = loadImage.hasMetaOption + var originalRequiresCanvas = loadImage.requiresCanvas + var originalRequiresMetaData = loadImage.requiresMetaData var originalTransformCoordinates = loadImage.transformCoordinates var originalGetTransformedOptions = loadImage.getTransformedOptions @@ -95,19 +95,19 @@ Exif orientation values to correctly display the letter F: } // Determines if the target image should be a canvas element: - loadImage.hasCanvasOption = function (options) { + loadImage.requiresCanvas = function (options) { return ( (options.orientation === true && !loadImage.orientation) || (options.orientation > 1 && options.orientation < 9) || - originalHasCanvasOption.call(loadImage, options) + originalRequiresCanvas.call(loadImage, options) ) } // Determines if meta data should be loaded automatically: - loadImage.hasMetaOption = function (options) { + loadImage.requiresMetaData = function (options) { return ( (options && options.orientation === true && !loadImage.orientation) || - originalHasMetaOption.call(loadImage, options) + originalRequiresMetaData.call(loadImage, options) ) } diff --git a/js/load-image-scale.js b/js/load-image-scale.js index 2f1fcc7..53fe5e6 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -109,15 +109,12 @@ } // Determines if the target image should be a canvas element: - loadImage.hasCanvasOption = function (options) { + loadImage.requiresCanvas = function (options) { return options.canvas || options.crop || !!options.aspectRatio } // Scales and/or crops the given image (img or canvas HTML element) - // using the given options. - // Returns a canvas object if the browser supports canvas - // and the hasCanvasOption method returns true or a canvas - // object is passed as image, else the scaled image: + // using the given options: loadImage.scale = function (img, options, data) { // eslint-disable-next-line no-param-reassign options = options || {} @@ -125,8 +122,7 @@ data = data || {} var canvas = document.createElement('canvas') var useCanvas = - img.getContext || - (loadImage.hasCanvasOption(options) && canvas.getContext) + img.getContext || (loadImage.requiresCanvas(options) && canvas.getContext) var width = img.naturalWidth || img.width var height = img.naturalHeight || img.height var destWidth = width diff --git a/js/load-image.js b/js/load-image.js index 50a36cd..865586d 100644 --- a/js/load-image.js +++ b/js/load-image.js @@ -54,7 +54,7 @@ return loadImage.onload(img, event, file, url, callback, options) } if (typeof file === 'string') { - if (loadImage.hasMetaOption(options)) { + if (loadImage.requiresMetaData(options)) { loadImage.fetchBlob(file, fetchBlobCallback, options) } else { fetchBlobCallback() @@ -102,7 +102,7 @@ // Determines if meta data should be loaded automatically. // Requires the load image meta extension to load meta data. - loadImage.hasMetaOption = function (options) { + loadImage.requiresMetaData = function (options) { return options && options.meta } From 0a0dc98c0103463de2e8911306c8726321c842e8 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 21:43:28 +0900 Subject: [PATCH 047/188] Refactor canvas+meta requirement checks for reuse. --- js/load-image-orientation.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index ce0557f..6920d73 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -94,11 +94,31 @@ Exif orientation values to correctly display the letter F: ) } + /** + * Determines if the image requires orientation. + * + * @param {object} [options] Options object + * @param {boolean} [withMetaData] Is meta data required for orientation + * @returns {boolean} Returns true if the image requires orientation + */ + function requiresOrientation(options, withMetaData) { + var orientation = options && options.orientation + return ( + // Exif orientation for browsers without automatic image orientation: + (orientation === true && !loadImage.orientation) || + // Orientation reset for browsers with automatic image orientation: + (orientation === 1 && loadImage.orientation) || + // Orientation to defined value, requires meta data for orientation reset: + ((!withMetaData || loadImage.orientation) && + orientation > 1 && + orientation < 9) + ) + } + // Determines if the target image should be a canvas element: loadImage.requiresCanvas = function (options) { return ( - (options.orientation === true && !loadImage.orientation) || - (options.orientation > 1 && options.orientation < 9) || + requiresOrientation(options) || originalRequiresCanvas.call(loadImage, options) ) } @@ -106,7 +126,7 @@ Exif orientation values to correctly display the letter F: // Determines if meta data should be loaded automatically: loadImage.requiresMetaData = function (options) { return ( - (options && options.orientation === true && !loadImage.orientation) || + requiresOrientation(options, true) || originalRequiresMetaData.call(loadImage, options) ) } From a3eb4cda44a6b7d3327af93fe970151dea697f0c Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 21:45:16 +0900 Subject: [PATCH 048/188] Move transform function below requirements checks. --- js/load-image-orientation.js | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 6920d73..edddb7c 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -67,33 +67,6 @@ Exif orientation values to correctly display the letter F: img.src = testImageURL })() - loadImage.transform = function (img, options, callback, file, data) { - originalTransform.call( - loadImage, - img, - options, - function (img, data) { - if (data) { - var exifOrientation = data.exif && data.exif.get('Orientation') - if ( - loadImage.orientation && - exifOrientation > 4 && - exifOrientation < 9 - ) { - // Automatic image orientation switched image dimensions - var originalWidth = data.originalWidth - var originalHeight = data.originalHeight - data.originalWidth = originalHeight - data.originalHeight = originalWidth - } - } - callback(img, data) - }, - file, - data - ) - } - /** * Determines if the image requires orientation. * @@ -131,6 +104,33 @@ Exif orientation values to correctly display the letter F: ) } + loadImage.transform = function (img, options, callback, file, data) { + originalTransform.call( + loadImage, + img, + options, + function (img, data) { + if (data) { + var exifOrientation = data.exif && data.exif.get('Orientation') + if ( + loadImage.orientation && + exifOrientation > 4 && + exifOrientation < 9 + ) { + // Automatic image orientation switched image dimensions + var originalWidth = data.originalWidth + var originalHeight = data.originalHeight + data.originalWidth = originalHeight + data.originalHeight = originalWidth + } + } + callback(img, data) + }, + file, + data + ) + } + // Transforms coordinate and dimension options // based on the given orientation option: loadImage.getTransformedOptions = function (img, opts, data) { From 212a3a3dcc138da248163795e2fa9221b3d4c614 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 23:35:29 +0900 Subject: [PATCH 049/188] Arrange Exif orientation visualization vertically. --- js/load-image-orientation.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index edddb7c..f700df1 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -10,16 +10,31 @@ */ /* - Exif orientation values to correctly display the letter F: - 1 2 3 4 5 6 7 8 + 1 2 + ██████ ██████ + ██ ██ + ████ ████ + ██ ██ + ██ ██ + + 3 4 + ██ ██ + ██ ██ + ████ ████ + ██ ██ + ██████ ██████ + + 5 6 +██████████ ██ +██ ██ ██ ██ +██ ██████████ -██████ ██████ ██ ██ ██████████ ██ ██ ██████████ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -████ ████ ████ ████ ██ ██████████ ██████████ ██ -██ ██ ██ ██ -██ ██ ██████ ██████ + 7 8 + ██ ██████████ + ██ ██ ██ ██ +██████████ ██ */ From 7e5f90b8c623bcdb2088715af89686f2ccb877b2 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 23:36:38 +0900 Subject: [PATCH 050/188] Update orientation requirements for canvas/meta. --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 865156c..75d87c7 100644 --- a/README.md +++ b/README.md @@ -332,11 +332,15 @@ an `integer` in the range of `1` to `8` or the boolean value `true`. When set to `true`, it will set the orientation value based on the EXIF data of the image, which will be parsed automatically if the exif library is available. -Setting `orientation` to an integer in the range of `2` to `8` enables the -`canvas` option. Setting `orientation` to `true` enables the `canvas` and `meta` options, unless the browser supports automatic image orientation (see -[browser support for image-orientation](https://caniuse.com/#feat=css-image-orientation)). +[browser support for image-orientation](https://caniuse.com/#feat=css-image-orientation)). +Setting `orientation` to `1` enables the `canvas` and `meta` options if the +browser does support automatic image orientation (to allow reset of the +orientation). +Setting `orientation` to an integer in the range of `2` to `8` always enables +the `canvas` option and also enables the `meta` option if the browser supports +automatic image orientation (again to allow reset). ### meta From 0f65d7d1241dc0f0463a531e612a5b56c9e856da Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Wed, 22 Apr 2020 23:38:06 +0900 Subject: [PATCH 051/188] Add Exif orientation visualization to the README. --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 75d87c7..a13ac05 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,34 @@ an `integer` in the range of `1` to `8` or the boolean value `true`. When set to `true`, it will set the orientation value based on the EXIF data of the image, which will be parsed automatically if the exif library is available. +Exif orientation values to correctly display the letter F: + +``` + 1 2 + ██████ ██████ + ██ ██ + ████ ████ + ██ ██ + ██ ██ + + 3 4 + ██ ██ + ██ ██ + ████ ████ + ██ ██ + ██████ ██████ + + 5 6 +██████████ ██ +██ ██ ██ ██ +██ ██████████ + + 7 8 + ██ ██████████ + ██ ██ ██ ██ +██████████ ██ +``` + Setting `orientation` to `true` enables the `canvas` and `meta` options, unless the browser supports automatic image orientation (see [browser support for image-orientation](https://caniuse.com/#feat=css-image-orientation)). From 14b599e924979e43f8f5d75e3afde4752aea5ac9 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Thu, 23 Apr 2020 00:32:05 +0900 Subject: [PATCH 052/188] Simplify original dimensions normalization. --- js/load-image-orientation.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index f700df1..30833de 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -126,12 +126,9 @@ Exif orientation values to correctly display the letter F: options, function (img, data) { if (data) { - var exifOrientation = data.exif && data.exif.get('Orientation') - if ( - loadImage.orientation && - exifOrientation > 4 && - exifOrientation < 9 - ) { + var autoOrientation = + loadImage.orientation && data.exif && data.exif.get('Orientation') + if (autoOrientation > 4 && autoOrientation < 9) { // Automatic image orientation switched image dimensions var originalWidth = data.originalWidth var originalHeight = data.originalHeight From f088760e7b29c88f75e622e97b2a937491a2fc60 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 26 Apr 2020 18:13:32 +0900 Subject: [PATCH 053/188] Normalize EXIF test names spelling. --- test/test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/test.js b/test/test.js index 84b4a4b..33ed5f8 100644 --- a/test/test.js +++ b/test/test.js @@ -1031,7 +1031,7 @@ }) describe('Metadata', function () { - it('Parse Exif tags', function (done) { + it('Parse EXIF tags', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exif).to.be.ok expect(data.exif.get('Orientation')).to.equal(6) @@ -1039,7 +1039,7 @@ }) }) - it('Do not parse Exif tags if disabled', function (done) { + it('Do not parse EXIF tags if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1050,7 +1050,7 @@ ) }) - it('Parse Exif tag offsets', function (done) { + it('Parse EXIF tag offsets', function (done) { loadImage.parseMetaData(blobJPEG, function (data) { expect(data.exifOffsets).to.be.ok expect(data.exifOffsets.get('Orientation')).to.equal(0x16) @@ -1060,7 +1060,7 @@ }) }) - it('Do not parse Exif tag offsets if disabled', function (done) { + it('Do not parse EXIF tag offsets if disabled', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1071,7 +1071,7 @@ ) }) - it('Only parse included Exif tags', function (done) { + it('Only parse included EXIF tags', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1091,7 +1091,7 @@ ) }) - it('Do not parse excluded Exif tags', function (done) { + it('Do not parse excluded EXIF tags', function (done) { loadImage.parseMetaData( blobJPEG, function (data) { @@ -1236,7 +1236,7 @@ ).to.be.ok }) - it('Write Exif Orientation tag and replace image head', function (done) { + it('Write EXIF Orientation tag and replace image head', function (done) { loadImage( blobJPEG, function (img, data) { @@ -1245,7 +1245,7 @@ expect(data.exif.get('Orientation')).to.equal(6) expect(data.iptc).to.be.ok expect(data.iptc.get('ObjectName')).to.equal('blueimp.net') - // Reset Exif Orientation data: + // Reset EXIF Orientation data: loadImage.writeExifData(data.imageHead, data, 'Orientation', 1) img.toBlob(function (blob) { loadImage.replaceHead(blob, data.imageHead, function (newBlob) { From 001fe7fcf345d1fb7e5a8faa9545a7eb09da5c94 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 26 Apr 2020 18:15:07 +0900 Subject: [PATCH 054/188] Test functions via instanceOf method. --- test/test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test.js b/test/test.js index 33ed5f8..64899a2 100644 --- a/test/test.js +++ b/test/test.js @@ -91,8 +91,8 @@ it('Return an object with onload and onerror methods', function () { var img = loadImage(blobGIF, function () {}) expect(img).to.be.an.instanceOf(Object) - expect(img.onload).to.be.a('function') - expect(img.onerror).to.be.a('function') + expect(img.onload).to.be.an.instanceOf(Function) + expect(img.onerror).to.be.an.instanceOf(Function) }) it('Load image url', function (done) { @@ -984,7 +984,7 @@ loadImage( blobGIF, function (img) { - expect(img.getContext).to.be.ok + expect(img.getContext).to.be.an.instanceOf(Function) expect(img.nodeName.toLowerCase()).to.equal('canvas') done() }, @@ -998,7 +998,7 @@ loadImage( blobGIF, function (img) { - expect(img.getContext).to.be.ok + expect(img.getContext).to.be.an.instanceOf(Function) expect(img.nodeName.toLowerCase()).to.equal('canvas') expect(img.width).to.equal(30) expect(img.height).to.equal(20) @@ -1018,7 +1018,7 @@ img = loadImage.scale(img, { maxWidth: 30 }) - expect(img.getContext).to.be.ok + expect(img.getContext).to.be.an.instanceOf(Function) expect(img.nodeName.toLowerCase()).to.equal('canvas') expect(img.width).to.equal(30) expect(img.height).to.equal(20) From 996dbb1bcfc60238961619be7a1889d8d409e383 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 26 Apr 2020 18:18:04 +0900 Subject: [PATCH 055/188] Remove unnecessary destX/destY arguments. --- js/load-image-scale.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/js/load-image-scale.js b/js/load-image-scale.js index 53fe5e6..4f6ab50 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -82,8 +82,6 @@ sourceY, sourceWidth, sourceHeight, - destX, - destY, destWidth, destHeight, options @@ -100,8 +98,8 @@ sourceY, sourceWidth, sourceHeight, - destX, - destY, + 0, + 0, destWidth, destHeight ) @@ -252,8 +250,6 @@ sourceY, sourceWidth, sourceHeight, - 0, - 0, canvas.width, canvas.height, options @@ -273,8 +269,6 @@ 0, sourceWidth, sourceHeight, - 0, - 0, sourceWidth, sourceHeight, options @@ -291,8 +285,6 @@ sourceY, sourceWidth, sourceHeight, - 0, - 0, destWidth, destHeight, options From 8776ebe52bbe9509ba4407a0ce873bda51639680 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 26 Apr 2020 18:22:33 +0900 Subject: [PATCH 056/188] Limit pixel ratio handling to scaling library. --- js/load-image-orientation.js | 4 ---- js/load-image-scale.js | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/js/load-image-orientation.js b/js/load-image-orientation.js index 30833de..d5e215e 100644 --- a/js/load-image-orientation.js +++ b/js/load-image-orientation.js @@ -235,13 +235,9 @@ Exif orientation values to correctly display the letter F: var ctx = canvas.getContext('2d') var width = canvas.width var height = canvas.height - var styleWidth = canvas.style.width - var styleHeight = canvas.style.height if (orientation > 4) { canvas.width = height canvas.height = width - canvas.style.width = styleHeight - canvas.style.height = styleWidth } switch (orientation) { case 2: diff --git a/js/load-image-scale.js b/js/load-image-scale.js index 4f6ab50..e398b13 100644 --- a/js/load-image-scale.js +++ b/js/load-image-scale.js @@ -220,18 +220,14 @@ } if (useCanvas) { pixelRatio = options.pixelRatio - if (pixelRatio > 1) { - if (parseInt(img.style.width, 10) === width / pixelRatio) { - // Source image is already scaled according to device pixel ratio - canvas.style.width = destWidth / pixelRatio + 'px' - canvas.style.height = destHeight / pixelRatio + 'px' - } else { - canvas.style.width = destWidth + 'px' - canvas.style.height = destHeight + 'px' - destWidth *= pixelRatio - destHeight *= pixelRatio - canvas.getContext('2d').scale(pixelRatio, pixelRatio) - } + if ( + pixelRatio > 1 && + // Check if image has not yet device pixel ratio applied: + parseInt(img.style.width, 10) !== width / pixelRatio + ) { + destWidth *= pixelRatio + destHeight *= pixelRatio + canvas.getContext('2d').scale(pixelRatio, pixelRatio) } downsamplingRatio = options.downsamplingRatio if ( @@ -278,6 +274,10 @@ canvas.width = destWidth canvas.height = destHeight loadImage.transformCoordinates(canvas, options, data) + if (pixelRatio > 1) { + canvas.style.width = canvas.width / pixelRatio + 'px' + canvas.style.height = canvas.height / pixelRatio + 'px' + } return loadImage.renderImageToCanvas( canvas, img, From 4c79652b5357fdf7fd5ce0b3f45c386e4e732719 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Sun, 26 Apr 2020 22:24:44 +0900 Subject: [PATCH 057/188] Fix IE10 ArrayBuffer.slice workaround. To use imageHead as DataView argument (e.g. for writing EXIF data), it needs to be a buffer. --- js/load-image-meta.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/js/load-image-meta.js b/js/load-image-meta.js index 63177d3..21c9961 100644 --- a/js/load-image-meta.js +++ b/js/load-image-meta.js @@ -96,6 +96,8 @@ var markerLength var parsers var i + var arr1 + var arr2 // Check for the JPEG marker (0xffd8): if (dataView.getUint16(0) === 0xffd8) { while (offset < maxOffset) { @@ -144,9 +146,12 @@ if (buffer.slice) { data.imageHead = buffer.slice(0, headLength) } else { - // Workaround for IE10, which does not yet - // support ArrayBuffer.slice: - data.imageHead = new Uint8Array(buffer).subarray(0, headLength) + // Workaround for IE10, which does not support + // ArrayBuffer.slice: + arr1 = new Uint8Array(buffer, 0, headLength) + arr2 = new Uint8Array(headLength) + arr2.set(arr1) + data.imageHead = arr2.buffer } } } else { From 681143cfb196bfeade745252fcc67fa04bb87d80 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 28 Apr 2020 19:40:14 +0900 Subject: [PATCH 058/188] Demo: take pixelRatio into account when cropping. Use contain:true instead of specifying both minWidth and maxWidth. Remove unnecessary downsamplingRatio option for cropping. --- js/demo/demo.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/demo/demo.js b/js/demo/demo.js index b42cdcd..fe1984c 100644 --- a/js/demo/demo.js +++ b/js/demo/demo.js @@ -250,10 +250,9 @@ $(function () { top: coordinates.y * pixelRatio, sourceWidth: coordinates.w * pixelRatio, sourceHeight: coordinates.h * pixelRatio, - minWidth: result.width(), - maxWidth: result.width(), + maxWidth: result.width() * pixelRatio, + contain: true, pixelRatio: pixelRatio, - downsamplingRatio: 0.5 }) ) coordinates = null From b86c22f62000513963980180454bb62901fba592 Mon Sep 17 00:00:00 2001 From: Sebastian Tschan Date: Tue, 28 Apr 2020 19:41:03 +0900 Subject: [PATCH 059/188] Demo: add orientation selector. --- index.html | 18 ++++++++++++++++++ js/demo/demo.js | 14 +++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index bc619ec..7192a00 100644 --- a/index.html +++ b/index.html @@ -80,6 +80,24 @@

Select an image file

Or drag & drop an image file onto this webpage.

+

+ + +

Result

Select an image file

+

+ + +

Result

Result

API.

-