Skip to content

Commit

Permalink
revert(color-contrast): revert upgrade to colorjs.io v0.5.0 to work w…
Browse files Browse the repository at this point in the history
…ith prototype.js (#4429)

This [puts back the v0.4.3
code](#4366) in `color.js` to
handle colorjs.io not handling rad and turn values in hsl.

I also decided to use core-js `Array.from` polyfill rather than our own
internal one since I needed to bring it in outside of the `polyfill.js`
file and could then add it as an import and reuse it in both places.

Closes: #4428
  • Loading branch information
straker authored Apr 25, 2024
1 parent 61cbf30 commit 29e7e2a
Show file tree
Hide file tree
Showing 9 changed files with 8,210 additions and 100 deletions.
41 changes: 40 additions & 1 deletion lib/commons/color/color.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Colorjs } from '../../core/imports';
import { Colorjs, ArrayFrom } from '../../core/imports';

const hexRegex = /^#[0-9a-f]{3,8}$/i;
const hslRegex = /hsl\(\s*([-\d.]+)(rad|turn)/;

/**
* @class Color
Expand Down Expand Up @@ -129,9 +130,37 @@ export default class Color {
* @instance
*/
parseString(colorString) {
// Colorjs <v0.5.0 does not support rad or turn angle values
// @see https://github.com/LeaVerou/color.js/issues/311
colorString = colorString.replace(hslRegex, (match, angle, unit) => {
const value = angle + unit;

switch (unit) {
case 'rad':
return match.replace(value, radToDeg(angle));
case 'turn':
return match.replace(value, turnToDeg(angle));
}
});

try {
// revert prototype.js override of Array.from
// in order to get color-contrast working
// @see https://github.com/dequelabs/axe-core/issues/4428
let prototypeArrayFrom;
if ('Prototype' in window && 'Version' in window.Prototype) {
prototypeArrayFrom = Array.from;
Array.from = ArrayFrom;
}

// srgb values are between 0 and 1
const color = new Colorjs(colorString).to('srgb');

if (prototypeArrayFrom) {
Array.from = prototypeArrayFrom;
prototypeArrayFrom = null;
}

this.r = color.r;
this.g = color.g;
this.b = color.b;
Expand Down Expand Up @@ -328,3 +357,13 @@ export default class Color {
function clamp(value, min, max) {
return Math.min(Math.max(min, value), max);
}

// convert radians to degrees
function radToDeg(rad) {
return (rad * 180) / Math.PI;
}

// convert turn to degrees
function turnToDeg(turn) {
return turn * 360;
}
10 changes: 9 additions & 1 deletion lib/core/imports/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import doT from '@deque/dot';
import emojiRegexText from 'emoji-regex';
import memoize from 'memoizee';
import Color from 'colorjs.io';
import ArrayFrom from 'core-js-pure/actual/array/from';

// prevent striping newline characters from strings (e.g. failure
// summaries). value must be synced with build/configure.js
Expand All @@ -17,4 +18,11 @@ doT.templateSettings.strip = false;
* @namespace imports
* @memberof axe
*/
export { CssSelectorParser, doT, emojiRegexText, memoize, Color as Colorjs };
export {
CssSelectorParser,
doT,
emojiRegexText,
memoize,
Color as Colorjs,
ArrayFrom
};
91 changes: 2 additions & 89 deletions lib/core/imports/polyfills.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Uint32Array } from 'typedarray';
import 'weakmap-polyfill';
import hasOwn from 'core-js-pure/actual/object/has-own';
import values from 'core-js-pure/actual/object/values';
import ArrayFrom from 'core-js-pure/actual/array/from';

if (!('hasOwn' in Object)) {
Object.hasOwn = hasOwn;
Expand Down Expand Up @@ -177,95 +178,7 @@ if (!Array.prototype.some) {
}

if (!Array.from) {
Object.defineProperty(Array, 'from', {
value: (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return (
typeof fn === 'function' || toStr.call(fn) === '[object Function]'
);
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) {
return 0;
}
if (number === 0 || !isFinite(number)) {
return number;
}
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};

// The length property of the from method is 1.
return function from(arrayLike /*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;

// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);

// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError(
'Array.from requires an array-like object - not null or undefined'
);
}

// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError(
'Array.from: when provided, the second argument must be a function'
);
}

// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}

// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);

// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);

// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] =
typeof T === 'undefined'
? mapFn(kValue, k)
: mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
})()
});
Array.from = ArrayFrom;
}

if (!String.prototype.includes) {
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
"chromedriver": "latest",
"clean-jsdoc-theme": "^4.2.17",
"clone": "^2.1.2",
"colorjs.io": "^0.5.0",
"colorjs.io": "^0.4.3",
"conventional-commits-parser": "^5.0.0",
"core-js": "^3.27.1",
"css-selector-parser": "^1.4.1",
Expand Down
18 changes: 18 additions & 0 deletions test/commons/color/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ describe('color.Color', () => {
assert.equal(c.alpha, 1);
});

it('supports negative rad on hue', () => {
const c = new Color();
c.parseColorFnString('hsl(-3.49rad, 40%, 50%)');
assert.equal(c.red, 77);
assert.equal(c.green, 179);
assert.equal(c.blue, 145);
assert.equal(c.alpha, 1);
});

it('supports turn on hue', () => {
const c = new Color();
c.parseColorFnString('hsl(0.444turn, 40%, 50%)');
Expand All @@ -174,6 +183,15 @@ describe('color.Color', () => {
assert.equal(c.blue, 144);
assert.equal(c.alpha, 1);
});

it('supports negative turn on hue', () => {
const c = new Color();
c.parseColorFnString('hsl(-0.556turn, 40%, 50%)');
assert.equal(c.red, 77);
assert.equal(c.green, 179);
assert.equal(c.blue, 144);
assert.equal(c.alpha, 1);
});
});

describe('with hwb()', () => {
Expand Down
Loading

0 comments on commit 29e7e2a

Please sign in to comment.