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