Skip to content

Commit ce524fe

Browse files
committed
Update parser
* add global values * scope variables * fix some functions
1 parent 4d19a92 commit ce524fe

File tree

2 files changed

+240
-117
lines changed

2 files changed

+240
-117
lines changed

lib/parsers.js

+79-101
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
********************************************************************/
55
'use strict';
66

7+
// FIXME: should move shorthandGetter(), shorthandSetter(), implicitSetter() and
8+
// subImplicitSetter() to CSSStyleDeclaration()
9+
710
const { resolve: resolveColor, utils } = require('@asamuzakjp/css-color');
811
const { cssCalc, isColor, isGradient, splitValue } = utils;
912

@@ -22,6 +25,9 @@ exports.TYPES = {
2225
UNIDENT: 0x8000,
2326
};
2427

28+
// CSS global values
29+
exports.GLOBAL_VALUES = Object.freeze(['initial', 'inherit', 'unset', 'revert', 'revert-layer']);
30+
2531
// regular expressions
2632
var DIGIT = '(?:0|[1-9]\\d*)';
2733
var NUMBER = `[+-]?(?:${DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${DIGIT})?`;
@@ -37,6 +43,7 @@ var calcRegEx =
3743

3844
// This will return one of the above types based on the passed in string
3945
exports.valueType = function valueType(val) {
46+
// see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString
4047
if (val === '' || val === null) {
4148
return exports.TYPES.NULL_OR_EMPTY_STR;
4249
}
@@ -53,7 +60,7 @@ exports.valueType = function valueType(val) {
5360
return exports.TYPES.CALC;
5461
}
5562
if (unitRegEx.test(val)) {
56-
const [, , unit] = unitRegEx.exec(val);
63+
var [, , unit] = unitRegEx.exec(val);
5764
if (!unit) {
5865
return exports.TYPES.NUMBER;
5966
}
@@ -162,7 +169,7 @@ exports.parseLength = function parseLength(val) {
162169
format: 'specifiedValue',
163170
});
164171
case exports.TYPES.LENGTH: {
165-
const [, numVal, unit] = unitRegEx.exec(val);
172+
var [, numVal, unit] = unitRegEx.exec(val);
166173
return `${parseFloat(numVal)}${unit}`;
167174
}
168175
default:
@@ -186,9 +193,10 @@ exports.parsePercent = function parsePercent(val) {
186193
return cssCalc(val, {
187194
format: 'specifiedValue',
188195
});
189-
case exports.TYPES.PERCENT:
190-
const [, numVal, unit] = unitRegEx.exec(val);
196+
case exports.TYPES.PERCENT: {
197+
var [, numVal, unit] = unitRegEx.exec(val);
191198
return `${parseFloat(numVal)}${unit}`;
199+
}
192200
default:
193201
if (varContainedRegEx.test(val)) {
194202
return val;
@@ -212,9 +220,10 @@ exports.parseMeasurement = function parseMeasurement(val) {
212220
format: 'specifiedValue',
213221
});
214222
case exports.TYPES.LENGTH:
215-
case exports.TYPES.PERCENT:
216-
const [, numVal, unit] = unitRegEx.exec(val);
223+
case exports.TYPES.PERCENT: {
224+
var [, numVal, unit] = unitRegEx.exec(val);
217225
return `${parseFloat(numVal)}${unit}`;
226+
}
218227
default:
219228
if (varContainedRegEx.test(val)) {
220229
return val;
@@ -236,7 +245,7 @@ exports.parseInheritingMeasurement = function parseInheritingMeasurement(val) {
236245
exports.parseUrl = function parseUrl(val) {
237246
var type = exports.valueType(val);
238247
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
239-
return val;
248+
return '';
240249
}
241250
var res = urlRegEx.exec(val);
242251
// does it match the regex?
@@ -293,10 +302,11 @@ exports.parseUrl = function parseUrl(val) {
293302
return 'url("' + urlstr + '")';
294303
};
295304

305+
// NOTE: seems not in use?
296306
exports.parseString = function parseString(val) {
297307
var type = exports.valueType(val);
298308
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
299-
return val;
309+
return '';
300310
}
301311
if (type !== exports.TYPES.STRING) {
302312
return undefined;
@@ -320,14 +330,38 @@ exports.parseString = function parseString(val) {
320330
return val;
321331
};
322332

323-
exports.parseColor = function parseColor(val) {
333+
exports.parseKeyword = function parseKeyword(val, validKeywords = []) {
324334
var type = exports.valueType(val);
325-
if (type === exports.TYPES.NULL_OR_EMPTY_STR || type === exports.TYPES.VAR) {
335+
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
336+
return '';
337+
}
338+
if (type === exports.TYPES.VAR) {
326339
return val;
327340
}
341+
if (type !== exports.TYPES.KEYWORD) {
342+
return undefined;
343+
}
344+
val = val.toString().toLowerCase();
345+
if (validKeywords.includes(val) || exports.GLOBAL_VALUES.includes(val)) {
346+
return val;
347+
}
348+
return undefined;
349+
};
350+
351+
exports.parseColor = function parseColor(val) {
352+
var type = exports.valueType(val);
353+
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
354+
return '';
355+
}
328356
if (type === exports.TYPES.UNDEFINED) {
329357
return undefined;
330358
}
359+
if (type === exports.TYPES.VAR) {
360+
return val;
361+
}
362+
if (type === exports.TYPES.KEYWORD) {
363+
return exports.parseKeyword(val);
364+
}
331365
if (/^[a-z]+$/i.test(val) && type === exports.TYPES.COLOR) {
332366
return val;
333367
}
@@ -346,7 +380,7 @@ exports.parseColor = function parseColor(val) {
346380
exports.parseAngle = function parseAngle(val) {
347381
var type = exports.valueType(val);
348382
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
349-
return val;
383+
return '';
350384
}
351385
if (type !== exports.TYPES.ANGLE) {
352386
return undefined;
@@ -368,34 +402,19 @@ exports.parseAngle = function parseAngle(val) {
368402
return flt + 'deg';
369403
};
370404

371-
exports.parseKeyword = function parseKeyword(val, validKeywords) {
405+
exports.parseImage = function parseImage(val) {
372406
var type = exports.valueType(val);
373407
if (type === exports.TYPES.NULL_OR_EMPTY_STR) {
374-
return val;
408+
return '';
375409
}
376-
if (type !== exports.TYPES.KEYWORD) {
410+
if (type === exports.TYPES.UNDEFINED) {
377411
return undefined;
378412
}
379-
val = val.toString().toLowerCase();
380-
var i;
381-
for (i = 0; i < validKeywords.length; i++) {
382-
if (validKeywords[i].toLowerCase() === val) {
383-
return validKeywords[i];
384-
}
385-
}
386-
return undefined;
387-
};
388-
389-
exports.parseImage = function parseImage(val) {
390-
if (/^(?:none|inherit)$/i.test(val)) {
391-
return val.toLowerCase();
392-
}
393-
var type = exports.valueType(val);
394-
if (type === exports.TYPES.NULL_OR_EMPTY_STR || type === exports.TYPES.VAR) {
413+
if (type === exports.TYPES.VAR) {
395414
return val;
396415
}
397-
if (type === exports.TYPES.UNDEFINED) {
398-
return undefined;
416+
if (type === exports.TYPES.KEYWORD) {
417+
return exports.parseKeyword(val, ['none']);
399418
}
400419
var values = splitValue(val, {
401420
delimiter: ',',
@@ -445,56 +464,22 @@ exports.dashedToCamelCase = function (dashed) {
445464
return camel;
446465
};
447466

448-
var isSpace = /\s/;
449-
var openingDeliminators = ['"', "'", '('];
450-
var closingDeliminators = ['"', "'", ')'];
451-
// this splits on whitespace, but keeps quoted and parened parts together
452-
var getParts = function (str) {
453-
var deliminatorStack = [];
454-
var length = str.length;
455-
var i;
456-
var parts = [];
457-
var currentPart = '';
458-
var openingIndex;
459-
var closingIndex;
460-
for (i = 0; i < length; i++) {
461-
openingIndex = openingDeliminators.indexOf(str[i]);
462-
closingIndex = closingDeliminators.indexOf(str[i]);
463-
if (isSpace.test(str[i])) {
464-
if (deliminatorStack.length === 0) {
465-
if (currentPart !== '') {
466-
parts.push(currentPart);
467-
}
468-
currentPart = '';
469-
} else {
470-
currentPart += str[i];
471-
}
472-
} else {
473-
if (str[i] === '\\') {
474-
i++;
475-
currentPart += str[i];
476-
} else {
477-
currentPart += str[i];
478-
if (closingIndex !== -1 && closingIndex === deliminatorStack[deliminatorStack.length - 1]) {
479-
deliminatorStack.pop();
480-
} else if (openingIndex !== -1) {
481-
deliminatorStack.push(openingIndex);
482-
}
483-
}
484-
}
485-
}
486-
if (currentPart !== '') {
487-
parts.push(currentPart);
467+
exports.camelToDashed = function (camelCase) {
468+
var dashed = camelCase.replace(/(?<=[a-z])[A-Z]/g, '-$&').toLowerCase();
469+
var vendorPrefixes = ['o', 'moz', 'ms', 'webkit'];
470+
var match = dashed.match(/^([a-z]+)\-/);
471+
if (match && vendorPrefixes.includes(match[1])) {
472+
dashed = '-' + dashed;
488473
}
489-
return parts;
474+
return dashed;
490475
};
491476

492-
/*
493-
* this either returns undefined meaning that it isn't valid
494-
* or returns an object where the keys are dashed short
495-
* hand properties and the values are the values to set
496-
* on them
497-
*/
477+
// this either returns undefined meaning that it isn't valid
478+
// or returns an object where the keys are dashed short
479+
// hand properties and the values are the values to set
480+
// on them
481+
// FIXME: need additional argument which indicates syntax
482+
// and/or use Map() for shorthandFor to ensure order of the longhand properties
498483
exports.shorthandParser = function parse(v, shorthandFor) {
499484
var obj = {};
500485
var type = exports.valueType(v);
@@ -504,19 +489,19 @@ exports.shorthandParser = function parse(v, shorthandFor) {
504489
});
505490
return obj;
506491
}
507-
492+
if (type === exports.TYPES.UNDEFINED) {
493+
return undefined;
494+
}
508495
if (typeof v === 'number') {
509496
v = v.toString();
510497
}
511-
512498
if (typeof v !== 'string') {
513499
return undefined;
514500
}
515-
516501
if (v.toLowerCase() === 'inherit') {
517502
return {};
518503
}
519-
var parts = getParts(v);
504+
var parts = splitValue(v);
520505
var valid = true;
521506
parts.forEach(function (part, i) {
522507
var partValid = false;
@@ -526,21 +511,29 @@ exports.shorthandParser = function parse(v, shorthandFor) {
526511
obj[property] = part;
527512
}
528513
});
529-
valid = valid && partValid;
514+
if (valid) {
515+
valid = partValid;
516+
}
530517
});
531518
if (!valid) {
532519
return undefined;
533520
}
534521
return obj;
535522
};
536523

524+
// FIXME: check against shorthandParser and reduce Object.keys().forEach() loops
537525
exports.shorthandSetter = function (property, shorthandFor) {
538526
return function (v) {
527+
if (v === undefined) {
528+
return;
529+
}
530+
if (v === null) {
531+
v = '';
532+
}
539533
var obj = exports.shorthandParser(v, shorthandFor);
540534
if (obj === undefined) {
541535
return;
542536
}
543-
//console.log('shorthandSetter for:', property, 'obj:', obj);
544537
Object.keys(obj).forEach(function (subprop) {
545538
// in case subprop is an implicit property, this will clear
546539
// *its* subpropertiesX
@@ -611,7 +604,7 @@ exports.implicitSetter = function (propertyBefore, propertyAfter, isValid, parse
611604
if (v.toLowerCase() === 'inherit' || v === '') {
612605
parts = [v];
613606
} else {
614-
parts = getParts(v);
607+
parts = splitValue(v);
615608
}
616609
if (parts.length < 1 || parts.length > 4) {
617610
return undefined;
@@ -646,12 +639,10 @@ exports.implicitSetter = function (propertyBefore, propertyAfter, isValid, parse
646639
};
647640
};
648641

649-
//
650642
// Companion to implicitSetter, but for the individual parts.
651643
// This sets the individual value, and checks to see if all four
652644
// sub-parts are set. If so, it sets the shorthand version and removes
653645
// the individual parts from the cssText.
654-
//
655646
exports.subImplicitSetter = function (prefix, part, isValid, parser) {
656647
var property = prefix + '-' + part;
657648
var subparts = [prefix + '-top', prefix + '-right', prefix + '-bottom', prefix + '-left'];
@@ -697,16 +688,3 @@ exports.subImplicitSetter = function (prefix, part, isValid, parser) {
697688
return v;
698689
};
699690
};
700-
701-
var camelToDashed = /[A-Z]/g;
702-
var firstSegment = /^\([^-]\)-/;
703-
var vendorPrefixes = ['o', 'moz', 'ms', 'webkit'];
704-
exports.camelToDashed = function (camelCase) {
705-
var match;
706-
var dashed = camelCase.replace(camelToDashed, '-$&').toLowerCase();
707-
match = dashed.match(firstSegment);
708-
if (match && vendorPrefixes.indexOf(match[1]) !== -1) {
709-
dashed = '-' + dashed;
710-
}
711-
return dashed;
712-
};

0 commit comments

Comments
 (0)