• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/*
17 * Customize the compiled style code into a styleSheet object, styleSheet object is divided into two parts, idSelectors
18 * and classSelectors. There are some detailed rules to explain:
19 * 1. Remove the "." And "#" symbols in front of the class selector and id selector;
20 * 2. Convert all numeric strings to numbers, such as:"32px" convert to number 32; "11" convert to number 11;
21 * 3. Convert all hex color to decimal number;
22 * 4. Convert all boolean strings to boolean type;
23 */
24const {
25  SPECIAL_STYLE,
26  REGEXP_NUMBER_PX,
27  REGEXP_COLOR,
28  REGEXP_UNIT,
29  REGXP_QUOTES,
30} = require('./lite-enum');
31
32/**
33 * Split style into id Selectors and classSelectors.
34 * @param {Object} value Preliminary compilation results of css files.
35 * @return {String} String result stylesheet.
36 */
37function transformStyle(value) {
38  const style = Function(`return ${value}`)();
39  const idSelectors = {};
40  const classSelectors = {};
41  const styleSheet = {};
42  let res = '';
43  const KEYFRAMES = '@KEYFRAMES';
44  const MEDIA_QUERY = '@MEDIA';
45  const keys = Object.keys(style);
46  for (const key of keys) {
47    if (key.charAt(0) === '.') {
48      classSelectors[key.slice(1)] = styleFormat(style[key]);
49    } else if (key.charAt(0) === '#') {
50      idSelectors[key.slice(1)] = styleFormat(style[key]);
51    } else if (key === KEYFRAMES) {
52      styleSheet['@keyframes'] = keyFrameFormat(style[key]);
53    } else if (key === MEDIA_QUERY) {
54      styleSheet['@media'] = mediaQueryFormat(style[key]);
55    } else {
56
57    }
58  }
59  if (style != null && keys.length !== 0) {
60    if (Object.keys(idSelectors).length !== 0) {
61      styleSheet['idSelectors'] = idSelectors;
62    }
63    if (Object.keys(classSelectors).length !== 0) {
64      styleSheet['classSelectors'] = classSelectors;
65    }
66  }
67  res = JSON.stringify(styleSheet);
68  return res;
69}
70
71/**
72 * keyFrame style special compilation.
73 * @param {Object} obj Preliminary compilation results of keyFrame style.
74 * @return {Object} keyFrame style object.
75 */
76function keyFrameFormat(obj) {
77  for (const key of Object.keys(obj)) {
78    const value = obj[key];
79    for (const styleValue of value) {
80      for (const styleKey of Object.keys(styleValue)) {
81        const innerValue = styleValue[styleKey];
82        if (REGEXP_COLOR.test(innerValue)) {
83          styleValue[styleKey] = parseInt(innerValue.slice(1), 16);
84        }
85        try {
86          styleValue[styleKey] = JSON.parse(styleValue[styleKey]);
87        } catch (e) {
88          // Values cannot be converted to objects are not processed
89        }
90      }
91    }
92  }
93  return obj;
94}
95
96/**
97 * media query special compilation.
98 * @param {Array} mediaQueries the array of media query
99 * @return {Array} media query style object
100 */
101function mediaQueryFormat(mediaQueries) {
102  const target = [];
103  for (const mediaQuery of mediaQueries) {
104    const { condition, ...selectors } = mediaQuery;
105    let style = transformStyle(JSON.stringify(selectors));
106    if (style) {
107      style = JSON.parse(style);
108      target.push({ condition, ...style });
109    }
110  }
111  return target;
112}
113
114const rules = [
115  {
116    match: function(key, value) {
117      return (
118        key === SPECIAL_STYLE.ANIMATION_DELAY ||
119        key === SPECIAL_STYLE.ANIMATION_DURATION
120      );
121    },
122    action: function(obj, key, value) {
123      obj[key] = value;
124    },
125  },
126  {
127    match: function(key, value) {
128      return key === SPECIAL_STYLE.ANIMATION_ITERATION_COUNT;
129    },
130    action: function(obj, key, value) {
131      if (value === -1) {
132        value = 'infinite';
133      }
134      obj[key] = value.toString();
135    },
136  },
137  {
138    match: function(key, value) {
139      return [
140        SPECIAL_STYLE.BACKGROUND_IMAGE,
141        SPECIAL_STYLE.BACKGROUND_IMAGE_ACTIVE,
142        SPECIAL_STYLE.BACKGROUND_IMAGE_CHECKED,
143      ].includes(key);
144    },
145    action: function(obj, key, value) {
146      obj[key] = value.replace(REGXP_QUOTES, '');
147    },
148  },
149  {
150    match: function(key, value) {
151      return !isNaN(Number(value));
152    },
153    action: function(obj, key, value) {
154      obj[key] = Number(value);
155    },
156  },
157  {
158    match: function(key, value) {
159      return REGEXP_NUMBER_PX.test(value);
160    },
161    action: function(obj, key, value) {
162      obj[key] = parseInt(value.replace(REGEXP_UNIT, ''), 10);
163    },
164  },
165  {
166    match: function(key, value) {
167      return REGEXP_COLOR.test(value);
168    },
169    action: function(obj, key, value) {
170      obj[key] = parseInt(value.slice(1), 16);
171    },
172  },
173  {
174    match: function(key, value) {
175      return value === 'true';
176    },
177    action: function(obj, key, value) {
178      obj[key] = true;
179    },
180  },
181  {
182    match: function(key, value) {
183      return value === 'false';
184    },
185    action: function(obj, key, value) {
186      obj[key] = false;
187    },
188  },
189];
190
191/**
192 * Loop and format style, There are two rules defined here, types of number+px and color convert to number 16777215.
193 * @param {Object} obj Preliminary compilation results of style object.
194 * @return {Object} style object.
195 */
196function styleFormat(obj) {
197  let value = '';
198  for (const key of Object.keys(obj)) {
199    value = obj[key];
200    for (let i = 0; i < rules.length; i++) {
201      if (rules[i].match(key, value)) {
202        rules[i].action(obj, key, value);
203        break;
204      }
205    }
206  }
207  return obj;
208}
209
210exports.transformStyle = transformStyle;
211