From 18838a477d5af192711445232c40d6680e264a65 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Thu, 27 Apr 2023 23:49:47 -0400 Subject: [PATCH 1/3] Make Color.js proxy-friendly, resolves #305 --- src/color.js | 18 +++---- src/space.js | 138 +++++++++++++++++++++++++++------------------------ 2 files changed, 82 insertions(+), 74 deletions(-) diff --git a/src/color.js b/src/color.js index 2b2e0a879..ec565b820 100644 --- a/src/color.js +++ b/src/color.js @@ -55,7 +55,13 @@ export default class Color { [space, coords, alpha] = args; } - this.#space = ColorSpace.get(space); + + Object.defineProperty(this, "space", { + value: ColorSpace.get(space), + writable: false, + enumerable: true, + configurable: true, // see note in https://262.ecma-international.org/8.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + }); this.coords = coords? coords.slice() : [0, 0, 0]; this.alpha = alpha < 1? alpha : 1; // this also deals with NaN etc @@ -67,7 +73,7 @@ export default class Color { } // Define getters and setters for each coordinate - for (let id in this.#space.coords) { + for (let id in this.space.coords) { Object.defineProperty(this, id, { get: () => this.get(id), set: value => this.set(id, value) @@ -75,14 +81,8 @@ export default class Color { } } - #space; - - get space () { - return this.#space; - } - get spaceId () { - return this.#space.id; + return this.space.id; } clone () { diff --git a/src/space.js b/src/space.js index ca80c855c..af6e8e326 100644 --- a/src/space.js +++ b/src/space.js @@ -57,7 +57,12 @@ export default class ColorSpace { this.referred = options.referred; // Compute ancestors and store them, since they will never change - this.#path = this.#getPath().reverse(); + Object.defineProperty(this, "path", { + value: getPath(this).reverse(), + writable: false, + enumerable: true, + configurable: true, + }) hooks.run("colorspace-init-end", this); } @@ -104,58 +109,9 @@ export default class ColorSpace { return false; } - #processFormat (format) { - if (format.coords && !format.coordGrammar) { - format.type ||= "function"; - format.name ||= "color"; - - // Format has not been processed - format.coordGrammar = parseCoordGrammar(format.coords); - - let coordFormats = Object.entries(this.coords).map(([id, coordMeta], i) => { - // Preferred format for each coord is the first one - let outputType = format.coordGrammar[i][0]; - - let fromRange = coordMeta.range || coordMeta.refRange; - let toRange = outputType.range, suffix = ""; - - // Non-strict equals intentional since outputType could be a string object - if (outputType == "") { - toRange = [0, 100]; - suffix = "%"; - } - else if (outputType == "") { - suffix = "deg"; - } - - return {fromRange, toRange, suffix}; - }); - - format.serializeCoords = (coords, precision) => { - return coords.map((c, i) => { - let {fromRange, toRange, suffix} = coordFormats[i]; - - if (fromRange && toRange) { - c = mapRange(fromRange, toRange, c); - } - - c = toPrecision(c, precision); - - if (suffix) { - c += suffix; - } - - return c; - }); - }; - } - - return format; - } - getFormat (format) { if (typeof format === "object") { - format = this.#processFormat(format); + format = processFormat(format, this); return format; } @@ -169,23 +125,16 @@ export default class ColorSpace { } if (ret) { - ret = this.#processFormat(ret); + ret = processFormat(ret, this); return ret; } return null; } - #path; - - #getPath () { - let ret = [this]; - - for (let space = this; space = space.base;) { - ret.push(space); - } - - return ret; + // We cannot rely on simple === because then ColorSpace objects cannot be proxied + equals (space) { + return this === space || this.id === space.id; } to (space, coords) { @@ -204,13 +153,13 @@ export default class ColorSpace { coords = coords.map(c => Number.isNaN(c)? 0 : c); // Find connection space = lowest common ancestor in the base tree - let myPath = this.#path; - let otherPath = space.#path; + let myPath = this.path; + let otherPath = space.path; let connectionSpace, connectionSpaceIndex; for (let i=0; i < myPath.length; i++) { - if (myPath[i] === otherPath[i]) { + if (myPath[i].equals(otherPath[i])) { connectionSpace = myPath[i]; connectionSpaceIndex = i; } @@ -397,3 +346,62 @@ export default class ColorSpace { name: "color", }; } + +function getPath (space) { + let ret = [space]; + + for (let s = space; s = s.base;) { + ret.push(s); + } + + return ret; +} + +function processFormat (format, {coords} = {}) { + if (format.coords && !format.coordGrammar) { + format.type ||= "function"; + format.name ||= "color"; + + // Format has not been processed + format.coordGrammar = parseCoordGrammar(format.coords); + + let coordFormats = Object.entries(coords).map(([id, coordMeta], i) => { + // Preferred format for each coord is the first one + let outputType = format.coordGrammar[i][0]; + + let fromRange = coordMeta.range || coordMeta.refRange; + let toRange = outputType.range, suffix = ""; + + // Non-strict equals intentional since outputType could be a string object + if (outputType == "") { + toRange = [0, 100]; + suffix = "%"; + } + else if (outputType == "") { + suffix = "deg"; + } + + return {fromRange, toRange, suffix}; + }); + + format.serializeCoords = (coords, precision) => { + return coords.map((c, i) => { + let {fromRange, toRange, suffix} = coordFormats[i]; + + if (fromRange && toRange) { + c = mapRange(fromRange, toRange, c); + } + + c = toPrecision(c, precision); + + if (suffix) { + c += suffix; + } + + return c; + }); + }; + } + + return format; +} \ No newline at end of file From 4ae3ca8f2ed1194563807c1269aff01bfc87c6bd Mon Sep 17 00:00:00 2001 From: Jonny Gerig Meyer Date: Fri, 28 Apr 2023 15:22:04 -0400 Subject: [PATCH 2/3] lint --- src/color.js | 1 - src/space.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/color.js b/src/color.js index ec565b820..b7c200ced 100644 --- a/src/color.js +++ b/src/color.js @@ -55,7 +55,6 @@ export default class Color { [space, coords, alpha] = args; } - Object.defineProperty(this, "space", { value: ColorSpace.get(space), writable: false, diff --git a/src/space.js b/src/space.js index af6e8e326..8f2415608 100644 --- a/src/space.js +++ b/src/space.js @@ -62,7 +62,7 @@ export default class ColorSpace { writable: false, enumerable: true, configurable: true, - }) + }); hooks.run("colorspace-init-end", this); } @@ -404,4 +404,4 @@ function processFormat (format, {coords} = {}) { } return format; -} \ No newline at end of file +} From 95c12ce57d510e9022936f2342ad5427cbabb578 Mon Sep 17 00:00:00 2001 From: Lea Verou Date: Sat, 29 Apr 2023 12:19:22 -0400 Subject: [PATCH 3/3] Use equals instead of === for color space equality --- src/space.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/space.js b/src/space.js index 8f2415608..b3e0bc1f0 100644 --- a/src/space.js +++ b/src/space.js @@ -144,7 +144,7 @@ export default class ColorSpace { space = ColorSpace.get(space); - if (this === space) { + if (this.equals(space)) { // Same space, no change needed return coords; }