44 ********************************************************************/
55'use strict' ;
66
7+ // FIXME: should move shorthandGetter(), shorthandSetter(), implicitSetter() and
8+ // subImplicitSetter() to CSSStyleDeclaration()
9+
710const { resolve : resolveColor , utils } = require ( '@asamuzakjp/css-color' ) ;
811const { 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
2632var DIGIT = '(?:0|[1-9]\\d*)' ;
2733var 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
3945exports . 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) {
236245exports . 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?
296306exports . 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) {
346380exports . 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 ( / ^ (?: n o n e | i n h e r i t ) $ / 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
498483exports . 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
537525exports . 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- //
655646exports . 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