CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
parse.mjs436 linesDownload Raw Back to style
1import * as util from '../util/index.mjs';2import * as is from '../is.mjs';3import * as math from '../math.mjs';4 5let styfn = {};6 7// a caching layer for property parsing8styfn.parse = function( name, value, propIsBypass, propIsFlat ){9  let self = this;10 11  // function values can't be cached in all cases, and there isn't much benefit of caching them anyway12  if( is.fn( value ) ){13    return self.parseImplWarn( name, value, propIsBypass, propIsFlat );14  }15 16  let flatKey = ( propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ) ? 'dontcare' : propIsFlat;17  let bypassKey = propIsBypass ? 't' : 'f';18  let valueKey = '' + value;19  let argHash = util.hashStrings( name, valueKey, bypassKey, flatKey );20  let propCache = self.propCache = self.propCache || [];21  let ret;22 23  if( !(ret = propCache[ argHash ]) ){24    ret = propCache[ argHash ] = self.parseImplWarn( name, value, propIsBypass, propIsFlat );25  }26 27  // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden28  // - mappings can't be shared b/c mappings are per-element29  if( propIsBypass || propIsFlat === 'mapping' ){30    // need a copy since props are mutated later in their lifecycles31    ret = util.copy( ret );32 33    if( ret ){34      ret.value = util.copy( ret.value ); // because it could be an array, e.g. colour35    }36  }37 38  return ret;39};40 41styfn.parseImplWarn = function( name, value, propIsBypass, propIsFlat ){42  let prop = this.parseImpl( name, value, propIsBypass, propIsFlat );43 44  if( !prop && value != null ){45    util.warn(`The style property \`${name}: ${value}\` is invalid`);46  }47 48  if( prop && (prop.name === 'width' || prop.name === 'height') && value === 'label' ){49    util.warn('The style value of `label` is deprecated for `' + prop.name + '`');50  }51 52  return prop;53};54 55// parse a property; return null on invalid; return parsed property otherwise56// fields :57// - name : the name of the property58// - value : the parsed, native-typed value of the property59// - strValue : a string value that represents the property value in valid css60// - bypass : true iff the property is a bypass property61styfn.parseImpl = function( name, value, propIsBypass, propIsFlat ){62  let self = this;63 64  name = util.camel2dash( name ); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')65 66  let property = self.properties[ name ];67  let passedValue = value;68  let types = self.types;69 70  if( !property ){ return null; } // return null on property of unknown name71  if( value === undefined ){ return null; } // can't assign undefined72 73  // the property may be an alias74  if( property.alias ){75    property = property.pointsTo;76    name = property.name;77  }78 79  let valueIsString = is.string( value );80  if( valueIsString ){ // trim the value to make parsing easier81    value = value.trim();82  }83 84  let type = property.type;85  if( !type ){ return null; } // no type, no luck86 87  // check if bypass is null or empty string (i.e. indication to delete bypass property)88  if( propIsBypass && (value === '' || value === null) ){89    return {90      name: name,91      value: value,92      bypass: true,93      deleteBypass: true94    };95  }96 97  // check if value is a function used as a mapper98  if( is.fn( value ) ){99    return {100      name: name,101      value: value,102      strValue: 'fn',103      mapped: types.fn,104      bypass: propIsBypass105    };106  }107 108  // check if value is mapped109  let data, mapData;110  if( !valueIsString || propIsFlat || value.length < 7 || value[1] !== 'a' ){111    // then don't bother to do the expensive regex checks112 113  } else if(value.length >= 7 && value[0] === 'd' && ( data = new RegExp( types.data.regex ).exec( value ) )){114    if( propIsBypass ){ return false; } // mappers not allowed in bypass115 116    let mapped = types.data;117 118    return {119      name: name,120      value: data,121      strValue: '' + value,122      mapped: mapped,123      field: data[1],124      bypass: propIsBypass125    };126 127  } else if(value.length >= 10 && value[0] === 'm' && ( mapData = new RegExp( types.mapData.regex ).exec( value ) )){128    if( propIsBypass ){ return false; } // mappers not allowed in bypass129    if( type.multiple ){ return false; } // impossible to map to num130 131    let mapped = types.mapData;132 133    // we can map only if the type is a colour or a number134    if( !(type.color || type.number) ){ return false; }135 136    let valueMin = this.parse( name, mapData[4] ); // parse to validate137    if( !valueMin || valueMin.mapped ){ return false; } // can't be invalid or mapped138 139    let valueMax = this.parse( name, mapData[5] ); // parse to validate140    if( !valueMax || valueMax.mapped ){ return false; } // can't be invalid or mapped141 142    // check if valueMin and valueMax are the same143    if( valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue ){144      util.warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');145 146      return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range147 148    } else if( type.color ){149      let c1 = valueMin.value;150      let c2 = valueMax.value;151 152      let same = c1[0] === c2[0] // red153        && c1[1] === c2[1] // green154        && c1[2] === c2[2] // blue155        && ( // optional alpha156          c1[3] === c2[3] // same alpha outright157          || (158            (c1[3] == null || c1[3] === 1) // full opacity for colour 1?159            &&160            (c2[3] == null || c2[3] === 1) // full opacity for colour 2?161          )162        )163      ;164 165      if( same ){ return false; } // can't make a mapper without a range166    }167 168    return {169      name: name,170      value: mapData,171      strValue: '' + value,172      mapped: mapped,173      field: mapData[1],174      fieldMin: parseFloat( mapData[2] ), // min & max are numeric175      fieldMax: parseFloat( mapData[3] ),176      valueMin: valueMin.value,177      valueMax: valueMax.value,178      bypass: propIsBypass179    };180  }181 182  if( type.multiple && propIsFlat !== 'multiple' ){183    let vals;184 185    if( valueIsString ){186      vals = value.split( /\s+/ );187    } else if( is.array( value ) ){188      vals = value;189    } else {190      vals = [ value ];191    }192 193    if( type.evenMultiple && vals.length % 2 !== 0 ){ return null; }194 195    let valArr = [];196    let unitsArr = [];197    let pfValArr = [];198    let strVal = '';199    let hasEnum = false;200 201    for( let i = 0; i < vals.length; i++ ){202      let p = self.parse( name, vals[i], propIsBypass, 'multiple' );203 204      hasEnum = hasEnum || is.string( p.value );205 206      valArr.push( p.value );207      pfValArr.push( p.pfValue != null ? p.pfValue : p.value );208      unitsArr.push( p.units );209      strVal += (i > 0 ? ' ' : '') + p.strValue;210    }211 212    if( type.validate && !type.validate( valArr, unitsArr ) ){213      return null;214    }215 216    if( type.singleEnum && hasEnum ){217      if( valArr.length === 1 && is.string( valArr[0] ) ){218        return {219          name: name,220          value: valArr[0],221          strValue: valArr[0],222          bypass: propIsBypass223        };224      } else {225        return null;226      }227    }228 229    return {230      name: name,231      value: valArr,232      pfValue: pfValArr,233      strValue: strVal,234      bypass: propIsBypass,235      units: unitsArr236    };237  }238 239  // several types also allow enums240  let checkEnums = function(){241    for( let i = 0; i < type.enums.length; i++ ){242      let en = type.enums[ i ];243 244      if( en === value ){245        return {246          name: name,247          value: value,248          strValue: '' + value,249          bypass: propIsBypass250        };251      }252    }253 254    return null;255  };256 257  // check the type and return the appropriate object258  if( type.number ){259    let units;260    let implicitUnits = 'px'; // not set => px261 262    if( type.units ){ // use specified units if set263      units = type.units;264    }265 266    if( type.implicitUnits ){267      implicitUnits = type.implicitUnits;268    }269 270    if( !type.unitless ){271      if( valueIsString ){272        let unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');273        if( units ){ unitsRegex = units; } // only allow explicit units if so set274        let match = value.match( '^(' + util.regex.number + ')(' + unitsRegex + ')?' + '$' );275 276        if( match ){277          value = match[1];278          units = match[2] || implicitUnits;279        }280 281      } else if( !units || type.implicitUnits ){282        units = implicitUnits; // implicitly px if unspecified283      }284    }285 286    value = parseFloat( value );287 288    // if not a number and enums not allowed, then the value is invalid289    if( isNaN( value ) && type.enums === undefined ){290      return null;291    }292 293    // check if this number type also accepts special keywords in place of numbers294    // (i.e. `left`, `auto`, etc)295    if( isNaN( value ) && type.enums !== undefined ){296      value = passedValue;297 298      return checkEnums();299    }300 301    // check if value must be an integer302    if( type.integer && !is.integer( value ) ){303      return null;304    }305 306    // check value is within range307    if( ( type.min !== undefined && ( value < type.min || (type.strictMin && value === type.min) ) )308    ||  ( type.max !== undefined && ( value > type.max || (type.strictMax && value === type.max) ) )309    ){310      return null;311    }312 313    let ret = {314      name: name,315      value: value,316      strValue: '' + value + (units ? units : ''),317      units: units,318      bypass: propIsBypass319    };320 321    // normalise value in pixels322    if( type.unitless || (units !== 'px' && units !== 'em') ){323      ret.pfValue = value;324    } else {325      ret.pfValue = ( units === 'px' || !units ? (value) : (this.getEmSizeInPixels() * value) );326    }327 328    // normalise value in ms329    if( units === 'ms' || units === 's' ){330      ret.pfValue = units === 'ms' ? value : 1000 * value;331    }332 333    // normalise value in rad334    if( units === 'deg' || units === 'rad' ){335      ret.pfValue = units === 'rad' ? value : math.deg2rad( value );336    }337 338    // normalize value in %339    if( units === '%' ){340      ret.pfValue = value / 100;341    }342 343    return ret;344 345  } else if( type.propList ){346 347    let props = [];348    let propsStr = '' + value;349 350    if( propsStr === 'none' ){351      // leave empty352 353    } else { // go over each prop354 355      let propsSplit = propsStr.split( /\s*,\s*|\s+/ );356      for( let i = 0; i < propsSplit.length; i++ ){357        let propName = propsSplit[ i ].trim();358 359        if( self.properties[ propName ] ){360          props.push( propName );361        } else {362          util.warn('`' + propName + '` is not a valid property name');363        }364      }365 366      if( props.length === 0 ){ return null; }367    }368 369    return {370      name: name,371      value: props,372      strValue: props.length === 0 ? 'none' : props.join(' '),373      bypass: propIsBypass374    };375 376  } else if( type.color ){377    let tuple = util.color2tuple( value );378 379    if( !tuple ){ return null; }380 381    return {382      name: name,383      value: tuple,384      pfValue: tuple,385      strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')', // n.b. no spaces b/c of multiple support386      bypass: propIsBypass387    };388 389  } else if( type.regex || type.regexes ){390 391    // first check enums392    if( type.enums ){393      let enumProp = checkEnums();394 395      if( enumProp ){ return enumProp; }396    }397 398    let regexes = type.regexes ? type.regexes : [ type.regex ];399 400    for( let i = 0; i < regexes.length; i++ ){401      let regex = new RegExp( regexes[ i ] ); // make a regex from the type string402      let m = regex.exec( value );403 404      if( m ){ // regex matches405        return {406          name: name,407          value: type.singleRegexMatchValue ? m[1] : m,408          strValue: '' + value,409          bypass: propIsBypass410        };411 412      }413    }414 415    return null; // didn't match any416 417  } else if( type.string ){418    // just return419    return {420      name: name,421      value: '' + value,422      strValue: '' + value,423      bypass: propIsBypass424    };425 426  } else if( type.enums ){ // check enums last because it's a combo type in others427    return checkEnums();428 429  } else {430    return null; // not a type we can handle431  }432 433};434 435export default styfn;436 
basant307/AI_Governance_Project · CoolFace