import deindent from '../../utils/deindent'; import { stringify, escapeHTML } from '../../utils/stringify'; import flattenReference from '../../utils/flattenReference'; import isVoidElementName from '../../utils/isVoidElementName'; import validCalleeObjects from '../../utils/validCalleeObjects'; import reservedNames from '../../utils/reservedNames'; import fixAttributeCasing from '../../utils/fixAttributeCasing'; import quoteIfNecessary from '../../utils/quoteIfNecessary'; import mungeAttribute from './shared/mungeAttribute'; import Node from './shared/Node'; import Block from '../dom/Block'; import Attribute from './Attribute'; import Binding from './Binding'; import EventHandler from './EventHandler'; import Ref from './Ref'; import Transition from './Transition'; import Action from './Action'; import Text from './Text'; import * as namespaces from '../../utils/namespaces'; import mapChildren from './shared/mapChildren'; export default class Element extends Node { type: 'Element'; name: string; attributes: Attribute[]; actions: Action[]; bindings: Binding[]; handlers: EventHandler[]; intro: Transition; outro: Transition; children: Node[]; ref: string; namespace: string; constructor(compiler, parent, scope, info: any) { super(compiler, parent, scope, info); this.name = info.name; const parentElement = parent.findNearest(/^Element/); this.namespace = this.name === 'svg' ? namespaces.svg : parentElement ? parentElement.namespace : this.compiler.namespace; this.attributes = []; this.actions = []; this.bindings = []; this.handlers = []; this.intro = null; this.outro = null; info.attributes.forEach(node => { switch (node.type) { case 'Action': this.actions.push(new Action(compiler, this, scope, node)); break; case 'Attribute': // special case if (node.name === 'xmlns') this.namespace = node.value[0].data; this.attributes.push(new Attribute(compiler, this, scope, node)); break; case 'Binding': this.bindings.push(new Binding(compiler, this, scope, node)); break; case 'EventHandler': this.handlers.push(new EventHandler(compiler, this, scope, node)); break; case 'Transition': const transition = new Transition(compiler, this, scope, node); if (node.intro) this.intro = transition; if (node.outro) this.outro = transition; break; case 'Ref': // TODO catch this in validation if (this.ref) throw new Error(`Duplicate refs`); compiler.usesRefs = true this.ref = node.name; break; default: throw new Error(`Not implemented: ${node.type}`); } }); // TODO break out attributes and directives here this.children = mapChildren(compiler, this, scope, info.children); } init( block: Block, stripWhitespace: boolean, nextSibling: Node ) { if (this.name === 'slot' || this.name === 'option') { this.cannotUseInnerHTML(); } this.attributes.forEach(attr => { if (attr.dependencies.size) { this.parent.cannotUseInnerHTML(); block.addDependencies(attr.dependencies); // special case —