Son CV dans un terminal web en Javascript! https://terminal-cv.gregandev.fr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

186 lines
4.4 KiB

'use strict';
/**
This regex represents a loose rule of an “image candidate string”.
@see https://html.spec.whatwg.org/multipage/images.html#srcset-attribute
An “image candidate string” roughly consists of the following:
1. Zero or more whitespace characters.
2. A non-empty URL that does not start or end with `,`.
3. Zero or more whitespace characters.
4. An optional “descriptor” that starts with a whitespace character.
5. Zero or more whitespace characters.
6. Each image candidate string is separated by a `,`.
We intentionally implement a loose rule here so that we can perform more aggressive error handling and reporting in the below code.
*/
const imageCandidateRegex = /\s*([^,]\S*[^,](?:\s+[^,]+)?)\s*(?:,|$)/;
const duplicateDescriptorCheck = (allDescriptors, value, postfix) => {
allDescriptors[postfix] = allDescriptors[postfix] || {};
if (allDescriptors[postfix][value]) {
throw new Error(`No more than one image candidate is allowed for a given descriptor: ${value}${postfix}`);
}
allDescriptors[postfix][value] = true;
};
const fallbackDescriptorDuplicateCheck = allDescriptors => {
if (allDescriptors.fallback) {
throw new Error('Only one fallback image candidate is allowed');
}
if (allDescriptors.x['1']) {
throw new Error('A fallback image is equivalent to a 1x descriptor, providing both is invalid.');
}
allDescriptors.fallback = true;
};
const descriptorCountCheck = (allDescriptors, currentDescriptors) => {
if (currentDescriptors.length === 0) {
fallbackDescriptorDuplicateCheck(allDescriptors);
} else if (currentDescriptors.length > 1) {
throw new Error(`Image candidate may have no more than one descriptor, found ${currentDescriptors.length}: ${currentDescriptors.join(' ')}`);
}
};
const validDescriptorCheck = (value, postfix, descriptor) => {
if (Number.isNaN(value)) {
throw new TypeError(`${descriptor || value} is not a valid number`);
}
switch (postfix) {
case 'w': {
if (value <= 0) {
throw new Error('Width descriptor must be greater than zero');
} else if (!Number.isInteger(value)) {
throw new TypeError('Width descriptor must be an integer');
}
break;
}
case 'x': {
if (value <= 0) {
throw new Error('Pixel density descriptor must be greater than zero');
}
break;
}
case 'h': {
throw new Error('Height descriptor is no longer allowed');
}
default: {
throw new Error(`Invalid srcset descriptor: ${descriptor}`);
}
}
};
exports.parse = (string, {strict = false} = {}) => {
const allDescriptors = strict ? {} : undefined;
return string.split(imageCandidateRegex)
.filter((part, index) => index % 2 === 1)
.map(part => {
const [url, ...descriptors] = part.trim().split(/\s+/);
const result = {url};
if (strict) {
descriptorCountCheck(allDescriptors, descriptors);
}
for (const descriptor of descriptors) {
const postfix = descriptor[descriptor.length - 1];
const value = Number.parseFloat(descriptor.slice(0, -1));
if (strict) {
validDescriptorCheck(value, postfix, descriptor);
duplicateDescriptorCheck(allDescriptors, value, postfix);
}
switch (postfix) {
case 'w': {
result.width = value;
break;
}
case 'h': {
result.height = value;
break;
}
case 'x': {
result.density = value;
break;
}
// No default
}
}
return result;
});
};
const knownDescriptors = new Set(['width', 'height', 'density']);
exports.stringify = (array, {strict = false} = {}) => {
const allDescriptors = strict ? {} : null;
return array.map(element => {
if (!element.url) {
if (strict) {
throw new Error('URL is required');
}
return '';
}
const descriptorKeys = Object.keys(element).filter(key => knownDescriptors.has(key));
if (strict) {
descriptorCountCheck(allDescriptors, descriptorKeys);
}
const result = [element.url];
for (const descriptorKey of descriptorKeys) {
const value = element[descriptorKey];
let postfix;
switch (descriptorKey) {
case 'width': {
postfix = 'w';
break;
}
case 'height': {
postfix = 'h';
break;
}
case 'density': {
postfix = 'x';
break;
}
// No default
}
const descriptor = `${value}${postfix}`;
if (strict) {
validDescriptorCheck(value, postfix);
duplicateDescriptorCheck(allDescriptors, value, postfix);
}
result.push(descriptor);
}
return result.join(' ');
}).join(', ');
};