import Node from './shared/Node'; import Element from './Element'; import getObject from '../../utils/getObject'; import getTailSnippet from '../../utils/getTailSnippet'; import flattenReference from '../../utils/flattenReference'; import Compiler from '../Compiler'; import Block from '../dom/Block'; import Expression from './shared/Expression'; import { dimensions } from '../../utils/patterns'; const readOnlyMediaAttributes = new Set([ 'duration', 'buffered', 'seekable', 'played' ]); // TODO a lot of this element-specific stuff should live in Element — // Binding should ideally be agnostic between Element and Component export default class Binding extends Node { name: string; value: Expression; isContextual: boolean; usesContext: boolean; obj: string; prop: string; constructor(compiler, parent, scope, info) { super(compiler, parent, scope, info); this.name = info.name; this.value = new Expression(compiler, this, scope, info.value); let obj; let prop; const { name } = getObject(this.value.node); this.isContextual = scope.names.has(name); if (this.value.node.type === 'MemberExpression') { prop = `[✂${this.value.node.property.start}-${this.value.node.property.end}✂]`; if (!this.value.node.computed) prop = `'${prop}'`; obj = `[✂${this.value.node.object.start}-${this.value.node.object.end}✂]`; this.usesContext = true; } else { obj = 'ctx'; prop = `'${name}'`; this.usesContext = scope.names.has(name); } this.obj = obj; this.prop = prop; } munge( block: Block ) { const node: Element = this.parent; const needsLock = node.name !== 'input' || !/radio|checkbox|range|color/.test(node.getStaticAttributeValue('type')); const isReadOnly = ( (node.isMediaNode() && readOnlyMediaAttributes.has(this.name)) || dimensions.test(this.name) ); let updateConditions: string[] = []; const { name } = getObject(this.value.node); const { snippet } = this.value; // special case: if you have e.g. `` // and `selected` is an object chosen with a if (binding.name === 'group') { const bindingGroup = getBindingGroup(compiler, binding.value.node); if (type === 'checkbox') { return `@getBindingGroupValue(#component._bindingGroups[${bindingGroup}])`; } return `${node.var}.__value`; } // if (type === 'range' || type === 'number') { return `@toNumber(${node.var}.${binding.name})`; } if ((binding.name === 'buffered' || binding.name === 'seekable' || binding.name === 'played')) { return `@timeRangesToArray(${node.var}.${binding.name})` } // everything else return `${node.var}.${binding.name}`; } function isComputed(node: Node) { while (node.type === 'MemberExpression') { if (node.computed) return true; node = node.object; } return false; }