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 16const content = require('./content') 17const data = require('./data') 18const { DEVICE_LEVEL } = require('../lite/lite-enum') 19const card = process.env.DEVICE_LEVEL === DEVICE_LEVEL.CARD 20const REG_CARD_ARRAY = /(\['.+?'\])|(\[".+?"\])/g 21const connectContent = `+ decodeURI('${encodeURI('')}') +`; 22 23/** 24 * Check if there is data binding. 25 * @param {Object} initValue Hml text token information. 26 * @param {Object} functionFlag Hml text token information. 27 * @return {String} Compiled data binding 28 */ 29function transExp(initValue, functionFlag, isValue, out, nodeLoc) { 30 let value = initValue.toString().trim() 31 const hasExpFlag = isExp(value) 32 if (hasExpFlag) { 33 value = parseExp(value, functionFlag, isValue, out, nodeLoc) 34 } 35 return value 36} 37/** 38 * parse data binding. 39 * @param {Object} value Hml text token information. 40 * @param {Object} functionFlag Hml text token information. 41 * @return {String} Compiled data binding 42 */ 43function parseExp(value, functionFlag, isValue, out, nodeLoc) { 44 const textArray = data.parseText(value) 45 const explist = [] 46 for (let i = 0; i < textArray.length; i++) { 47 const exp = textArray[i] 48 let transValue 49 if (exp.tag) { 50 if (card) { 51 checkCard(exp.value, out, nodeLoc) 52 } 53 transValue = card ? `{{${transCardArray(exp.value)}}}` : content.parseExpression(exp.value) 54 if (textArray.length !== 1 && !card) { 55 transValue = `(${transValue})` 56 } 57 } else { 58 transValue = card ? exp.value : `decodeURI('${encodeURI(exp.value).replace(/\'/g, '%27')}')` 59 } 60 explist.push(transValue) 61 } 62 if (card && checkCardVersionLimit() && isValue) { 63 if (explist.length > 1) { 64 out.log.push({ 65 line: nodeLoc.line || 1, 66 column: nodeLoc.col || 1, 67 reason: 'ERROR: Variable concatenation is not supported currently.', 68 }) 69 } 70 } 71 let func = explist.join(card ? '' : connectContent); 72 if (functionFlag !== false && !card) { 73 func = eval('(function () {return ' + func + '})') 74 } 75 func = card && textArray.length > 1 ? '$f(' + func + ')' : func 76 77 return func 78} 79 80/** 81 * Check if there is data binding in the list. 82 * @param {String} initValue Hml text token information. 83 * @return {String} Compiled data binding in list 84 */ 85function transExpForList(initValue) { 86 let value = initValue.toString().trim() 87 const hasExpListFlag = containExp(value) 88 if (hasExpListFlag) { 89 value = parseExpList(value) 90 } 91 return value 92} 93/** 94 * parse data binding in list. 95 * @param {String} value Hml text token information. 96 * @return {String} Compiled data binding in list 97 */ 98function parseExpList(value) { 99 const exprMatch = value.match(/{{{([\s\S]+?)}}}|{{([\s\S]+?)}}/g) 100 const exprArray = value.replace(/{{{([\s\S]+?)}}}|{{([\s\S]+?)}}/g, '&e').split(/\s+/) 101 let n = 0 102 const result = [] 103 exprArray.forEach((item)=>{ 104 if (item.indexOf('&e') >= 0) { 105 while (item.indexOf('&e') >= 0) { 106 item = item.replace('&e', exprMatch[n++]) 107 } 108 const textArray = data.parseText(item) 109 const exprlist = [] 110 if (textArray) { 111 textArray.forEach(function(text) { 112 const transValue = text.tag ? 113 card ? `{{${text.value}}}` : content.parseExpression(text.value) : 114 card ? text.value : `'${text.value}'` 115 exprlist.push(transValue) 116 }) 117 result.push(exprlist.join('+')) 118 } 119 } else { 120 const value = card ? item : `'${item}'` 121 result.push(value) 122 } 123 }) 124 125 return card ? result : "(function () {return [" + result.join(", ") + "]})" 126} 127 128/** 129 * Regexp determine whether there is data binding. 130 * @param {String} value expression value. 131 * @return {Boolean} Test results 132 */ 133function isExp(value) { 134 const REGEXP_DATABIND = /{{{([\s\S]+?)}}}|{{([\s\S]+?)}}/ 135 return REGEXP_DATABIND.test(value) 136} 137 138/** 139 * Global regexp determine whether there is data binding. 140 * @param {String} value expression value. 141 * @return {Boolean} Test results 142 */ 143function containExp(value) { 144 const REGEXP_DATABIND_ALL = /{{{([\s\S]+?)}}}|{{([\s\S]+?)}}/g 145 return REGEXP_DATABIND_ALL.test(value) 146} 147 148/** 149 * Replace value on match. 150 * @param {String} value expression value. 151 * @return {String} The result after replacement 152 */ 153function removeAllExpFix(value) { 154 const REGEXP_PRE_DATABIND = /\{\{\{?|\}\}\}?/g 155 return containExp(value) ? value.replace(REGEXP_PRE_DATABIND, '') : value 156} 157 158/** 159 * Change array format in card 160 * @param {String} value Array in card 161 * @return {String} The card array format 162 */ 163function transCardArray(value) { 164 value = value.replace(REG_CARD_ARRAY, item => { 165 return `.${item.slice(2, -2)}.` 166 }) 167 if (value.charAt(value.length - 1) === '.') { 168 value = value.slice(0, -1) 169 } 170 return value 171} 172 173function checkCard(value, out, nodeLoc) { 174 if (!checkApi(value) && !checkIdxAndItem(value)) { 175 if (checkCardVersionLimit() && !checkVariable(value)){ 176 out.log.push({ 177 line: nodeLoc.line || 1, 178 column: nodeLoc.col || 1, 179 reason: `ERROR: Version 5: The expression '${value}' is not supported. Only single variables are supported.` 180 }) 181 } else if (!checkCardVersionLimit() && !checkExpression(value)) { 182 out.log.push({ 183 line: nodeLoc.line || 1, 184 column: nodeLoc.col || 1, 185 reason: `ERROR: Version 6 and above: The expression '${value}' is not supported. Only the binocular expression, ` + 186 `OR expression, AND expression and NOT expression are supported.` 187 }) 188 } 189 } 190} 191 192function checkCardVersionLimit() { 193 return parseInt(process.env.PLATFORM_VERSION.replace('Version', '')) < 6 194} 195 196function checkApi(value) { 197 return /^\$(tc|t|r)/m.test(value) 198} 199 200function checkExpression(value) { 201 return checkVariable(value) || checkOR(value) || checkAND(value) || checkNOT(value) || checkBinocular(value) 202} 203 204function checkIdxAndItem(value) { 205 return value === '$idx' || value === '$item' 206} 207 208function checkVariable(value) { 209 return /^[a-zA-Z\$_][a-zA-Z\d_\.\[\]'"`\s]*$/.test(value) 210} 211 212function checkOR(value) { 213 return /^[a-zA-Z\$_][a-zA-Z\d_\.\[\]'"`\s]*\|\|[a-zA-Z\$_\s][a-zA-Z\d_\.\[\]'"`\s]*$/m.test(value) 214} 215 216function checkAND(value) { 217 return /^[a-zA-Z\$_][a-zA-Z\d_\.\[\]'"`\s]*&&[a-zA-Z\$_\s][a-zA-Z\d_\.\[\]'"`\s]*$/m.test(value) 218} 219 220function checkNOT(value) { 221 return /^![a-zA-Z\$_][a-zA-Z\d_\.\[\]'"`\s]*$/m.test(value) 222} 223 224function checkBinocular(value) { 225 return /^[a-zA-Z\$_][a-zA-Z\d_\.\[\]'"`\s]*\?[a-zA-Z\$_\s][a-zA-Z\d_\.\[\]'"`\s]*:[a-zA-Z\$_\s][a-zA-Z\d_\.\[\]'"`\s]*$/m.test(value) 226} 227 228transExp.checkApi = checkApi 229transExp.checkExpression = checkExpression 230transExp.checkIdxAndItem = checkIdxAndItem 231transExp.checkVariable = checkVariable 232transExp.checkAND = checkAND 233transExp.checkOR = checkOR 234transExp.checkNOT = checkNOT 235transExp.isExp = isExp 236transExp.containExp = containExp 237transExp.removeAllExpFix = removeAllExpFix 238transExp.transExpForList = transExpForList 239transExp.transCardArray = transCardArray 240module.exports = transExp 241