|
|
@ -3,86 +3,84 @@ import Element from '../../../nodes/Element';
|
|
|
|
import Wrapper from '../shared/Wrapper';
|
|
|
|
import Wrapper from '../shared/Wrapper';
|
|
|
|
import Block from '../../Block';
|
|
|
|
import Block from '../../Block';
|
|
|
|
import Node from '../../../nodes/shared/Node';
|
|
|
|
import Node from '../../../nodes/shared/Node';
|
|
|
|
import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../../utils/quoteIfNecessary';
|
|
|
|
import { is_void, quote_prop_if_necessary, quote_name_if_necessary, sanitize } from '../../../../utils/names';
|
|
|
|
import isVoidElementName from '../../../../utils/isVoidElementName';
|
|
|
|
|
|
|
|
import FragmentWrapper from '../Fragment';
|
|
|
|
import FragmentWrapper from '../Fragment';
|
|
|
|
import { stringify, escapeHTML, escape } from '../../../../utils/stringify';
|
|
|
|
import { stringify, escape_html, escape } from '../../../utils/stringify';
|
|
|
|
import TextWrapper from '../Text';
|
|
|
|
import TextWrapper from '../Text';
|
|
|
|
import fixAttributeCasing from '../../../../utils/fixAttributeCasing';
|
|
|
|
import fix_attribute_casing from './fix_attribute_casing';
|
|
|
|
import deindent from '../../../../utils/deindent';
|
|
|
|
import deindent from '../../../utils/deindent';
|
|
|
|
import { namespaces } from '../../../../utils/namespaces';
|
|
|
|
import { namespaces } from '../../../../utils/namespaces';
|
|
|
|
import AttributeWrapper from './Attribute';
|
|
|
|
import AttributeWrapper from './Attribute';
|
|
|
|
import StyleAttributeWrapper from './StyleAttribute';
|
|
|
|
import StyleAttributeWrapper from './StyleAttribute';
|
|
|
|
import { dimensions } from '../../../../utils/patterns';
|
|
|
|
import { dimensions } from '../../../../utils/patterns';
|
|
|
|
import Binding from './Binding';
|
|
|
|
import Binding from './Binding';
|
|
|
|
import InlineComponentWrapper from '../InlineComponent';
|
|
|
|
import InlineComponentWrapper from '../InlineComponent';
|
|
|
|
import addToSet from '../../../../utils/addToSet';
|
|
|
|
import add_to_set from '../../../utils/add_to_set';
|
|
|
|
import addEventHandlers from '../shared/addEventHandlers';
|
|
|
|
import add_event_handlers from '../shared/add_event_handlers';
|
|
|
|
import addActions from '../shared/addActions';
|
|
|
|
import add_actions from '../shared/add_actions';
|
|
|
|
import createDebuggingComment from '../../../../utils/createDebuggingComment';
|
|
|
|
import create_debugging_comment from '../shared/create_debugging_comment';
|
|
|
|
import sanitize from '../../../../utils/sanitize';
|
|
|
|
|
|
|
|
import { get_context_merger } from '../shared/get_context_merger';
|
|
|
|
import { get_context_merger } from '../shared/get_context_merger';
|
|
|
|
|
|
|
|
|
|
|
|
const events = [
|
|
|
|
const events = [
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['input'],
|
|
|
|
event_names: ['input'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.name === 'textarea' ||
|
|
|
|
node.name === 'textarea' ||
|
|
|
|
node.name === 'input' && !/radio|checkbox|range/.test(node.getStaticAttributeValue('type'))
|
|
|
|
node.name === 'input' && !/radio|checkbox|range/.test(node.get_static_attribute_value('type'))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['change'],
|
|
|
|
event_names: ['change'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.name === 'select' ||
|
|
|
|
node.name === 'select' ||
|
|
|
|
node.name === 'input' && /radio|checkbox/.test(node.getStaticAttributeValue('type'))
|
|
|
|
node.name === 'input' && /radio|checkbox/.test(node.get_static_attribute_value('type'))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['change', 'input'],
|
|
|
|
event_names: ['change', 'input'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.name === 'input' && node.getStaticAttributeValue('type') === 'range'
|
|
|
|
node.name === 'input' && node.get_static_attribute_value('type') === 'range'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['resize'],
|
|
|
|
event_names: ['resize'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
dimensions.test(name)
|
|
|
|
dimensions.test(name)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// media events
|
|
|
|
// media events
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['timeupdate'],
|
|
|
|
event_names: ['timeupdate'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.isMediaNode() &&
|
|
|
|
node.is_media_node() &&
|
|
|
|
(name === 'currentTime' || name === 'played')
|
|
|
|
(name === 'currentTime' || name === 'played')
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['durationchange'],
|
|
|
|
event_names: ['durationchange'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.isMediaNode() &&
|
|
|
|
node.is_media_node() &&
|
|
|
|
name === 'duration'
|
|
|
|
name === 'duration'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['play', 'pause'],
|
|
|
|
event_names: ['play', 'pause'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.isMediaNode() &&
|
|
|
|
node.is_media_node() &&
|
|
|
|
name === 'paused'
|
|
|
|
name === 'paused'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['progress'],
|
|
|
|
event_names: ['progress'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.isMediaNode() &&
|
|
|
|
node.is_media_node() &&
|
|
|
|
name === 'buffered'
|
|
|
|
name === 'buffered'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['loadedmetadata'],
|
|
|
|
event_names: ['loadedmetadata'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.isMediaNode() &&
|
|
|
|
node.is_media_node() &&
|
|
|
|
(name === 'buffered' || name === 'seekable')
|
|
|
|
(name === 'buffered' || name === 'seekable')
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
|
|
|
eventNames: ['volumechange'],
|
|
|
|
event_names: ['volumechange'],
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
filter: (node: Element, name: string) =>
|
|
|
|
node.isMediaNode() &&
|
|
|
|
node.is_media_node() &&
|
|
|
|
name === 'volume'
|
|
|
|
name === 'volume'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
];
|
|
|
|
];
|
|
|
@ -92,10 +90,10 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
fragment: FragmentWrapper;
|
|
|
|
fragment: FragmentWrapper;
|
|
|
|
attributes: AttributeWrapper[];
|
|
|
|
attributes: AttributeWrapper[];
|
|
|
|
bindings: Binding[];
|
|
|
|
bindings: Binding[];
|
|
|
|
classDependencies: string[];
|
|
|
|
class_dependencies: string[];
|
|
|
|
|
|
|
|
|
|
|
|
slot_block: Block;
|
|
|
|
slot_block: Block;
|
|
|
|
selectBindingDependencies?: Set<string>;
|
|
|
|
select_binding_dependencies?: Set<string>;
|
|
|
|
|
|
|
|
|
|
|
|
var: string;
|
|
|
|
var: string;
|
|
|
|
|
|
|
|
|
|
|
@ -104,13 +102,13 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
block: Block,
|
|
|
|
block: Block,
|
|
|
|
parent: Wrapper,
|
|
|
|
parent: Wrapper,
|
|
|
|
node: Element,
|
|
|
|
node: Element,
|
|
|
|
stripWhitespace: boolean,
|
|
|
|
strip_whitespace: boolean,
|
|
|
|
nextSibling: Wrapper
|
|
|
|
next_sibling: Wrapper
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
super(renderer, block, parent, node);
|
|
|
|
super(renderer, block, parent, node);
|
|
|
|
this.var = node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
|
|
this.var = node.name.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
|
|
|
|
|
|
|
|
|
|
this.classDependencies = [];
|
|
|
|
this.class_dependencies = [];
|
|
|
|
|
|
|
|
|
|
|
|
this.attributes = this.node.attributes.map(attribute => {
|
|
|
|
this.attributes = this.node.attributes.map(attribute => {
|
|
|
|
if (attribute.name === 'slot') {
|
|
|
|
if (attribute.name === 'slot') {
|
|
|
@ -129,12 +127,12 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (owner && owner.node.type === 'InlineComponent') {
|
|
|
|
if (owner && owner.node.type === 'InlineComponent') {
|
|
|
|
const name = attribute.getStaticValue();
|
|
|
|
const name = attribute.get_static_value();
|
|
|
|
|
|
|
|
|
|
|
|
if (!(owner as InlineComponentWrapper).slots.has(name)) {
|
|
|
|
if (!(owner as InlineComponentWrapper).slots.has(name)) {
|
|
|
|
const child_block = block.child({
|
|
|
|
const child_block = block.child({
|
|
|
|
comment: createDebuggingComment(node, this.renderer.component),
|
|
|
|
comment: create_debugging_comment(node, this.renderer.component),
|
|
|
|
name: this.renderer.component.getUniqueName(`create_${sanitize(name)}_slot`)
|
|
|
|
name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const fn = get_context_merger(this.node.lets);
|
|
|
|
const fn = get_context_merger(this.node.lets);
|
|
|
@ -163,46 +161,46 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this));
|
|
|
|
this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this));
|
|
|
|
|
|
|
|
|
|
|
|
if (node.intro || node.outro) {
|
|
|
|
if (node.intro || node.outro) {
|
|
|
|
if (node.intro) block.addIntro(node.intro.is_local);
|
|
|
|
if (node.intro) block.add_intro(node.intro.is_local);
|
|
|
|
if (node.outro) block.addOutro(node.outro.is_local);
|
|
|
|
if (node.outro) block.add_outro(node.outro.is_local);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (node.animation) {
|
|
|
|
if (node.animation) {
|
|
|
|
block.addAnimation();
|
|
|
|
block.add_animation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// add directive and handler dependencies
|
|
|
|
// add directive and handler dependencies
|
|
|
|
[node.animation, node.outro, ...node.actions, ...node.classes].forEach(directive => {
|
|
|
|
[node.animation, node.outro, ...node.actions, ...node.classes].forEach(directive => {
|
|
|
|
if (directive && directive.expression) {
|
|
|
|
if (directive && directive.expression) {
|
|
|
|
block.addDependencies(directive.expression.dependencies);
|
|
|
|
block.add_dependencies(directive.expression.dependencies);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
node.handlers.forEach(handler => {
|
|
|
|
node.handlers.forEach(handler => {
|
|
|
|
if (handler.expression) {
|
|
|
|
if (handler.expression) {
|
|
|
|
block.addDependencies(handler.expression.dependencies);
|
|
|
|
block.add_dependencies(handler.expression.dependencies);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (this.parent) {
|
|
|
|
if (this.parent) {
|
|
|
|
if (node.actions.length > 0) this.parent.cannotUseInnerHTML();
|
|
|
|
if (node.actions.length > 0) this.parent.cannot_use_innerhtml();
|
|
|
|
if (node.animation) this.parent.cannotUseInnerHTML();
|
|
|
|
if (node.animation) this.parent.cannot_use_innerhtml();
|
|
|
|
if (node.bindings.length > 0) this.parent.cannotUseInnerHTML();
|
|
|
|
if (node.bindings.length > 0) this.parent.cannot_use_innerhtml();
|
|
|
|
if (node.classes.length > 0) this.parent.cannotUseInnerHTML();
|
|
|
|
if (node.classes.length > 0) this.parent.cannot_use_innerhtml();
|
|
|
|
if (node.intro || node.outro) this.parent.cannotUseInnerHTML();
|
|
|
|
if (node.intro || node.outro) this.parent.cannot_use_innerhtml();
|
|
|
|
if (node.handlers.length > 0) this.parent.cannotUseInnerHTML();
|
|
|
|
if (node.handlers.length > 0) this.parent.cannot_use_innerhtml();
|
|
|
|
|
|
|
|
|
|
|
|
if (this.node.name === 'option') this.parent.cannotUseInnerHTML();
|
|
|
|
if (this.node.name === 'option') this.parent.cannot_use_innerhtml();
|
|
|
|
|
|
|
|
|
|
|
|
if (renderer.options.dev) {
|
|
|
|
if (renderer.options.dev) {
|
|
|
|
this.parent.cannotUseInnerHTML(); // need to use addLoc
|
|
|
|
this.parent.cannot_use_innerhtml(); // need to use add_location
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.fragment = new FragmentWrapper(renderer, block, node.children, this, stripWhitespace, nextSibling);
|
|
|
|
this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling);
|
|
|
|
|
|
|
|
|
|
|
|
if (this.slot_block) {
|
|
|
|
if (this.slot_block) {
|
|
|
|
block.parent.addDependencies(block.dependencies);
|
|
|
|
block.parent.add_dependencies(block.dependencies);
|
|
|
|
|
|
|
|
|
|
|
|
// appalling hack
|
|
|
|
// appalling hack
|
|
|
|
const index = block.parent.wrappers.indexOf(this);
|
|
|
|
const index = block.parent.wrappers.indexOf(this);
|
|
|
@ -211,11 +209,11 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
render(block: Block, parentNode: string, parentNodes: string) {
|
|
|
|
render(block: Block, parent_node: string, parent_nodes: string) {
|
|
|
|
const { renderer } = this;
|
|
|
|
const { renderer } = this;
|
|
|
|
|
|
|
|
|
|
|
|
if (this.node.name === 'slot') {
|
|
|
|
if (this.node.name === 'slot') {
|
|
|
|
const slotName = this.getStaticAttributeValue('name') || 'default';
|
|
|
|
const slotName = this.get_static_attribute_value('name') || 'default';
|
|
|
|
renderer.slots.add(slotName);
|
|
|
|
renderer.slots.add(slotName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -226,58 +224,58 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const node = this.var;
|
|
|
|
const node = this.var;
|
|
|
|
const nodes = parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parentNodes is null
|
|
|
|
const nodes = parent_nodes && block.get_unique_name(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parent_nodes is null
|
|
|
|
|
|
|
|
|
|
|
|
block.addVariable(node);
|
|
|
|
block.add_variable(node);
|
|
|
|
const renderStatement = this.getRenderStatement();
|
|
|
|
const render_statement = this.get_render_statement();
|
|
|
|
block.builders.create.addLine(
|
|
|
|
block.builders.create.add_line(
|
|
|
|
`${node} = ${renderStatement};`
|
|
|
|
`${node} = ${render_statement};`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (renderer.options.hydratable) {
|
|
|
|
if (renderer.options.hydratable) {
|
|
|
|
if (parentNodes) {
|
|
|
|
if (parent_nodes) {
|
|
|
|
block.builders.claim.addBlock(deindent`
|
|
|
|
block.builders.claim.add_block(deindent`
|
|
|
|
${node} = ${this.getClaimStatement(parentNodes)};
|
|
|
|
${node} = ${this.get_claim_statement(parent_nodes)};
|
|
|
|
var ${nodes} = @children(${this.node.name === 'template' ? `${node}.content` : node});
|
|
|
|
var ${nodes} = @children(${this.node.name === 'template' ? `${node}.content` : node});
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
block.builders.claim.addLine(
|
|
|
|
block.builders.claim.add_line(
|
|
|
|
`${node} = ${renderStatement};`
|
|
|
|
`${node} = ${render_statement};`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (parentNode) {
|
|
|
|
if (parent_node) {
|
|
|
|
block.builders.mount.addLine(
|
|
|
|
block.builders.mount.add_line(
|
|
|
|
`@append(${parentNode}, ${node});`
|
|
|
|
`@append(${parent_node}, ${node});`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (parentNode === 'document.head') {
|
|
|
|
if (parent_node === 'document.head') {
|
|
|
|
block.builders.destroy.addLine(`@detachNode(${node});`);
|
|
|
|
block.builders.destroy.add_line(`@detach(${node});`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
block.builders.mount.addLine(`@insert(#target, ${node}, anchor);`);
|
|
|
|
block.builders.mount.add_line(`@insert(#target, ${node}, anchor);`);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO we eventually need to consider what happens to elements
|
|
|
|
// TODO we eventually need to consider what happens to elements
|
|
|
|
// that belong to the same outgroup as an outroing element...
|
|
|
|
// that belong to the same outgroup as an outroing element...
|
|
|
|
block.builders.destroy.addConditional('detach', `@detachNode(${node});`);
|
|
|
|
block.builders.destroy.add_conditional('detaching', `@detach(${node});`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// insert static children with textContent or innerHTML
|
|
|
|
// insert static children with textContent or innerHTML
|
|
|
|
if (!this.node.namespace && this.canUseInnerHTML && this.fragment.nodes.length > 0) {
|
|
|
|
if (!this.node.namespace && this.can_use_innerhtml && this.fragment.nodes.length > 0) {
|
|
|
|
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
|
|
|
|
if (this.fragment.nodes.length === 1 && this.fragment.nodes[0].node.type === 'Text') {
|
|
|
|
block.builders.create.addLine(
|
|
|
|
block.builders.create.add_line(
|
|
|
|
`${node}.textContent = ${stringify(this.fragment.nodes[0].data)};`
|
|
|
|
`${node}.textContent = ${stringify(this.fragment.nodes[0].data)};`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const innerHTML = escape(
|
|
|
|
const inner_html = escape(
|
|
|
|
this.fragment.nodes
|
|
|
|
this.fragment.nodes
|
|
|
|
.map(toHTML)
|
|
|
|
.map(to_html)
|
|
|
|
.join('')
|
|
|
|
.join('')
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.create.addLine(
|
|
|
|
block.builders.create.add_line(
|
|
|
|
`${node}.innerHTML = \`${innerHTML}\`;`
|
|
|
|
`${node}.innerHTML = \`${inner_html}\`;`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -290,31 +288,31 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const eventHandlerOrBindingUsesContext = (
|
|
|
|
const event_handler_or_binding_uses_context = (
|
|
|
|
this.bindings.some(binding => binding.handler.usesContext) ||
|
|
|
|
this.bindings.some(binding => binding.handler.uses_context) ||
|
|
|
|
this.node.handlers.some(handler => handler.usesContext) ||
|
|
|
|
this.node.handlers.some(handler => handler.uses_context) ||
|
|
|
|
this.node.actions.some(action => action.usesContext)
|
|
|
|
this.node.actions.some(action => action.uses_context)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (eventHandlerOrBindingUsesContext) {
|
|
|
|
if (event_handler_or_binding_uses_context) {
|
|
|
|
block.maintainContext = true;
|
|
|
|
block.maintain_context = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.addBindings(block);
|
|
|
|
this.add_bindings(block);
|
|
|
|
this.addEventHandlers(block);
|
|
|
|
this.add_event_handlers(block);
|
|
|
|
this.addAttributes(block);
|
|
|
|
this.add_attributes(block);
|
|
|
|
this.addTransitions(block);
|
|
|
|
this.add_transitions(block);
|
|
|
|
this.addAnimation(block);
|
|
|
|
this.add_animation(block);
|
|
|
|
this.addActions(block);
|
|
|
|
this.add_actions(block);
|
|
|
|
this.addClasses(block);
|
|
|
|
this.add_classes(block);
|
|
|
|
|
|
|
|
|
|
|
|
if (nodes && this.renderer.options.hydratable) {
|
|
|
|
if (nodes && this.renderer.options.hydratable) {
|
|
|
|
block.builders.claim.addLine(
|
|
|
|
block.builders.claim.add_line(
|
|
|
|
`${nodes}.forEach(@detachNode);`
|
|
|
|
`${nodes}.forEach(@detach);`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function toHTML(wrapper: ElementWrapper | TextWrapper) {
|
|
|
|
function to_html(wrapper: ElementWrapper | TextWrapper) {
|
|
|
|
if (wrapper.node.type === 'Text') {
|
|
|
|
if (wrapper.node.type === 'Text') {
|
|
|
|
const { parent } = wrapper.node;
|
|
|
|
const { parent } = wrapper.node;
|
|
|
|
|
|
|
|
|
|
|
@ -325,7 +323,7 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
|
|
|
|
|
|
|
|
return raw
|
|
|
|
return raw
|
|
|
|
? wrapper.node.data
|
|
|
|
? wrapper.node.data
|
|
|
|
: escapeHTML(wrapper.node.data)
|
|
|
|
: escape_html(wrapper.node.data)
|
|
|
|
.replace(/\\/g, '\\\\')
|
|
|
|
.replace(/\\/g, '\\\\')
|
|
|
|
.replace(/`/g, '\\`')
|
|
|
|
.replace(/`/g, '\\`')
|
|
|
|
.replace(/\$/g, '\\$');
|
|
|
|
.replace(/\$/g, '\\$');
|
|
|
@ -336,67 +334,67 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
let open = `<${wrapper.node.name}`;
|
|
|
|
let open = `<${wrapper.node.name}`;
|
|
|
|
|
|
|
|
|
|
|
|
(wrapper as ElementWrapper).attributes.forEach((attr: AttributeWrapper) => {
|
|
|
|
(wrapper as ElementWrapper).attributes.forEach((attr: AttributeWrapper) => {
|
|
|
|
open += ` ${fixAttributeCasing(attr.node.name)}${attr.stringify()}`
|
|
|
|
open += ` ${fix_attribute_casing(attr.node.name)}${attr.stringify()}`
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (isVoidElementName(wrapper.node.name)) return open + '>';
|
|
|
|
if (is_void(wrapper.node.name)) return open + '>';
|
|
|
|
|
|
|
|
|
|
|
|
return `${open}>${wrapper.fragment.nodes.map(toHTML).join('')}</${wrapper.node.name}>`;
|
|
|
|
return `${open}>${wrapper.fragment.nodes.map(to_html).join('')}</${wrapper.node.name}>`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (renderer.options.dev) {
|
|
|
|
if (renderer.options.dev) {
|
|
|
|
const loc = renderer.locate(this.node.start);
|
|
|
|
const loc = renderer.locate(this.node.start);
|
|
|
|
block.builders.hydrate.addLine(
|
|
|
|
block.builders.hydrate.add_line(
|
|
|
|
`@addLoc(${this.var}, ${renderer.fileVar}, ${loc.line}, ${loc.column}, ${this.node.start});`
|
|
|
|
`@add_location(${this.var}, ${renderer.file_var}, ${loc.line}, ${loc.column}, ${this.node.start});`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getRenderStatement() {
|
|
|
|
get_render_statement() {
|
|
|
|
const { name, namespace } = this.node;
|
|
|
|
const { name, namespace } = this.node;
|
|
|
|
|
|
|
|
|
|
|
|
if (namespace === 'http://www.w3.org/2000/svg') {
|
|
|
|
if (namespace === 'http://www.w3.org/2000/svg') {
|
|
|
|
return `@createSvgElement("${name}")`;
|
|
|
|
return `@svg_element("${name}")`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (namespace) {
|
|
|
|
if (namespace) {
|
|
|
|
return `document.createElementNS("${namespace}", "${name}")`;
|
|
|
|
return `document.createElementNS("${namespace}", "${name}")`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return `@createElement("${name}")`;
|
|
|
|
return `@element("${name}")`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getClaimStatement(nodes: string) {
|
|
|
|
get_claim_statement(nodes: string) {
|
|
|
|
const attributes = this.node.attributes
|
|
|
|
const attributes = this.node.attributes
|
|
|
|
.filter((attr: Node) => attr.type === 'Attribute')
|
|
|
|
.filter((attr: Node) => attr.type === 'Attribute')
|
|
|
|
.map((attr: Node) => `${quoteNameIfNecessary(attr.name)}: true`)
|
|
|
|
.map((attr: Node) => `${quote_name_if_necessary(attr.name)}: true`)
|
|
|
|
.join(', ');
|
|
|
|
.join(', ');
|
|
|
|
|
|
|
|
|
|
|
|
const name = this.node.namespace
|
|
|
|
const name = this.node.namespace
|
|
|
|
? this.node.name
|
|
|
|
? this.node.name
|
|
|
|
: this.node.name.toUpperCase();
|
|
|
|
: this.node.name.toUpperCase();
|
|
|
|
|
|
|
|
|
|
|
|
return `@claimElement(${nodes}, "${name}", ${attributes
|
|
|
|
return `@claim_element(${nodes}, "${name}", ${attributes
|
|
|
|
? `{ ${attributes} }`
|
|
|
|
? `{ ${attributes} }`
|
|
|
|
: `{}`}, ${this.node.namespace === namespaces.svg ? true : false})`;
|
|
|
|
: `{}`}, ${this.node.namespace === namespaces.svg ? true : false})`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addBindings(block: Block) {
|
|
|
|
add_bindings(block: Block) {
|
|
|
|
const { renderer } = this;
|
|
|
|
const { renderer } = this;
|
|
|
|
|
|
|
|
|
|
|
|
if (this.bindings.length === 0) return;
|
|
|
|
if (this.bindings.length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
renderer.component.has_reactive_assignments = true;
|
|
|
|
renderer.component.has_reactive_assignments = true;
|
|
|
|
|
|
|
|
|
|
|
|
const lock = this.bindings.some(binding => binding.needsLock) ?
|
|
|
|
const lock = this.bindings.some(binding => binding.needs_lock) ?
|
|
|
|
block.getUniqueName(`${this.var}_updating`) :
|
|
|
|
block.get_unique_name(`${this.var}_updating`) :
|
|
|
|
null;
|
|
|
|
null;
|
|
|
|
|
|
|
|
|
|
|
|
if (lock) block.addVariable(lock, 'false');
|
|
|
|
if (lock) block.add_variable(lock, 'false');
|
|
|
|
|
|
|
|
|
|
|
|
const groups = events
|
|
|
|
const groups = events
|
|
|
|
.map(event => ({
|
|
|
|
.map(event => ({
|
|
|
|
events: event.eventNames,
|
|
|
|
events: event.event_names,
|
|
|
|
bindings: this.bindings
|
|
|
|
bindings: this.bindings
|
|
|
|
.filter(binding => binding.node.name !== 'this')
|
|
|
|
.filter(binding => binding.node.name !== 'this')
|
|
|
|
.filter(binding => event.filter(this.node, binding.node.name))
|
|
|
|
.filter(binding => event.filter(this.node, binding.node.name))
|
|
|
@ -404,7 +402,7 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
.filter(group => group.bindings.length);
|
|
|
|
.filter(group => group.bindings.length);
|
|
|
|
|
|
|
|
|
|
|
|
groups.forEach(group => {
|
|
|
|
groups.forEach(group => {
|
|
|
|
const handler = renderer.component.getUniqueName(`${this.var}_${group.events.join('_')}_handler`);
|
|
|
|
const handler = renderer.component.get_unique_name(`${this.var}_${group.events.join('_')}_handler`);
|
|
|
|
|
|
|
|
|
|
|
|
renderer.component.add_var({
|
|
|
|
renderer.component.add_var({
|
|
|
|
name: handler,
|
|
|
|
name: handler,
|
|
|
@ -413,16 +411,16 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// TODO figure out how to handle locks
|
|
|
|
// TODO figure out how to handle locks
|
|
|
|
const needsLock = group.bindings.some(binding => binding.needsLock);
|
|
|
|
const needs_lock = group.bindings.some(binding => binding.needs_lock);
|
|
|
|
|
|
|
|
|
|
|
|
const dependencies = new Set();
|
|
|
|
const dependencies = new Set();
|
|
|
|
const contextual_dependencies = new Set();
|
|
|
|
const contextual_dependencies = new Set();
|
|
|
|
|
|
|
|
|
|
|
|
group.bindings.forEach(binding => {
|
|
|
|
group.bindings.forEach(binding => {
|
|
|
|
// TODO this is a mess
|
|
|
|
// TODO this is a mess
|
|
|
|
addToSet(dependencies, binding.get_dependencies());
|
|
|
|
add_to_set(dependencies, binding.get_dependencies());
|
|
|
|
addToSet(contextual_dependencies, binding.node.expression.contextual_dependencies);
|
|
|
|
add_to_set(contextual_dependencies, binding.node.expression.contextual_dependencies);
|
|
|
|
addToSet(contextual_dependencies, binding.handler.contextual_dependencies);
|
|
|
|
add_to_set(contextual_dependencies, binding.handler.contextual_dependencies);
|
|
|
|
|
|
|
|
|
|
|
|
binding.render(block, lock);
|
|
|
|
binding.render(block, lock);
|
|
|
|
});
|
|
|
|
});
|
|
|
@ -432,23 +430,23 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
// own hands
|
|
|
|
// own hands
|
|
|
|
let animation_frame;
|
|
|
|
let animation_frame;
|
|
|
|
if (group.events[0] === 'timeupdate') {
|
|
|
|
if (group.events[0] === 'timeupdate') {
|
|
|
|
animation_frame = block.getUniqueName(`${this.var}_animationframe`);
|
|
|
|
animation_frame = block.get_unique_name(`${this.var}_animationframe`);
|
|
|
|
block.addVariable(animation_frame);
|
|
|
|
block.add_variable(animation_frame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const has_local_function = contextual_dependencies.size > 0 || needsLock || animation_frame;
|
|
|
|
const has_local_function = contextual_dependencies.size > 0 || needs_lock || animation_frame;
|
|
|
|
|
|
|
|
|
|
|
|
let callee;
|
|
|
|
let callee;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO dry this out — similar code for event handlers and component bindings
|
|
|
|
// TODO dry this out — similar code for event handlers and component bindings
|
|
|
|
if (has_local_function) {
|
|
|
|
if (has_local_function) {
|
|
|
|
// need to create a block-local function that calls an instance-level function
|
|
|
|
// need to create a block-local function that calls an instance-level function
|
|
|
|
block.builders.init.addBlock(deindent`
|
|
|
|
block.builders.init.add_block(deindent`
|
|
|
|
function ${handler}() {
|
|
|
|
function ${handler}() {
|
|
|
|
${animation_frame && deindent`
|
|
|
|
${animation_frame && deindent`
|
|
|
|
cancelAnimationFrame(${animation_frame});
|
|
|
|
cancelAnimationFrame(${animation_frame});
|
|
|
|
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`}
|
|
|
|
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`}
|
|
|
|
${needsLock && `${lock} = true;`}
|
|
|
|
${needs_lock && `${lock} = true;`}
|
|
|
|
ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''});
|
|
|
|
ctx.${handler}.call(${this.var}${contextual_dependencies.size > 0 ? ', ctx' : ''});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
`);
|
|
|
@ -468,48 +466,48 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
group.events.forEach(name => {
|
|
|
|
group.events.forEach(name => {
|
|
|
|
if (name === 'resize') {
|
|
|
|
if (name === 'resize') {
|
|
|
|
// special case
|
|
|
|
// special case
|
|
|
|
const resize_listener = block.getUniqueName(`${this.var}_resize_listener`);
|
|
|
|
const resize_listener = block.get_unique_name(`${this.var}_resize_listener`);
|
|
|
|
block.addVariable(resize_listener);
|
|
|
|
block.add_variable(resize_listener);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.mount.addLine(
|
|
|
|
block.builders.mount.add_line(
|
|
|
|
`${resize_listener} = @addResizeListener(${this.var}, ${callee}.bind(${this.var}));`
|
|
|
|
`${resize_listener} = @add_resize_listener(${this.var}, ${callee}.bind(${this.var}));`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.destroy.addLine(
|
|
|
|
block.builders.destroy.add_line(
|
|
|
|
`${resize_listener}.cancel();`
|
|
|
|
`${resize_listener}.cancel();`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
block.event_listeners.push(
|
|
|
|
block.event_listeners.push(
|
|
|
|
`@addListener(${this.var}, "${name}", ${callee})`
|
|
|
|
`@listen(${this.var}, "${name}", ${callee})`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const someInitialStateIsUndefined = group.bindings
|
|
|
|
const some_initial_state_is_undefined = group.bindings
|
|
|
|
.map(binding => `${binding.snippet} === void 0`)
|
|
|
|
.map(binding => `${binding.snippet} === void 0`)
|
|
|
|
.join(' || ');
|
|
|
|
.join(' || ');
|
|
|
|
|
|
|
|
|
|
|
|
if (this.node.name === 'select' || group.bindings.find(binding => binding.node.name === 'indeterminate' || binding.isReadOnlyMediaAttribute())) {
|
|
|
|
if (this.node.name === 'select' || group.bindings.find(binding => binding.node.name === 'indeterminate' || binding.is_readonly_media_attribute())) {
|
|
|
|
const callback = has_local_function ? handler : `() => ${callee}.call(${this.var})`;
|
|
|
|
const callback = has_local_function ? handler : `() => ${callee}.call(${this.var})`;
|
|
|
|
block.builders.hydrate.addLine(
|
|
|
|
block.builders.hydrate.add_line(
|
|
|
|
`if (${someInitialStateIsUndefined}) @add_render_callback(${callback});`
|
|
|
|
`if (${some_initial_state_is_undefined}) @add_render_callback(${callback});`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (group.events[0] === 'resize') {
|
|
|
|
if (group.events[0] === 'resize') {
|
|
|
|
block.builders.hydrate.addLine(
|
|
|
|
block.builders.hydrate.add_line(
|
|
|
|
`@add_render_callback(() => ${callee}.call(${this.var}));`
|
|
|
|
`@add_render_callback(() => ${callee}.call(${this.var}));`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (lock) {
|
|
|
|
if (lock) {
|
|
|
|
block.builders.update.addLine(`${lock} = false;`);
|
|
|
|
block.builders.update.add_line(`${lock} = false;`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const this_binding = this.bindings.find(b => b.node.name === 'this');
|
|
|
|
const this_binding = this.bindings.find(b => b.node.name === 'this');
|
|
|
|
if (this_binding) {
|
|
|
|
if (this_binding) {
|
|
|
|
const name = renderer.component.getUniqueName(`${this.var}_binding`);
|
|
|
|
const name = renderer.component.get_unique_name(`${this.var}_binding`);
|
|
|
|
|
|
|
|
|
|
|
|
renderer.component.add_var({
|
|
|
|
renderer.component.add_var({
|
|
|
|
name,
|
|
|
|
name,
|
|
|
@ -522,7 +520,7 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
const args = [];
|
|
|
|
const args = [];
|
|
|
|
for (const arg of handler.contextual_dependencies) {
|
|
|
|
for (const arg of handler.contextual_dependencies) {
|
|
|
|
args.push(arg);
|
|
|
|
args.push(arg);
|
|
|
|
block.addVariable(arg, `ctx.${arg}`);
|
|
|
|
block.add_variable(arg, `ctx.${arg}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
renderer.component.partly_hoisted.push(deindent`
|
|
|
|
renderer.component.partly_hoisted.push(deindent`
|
|
|
@ -532,9 +530,9 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.mount.addLine(`@add_binding_callback(() => ctx.${name}(${[this.var, 'null'].concat(args).join(', ')}));`);
|
|
|
|
block.builders.mount.add_line(`@add_binding_callback(() => ctx.${name}(${[this.var, 'null'].concat(args).join(', ')}));`);
|
|
|
|
block.builders.destroy.addLine(`ctx.${name}(${['null', this.var].concat(args).join(', ')});`);
|
|
|
|
block.builders.destroy.add_line(`ctx.${name}(${['null', this.var].concat(args).join(', ')});`);
|
|
|
|
block.builders.update.addLine(deindent`
|
|
|
|
block.builders.update.add_line(deindent`
|
|
|
|
if (changed.items) {
|
|
|
|
if (changed.items) {
|
|
|
|
ctx.${name}(${['null', this.var].concat(args).join(', ')});
|
|
|
|
ctx.${name}(${['null', this.var].concat(args).join(', ')});
|
|
|
|
${args.map(a => `${a} = ctx.${a}`).join(', ')};
|
|
|
|
${args.map(a => `${a} = ctx.${a}`).join(', ')};
|
|
|
@ -544,25 +542,25 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addAttributes(block: Block) {
|
|
|
|
add_attributes(block: Block) {
|
|
|
|
if (this.node.attributes.find(attr => attr.type === 'Spread')) {
|
|
|
|
if (this.node.attributes.find(attr => attr.type === 'Spread')) {
|
|
|
|
this.addSpreadAttributes(block);
|
|
|
|
this.add_spread_attributes(block);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.attributes.forEach((attribute: Attribute) => {
|
|
|
|
this.attributes.forEach((attribute: Attribute) => {
|
|
|
|
if (attribute.node.name === 'class' && attribute.node.isDynamic) {
|
|
|
|
if (attribute.node.name === 'class' && attribute.node.is_dynamic) {
|
|
|
|
this.classDependencies.push(...attribute.node.dependencies);
|
|
|
|
this.class_dependencies.push(...attribute.node.dependencies);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attribute.render(block);
|
|
|
|
attribute.render(block);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addSpreadAttributes(block: Block) {
|
|
|
|
add_spread_attributes(block: Block) {
|
|
|
|
const levels = block.getUniqueName(`${this.var}_levels`);
|
|
|
|
const levels = block.get_unique_name(`${this.var}_levels`);
|
|
|
|
const data = block.getUniqueName(`${this.var}_data`);
|
|
|
|
const data = block.get_unique_name(`${this.var}_data`);
|
|
|
|
|
|
|
|
|
|
|
|
const initialProps = [];
|
|
|
|
const initial_props = [];
|
|
|
|
const updates = [];
|
|
|
|
const updates = [];
|
|
|
|
|
|
|
|
|
|
|
|
this.node.attributes
|
|
|
|
this.node.attributes
|
|
|
@ -572,23 +570,23 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})`
|
|
|
|
? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})`
|
|
|
|
: null;
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
|
|
if (attr.isSpread) {
|
|
|
|
if (attr.is_spread) {
|
|
|
|
const snippet = attr.expression.render(block);
|
|
|
|
const snippet = attr.expression.render(block);
|
|
|
|
|
|
|
|
|
|
|
|
initialProps.push(snippet);
|
|
|
|
initial_props.push(snippet);
|
|
|
|
|
|
|
|
|
|
|
|
updates.push(condition ? `${condition} && ${snippet}` : snippet);
|
|
|
|
updates.push(condition ? `${condition} && ${snippet}` : snippet);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const snippet = `{ ${quoteNameIfNecessary(attr.name)}: ${attr.getValue(block)} }`;
|
|
|
|
const snippet = `{ ${quote_name_if_necessary(attr.name)}: ${attr.get_value(block)} }`;
|
|
|
|
initialProps.push(snippet);
|
|
|
|
initial_props.push(snippet);
|
|
|
|
|
|
|
|
|
|
|
|
updates.push(condition ? `${condition} && ${snippet}` : snippet);
|
|
|
|
updates.push(condition ? `${condition} && ${snippet}` : snippet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.init.addBlock(deindent`
|
|
|
|
block.builders.init.add_block(deindent`
|
|
|
|
var ${levels} = [
|
|
|
|
var ${levels} = [
|
|
|
|
${initialProps.join(',\n')}
|
|
|
|
${initial_props.join(',\n')}
|
|
|
|
];
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
var ${data} = {};
|
|
|
|
var ${data} = {};
|
|
|
@ -597,22 +595,22 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.hydrate.addLine(
|
|
|
|
block.builders.hydrate.add_line(
|
|
|
|
`@setAttributes(${this.var}, ${data});`
|
|
|
|
`@set_attributes(${this.var}, ${data});`
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.update.addBlock(deindent`
|
|
|
|
block.builders.update.add_block(deindent`
|
|
|
|
@setAttributes(${this.var}, @getSpreadUpdate(${levels}, [
|
|
|
|
@set_attributes(${this.var}, @get_spread_update(${levels}, [
|
|
|
|
${updates.join(',\n')}
|
|
|
|
${updates.join(',\n')}
|
|
|
|
]));
|
|
|
|
]));
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addEventHandlers(block: Block) {
|
|
|
|
add_event_handlers(block: Block) {
|
|
|
|
addEventHandlers(block, this.var, this.node.handlers);
|
|
|
|
add_event_handlers(block, this.var, this.node.handlers);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addTransitions(
|
|
|
|
add_transitions(
|
|
|
|
block: Block
|
|
|
|
block: Block
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
const { intro, outro } = this.node;
|
|
|
|
const { intro, outro } = this.node;
|
|
|
@ -622,12 +620,12 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
|
|
|
|
|
|
|
|
if (intro === outro) {
|
|
|
|
if (intro === outro) {
|
|
|
|
// bidirectional transition
|
|
|
|
// bidirectional transition
|
|
|
|
const name = block.getUniqueName(`${this.var}_transition`);
|
|
|
|
const name = block.get_unique_name(`${this.var}_transition`);
|
|
|
|
const snippet = intro.expression
|
|
|
|
const snippet = intro.expression
|
|
|
|
? intro.expression.render(block)
|
|
|
|
? intro.expression.render(block)
|
|
|
|
: '{}';
|
|
|
|
: '{}';
|
|
|
|
|
|
|
|
|
|
|
|
block.addVariable(name);
|
|
|
|
block.add_variable(name);
|
|
|
|
|
|
|
|
|
|
|
|
const fn = component.qualify(intro.name);
|
|
|
|
const fn = component.qualify(intro.name);
|
|
|
|
|
|
|
|
|
|
|
@ -644,31 +642,31 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
`;
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
if (intro.is_local) {
|
|
|
|
if (intro.is_local) {
|
|
|
|
block.builders.intro.addBlock(deindent`
|
|
|
|
block.builders.intro.add_block(deindent`
|
|
|
|
if (#local) {
|
|
|
|
if (#local) {
|
|
|
|
${intro_block}
|
|
|
|
${intro_block}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.outro.addBlock(deindent`
|
|
|
|
block.builders.outro.add_block(deindent`
|
|
|
|
if (#local) {
|
|
|
|
if (#local) {
|
|
|
|
${outro_block}
|
|
|
|
${outro_block}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
block.builders.intro.addBlock(intro_block);
|
|
|
|
block.builders.intro.add_block(intro_block);
|
|
|
|
block.builders.outro.addBlock(outro_block);
|
|
|
|
block.builders.outro.add_block(outro_block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.destroy.addConditional('detach', `if (${name}) ${name}.end();`);
|
|
|
|
block.builders.destroy.add_conditional('detaching', `if (${name}) ${name}.end();`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
else {
|
|
|
|
const introName = intro && block.getUniqueName(`${this.var}_intro`);
|
|
|
|
const intro_name = intro && block.get_unique_name(`${this.var}_intro`);
|
|
|
|
const outroName = outro && block.getUniqueName(`${this.var}_outro`);
|
|
|
|
const outro_name = outro && block.get_unique_name(`${this.var}_outro`);
|
|
|
|
|
|
|
|
|
|
|
|
if (intro) {
|
|
|
|
if (intro) {
|
|
|
|
block.addVariable(introName);
|
|
|
|
block.add_variable(intro_name);
|
|
|
|
const snippet = intro.expression
|
|
|
|
const snippet = intro.expression
|
|
|
|
? intro.expression.render(block)
|
|
|
|
? intro.expression.render(block)
|
|
|
|
: '{}';
|
|
|
|
: '{}';
|
|
|
@ -680,19 +678,19 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
if (outro) {
|
|
|
|
if (outro) {
|
|
|
|
intro_block = deindent`
|
|
|
|
intro_block = deindent`
|
|
|
|
@add_render_callback(() => {
|
|
|
|
@add_render_callback(() => {
|
|
|
|
if (${outroName}) ${outroName}.end(1);
|
|
|
|
if (${outro_name}) ${outro_name}.end(1);
|
|
|
|
if (!${introName}) ${introName} = @create_in_transition(${this.var}, ${fn}, ${snippet});
|
|
|
|
if (!${intro_name}) ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet});
|
|
|
|
${introName}.start();
|
|
|
|
${intro_name}.start();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
`;
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.outro.addLine(`if (${introName}) ${introName}.invalidate();`);
|
|
|
|
block.builders.outro.add_line(`if (${intro_name}) ${intro_name}.invalidate();`);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
intro_block = deindent`
|
|
|
|
intro_block = deindent`
|
|
|
|
if (!${introName}) {
|
|
|
|
if (!${intro_name}) {
|
|
|
|
@add_render_callback(() => {
|
|
|
|
@add_render_callback(() => {
|
|
|
|
${introName} = @create_in_transition(${this.var}, ${fn}, ${snippet});
|
|
|
|
${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet});
|
|
|
|
${introName}.start();
|
|
|
|
${intro_name}.start();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
`;
|
|
|
@ -706,11 +704,11 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
`;
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.intro.addBlock(intro_block);
|
|
|
|
block.builders.intro.add_block(intro_block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (outro) {
|
|
|
|
if (outro) {
|
|
|
|
block.addVariable(outroName);
|
|
|
|
block.add_variable(outro_name);
|
|
|
|
const snippet = outro.expression
|
|
|
|
const snippet = outro.expression
|
|
|
|
? outro.expression.render(block)
|
|
|
|
? outro.expression.render(block)
|
|
|
|
: '{}';
|
|
|
|
: '{}';
|
|
|
@ -718,15 +716,15 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
const fn = component.qualify(outro.name);
|
|
|
|
const fn = component.qualify(outro.name);
|
|
|
|
|
|
|
|
|
|
|
|
if (!intro) {
|
|
|
|
if (!intro) {
|
|
|
|
block.builders.intro.addBlock(deindent`
|
|
|
|
block.builders.intro.add_block(deindent`
|
|
|
|
if (${outroName}) ${outroName}.end(1);
|
|
|
|
if (${outro_name}) ${outro_name}.end(1);
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO hide elements that have outro'd (unless they belong to a still-outroing
|
|
|
|
// TODO hide elements that have outro'd (unless they belong to a still-outroing
|
|
|
|
// group) prior to their removal from the DOM
|
|
|
|
// group) prior to their removal from the DOM
|
|
|
|
let outro_block = deindent`
|
|
|
|
let outro_block = deindent`
|
|
|
|
${outroName} = @create_out_transition(${this.var}, ${fn}, ${snippet});
|
|
|
|
${outro_name} = @create_out_transition(${this.var}, ${fn}, ${snippet});
|
|
|
|
`;
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
if (outro_block) {
|
|
|
|
if (outro_block) {
|
|
|
@ -737,29 +735,29 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
`;
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.outro.addBlock(outro_block);
|
|
|
|
block.builders.outro.add_block(outro_block);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.destroy.addConditional('detach', `if (${outroName}) ${outroName}.end();`);
|
|
|
|
block.builders.destroy.add_conditional('detaching', `if (${outro_name}) ${outro_name}.end();`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addAnimation(block: Block) {
|
|
|
|
add_animation(block: Block) {
|
|
|
|
if (!this.node.animation) return;
|
|
|
|
if (!this.node.animation) return;
|
|
|
|
|
|
|
|
|
|
|
|
const { component } = this.renderer;
|
|
|
|
const { component } = this.renderer;
|
|
|
|
|
|
|
|
|
|
|
|
const rect = block.getUniqueName('rect');
|
|
|
|
const rect = block.get_unique_name('rect');
|
|
|
|
const stop_animation = block.getUniqueName('stop_animation');
|
|
|
|
const stop_animation = block.get_unique_name('stop_animation');
|
|
|
|
|
|
|
|
|
|
|
|
block.addVariable(rect);
|
|
|
|
block.add_variable(rect);
|
|
|
|
block.addVariable(stop_animation, '@noop');
|
|
|
|
block.add_variable(stop_animation, '@noop');
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.measure.addBlock(deindent`
|
|
|
|
block.builders.measure.add_block(deindent`
|
|
|
|
${rect} = ${this.var}.getBoundingClientRect();
|
|
|
|
${rect} = ${this.var}.getBoundingClientRect();
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.fix.addBlock(deindent`
|
|
|
|
block.builders.fix.add_block(deindent`
|
|
|
|
@fix_position(${this.var});
|
|
|
|
@fix_position(${this.var});
|
|
|
|
${stop_animation}();
|
|
|
|
${stop_animation}();
|
|
|
|
`);
|
|
|
|
`);
|
|
|
@ -768,37 +766,37 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
|
|
|
|
|
|
|
|
const name = component.qualify(this.node.animation.name);
|
|
|
|
const name = component.qualify(this.node.animation.name);
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.animate.addBlock(deindent`
|
|
|
|
block.builders.animate.add_block(deindent`
|
|
|
|
${stop_animation}();
|
|
|
|
${stop_animation}();
|
|
|
|
${stop_animation} = @create_animation(${this.var}, ${rect}, ${name}, ${params});
|
|
|
|
${stop_animation} = @create_animation(${this.var}, ${rect}, ${name}, ${params});
|
|
|
|
`);
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addActions(block: Block) {
|
|
|
|
add_actions(block: Block) {
|
|
|
|
addActions(this.renderer.component, block, this.var, this.node.actions);
|
|
|
|
add_actions(this.renderer.component, block, this.var, this.node.actions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addClasses(block: Block) {
|
|
|
|
add_classes(block: Block) {
|
|
|
|
this.node.classes.forEach(classDir => {
|
|
|
|
this.node.classes.forEach(class_directive => {
|
|
|
|
const { expression, name } = classDir;
|
|
|
|
const { expression, name } = class_directive;
|
|
|
|
let snippet, dependencies;
|
|
|
|
let snippet, dependencies;
|
|
|
|
if (expression) {
|
|
|
|
if (expression) {
|
|
|
|
snippet = expression.render(block);
|
|
|
|
snippet = expression.render(block);
|
|
|
|
dependencies = expression.dependencies;
|
|
|
|
dependencies = expression.dependencies;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
snippet = `${quotePropIfNecessary(name)}`;
|
|
|
|
snippet = `${quote_prop_if_necessary(name)}`;
|
|
|
|
dependencies = new Set([name]);
|
|
|
|
dependencies = new Set([name]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const updater = `@toggleClass(${this.var}, "${name}", ${snippet});`;
|
|
|
|
const updater = `@toggle_class(${this.var}, "${name}", ${snippet});`;
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.hydrate.addLine(updater);
|
|
|
|
block.builders.hydrate.add_line(updater);
|
|
|
|
|
|
|
|
|
|
|
|
if ((dependencies && dependencies.size > 0) || this.classDependencies.length) {
|
|
|
|
if ((dependencies && dependencies.size > 0) || this.class_dependencies.length) {
|
|
|
|
const allDeps = this.classDependencies.concat(...dependencies);
|
|
|
|
const all_dependencies = this.class_dependencies.concat(...dependencies);
|
|
|
|
const deps = allDeps.map(dependency => `changed${quotePropIfNecessary(dependency)}`).join(' || ');
|
|
|
|
const deps = all_dependencies.map(dependency => `changed${quote_prop_if_necessary(dependency)}`).join(' || ');
|
|
|
|
const condition = allDeps.length > 1 ? `(${deps})` : deps;
|
|
|
|
const condition = all_dependencies.length > 1 ? `(${deps})` : deps;
|
|
|
|
|
|
|
|
|
|
|
|
block.builders.update.addConditional(
|
|
|
|
block.builders.update.add_conditional(
|
|
|
|
condition,
|
|
|
|
condition,
|
|
|
|
updater
|
|
|
|
updater
|
|
|
|
);
|
|
|
|
);
|
|
|
@ -806,14 +804,14 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getStaticAttributeValue(name: string) {
|
|
|
|
get_static_attribute_value(name: string) {
|
|
|
|
const attribute = this.node.attributes.find(
|
|
|
|
const attribute = this.node.attributes.find(
|
|
|
|
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
|
|
|
|
(attr: Attribute) => attr.type === 'Attribute' && attr.name.toLowerCase() === name
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (!attribute) return null;
|
|
|
|
if (!attribute) return null;
|
|
|
|
|
|
|
|
|
|
|
|
if (attribute.isTrue) return true;
|
|
|
|
if (attribute.is_true) return true;
|
|
|
|
if (attribute.chunks.length === 0) return '';
|
|
|
|
if (attribute.chunks.length === 0) return '';
|
|
|
|
|
|
|
|
|
|
|
|
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
|
|
|
|
if (attribute.chunks.length === 1 && attribute.chunks[0].type === 'Text') {
|
|
|
@ -823,16 +821,16 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
return null;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addCssClass(className = this.component.stylesheet.id) {
|
|
|
|
add_css_class(class_name = this.component.stylesheet.id) {
|
|
|
|
const classAttribute = this.attributes.find(a => a.name === 'class');
|
|
|
|
const class_attribute = this.attributes.find(a => a.name === 'class');
|
|
|
|
if (classAttribute && !classAttribute.isTrue) {
|
|
|
|
if (class_attribute && !class_attribute.is_true) {
|
|
|
|
if (classAttribute.chunks.length === 1 && classAttribute.chunks[0].type === 'Text') {
|
|
|
|
if (class_attribute.chunks.length === 1 && class_attribute.chunks[0].type === 'Text') {
|
|
|
|
(classAttribute.chunks[0] as Text).data += ` ${className}`;
|
|
|
|
(class_attribute.chunks[0] as Text).data += ` ${class_name}`;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
(classAttribute.chunks as Node[]).push(
|
|
|
|
(class_attribute.chunks as Node[]).push(
|
|
|
|
new Text(this.component, this, this.scope, {
|
|
|
|
new Text(this.component, this, this.scope, {
|
|
|
|
type: 'Text',
|
|
|
|
type: 'Text',
|
|
|
|
data: ` ${className}`
|
|
|
|
data: ` ${class_name}`
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -841,7 +839,7 @@ export default class ElementWrapper extends Wrapper {
|
|
|
|
new Attribute(this.component, this, this.scope, {
|
|
|
|
new Attribute(this.component, this, this.scope, {
|
|
|
|
type: 'Attribute',
|
|
|
|
type: 'Attribute',
|
|
|
|
name: 'class',
|
|
|
|
name: 'class',
|
|
|
|
value: [{ type: 'Text', data: className }]
|
|
|
|
value: [{ type: 'Text', data: class_name }]
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|