@automattic/jetpack-image-guide 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/build/MeasurableImage.d.ts +1 -1
- package/build/index.asset.php +1 -1
- package/build/index.js +1 -1
- package/package.json +3 -3
- package/.gitattributes +0 -17
- package/babel.config.cjs +0 -10
- package/src/MeasurableImage.ts +0 -140
- package/src/find-image-elements.ts +0 -125
- package/src/index.ts +0 -5
- package/webpack.config.cjs +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.2] - 2023-09-01
|
|
9
|
+
### Changed
|
|
10
|
+
- Updated package dependencies. [#31815] [#32605]
|
|
11
|
+
- Update tiny image check to treat images who's dimentions are missing as tiny. [#32144]
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
- Remove unnecessary files from mirror repo and published package. [#32674]
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Handle zero dimensions in getOversizedRatio [#32548]
|
|
18
|
+
|
|
19
|
+
## [0.5.1] - 2023-07-11
|
|
20
|
+
### Fixed
|
|
21
|
+
- Image Guide: Fix for broken background-images causing the image guide not to load [#31792]
|
|
22
|
+
|
|
8
23
|
## [0.5.0] - 2023-07-07
|
|
9
24
|
### Added
|
|
10
25
|
- Boost: exclude small images from Image Size Analysis [#31504]
|
|
@@ -59,5 +74,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
59
74
|
### Removed
|
|
60
75
|
- Minor package.json change - removing private entry.
|
|
61
76
|
|
|
77
|
+
[0.5.2]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.1...v0.5.2
|
|
78
|
+
[0.5.1]: https://github.com/Automattic/jetpack-image-guide/compare/v0.5.0...v0.5.1
|
|
62
79
|
[0.5.0]: https://github.com/Automattic/jetpack-image-guide/compare/v0.4.0...v0.5.0
|
|
63
80
|
[0.4.0]: https://github.com/Automattic/jetpack-image-guide/compare/v0.3.0...v0.4.0
|
|
@@ -62,7 +62,7 @@ export declare class MeasurableImage {
|
|
|
62
62
|
*/
|
|
63
63
|
private fetchFileDimensions;
|
|
64
64
|
/**
|
|
65
|
-
* Checks if the image is too small and should be ignored. Will return
|
|
65
|
+
* Checks if the image is too small and should be ignored. Will return true on images
|
|
66
66
|
* that don't load at all - we can't establish they're tiny!
|
|
67
67
|
*
|
|
68
68
|
* @returns {boolean} - if the image is smaller than 65 pixels width and height return true
|
package/build/index.asset.php
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<?php return array('dependencies' => array(), 'version' => '
|
|
1
|
+
<?php return array('dependencies' => array(), 'version' => 'e657184e174bd13a2997');
|
package/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ImageGuideLibrary=e():t.ImageGuideLibrary=e()}(self,(()=>(()=>{"use strict";var t={38:(t,e,n)=>{n.d(e,{z:()=>i});var r=function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function u(t){try{c(r.next(t))}catch(t){i(t)}}function a(t){try{c(r.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,a)}c((r=r.apply(t,e||[])).next())}))},o=function(t,e){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,a[0]&&(u=0)),u;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return u.label++,{value:a[1],done:!1};case 5:u.label++,r=a[1],a=[0];continue;case 7:a=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){u=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]<o[3])){u.label=a[1];break}if(6===a[0]&&u.label<o[1]){u.label=o[1],o=a;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(a);break}o[2]&&u.ops.pop(),u.trys.pop();continue}a=e.call(t,u)}catch(t){a=[6,t],r=0}finally{n=o=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}},i=function(){function t(t,e,n){void 0===n&&(n=null),this.node=t,this.getURLCallback=e,this.fetch=null!=n?n:window.fetch.bind(window)}return t.prototype.getURL=function(){return this.getURLCallback(this.node)},t.prototype.getSizeOnPage=function(){var t=this.node.getBoundingClientRect(),e=t.width,n=t.height;return{width:Math.round(e),height:Math.round(n)}},t.prototype.getFileSize=function(t){return r(this,void 0,void 0,(function(){var e,n,r,i,u;return o(this,(function(o){switch(o.label){case 0:return[4,Promise.all([this.fetchFileWeight(t),this.fetchFileDimensions(t)])];case 1:return e=o.sent(),n=e[0],r=e[1],i=r.width,u=r.height,[2,{width:i,height:u,weight:n}]}}))}))},t.prototype.getPotentialSavings=function(t,e){var n=this.getOversizedRatio(t,e);return n<=1?null:Math.round(t.weight-t.weight/n)},t.prototype.getExpectedSize=function(t){var e=window.devicePixelRatio||1;return{width:Math.round(t.width*e),height:Math.round(t.height*e)}},t.prototype.getOversizedRatio=function(t,e){var n=this.getExpectedSize(e),r=n.width,o=n.height;return t.width*t.height/(r*o)},t.prototype.fetchFileWeight=function(t){return r(this,void 0,void 0,(function(){var e;return o(this,(function(n){switch(n.label){case 0:return[4,this.fetch(t)];case 1:return(e=n.sent()).ok?[2,parseInt(e.headers.get("content-length"),10)/1024]:(console.log("Can't get image size for ".concat(t," likely due to a CORS error.")),[2,-1])}}))}))},t.prototype.fetchFileDimensions=function(t){return r(this,void 0,void 0,(function(){var e;return o(this,(function(n){return(e=new Image).src=t,[2,new Promise((function(t,n){e.onload=function(){t({width:Math.round(e.width),height:Math.round(e.height)})},e.onerror=function(){n("Unable to load image.")}}))]}))}))},t.prototype.isImageTiny=function(){return r(this,void 0,void 0,(function(){var t,e;return o(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),t=65,[4,this.fetchFileDimensions(this.getURL())];case 1:return[2,(e=n.sent()).width<t||e.height<t];case 2:return n.sent(),[2,!
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ImageGuideLibrary=e():t.ImageGuideLibrary=e()}(self,(()=>(()=>{"use strict";var t={38:(t,e,n)=>{n.d(e,{z:()=>i});var r=function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function u(t){try{c(r.next(t))}catch(t){i(t)}}function a(t){try{c(r.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,a)}c((r=r.apply(t,e||[])).next())}))},o=function(t,e){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,a[0]&&(u=0)),u;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return u.label++,{value:a[1],done:!1};case 5:u.label++,r=a[1],a=[0];continue;case 7:a=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){u=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]<o[3])){u.label=a[1];break}if(6===a[0]&&u.label<o[1]){u.label=o[1],o=a;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(a);break}o[2]&&u.ops.pop(),u.trys.pop();continue}a=e.call(t,u)}catch(t){a=[6,t],r=0}finally{n=o=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}},i=function(){function t(t,e,n){void 0===n&&(n=null),this.node=t,this.getURLCallback=e,this.fetch=null!=n?n:window.fetch.bind(window)}return t.prototype.getURL=function(){return this.getURLCallback(this.node)},t.prototype.getSizeOnPage=function(){var t=this.node.getBoundingClientRect(),e=t.width,n=t.height;return{width:Math.round(e),height:Math.round(n)}},t.prototype.getFileSize=function(t){return r(this,void 0,void 0,(function(){var e,n,r,i,u;return o(this,(function(o){switch(o.label){case 0:return[4,Promise.all([this.fetchFileWeight(t),this.fetchFileDimensions(t)])];case 1:return e=o.sent(),n=e[0],r=e[1],i=r.width,u=r.height,[2,{width:i,height:u,weight:n}]}}))}))},t.prototype.getPotentialSavings=function(t,e){var n=this.getOversizedRatio(t,e);return n<=1?null:Math.round(t.weight-t.weight/n)},t.prototype.getExpectedSize=function(t){var e=window.devicePixelRatio||1;return{width:Math.round(t.width*e),height:Math.round(t.height*e)}},t.prototype.getOversizedRatio=function(t,e){if(0===t.width||0===t.height)return 1;var n=this.getExpectedSize(e),r=n.width,o=n.height;return 0===r||0===o?1:t.width*t.height/(r*o)},t.prototype.fetchFileWeight=function(t){return r(this,void 0,void 0,(function(){var e;return o(this,(function(n){switch(n.label){case 0:return[4,this.fetch(t)];case 1:return(e=n.sent()).ok?[2,parseInt(e.headers.get("content-length"),10)/1024]:(console.log("Can't get image size for ".concat(t," likely due to a CORS error.")),[2,-1])}}))}))},t.prototype.fetchFileDimensions=function(t){return r(this,void 0,void 0,(function(){var e;return o(this,(function(n){return(e=new Image).src=t,[2,new Promise((function(t,n){e.onload=function(){t({width:Math.round(e.width),height:Math.round(e.height)})},e.onerror=function(){n("Unable to load image.")}}))]}))}))},t.prototype.isImageTiny=function(){return r(this,void 0,void 0,(function(){var t,e;return o(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),t=65,[4,this.fetchFileDimensions(this.getURL())];case 1:return[2,(e=n.sent()).width<t||e.height<t];case 2:return n.sent(),[2,!0];case 3:return[2]}}))}))},t}()},705:(t,e,n)=>{n.d(e,{RK:()=>c});var r=n(38),o=function(t,e,n,r){return new(n||(n=Promise))((function(o,i){function u(t){try{c(r.next(t))}catch(t){i(t)}}function a(t){try{c(r.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?o(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,a)}c((r=r.apply(t,e||[])).next())}))},i=function(t,e){var n,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,a[0]&&(u=0)),u;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return u.label++,{value:a[1],done:!1};case 5:u.label++,r=a[1],a=[0];continue;case 7:a=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){u=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]<o[3])){u.label=a[1];break}if(6===a[0]&&u.label<o[1]){u.label=o[1],o=a;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(a);break}o[2]&&u.ops.pop(),u.trys.pop();continue}a=e.call(t,u)}catch(t){a=[6,t],r=0}finally{n=o=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}};function u(t){return l(t.currentSrc)?t.currentSrc:l(t.src)?t.src:null}function a(t){var e=getComputedStyle(t).backgroundImage.match(/url\(\s*(['"])(.*?)\1\s*\)/i),n=window.location.href.split("#")[0];return e&&e[2]&&e[2]!==n&&l(e[2])?e[2]:null}function c(t,e){return void 0===e&&(e=null),o(this,void 0,void 0,(function(){var n;return i(this,(function(o){return n=function(t){return t.filter((function(t){return t instanceof HTMLImageElement||t instanceof HTMLElement&&"none"!==getComputedStyle(t).backgroundImage}))}(t),[2,n.map((function(t){return t instanceof HTMLImageElement?new r.z(t,u,e):t instanceof HTMLElement&&a(t)?new r.z(t,a):null})).filter((function(t){return null!==t}))]}))}))}function l(t){if(t.startsWith("/"))return!t.split("?")[0].endsWith(".svg");try{var e=new URL(t);return"http:"===e.protocol||"https:"===e.protocol}catch(t){return!1}}}},e={};function n(r){var o=e[r];if(void 0!==o)return o.exports;var i=e[r]={exports:{}};return t[r](i,i.exports,n),i.exports}n.d=(t,e)=>{for(var r in e)n.o(e,r)&&!n.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var r={};return(()=>{n.r(r),n.d(r,{MeasurableImage:()=>t.z,getMeasurableImages:()=>e.RK});var t=n(38),e=n(705)})(),r})()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-image-guide",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Go through the dom to analyze image size on screen vs actual file size.",
|
|
5
5
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/image-guide/#readme",
|
|
6
6
|
"type": "module",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@automattic/jetpack-webpack-config": "workspace:*",
|
|
25
|
-
"@typescript-eslint/parser": "5.
|
|
26
|
-
"eslint": "8.
|
|
25
|
+
"@typescript-eslint/parser": "5.62.0",
|
|
26
|
+
"eslint": "8.44.0",
|
|
27
27
|
"jest": "29.4.3",
|
|
28
28
|
"ts-loader": "9.4.2",
|
|
29
29
|
"typescript": "5.0.4",
|
package/.gitattributes
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Files not needed to be distributed in the package.
|
|
2
|
-
.gitattributes export-ignore
|
|
3
|
-
node_modules export-ignore
|
|
4
|
-
package.json export-ignore
|
|
5
|
-
|
|
6
|
-
# Files to include in the mirror repo, but excluded via gitignore
|
|
7
|
-
# Remember to end all directories with `/**` to properly tag every file.
|
|
8
|
-
/build/** production-include
|
|
9
|
-
|
|
10
|
-
# Files to exclude from the mirror repo
|
|
11
|
-
/changelog/** production-exclude
|
|
12
|
-
/.eslintrc.cjs production-exclude
|
|
13
|
-
.gitignore production-exclude
|
|
14
|
-
tests/** production-exclude
|
|
15
|
-
babel.config.js production-exclude
|
|
16
|
-
webpack.config.js production-exclude
|
|
17
|
-
tsconfig.json production-exclude
|
package/babel.config.cjs
DELETED
package/src/MeasurableImage.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
export type SourceCallbackFn = ( node: HTMLElement ) => string | null;
|
|
2
|
-
export type Dimensions = { width: number; height: number };
|
|
3
|
-
export type Weight = { weight: number };
|
|
4
|
-
|
|
5
|
-
export type FetchFn = ( input: URL | RequestInfo, init?: RequestInit ) => Promise< Response >;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A class that represents a DOM Element that
|
|
9
|
-
* has an image that should be measured and
|
|
10
|
-
* provides measurement utilities.
|
|
11
|
-
*/
|
|
12
|
-
export class MeasurableImage {
|
|
13
|
-
readonly node: HTMLElement | HTMLImageElement;
|
|
14
|
-
private getURLCallback: SourceCallbackFn;
|
|
15
|
-
|
|
16
|
-
public fetch: FetchFn;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Constructor.
|
|
20
|
-
*
|
|
21
|
-
* @param {HTMLElement | HTMLImageElement} node - The DOM Element that contains the image.
|
|
22
|
-
* @param {SourceCallbackFn} getURL - A function that takes in the node and returns the URL of the image.
|
|
23
|
-
* @param {FetchFn} fetchFn - A function that fetches a URL and returns a Promise.
|
|
24
|
-
*/
|
|
25
|
-
constructor(
|
|
26
|
-
node: HTMLElement | HTMLImageElement,
|
|
27
|
-
getURL: SourceCallbackFn,
|
|
28
|
-
fetchFn: FetchFn | null = null
|
|
29
|
-
) {
|
|
30
|
-
this.node = node;
|
|
31
|
-
this.getURLCallback = getURL;
|
|
32
|
-
this.fetch = fetchFn ?? window.fetch.bind( window );
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
public getURL() {
|
|
36
|
-
return this.getURLCallback( this.node );
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
public getSizeOnPage() {
|
|
40
|
-
const { width, height } = this.node.getBoundingClientRect();
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
width: Math.round( width ),
|
|
44
|
-
height: Math.round( height ),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
public async getFileSize( url: string ) {
|
|
49
|
-
const [ weight, { width, height } ] = await Promise.all( [
|
|
50
|
-
this.fetchFileWeight( url ),
|
|
51
|
-
this.fetchFileDimensions( url ),
|
|
52
|
-
] );
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
width,
|
|
56
|
-
height,
|
|
57
|
-
weight,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public getPotentialSavings( fileSize: Dimensions & Weight, sizeOnPage: Dimensions ) {
|
|
62
|
-
const oversizedRatio = this.getOversizedRatio( fileSize, sizeOnPage );
|
|
63
|
-
if ( oversizedRatio <= 1 ) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
return Math.round( fileSize.weight - fileSize.weight / oversizedRatio );
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* To get the expected size of the image,
|
|
71
|
-
* the image size on page has to be multiplied by the device pixel ratio.
|
|
72
|
-
*
|
|
73
|
-
* @param {Dimensions} sizeOnPage - The size of the image on the page.
|
|
74
|
-
* @returns {object} - The expected size of the image.
|
|
75
|
-
*/
|
|
76
|
-
public getExpectedSize( sizeOnPage: Dimensions ) {
|
|
77
|
-
const dpr = window.devicePixelRatio || 1;
|
|
78
|
-
return {
|
|
79
|
-
width: Math.round( sizeOnPage.width * dpr ),
|
|
80
|
-
height: Math.round( sizeOnPage.height * dpr ),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
public getOversizedRatio( fileSize: Dimensions, sizeOnPage: Dimensions ) {
|
|
85
|
-
const { width, height } = this.getExpectedSize( sizeOnPage );
|
|
86
|
-
return ( fileSize.width * fileSize.height ) / ( width * height );
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Fetches the weight of the image at the given URL,
|
|
91
|
-
* by reading the Content-Length header.
|
|
92
|
-
*
|
|
93
|
-
* @param {string} url - string The URL of the image.
|
|
94
|
-
*/
|
|
95
|
-
private async fetchFileWeight( url: string ) {
|
|
96
|
-
const response = await this.fetch( url );
|
|
97
|
-
if ( ! response.ok ) {
|
|
98
|
-
// eslint-disable-next-line no-console
|
|
99
|
-
console.log( `Can't get image size for ${ url } likely due to a CORS error.` );
|
|
100
|
-
return -1;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return parseInt( response.headers.get( 'content-length' ), 10 ) / 1024;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Fetches the dimensions of the image at the given URL,
|
|
108
|
-
* This creates a new image element and loads the image.
|
|
109
|
-
*
|
|
110
|
-
* @param {string} url - image url
|
|
111
|
-
*/
|
|
112
|
-
private async fetchFileDimensions( url: string ) {
|
|
113
|
-
const img = new Image();
|
|
114
|
-
img.src = url;
|
|
115
|
-
return new Promise< { width: number; height: number } >( ( resolve, reject ) => {
|
|
116
|
-
img.onload = () => {
|
|
117
|
-
resolve( { width: Math.round( img.width ), height: Math.round( img.height ) } );
|
|
118
|
-
};
|
|
119
|
-
img.onerror = () => {
|
|
120
|
-
reject( 'Unable to load image.' );
|
|
121
|
-
};
|
|
122
|
-
} );
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Checks if the image is too small and should be ignored. Will return false on images
|
|
127
|
-
* that don't load at all - we can't establish they're tiny!
|
|
128
|
-
*
|
|
129
|
-
* @returns {boolean} - if the image is smaller than 65 pixels width and height return true
|
|
130
|
-
*/
|
|
131
|
-
public async isImageTiny(): Promise< boolean > {
|
|
132
|
-
try {
|
|
133
|
-
const minSize = 65;
|
|
134
|
-
const dimensions = await this.fetchFileDimensions( this.getURL() );
|
|
135
|
-
return dimensions.width < minSize || dimensions.height < minSize;
|
|
136
|
-
} catch ( err ) {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { FetchFn, MeasurableImage } from './MeasurableImage';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Get elements that either are image tags or have a background image.
|
|
5
|
-
*
|
|
6
|
-
* @param {Element[]} nodes - A list of nodes to filter
|
|
7
|
-
* @returns {HTMLElement[] | HTMLImageElement[]} - A list of nodes that are either image tags or have a background image.
|
|
8
|
-
*/
|
|
9
|
-
export function findMeasurableElements( nodes: Element[] ): HTMLElement[] | HTMLImageElement[] {
|
|
10
|
-
return nodes.filter( ( el ): el is HTMLElement | HTMLImageElement => {
|
|
11
|
-
if ( el instanceof HTMLImageElement ) {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
if ( el instanceof HTMLElement ) {
|
|
15
|
-
const style = getComputedStyle( el );
|
|
16
|
-
return 'none' !== style.backgroundImage;
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
} );
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get the current image source from a node.
|
|
24
|
-
*
|
|
25
|
-
* @param {HTMLImageElement} node - HTMLImageElement
|
|
26
|
-
* @returns {string | null} - The URL of the image or null if it can't be determined.
|
|
27
|
-
*/
|
|
28
|
-
export function imageTagSource( node: HTMLImageElement ) {
|
|
29
|
-
if ( imageLikeURL( node.currentSrc ) ) {
|
|
30
|
-
return node.currentSrc;
|
|
31
|
-
}
|
|
32
|
-
if ( imageLikeURL( node.src ) ) {
|
|
33
|
-
return node.src;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Get the background image URL from a node.
|
|
41
|
-
*
|
|
42
|
-
* @param {HTMLImageElement} node - HTMLElement
|
|
43
|
-
* @returns {string | null} - The URL of the image or null if it can't be determined.
|
|
44
|
-
*/
|
|
45
|
-
export function backgroundImageSource( node: HTMLElement ) {
|
|
46
|
-
const src = getComputedStyle( node ).backgroundImage;
|
|
47
|
-
const url = src.match( /url\(\s*(['"])(.*?)\1\s*\)/i );
|
|
48
|
-
|
|
49
|
-
// If background image is `url('')`, the computed value becomes the current page URL. So we need to check for that.
|
|
50
|
-
const currentUrlWithoutHash = window.location.href.split( '#' )[ 0 ];
|
|
51
|
-
|
|
52
|
-
if ( url && url[ 2 ] && url[ 2 ] !== currentUrlWithoutHash && imageLikeURL( url[ 2 ] ) ) {
|
|
53
|
-
return url[ 2 ];
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Create MeasurableImage objects from a list of nodes
|
|
60
|
-
* and remove any nodes that can't be measured.
|
|
61
|
-
*
|
|
62
|
-
* @param {Element[]} domNodes - A list of nodes to measure
|
|
63
|
-
* @param {(input: string, init?: Array) => Promise<Response>} fetchFn - A function that fetches a URL and returns a Promise.
|
|
64
|
-
* @returns {MeasurableImage[]} - A list of MeasurableImage objects.
|
|
65
|
-
*/
|
|
66
|
-
export async function getMeasurableImages(
|
|
67
|
-
domNodes: Element[],
|
|
68
|
-
fetchFn: FetchFn | null = null
|
|
69
|
-
): Promise< MeasurableImage[] > {
|
|
70
|
-
const nodes = findMeasurableElements( domNodes );
|
|
71
|
-
const images = nodes.map( node => {
|
|
72
|
-
if ( node instanceof HTMLImageElement ) {
|
|
73
|
-
return new MeasurableImage( node, imageTagSource, fetchFn );
|
|
74
|
-
} else if ( node instanceof HTMLElement ) {
|
|
75
|
-
if ( ! backgroundImageSource( node ) ) {
|
|
76
|
-
/**
|
|
77
|
-
* Background elements that have no valid URL
|
|
78
|
-
* shouldn't be measured.
|
|
79
|
-
*/
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return new MeasurableImage( node, backgroundImageSource );
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return null;
|
|
87
|
-
} );
|
|
88
|
-
|
|
89
|
-
return images;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* isImageLikeURL - a helper function to determine if URL could be an image.
|
|
94
|
-
*
|
|
95
|
-
* This function ensures that the value passed in looks like a URL.
|
|
96
|
-
* This is because `background: url(...)` and `src="..."` can
|
|
97
|
-
* contain various values that are not URLs, like:
|
|
98
|
-
* - none
|
|
99
|
-
* - linear-gradient(...)
|
|
100
|
-
* - data:image/png;base64,...
|
|
101
|
-
* - ...
|
|
102
|
-
*
|
|
103
|
-
* For the purposes of analyzing image sizes,
|
|
104
|
-
* we also don't consider SVGs to be images.
|
|
105
|
-
*
|
|
106
|
-
* @param {string} value - string to check
|
|
107
|
-
* @returns {boolean} - true if the value looks like a URL
|
|
108
|
-
*/
|
|
109
|
-
function imageLikeURL( value: string ): boolean {
|
|
110
|
-
// Look for relative URLs that are not SVGs
|
|
111
|
-
// Intentionally not using an allow-list because images may
|
|
112
|
-
// be served from weird URLs like /images/1234?size=large
|
|
113
|
-
if ( value.startsWith( '/' ) ) {
|
|
114
|
-
// Remove query string parameters
|
|
115
|
-
const path = value.split( '?' )[ 0 ];
|
|
116
|
-
return ! path.endsWith( '.svg' );
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
const url = new URL( value );
|
|
121
|
-
return url.protocol === 'http:' || url.protocol === 'https:';
|
|
122
|
-
} catch ( e ) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/index.ts
DELETED
package/webpack.config.cjs
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
const path = require( 'path' );
|
|
2
|
-
const jetpackWebpackConfig = require( '@automattic/jetpack-webpack-config/webpack' );
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
entry: './src/index.ts',
|
|
6
|
-
mode: jetpackWebpackConfig.mode,
|
|
7
|
-
devtool: jetpackWebpackConfig.isProduction ? false : 'source-map',
|
|
8
|
-
module: {
|
|
9
|
-
strictExportPresence: true,
|
|
10
|
-
rules: [
|
|
11
|
-
{
|
|
12
|
-
test: /\.ts?$/,
|
|
13
|
-
use: 'ts-loader',
|
|
14
|
-
exclude: /node_modules/,
|
|
15
|
-
},
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
|
-
optimization: {
|
|
19
|
-
...jetpackWebpackConfig.optimization,
|
|
20
|
-
},
|
|
21
|
-
resolve: {
|
|
22
|
-
...jetpackWebpackConfig.resolve,
|
|
23
|
-
},
|
|
24
|
-
output: {
|
|
25
|
-
...jetpackWebpackConfig.output,
|
|
26
|
-
path: path.resolve( __dirname, 'build' ),
|
|
27
|
-
filename: 'index.js',
|
|
28
|
-
library: {
|
|
29
|
-
name: 'ImageGuideLibrary',
|
|
30
|
-
type: 'umd',
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
plugins: [ ...jetpackWebpackConfig.StandardPlugins() ],
|
|
34
|
-
};
|