1 | import * as util from '../util';
|
2 | import * as is from '../is';
|
3 | import * as math from '../math';
|
4 |
|
5 | let styfn = {};
|
6 |
|
7 |
|
8 | styfn.parse = function( name, value, propIsBypass, propIsFlat ){
|
9 | let self = this;
|
10 |
|
11 |
|
12 | 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 |
|
28 |
|
29 | if( propIsBypass || propIsFlat === 'mapping' ){
|
30 |
|
31 | ret = util.copy( ret );
|
32 |
|
33 | if( ret ){
|
34 | ret.value = util.copy( ret.value );
|
35 | }
|
36 | }
|
37 |
|
38 | return ret;
|
39 | };
|
40 |
|
41 | styfn.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 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | styfn.parseImpl = function( name, value, propIsBypass, propIsFlat ){
|
62 | let self = this;
|
63 |
|
64 | name = util.camel2dash( name );
|
65 |
|
66 | let property = self.properties[ name ];
|
67 | let passedValue = value;
|
68 | let types = self.types;
|
69 |
|
70 | if( !property ){ return null; }
|
71 | if( value === undefined ){ return null; }
|
72 |
|
73 |
|
74 | if( property.alias ){
|
75 | property = property.pointsTo;
|
76 | name = property.name;
|
77 | }
|
78 |
|
79 | let valueIsString = is.string( value );
|
80 | if( valueIsString ){
|
81 | value = value.trim();
|
82 | }
|
83 |
|
84 | let type = property.type;
|
85 | if( !type ){ return null; }
|
86 |
|
87 |
|
88 | if( propIsBypass && (value === '' || value === null) ){
|
89 | return {
|
90 | name: name,
|
91 | value: value,
|
92 | bypass: true,
|
93 | deleteBypass: true
|
94 | };
|
95 | }
|
96 |
|
97 |
|
98 | if( is.fn( value ) ){
|
99 | return {
|
100 | name: name,
|
101 | value: value,
|
102 | strValue: 'fn',
|
103 | mapped: types.fn,
|
104 | bypass: propIsBypass
|
105 | };
|
106 | }
|
107 |
|
108 |
|
109 | let data, mapData;
|
110 | if( !valueIsString || propIsFlat || value.length < 7 || value[1] !== 'a' ){
|
111 |
|
112 |
|
113 | } else if(value.length >= 7 && value[0] === 'd' && ( data = new RegExp( types.data.regex ).exec( value ) )){
|
114 | if( propIsBypass ){ return false; }
|
115 |
|
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: propIsBypass
|
125 | };
|
126 |
|
127 | } else if(value.length >= 10 && value[0] === 'm' && ( mapData = new RegExp( types.mapData.regex ).exec( value ) )){
|
128 | if( propIsBypass ){ return false; }
|
129 | if( type.multiple ){ return false; }
|
130 |
|
131 | let mapped = types.mapData;
|
132 |
|
133 |
|
134 | if( !(type.color || type.number) ){ return false; }
|
135 |
|
136 | let valueMin = this.parse( name, mapData[4] );
|
137 | if( !valueMin || valueMin.mapped ){ return false; }
|
138 |
|
139 | let valueMax = this.parse( name, mapData[5] );
|
140 | if( !valueMax || valueMax.mapped ){ return false; }
|
141 |
|
142 |
|
143 | 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);
|
147 |
|
148 | } else if( type.color ){
|
149 | let c1 = valueMin.value;
|
150 | let c2 = valueMax.value;
|
151 |
|
152 | let same = c1[0] === c2[0]
|
153 | && c1[1] === c2[1]
|
154 | && c1[2] === c2[2]
|
155 | && (
|
156 | c1[3] === c2[3]
|
157 | || (
|
158 | (c1[3] == null || c1[3] === 1)
|
159 | &&
|
160 | (c2[3] == null || c2[3] === 1)
|
161 | )
|
162 | )
|
163 | ;
|
164 |
|
165 | if( same ){ return false; }
|
166 | }
|
167 |
|
168 | return {
|
169 | name: name,
|
170 | value: mapData,
|
171 | strValue: '' + value,
|
172 | mapped: mapped,
|
173 | field: mapData[1],
|
174 | fieldMin: parseFloat( mapData[2] ),
|
175 | fieldMax: parseFloat( mapData[3] ),
|
176 | valueMin: valueMin.value,
|
177 | valueMax: valueMax.value,
|
178 | bypass: propIsBypass
|
179 | };
|
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: propIsBypass
|
223 | };
|
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: unitsArr
|
236 | };
|
237 | }
|
238 |
|
239 |
|
240 | 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: propIsBypass
|
250 | };
|
251 | }
|
252 | }
|
253 |
|
254 | return null;
|
255 | };
|
256 |
|
257 |
|
258 | if( type.number ){
|
259 | let units;
|
260 | let implicitUnits = 'px';
|
261 |
|
262 | if( type.units ){
|
263 | 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; }
|
274 | 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;
|
283 | }
|
284 | }
|
285 |
|
286 | value = parseFloat( value );
|
287 |
|
288 |
|
289 | if( isNaN( value ) && type.enums === undefined ){
|
290 | return null;
|
291 | }
|
292 |
|
293 |
|
294 |
|
295 | if( isNaN( value ) && type.enums !== undefined ){
|
296 | value = passedValue;
|
297 |
|
298 | return checkEnums();
|
299 | }
|
300 |
|
301 |
|
302 | if( type.integer && !is.integer( value ) ){
|
303 | return null;
|
304 | }
|
305 |
|
306 |
|
307 | 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: propIsBypass
|
319 | };
|
320 |
|
321 |
|
322 | 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 |
|
329 | if( units === 'ms' || units === 's' ){
|
330 | ret.pfValue = units === 'ms' ? value : 1000 * value;
|
331 | }
|
332 |
|
333 |
|
334 | if( units === 'deg' || units === 'rad' ){
|
335 | ret.pfValue = units === 'rad' ? value : math.deg2rad( value );
|
336 | }
|
337 |
|
338 |
|
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 |
|
352 |
|
353 | } else {
|
354 |
|
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: propIsBypass
|
374 | };
|
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] + ')',
|
386 | bypass: propIsBypass
|
387 | };
|
388 |
|
389 | } else if( type.regex || type.regexes ){
|
390 |
|
391 |
|
392 | 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 ] );
|
402 | let m = regex.exec( value );
|
403 |
|
404 | if( m ){
|
405 | return {
|
406 | name: name,
|
407 | value: type.singleRegexMatchValue ? m[1] : m,
|
408 | strValue: '' + value,
|
409 | bypass: propIsBypass
|
410 | };
|
411 |
|
412 | }
|
413 | }
|
414 |
|
415 | return null;
|
416 |
|
417 | } else if( type.string ){
|
418 |
|
419 | return {
|
420 | name: name,
|
421 | value: '' + value,
|
422 | strValue: '' + value,
|
423 | bypass: propIsBypass
|
424 | };
|
425 |
|
426 | } else if( type.enums ){
|
427 | return checkEnums();
|
428 |
|
429 | } else {
|
430 | return null;
|
431 | }
|
432 |
|
433 | };
|
434 |
|
435 | export default styfn;
|