1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19/* 20 * 2021.01.08 - Rewrite some functions and remove some redundant judgments to fit framework. 21 * Copyright (c) 2021 Huawei Device Co., Ltd. 22 */ 23 24/** 25 * @fileOverview 26 * Directive Parser 27 */ 28 29import { 30 typof, 31 camelize, 32 Log 33} from '../../utils/index'; 34import Watcher from '../reactivity/watcher'; 35import { 36 setDescendantStyle 37} from './selector'; 38import { 39 getDefaultPropValue 40} from '../util/props'; 41import { 42 matchMediaQueryCondition 43} from '../extend/mediaquery/mediaQuery'; 44import { 45 TemplateInterface, 46 FragBlockInterface, 47 AttrInterface 48} from './compiler'; 49import Vm from './index'; 50import Element from '../../vdom/Element'; 51 52const SETTERS = { 53 attr: 'setAttr', 54 style: 'setStyle', 55 data: 'setData', 56 event: 'addEvent', 57 idStyle: 'setIdStyle', 58 tagStyle: 'setTagStyle' 59}; 60 61/** 62 * Bind id, attr, classnames, style, events to an element. 63 * @param {Vm} vm - Vm object. 64 * @param {Element} el - Element to be bind. 65 * @param {TemplateInterface} template - Structure of the component. 66 * @param {Element | FragBlockInterface} parentElement - Parent element of current element. 67 */ 68export function bindElement(vm: Vm, el: Element, template: TemplateInterface, parentElement: Element | FragBlockInterface): void { 69 // Set descendant style. 70 setDescendantStyle( 71 vm.selector, 72 { 73 id: template.id, 74 class: template.classList, 75 tag: template.type 76 }, 77 parentElement, 78 vm, 79 function(style: {[key: string]: any}) { 80 if (!style) { 81 return; 82 } 83 const css = vm.css || {}; 84 setAnimation(style, css); 85 setFontFace(style, css); 86 setStyle(vm, el, style); 87 } 88 ); 89 90 // inherit 'show' attribute of custom component 91 if (el.isCustomComponent) { 92 const value = vm['show']; 93 if (template.attr && value !== undefined) { 94 if (typeof value === 'function') { 95 // vm['show'] is assigned to this.show in initPropsToData() 96 template.attr['show'] = function() { 97 return this.show; 98 }; 99 } else { 100 template.attr['show'] = value; 101 } 102 } 103 } 104 105 setId(vm, el, template.id, vm); 106 setAttr(vm, el, template.attr); 107 setStyle(vm, el, template.style); 108 setIdStyle(vm, el, template.id); 109 setClass(vm, el, template.classList); 110 setTagStyle(vm, el, template.type); 111 applyStyle(vm, el); 112 113 bindEvents(vm, el, template.events); 114 bindEvents(vm, el, template.onBubbleEvents, ''); 115 bindEvents(vm, el, template.onCaptureEvents, 'capture'); 116 bindEvents(vm, el, template.catchBubbleEvents, 'catchbubble'); 117 bindEvents(vm, el, template.catchCaptureEvents, 'catchcapture'); 118 119 if (!vm.isHide && !vm.init) { 120 el.addEvent('hide'); 121 vm.isHide = true; 122 } 123} 124 125/** 126 * <p>Bind all props to sub vm and bind all style, events to the root element</p> 127 * <p>of the sub vm if it doesn't have a replaced multi-node fragment.</p> 128 * @param {Vm} vm - Vm object. 129 * @param {Vm} subVm - Sub vm. 130 * @param {TemplateInterface} template - Structure of the component. 131 * @param {Object} repeatItem - Item object. 132 */ 133export function bindSubVm(vm: Vm, rawSubVm: Vm, rawTemplate: TemplateInterface, repeatItem: object): void { 134 const subVm: any = rawSubVm || {}; 135 const template: any = rawTemplate || {}; 136 const options: any = subVm.vmOptions || {}; 137 138 let props = options.props; 139 if (isArray(props) || !props) { 140 if (isArray(props)) { 141 props = props.reduce((result, value) => { 142 result[value] = true; 143 return result; 144 }, {}); 145 } 146 mergeProps(repeatItem, props, vm, subVm); 147 mergeProps(template.attr, props, vm, subVm); 148 } else { 149 const attrData = template.attr || {}; 150 const repeatData = repeatItem || {}; 151 Object.keys(props).forEach(key => { 152 const prop = props[key]; 153 let value = attrData[key] || repeatData[key] || undefined; 154 if (value === undefined) { 155 value = getDefaultPropValue(vm, prop); 156 } 157 mergePropsObject(key, value, vm, subVm); 158 }); 159 } 160} 161 162/** 163 * Merge class and styles from vm to sub vm. 164 * @param {Vm} vm - Vm object. 165 * @param {Vm} subVm - Sub vm. 166 * @param {TemplateInterface} template - Structure of the component. 167 * @param {Element | FragBlockInterface} target - The target of element. 168 */ 169export function bindSubVmAfterInitialized(vm: Vm, subVm: Vm, template: TemplateInterface, target: Element | FragBlockInterface): void { 170 mergeClassStyle(template.classList, vm, subVm); 171 mergeStyle(template.style, vm, subVm); 172 if (target.children) { 173 target.children[target.children.length - 1]._vm = subVm; 174 } else { 175 target.vm = subVm; 176 } 177 bindSubEvent(vm, subVm, template); 178} 179 180/** 181 * Bind custom event from vm to sub vm for calling parent method. 182 * @param {Vm} vm - Vm object. 183 * @param {Vm} subVm - Sub vm. 184 * @param {TemplateInterface} template - Structure of the component. 185 */ 186function bindSubEvent(vm: Vm, subVm: Vm, template: TemplateInterface): void { 187 if (template.events) { 188 for (const type in template.events) { 189 subVm.$on(camelize(type), function() { 190 const args = []; 191 for (const i in arguments) { 192 args[i] = arguments[i]; 193 } 194 if (vm[template.events[type]] 195 && typeof vm[template.events[type]] === 'function') { 196 vm[template.events[type]].apply(vm, args); 197 } 198 }); 199 } 200 } 201} 202 203/** 204 * Merge props from vm to sub vm. 205 * @param {string} key - Get vm object by key. 206 * @param {*} value - Default Value. 207 * @param {Vm} vm - Vm object. 208 * @param {Vm} subVm - Sub vm. 209 * @return {*} Sub vm object. 210 */ 211function mergePropsObject(key: string, value: any, vm: Vm, subVm: Vm): any { 212 subVm.props.push(key); 213 if (typeof value === 'function') { 214 const returnValue = watch(vm, value, function(v) { 215 subVm[key] = v; 216 }); 217 // 'show' attribute will be inherited by elements in custom component 218 if (key === 'show') { 219 subVm[key] = value; 220 } else { 221 subVm[key] = returnValue; 222 } 223 } else { 224 const realValue = 225 value && value.__hasDefault ? value.__isDefaultValue : value; 226 subVm[key] = realValue; 227 } 228 return subVm[key]; 229} 230 231/** 232 * Bind props from vm to sub vm and watch their updates. 233 * @param {Object} target - Target object. 234 * @param {*} props - Vm props. 235 * @param {Vm} vm - Vm object. 236 * @param {Vm} subVm - Sub vm. 237 */ 238function mergeProps(target: object, props: any, vm: Vm, subVm: Vm): void { 239 if (!target) { 240 return; 241 } 242 for (const key in target) { 243 if (!props || props[key] || key === 'show') { 244 subVm.props.push(key); 245 const value = target[key]; 246 if (typeof value === 'function') { 247 const returnValue = watch(vm, value, function(v) { 248 subVm[key] = v; 249 }); 250 // 'show' attribute will be inherited by elements in custom component 251 if (key === 'show') { 252 subVm[key] = value; 253 } else { 254 subVm[key] = returnValue; 255 } 256 } else { 257 subVm[key] = value; 258 } 259 } 260 } 261} 262 263/** 264 * Bind style from vm to sub vm and watch their updates. 265 * @param {Object} target - Target object. 266 * @param {Vm} vm - Vm object. 267 * @param {Vm} subVm - Sub vm. 268 */ 269function mergeStyle(target: { [key: string]: any }, vm: Vm, subVm: Vm): void { 270 for (const key in target) { 271 const value = target[key]; 272 if (typeof value === 'function') { 273 const returnValue = watch(vm, value, function(v) { 274 if (subVm.rootEl) { 275 subVm.rootEl.setStyle(key, v); 276 } 277 }); 278 subVm.rootEl.setStyle(key, returnValue); 279 } else { 280 if (subVm.rootEl) { 281 subVm.rootEl.setStyle(key, value); 282 } 283 } 284 } 285} 286 287/** 288 * Bind class and style from vm to sub vm and watch their updates. 289 * @param {Object} target - Target object. 290 * @param {Vm} vm - Vm object. 291 * @param {Vm} subVm - Sub vm. 292 */ 293function mergeClassStyle(target: Function | string[], vm: Vm, subVm: Vm): void { 294 const css = vm.css || {}; 295 if (!subVm.rootEl) { 296 return; 297 } 298 299 /** 300 * Class name. 301 * @constant {string} 302 */ 303 const CLASS_NAME = '@originalRootEl'; 304 css['.' + CLASS_NAME] = subVm.rootEl.classStyle; 305 306 function addClassName(list, name) { 307 if (typof(list) === 'array') { 308 list.unshift(name); 309 } 310 } 311 312 if (typeof target === 'function') { 313 const value = watch(vm, target, v => { 314 addClassName(v, CLASS_NAME); 315 setClassStyle(subVm.rootEl, css, v); 316 }); 317 addClassName(value, CLASS_NAME); 318 setClassStyle(subVm.rootEl, css, value); 319 } else if (target !== undefined) { 320 addClassName(target, CLASS_NAME); 321 setClassStyle(subVm.rootEl, css, target); 322 } 323} 324 325/** 326 * Bind id to an element. Note: Each id is unique in a whole vm. 327 * @param {Vm} vm - Vm object. 328 * @param {Element} el - Element object. 329 * @param {Function | string} id - Unique vm id. 330 * @param {Vm} target - Target vm. 331 */ 332export function setId(vm: Vm, el: Element, id: Function | string, target: Vm): void { 333 const map = Object.create(null); 334 Object.defineProperties(map, { 335 vm: { 336 value: target, 337 writable: false, 338 configurable: false 339 }, 340 el: { 341 get: () => el || target.rootEl, 342 configurable: false 343 } 344 }); 345 if (typeof id === 'function') { 346 const handler = id; 347 const newId = handler.call(vm); 348 if (newId || newId === 0) { 349 setElementId(el, newId); 350 vm.ids[newId] = map; 351 } 352 watch(vm, handler, (newId) => { 353 if (newId) { 354 setElementId(el, newId); 355 vm.ids[newId] = map; 356 } 357 }); 358 } else if (id && typeof id === 'string') { 359 setElementId(el, id); 360 vm.ids[id] = map; 361 } 362} 363 364/** 365 * Set id to Element. 366 * @param {Element} el - Element object. 367 * @param {string} id - Element id. 368 */ 369function setElementId(el: Element, id: string): void { 370 if (el) { 371 el.id = id; 372 } 373} 374 375/** 376 * Bind attr to an element. 377 * @param {Vm} vm - Vm object. 378 * @param {Element} el - Element. 379 * @param {AttrInterface} attr - Attr to bind. 380 */ 381function setAttr(vm: Vm, el: Element, attr: Partial<AttrInterface>): void { 382 if (attr && attr.data) { 383 // address data independently 384 bindDir(vm, el, 'data', attr.data); 385 } 386 bindDir(vm, el, 'attr', attr); 387} 388 389/** 390 * Set font family and get font resource. 391 * @param {Object} css - Css style. 392 * @param {string | string[]} fontFamilyNames - Font family names. 393 * @return {*} Font resource. 394 */ 395function _getFontFamily(css: any, fontFamilyNames: string | string[]): any[] { 396 let results = []; 397 const familyMap = css['@FONT-FACE']; 398 if (typeof fontFamilyNames === 'string') { 399 fontFamilyNames.split(',').forEach(fontFamilyName => { 400 fontFamilyName = fontFamilyName.trim(); 401 let find = false; 402 if (familyMap && Array.isArray(familyMap)) { 403 let len = familyMap.length; 404 while (len) { 405 if ( 406 familyMap[len - 1].fontFamily && 407 familyMap[len - 1].fontFamily === fontFamilyName 408 ) { 409 results.push(familyMap[len - 1]); 410 find = true; 411 } 412 len--; 413 } 414 } else if (familyMap && typeof familyMap === 'object') { 415 const definedFontFamily = familyMap[fontFamilyName]; 416 if (definedFontFamily && definedFontFamily.src) { 417 if (Array.isArray(definedFontFamily.src)) { 418 definedFontFamily.src = definedFontFamily.src.map(item => `url("${item}")`).join(','); 419 } 420 results.push(definedFontFamily); 421 find = true; 422 } 423 } 424 if (!find) { 425 results.push({ 'fontFamily': fontFamilyName }); 426 } 427 }); 428 } else if (Array.isArray(fontFamilyNames)) { 429 results = fontFamilyNames; 430 } else if (fontFamilyNames) { 431 Log.warn(`GetFontFamily Array error, unexpected fontFamilyNames type [${typeof fontFamilyNames}].`); 432 } 433 return results; 434} 435 436/** 437 * Select class style. 438 * @param {Object} css - Css style. 439 * @param {Function | string[]} classList - List of class label. 440 * @param {number} index - Index of classList. 441 * @return {*} Select style. 442 */ 443function selectClassStyle(css: object, classList: Function | string[], index: number, vm: Vm): any { 444 const key = '.' + classList[index]; 445 return selectStyle(css, key, vm); 446} 447 448/** 449 * Select id style. 450 * @param {Object} css - Css style. 451 * @param {string} id - Id label. 452 * @param {Vm} vm - Vm object. 453 * @return {*} Select style. 454 */ 455function selectIdStyle(css: object, id: string, vm: Vm): any { 456 const key = '#' + id; 457 return selectStyle(css, key, vm); 458} 459 460/** 461 * Replace style. 462 * @param {*} oStyle - Current style. 463 * @param {*} rStyle - New style. 464 */ 465function replaceStyle(oStyle: any, rStyle: any): void { 466 if (!rStyle || rStyle.length <= 0) { 467 return; 468 } 469 Object.keys(rStyle).forEach(function(key) { 470 oStyle[key] = rStyle[key]; 471 }); 472} 473 474/** 475 * Select style for class label, id label. 476 * @param {Object} css - Css style. 477 * @param {string} key - Key index. 478 * @param {Vm} vm - Vm object. 479 * @return {*} 480 */ 481function selectStyle(css: object, key: string, vm: Vm): any { 482 const style = css[key]; 483 if (!vm) { 484 return style; 485 } 486 const mediaStatus = vm.mediaStatus; 487 if (!mediaStatus) { 488 return style; 489 } 490 const mqArr = css['@MEDIA']; 491 if (!mqArr) { 492 vm.init = true; 493 return style; 494 } 495 const classStyle = {}; 496 if (style) { 497 Object.keys(style).forEach(function(key) { 498 classStyle[key] = style[key]; 499 }); 500 } 501 for (let i$1 = 0; i$1 < mqArr.length; i$1++) { 502 if (matchMediaQueryCondition(mqArr[i$1].condition, mediaStatus, false)) { 503 replaceStyle(classStyle, mqArr[i$1][key]); 504 } 505 } 506 return classStyle; 507} 508 509/** 510 * Set class style after SelectClassStyle. 511 * @param {Element} el - Element object. 512 * @param {Object} css - Css style. 513 * @param {string[]} classList - List of class label. 514 */ 515function setClassStyle(el: Element, css: object, classList: string[], vm?: Vm): void { 516 const SPACE_REG: RegExp = /\s+/; 517 const newClassList: string[] = []; 518 if (Array.isArray(classList)) { 519 classList.forEach(v => { 520 if (typeof v === 'string' && SPACE_REG.test(v)) { 521 newClassList.push(...v.trim().split(SPACE_REG)); 522 } else { 523 newClassList.push(v); 524 } 525 }); 526 } 527 classList = newClassList; 528 const classStyle = {}; 529 const length = classList.length; 530 if (length === 1) { 531 const style = selectClassStyle(css, classList, 0, vm); 532 if (style) { 533 Object.keys(style).forEach((key) => { 534 classStyle[key] = style[key]; 535 }); 536 } 537 } else { 538 const rets = []; 539 const keys = Object.keys(css || {}); 540 for (let i = 0; i < length; i++) { 541 const clsKey = '.' + classList[i]; 542 const style = selectStyle(css, clsKey, vm); 543 if (style) { 544 const order = clsKey === '.@originalRootEl' ? -1000 : keys.indexOf(clsKey); 545 rets.push({style: style, order: order}); 546 } 547 } 548 if (rets.length === 1) { 549 const style = rets[0].style; 550 if (style) { 551 Object.keys(style).forEach((key) => { 552 classStyle[key] = style[key]; 553 }); 554 } 555 } else if (rets.length > 1) { 556 rets.sort(function(a, b) { 557 if (!a) { 558 return -1; 559 } else if (!b) { 560 return 1; 561 } else { 562 return a.order > b.order ? 1 : -1; 563 } 564 }); 565 const retStyle = {}; 566 rets.forEach(function(key) { 567 if (key && key.style) { 568 Object.assign(retStyle, key.style); 569 } 570 }); 571 Object.keys(retStyle).forEach((key) => { 572 classStyle[key] = retStyle[key]; 573 }); 574 } 575 } 576 577 const keyframes = css['@KEYFRAMES']; 578 if (keyframes) { 579 /* 580 * Assign @KEYFRAMES's value. 581 */ 582 const animationName = classStyle['animationName']; 583 if (animationName) { 584 classStyle['animationName'] = keyframes[animationName]; 585 classStyle['animationName'].push({'animationName': animationName}); 586 } 587 const transitionEnter = classStyle['transitionEnter']; 588 if (transitionEnter) { 589 classStyle['transitionEnter'] = keyframes[transitionEnter]; 590 } 591 const transitionExit = classStyle['transitionExit']; 592 if (transitionExit) { 593 classStyle['transitionExit'] = keyframes[transitionExit]; 594 } 595 const sharedTransitionName = classStyle['sharedTransitionName']; 596 if (sharedTransitionName) { 597 classStyle['sharedTransitionName'] = keyframes[sharedTransitionName]; 598 } 599 } 600 const fontFace = classStyle['fontFamily']; 601 if (fontFace) { 602 const fontCompileList = _getFontFamily(css, fontFace); 603 classStyle['fontFamily'] = fontCompileList; 604 } 605 el.setClassStyle(classStyle); 606 el.classList = classList; 607} 608 609/** 610 * Bind classnames to an element 611 * @param {Vm} vm - Vm object. 612 * @param {Element} el - Element object. 613 * @param {Function | string[]} classList - List of class label. 614 */ 615export function setClass(vm: Vm, el: Element, classList: Function | string[]): void { 616 if (typeof classList !== 'function' && !Array.isArray(classList)) { 617 return; 618 } 619 if (Array.isArray(classList) && !classList.length) { 620 el.setClassStyle({}); 621 return; 622 } 623 const style = vm.css || {}; 624 if (typeof classList === 'function') { 625 const value = watch(vm, classList, v => { 626 setClassStyle(el, style, v, vm); 627 }); 628 setClassStyle(el, style, value, vm); 629 } else { 630 setClassStyle(el, style, classList, vm); 631 } 632} 633 634/** 635 * Support css selector by id and component. 636 * @param {Vm} vm - Vm object. 637 * @param {Element} el - ELement component. 638 * @param {Function | string} id - Id label. 639 */ 640export function setIdStyle(vm: Vm, el: Element, id: Function | string): void { 641 if (id) { 642 const css = vm.css || {}; 643 if (typeof id === 'function') { 644 const value = watch(vm, id, v => { 645 doSetStyle(vm, el, selectIdStyle(css, v, vm), css, 'idStyle'); 646 }); 647 doSetStyle(vm, el, selectIdStyle(css, value, vm), css, 'idStyle'); 648 } else if (typeof id === 'string') { 649 doSetStyle(vm, el, selectIdStyle(css, id, vm), css, 'idStyle'); 650 } 651 } 652} 653 654/** 655 * Set style. 656 * @param {Vm} vm - Vm object. 657 * @param {Element} el - ELement component. 658 * @param {*} style - Style to be Set. 659 * @param {*} css - Css style. 660 * @param {string} name - Bind by name. 661 */ 662function doSetStyle(vm: Vm, el: Element, style: any, css: any, name: string): void { 663 if (!style) { 664 return; 665 } 666 const typeStyle = {}; 667 Object.assign(typeStyle, style); 668 setAnimation(typeStyle, css); 669 setFontFace(typeStyle, css); 670 bindDir(vm, el, name, typeStyle); 671} 672 673/** 674 * Set FontFace. 675 * @param {*} style - Style. 676 * @param {*} css - Css style. 677 */ 678function setFontFace(style: any, css: any): void { 679 const fontFace = style['fontFamily']; 680 if (fontFace) { 681 const fontCompileList = _getFontFamily(css, fontFace); 682 style['fontFamily'] = fontCompileList; 683 } 684} 685 686/** 687 * Set Animation 688 * @param {*} style - Style. 689 * @param {*} css - Css style. 690 */ 691function setAnimation(style: any, css: any): void { 692 const animationName = style['animationName']; 693 const keyframes = css['@KEYFRAMES']; 694 if (animationName && keyframes) { 695 style['animationName'] = keyframes[animationName]; 696 if (style['animationName']) { 697 style['animationName'].push({'animationName': animationName}); 698 } 699 } 700} 701 702/** 703 * Set tag style. 704 * @param {Vm} vm - Vm object. 705 * @param {Element} el - ELement component. 706 * @param {string} tag - Tag. 707 */ 708export function setTagStyle(vm: Vm, el: Element, tag: string): void { 709 const css = vm.css || {}; 710 if (tag && typeof tag === 'string') { 711 doSetStyle(vm, el, selectStyle(css, tag, vm), css, 'tagStyle'); 712 } 713} 714 715/** 716 * Bind style to an element. 717 * @param {Vm} vm - Vm object. 718 * @param {Element} el - ELement component. 719 * @param {*} style - Style. 720 */ 721function setStyle(vm: Vm, el: Element, style: any): void { 722 bindDir(vm, el, 'style', style); 723} 724 725/** 726 * Add an event type and handler to an element and generate a dom update. 727 * @param {Vm} vm - Vm object. 728 * @param {Element} el - ELement component. 729 * @param {string} type - Type added to event. 730 * @param {Function} handler - Handle added to event. 731 */ 732function setEvent(vm: Vm, el: Element, type: string, handler: Function): void { 733 el.addEvent(type, handler.bind(vm)); 734} 735 736/** 737 * Add all events of an element. 738 * @param {Vm} vm - Vm object. 739 * @param {Element} el - ELement component. 740 * @param {Object} events - Events of an element. 741 */ 742function bindEvents(vm: Vm, el: Element, events: object, eventType?: string): void { 743 if (!events) { 744 return; 745 } 746 const keys = Object.keys(events); 747 let i = keys.length; 748 while (i--) { 749 const key = keys[i]; 750 let handler = events[key]; 751 if (typeof handler === 'string') { 752 handler = vm[handler]; 753 if (!handler || typeof handler !== 'function') { 754 Log.warn(`The event handler '${events[key]}' is undefined or is not function.`); 755 continue; 756 } 757 } 758 const eventName: string = eventType ? eventType + key : key; 759 setEvent(vm, el, eventName, handler); 760 } 761} 762 763/** 764 * <p>Set a series of members as a kind of an element.</p> 765 * <p>for example: style, attr, ...</p> 766 * <p>if the value is a function then bind the data changes.</p> 767 * @param {Vm} vm - Vm object. 768 * @param {Element} el - ELement component. 769 * @param {string} name - Method name. 770 * @param {Object} data - Data that needed. 771 */ 772function bindDir(vm: Vm, el: Element, name: string, data: object): void { 773 if (!data) { 774 return; 775 } 776 const keys = Object.keys(data); 777 let i = keys.length; 778 if (!i) { 779 return; 780 } 781 const methodName = SETTERS[name]; 782 const method = el[methodName]; 783 const isSetStyle = methodName === 'setStyle'; 784 while (i--) { 785 const key = keys[i]; 786 const value = data[key]; 787 if (key === 'ref') { 788 vm.$refs[value] = el; 789 } 790 const isSetFont = isSetStyle && key === 'fontFamily'; 791 const setValue = function(value) { 792 if (isSetFont) { 793 value = filterFontFamily(vm, value); 794 } 795 method.call(el, key, value); 796 }; 797 if (typeof value === 'function') { 798 bindKey(vm, el, setValue, value); 799 } else { 800 setValue(value); 801 } 802 } 803} 804 805/** 806 * Bind data changes to a certain key to a name series in an element. 807 * @param {Vm} vm - Vm object. 808 * @param {Element} el - ELement component. 809 * @param {Function} setValue - Set value. 810 * @param {Function} calc - Watch the calc and return a value by calc.call(). 811 */ 812function bindKey(vm: Vm, el: Element, setValue: Function, calc: Function): void { 813 // Watch the calc, and returns a value by calc.call(). 814 const watcher = newWatch(vm, calc, (value) => { 815 function handler() { 816 setValue(value); 817 } 818 const differ = vm && vm.app && vm.app.differ; 819 if (differ) { 820 differ.append('element', el.ref, handler); 821 } else { 822 handler(); 823 } 824 }); 825 el.watchers.push(watcher); 826 setValue(watcher.value); 827} 828 829/** 830 * FontFamily Filter. 831 * @param {Vm} vm - Vm object. 832 * @param {string} fontFamilyName - FontFamily name. 833 * @return {*} FontFamily Filter. 834 */ 835export function filterFontFamily(vm: Vm, fontFamilyName: string): any[] { 836 const css = vm.css || {}; 837 return _getFontFamily(css, fontFamilyName); 838} 839 840/** 841 * Watch the calc. 842 * @param {Vm} vm - Vm object. 843 * @param {Function} calc - Watch the calc, and returns a value by calc.call(). 844 * @param {Function} callback - Callback callback Function. 845 * @return {Watcher} New watcher for rhe calc value. 846 */ 847export function newWatch(vm: Vm, calc: Function, callback: Function): Watcher { 848 const watcher = new Watcher(vm, calc, function(value, oldValue) { 849 if (typeof value !== 'object' && value === oldValue) { 850 return; 851 } 852 callback(value); 853 }, null); 854 return watcher; 855} 856 857/** 858 * Watch a calc function and callback if the calc value changes. 859 * @param {Vm} vm - Vm object. 860 * @param {Function} calc - Watch the calc, and returns a value by calc.call(). 861 * @param {Function} callback - Callback callback Function. 862 * @return {*} Watcher value. 863 */ 864export function watch(vm: Vm, calc: Function, callback: Function): any { 865 const watcher = new Watcher(vm, calc, function(value, oldValue) { 866 if (typeof value !== 'object' && value === oldValue) { 867 return; 868 } 869 callback(value); 870 }, null); 871 return watcher.value; 872} 873 874/** 875 * Apply style to an element. 876 * @param {Vm} vm - Vm object. 877 * @param {Element} el - Element object. 878 */ 879function applyStyle(vm: Vm, el: Element): void { 880 const css = vm.css || {}; 881 const allStyle = el.style; 882 setAnimation(allStyle, css); 883} 884 885/** 886 * Check if it is an Array. 887 * @param {*} params - Any value. 888 * @return {boolean} Return true if it is an array. Otherwise return false. 889 */ 890function isArray(params: any): params is Array<string> { 891 return Array.isArray(params); 892} 893