mirror of https://github.com/sveltejs/svelte
parent
dd0f093582
commit
9b70523529
@ -0,0 +1,296 @@
|
||||
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';
|
||||
|
||||
const readOnlyMediaAttributes = new Set([
|
||||
'duration',
|
||||
'buffered',
|
||||
'seekable',
|
||||
'played'
|
||||
]);
|
||||
|
||||
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);
|
||||
|
||||
let updateCondition: string;
|
||||
|
||||
const { name } = getObject(this.value.node);
|
||||
const { snippet } = this.value;
|
||||
|
||||
// special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>`
|
||||
// and `selected` is an object chosen with a <select>, then when `checked` changes,
|
||||
// we need to tell the component to update all the values `selected` might be
|
||||
// pointing to
|
||||
// TODO should this happen in preprocess?
|
||||
const dependencies = new Set(this.value.dependencies);
|
||||
this.value.dependencies.forEach((prop: string) => {
|
||||
const indirectDependencies = this.compiler.indirectDependencies.get(prop);
|
||||
if (indirectDependencies) {
|
||||
indirectDependencies.forEach(indirectDependency => {
|
||||
dependencies.add(indirectDependency);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// view to model
|
||||
const valueFromDom = getValueFromDom(this.compiler, node, this);
|
||||
const handler = getEventHandler(this, this.compiler, block, name, snippet, dependencies, valueFromDom);
|
||||
|
||||
// model to view
|
||||
let updateDom = getDomUpdater(node, this, snippet);
|
||||
let initialUpdate = updateDom;
|
||||
|
||||
// special cases
|
||||
if (this.name === 'group') {
|
||||
const bindingGroup = getBindingGroup(this.compiler, this.value.node);
|
||||
|
||||
block.builders.hydrate.addLine(
|
||||
`#component._bindingGroups[${bindingGroup}].push(${node.var});`
|
||||
);
|
||||
|
||||
block.builders.destroy.addLine(
|
||||
`#component._bindingGroups[${bindingGroup}].splice(#component._bindingGroups[${bindingGroup}].indexOf(${node.var}), 1);`
|
||||
);
|
||||
}
|
||||
|
||||
if (this.name === 'currentTime' || this.name === 'volume') {
|
||||
updateCondition = `!isNaN(${snippet})`;
|
||||
|
||||
if (this.name === 'currentTime')
|
||||
initialUpdate = null;
|
||||
}
|
||||
|
||||
if (this.name === 'paused') {
|
||||
// this is necessary to prevent audio restarting by itself
|
||||
const last = block.getUniqueName(`${node.var}_is_paused`);
|
||||
block.addVariable(last, 'true');
|
||||
|
||||
updateCondition = `${last} !== (${last} = ${snippet})`;
|
||||
updateDom = `${node.var}[${last} ? "pause" : "play"]();`;
|
||||
initialUpdate = null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
object: name,
|
||||
handler,
|
||||
updateDom,
|
||||
initialUpdate,
|
||||
needsLock: !isReadOnly && needsLock,
|
||||
updateCondition,
|
||||
isReadOnlyMediaAttribute: this.isReadOnlyMediaAttribute()
|
||||
};
|
||||
}
|
||||
|
||||
isReadOnlyMediaAttribute() {
|
||||
return readOnlyMediaAttributes.has(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
function getDomUpdater(
|
||||
node: Element,
|
||||
binding: Binding,
|
||||
snippet: string
|
||||
) {
|
||||
if (binding.isReadOnlyMediaAttribute()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (node.name === 'select') {
|
||||
return node.getStaticAttributeValue('multiple') === true ?
|
||||
`@selectOptions(${node.var}, ${snippet})` :
|
||||
`@selectOption(${node.var}, ${snippet})`;
|
||||
}
|
||||
|
||||
if (binding.name === 'group') {
|
||||
const type = node.getStaticAttributeValue('type');
|
||||
|
||||
const condition = type === 'checkbox'
|
||||
? `~${snippet}.indexOf(${node.var}.__value)`
|
||||
: `${node.var}.__value === ${snippet}`;
|
||||
|
||||
return `${node.var}.checked = ${condition};`
|
||||
}
|
||||
|
||||
return `${node.var}.${binding.name} = ${snippet};`;
|
||||
}
|
||||
|
||||
function getBindingGroup(compiler: Compiler, value: Node) {
|
||||
const { parts } = flattenReference(value); // TODO handle cases involving computed member expressions
|
||||
const keypath = parts.join('.');
|
||||
|
||||
// TODO handle contextual bindings — `keypath` should include unique ID of
|
||||
// each block that provides context
|
||||
let index = compiler.bindingGroups.indexOf(keypath);
|
||||
if (index === -1) {
|
||||
index = compiler.bindingGroups.length;
|
||||
compiler.bindingGroups.push(keypath);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function getEventHandler(
|
||||
binding: Binding,
|
||||
compiler: Compiler,
|
||||
block: Block,
|
||||
name: string,
|
||||
snippet: string,
|
||||
dependencies: string[],
|
||||
value: string,
|
||||
isContextual: boolean
|
||||
) {
|
||||
const storeDependencies = [...dependencies].filter(prop => prop[0] === '$').map(prop => prop.slice(1));
|
||||
dependencies = [...dependencies].filter(prop => prop[0] !== '$');
|
||||
|
||||
if (binding.isContextual) {
|
||||
const tail = binding.value.node.type === 'MemberExpression'
|
||||
? getTailSnippet(binding.value.node)
|
||||
: '';
|
||||
|
||||
const list = `ctx.${block.listNames.get(name)}`;
|
||||
const index = `ctx.${block.indexNames.get(name)}`;
|
||||
|
||||
return {
|
||||
usesContext: true,
|
||||
usesState: true,
|
||||
usesStore: storeDependencies.length > 0,
|
||||
mutation: `${list}[${index}]${tail} = ${value};`,
|
||||
props: dependencies.map(prop => `${prop}: ctx.${prop}`),
|
||||
storeProps: storeDependencies.map(prop => `${prop}: $.${prop}`)
|
||||
};
|
||||
}
|
||||
|
||||
if (binding.value.node.type === 'MemberExpression') {
|
||||
// This is a little confusing, and should probably be tidied up
|
||||
// at some point. It addresses a tricky bug (#893), wherein
|
||||
// Svelte tries to `set()` a computed property, which throws an
|
||||
// error in dev mode. a) it's possible that we should be
|
||||
// replacing computations with *their* dependencies, and b)
|
||||
// we should probably populate `compiler.target.readonly` sooner so
|
||||
// that we don't have to do the `.some()` here
|
||||
dependencies = dependencies.filter(prop => !compiler.computations.some(computation => computation.key === prop));
|
||||
|
||||
return {
|
||||
usesContext: false,
|
||||
usesState: true,
|
||||
usesStore: storeDependencies.length > 0,
|
||||
mutation: `${snippet} = ${value}`,
|
||||
props: dependencies.map((prop: string) => `${prop}: ctx.${prop}`),
|
||||
storeProps: storeDependencies.map(prop => `${prop}: $.${prop}`)
|
||||
};
|
||||
}
|
||||
|
||||
let props;
|
||||
let storeProps;
|
||||
|
||||
if (name[0] === '$') {
|
||||
props = [];
|
||||
storeProps = [`${name.slice(1)}: ${value}`];
|
||||
} else {
|
||||
props = [`${name}: ${value}`];
|
||||
storeProps = [];
|
||||
}
|
||||
|
||||
return {
|
||||
usesContext: false,
|
||||
usesState: false,
|
||||
usesStore: false,
|
||||
mutation: null,
|
||||
props,
|
||||
storeProps
|
||||
};
|
||||
}
|
||||
|
||||
function getValueFromDom(
|
||||
compiler: Compiler,
|
||||
node: Element,
|
||||
binding: Node
|
||||
) {
|
||||
// <select bind:value='selected>
|
||||
if (node.name === 'select') {
|
||||
return node.getStaticAttributeValue('multiple') === true ?
|
||||
`@selectMultipleValue(${node.var})` :
|
||||
`@selectValue(${node.var})`;
|
||||
}
|
||||
|
||||
const type = node.getStaticAttributeValue('type');
|
||||
|
||||
// <input type='checkbox' bind:group='foo'>
|
||||
if (binding.name === 'group') {
|
||||
const bindingGroup = getBindingGroup(compiler, binding.value.node);
|
||||
if (type === 'checkbox') {
|
||||
return `@getBindingGroupValue(#component._bindingGroups[${bindingGroup}])`;
|
||||
}
|
||||
|
||||
return `${node.var}.__value`;
|
||||
}
|
||||
|
||||
// <input type='range|number' bind: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;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import Node from './shared/Node';
|
||||
import Compiler from '../Compiler';
|
||||
import mapChildren from './shared/mapChildren';
|
||||
import Block from '../dom/Block';
|
||||
import TemplateScope from './shared/TemplateScope';
|
||||
|
||||
export default class Fragment extends Node {
|
||||
block: Block;
|
||||
children: Node[];
|
||||
scope: TemplateScope;
|
||||
|
||||
constructor(compiler: Compiler, info: any) {
|
||||
const scope = new TemplateScope();
|
||||
super(compiler, null, scope, info);
|
||||
|
||||
this.scope = scope;
|
||||
this.children = mapChildren(compiler, this, scope, info.children);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.block = new Block({
|
||||
compiler: this.compiler,
|
||||
name: '@create_main_fragment',
|
||||
key: null,
|
||||
|
||||
indexNames: new Map(),
|
||||
listNames: new Map(),
|
||||
|
||||
dependencies: new Set(),
|
||||
});
|
||||
|
||||
this.compiler.target.blocks.push(this.block);
|
||||
this.initChildren(this.block, true, null);
|
||||
|
||||
this.block.hasUpdateMethod = true;
|
||||
}
|
||||
|
||||
build() {
|
||||
this.init();
|
||||
|
||||
this.children.forEach(child => {
|
||||
child.build(this.block, null, 'nodes');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,505 @@
|
||||
import deindent from '../../utils/deindent';
|
||||
import Node from './shared/Node';
|
||||
import ElseBlock from './ElseBlock';
|
||||
import Compiler from '../Compiler';
|
||||
import Block from '../dom/Block';
|
||||
import createDebuggingComment from '../../utils/createDebuggingComment';
|
||||
import Expression from './shared/Expression';
|
||||
import mapChildren from './shared/mapChildren';
|
||||
|
||||
function isElseIf(node: ElseBlock) {
|
||||
return (
|
||||
node && node.children.length === 1 && node.children[0].type === 'IfBlock'
|
||||
);
|
||||
}
|
||||
|
||||
function isElseBranch(branch) {
|
||||
return branch.block && !branch.condition;
|
||||
}
|
||||
|
||||
export default class IfBlock extends Node {
|
||||
type: 'IfBlock';
|
||||
expression: Expression;
|
||||
children: any[];
|
||||
else: ElseBlock;
|
||||
|
||||
block: Block;
|
||||
|
||||
constructor(compiler, parent, scope, info) {
|
||||
super(compiler, parent, scope, info);
|
||||
|
||||
this.expression = new Expression(compiler, this, scope, info.expression);
|
||||
this.children = mapChildren(compiler, this, scope, info.children);
|
||||
|
||||
this.else = info.else
|
||||
? new ElseBlock(compiler, this, scope, info.else)
|
||||
: null;
|
||||
}
|
||||
|
||||
init(
|
||||
block: Block,
|
||||
stripWhitespace: boolean,
|
||||
nextSibling: Node
|
||||
) {
|
||||
const { compiler } = this;
|
||||
|
||||
this.cannotUseInnerHTML();
|
||||
|
||||
const blocks: Block[] = [];
|
||||
let dynamic = false;
|
||||
let hasIntros = false;
|
||||
let hasOutros = false;
|
||||
|
||||
function attachBlocks(node: IfBlock) {
|
||||
node.var = block.getUniqueName(`if_block`);
|
||||
|
||||
block.addDependencies(node.expression.dependencies);
|
||||
|
||||
node.block = block.child({
|
||||
comment: createDebuggingComment(node, compiler),
|
||||
name: compiler.getUniqueName(`create_if_block`),
|
||||
});
|
||||
|
||||
blocks.push(node.block);
|
||||
node.initChildren(node.block, stripWhitespace, nextSibling);
|
||||
|
||||
if (node.block.dependencies.size > 0) {
|
||||
dynamic = true;
|
||||
block.addDependencies(node.block.dependencies);
|
||||
}
|
||||
|
||||
if (node.block.hasIntroMethod) hasIntros = true;
|
||||
if (node.block.hasOutroMethod) hasOutros = true;
|
||||
|
||||
if (isElseIf(node.else)) {
|
||||
attachBlocks(node.else.children[0]);
|
||||
} else if (node.else) {
|
||||
node.else.block = block.child({
|
||||
comment: createDebuggingComment(node.else, compiler),
|
||||
name: compiler.getUniqueName(`create_if_block`),
|
||||
});
|
||||
|
||||
blocks.push(node.else.block);
|
||||
node.else.initChildren(
|
||||
node.else.block,
|
||||
stripWhitespace,
|
||||
nextSibling
|
||||
);
|
||||
|
||||
if (node.else.block.dependencies.size > 0) {
|
||||
dynamic = true;
|
||||
block.addDependencies(node.else.block.dependencies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attachBlocks(this);
|
||||
|
||||
blocks.forEach(block => {
|
||||
block.hasUpdateMethod = dynamic;
|
||||
block.hasIntroMethod = hasIntros;
|
||||
block.hasOutroMethod = hasOutros;
|
||||
});
|
||||
|
||||
compiler.target.blocks.push(...blocks);
|
||||
}
|
||||
|
||||
build(
|
||||
block: Block,
|
||||
parentNode: string,
|
||||
parentNodes: string
|
||||
) {
|
||||
const name = this.var;
|
||||
|
||||
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
|
||||
const anchor = needsAnchor
|
||||
? block.getUniqueName(`${name}_anchor`)
|
||||
: (this.next && this.next.var) || 'null';
|
||||
|
||||
const branches = this.getBranches(block, parentNode, parentNodes, this);
|
||||
|
||||
const hasElse = isElseBranch(branches[branches.length - 1]);
|
||||
const if_name = hasElse ? '' : `if (${name}) `;
|
||||
|
||||
const dynamic = branches[0].hasUpdateMethod; // can use [0] as proxy for all, since they necessarily have the same value
|
||||
const hasOutros = branches[0].hasOutroMethod;
|
||||
|
||||
const vars = { name, anchor, if_name, hasElse };
|
||||
|
||||
if (this.else) {
|
||||
if (hasOutros) {
|
||||
this.buildCompoundWithOutros(block, parentNode, parentNodes, branches, dynamic, vars);
|
||||
} else {
|
||||
this.buildCompound(block, parentNode, parentNodes, branches, dynamic, vars);
|
||||
}
|
||||
} else {
|
||||
this.buildSimple(block, parentNode, parentNodes, branches[0], dynamic, vars);
|
||||
}
|
||||
|
||||
block.builders.create.addLine(`${if_name}${name}.c();`);
|
||||
|
||||
if (parentNodes) {
|
||||
block.builders.claim.addLine(
|
||||
`${if_name}${name}.l(${parentNodes});`
|
||||
);
|
||||
}
|
||||
|
||||
if (needsAnchor) {
|
||||
block.addElement(
|
||||
anchor,
|
||||
`@createComment()`,
|
||||
parentNodes && `@createComment()`,
|
||||
parentNode
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
buildCompound(
|
||||
block: Block,
|
||||
parentNode: string,
|
||||
parentNodes: string,
|
||||
branches,
|
||||
dynamic,
|
||||
{ name, anchor, hasElse, if_name }
|
||||
) {
|
||||
const select_block_type = this.compiler.getUniqueName(`select_block_type`);
|
||||
const current_block_type = block.getUniqueName(`current_block_type`);
|
||||
const current_block_type_and = hasElse ? '' : `${current_block_type} && `;
|
||||
|
||||
block.builders.init.addBlock(deindent`
|
||||
function ${select_block_type}(ctx) {
|
||||
${branches
|
||||
.map(({ condition, block }) => `${condition ? `if (${condition}) ` : ''}return ${block};`)
|
||||
.join('\n')}
|
||||
}
|
||||
`);
|
||||
|
||||
block.builders.init.addBlock(deindent`
|
||||
var ${current_block_type} = ${select_block_type}(ctx);
|
||||
var ${name} = ${current_block_type_and}${current_block_type}(#component, ctx);
|
||||
`);
|
||||
|
||||
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
|
||||
|
||||
const initialMountNode = parentNode || '#target';
|
||||
const anchorNode = parentNode ? 'null' : 'anchor';
|
||||
block.builders.mount.addLine(
|
||||
`${if_name}${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
|
||||
);
|
||||
|
||||
const updateMountNode = this.getUpdateMountNode(anchor);
|
||||
|
||||
const changeBlock = deindent`
|
||||
${hasElse
|
||||
? deindent`
|
||||
${name}.u();
|
||||
${name}.d();
|
||||
`
|
||||
: deindent`
|
||||
if (${name}) {
|
||||
${name}.u();
|
||||
${name}.d();
|
||||
}`}
|
||||
${name} = ${current_block_type_and}${current_block_type}(#component, ctx);
|
||||
${if_name}${name}.c();
|
||||
${if_name}${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
|
||||
`;
|
||||
|
||||
if (dynamic) {
|
||||
block.builders.update.addBlock(deindent`
|
||||
if (${current_block_type} === (${current_block_type} = ${select_block_type}(ctx)) && ${name}) {
|
||||
${name}.p(changed, ctx);
|
||||
} else {
|
||||
${changeBlock}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.builders.update.addBlock(deindent`
|
||||
if (${current_block_type} !== (${current_block_type} = ${select_block_type}(ctx))) {
|
||||
${changeBlock}
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
block.builders.unmount.addLine(`${if_name}${name}.u();`);
|
||||
|
||||
block.builders.destroy.addLine(`${if_name}${name}.d();`);
|
||||
}
|
||||
|
||||
// if any of the siblings have outros, we need to keep references to the blocks
|
||||
// (TODO does this only apply to bidi transitions?)
|
||||
buildCompoundWithOutros(
|
||||
block: Block,
|
||||
parentNode: string,
|
||||
parentNodes: string,
|
||||
branches,
|
||||
dynamic,
|
||||
{ name, anchor, hasElse }
|
||||
) {
|
||||
const select_block_type = block.getUniqueName(`select_block_type`);
|
||||
const current_block_type_index = block.getUniqueName(`current_block_type_index`);
|
||||
const previous_block_index = block.getUniqueName(`previous_block_index`);
|
||||
const if_block_creators = block.getUniqueName(`if_block_creators`);
|
||||
const if_blocks = block.getUniqueName(`if_blocks`);
|
||||
|
||||
const if_current_block_type_index = hasElse
|
||||
? ''
|
||||
: `if (~${current_block_type_index}) `;
|
||||
|
||||
block.addVariable(current_block_type_index);
|
||||
block.addVariable(name);
|
||||
|
||||
block.builders.init.addBlock(deindent`
|
||||
var ${if_block_creators} = [
|
||||
${branches.map(branch => branch.block).join(',\n')}
|
||||
];
|
||||
|
||||
var ${if_blocks} = [];
|
||||
|
||||
function ${select_block_type}(ctx) {
|
||||
${branches
|
||||
.map(({ condition, block }, i) => `${condition ? `if (${condition}) ` : ''}return ${block ? i : -1};`)
|
||||
.join('\n')}
|
||||
}
|
||||
`);
|
||||
|
||||
if (hasElse) {
|
||||
block.builders.init.addBlock(deindent`
|
||||
${current_block_type_index} = ${select_block_type}(ctx);
|
||||
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, ctx);
|
||||
`);
|
||||
} else {
|
||||
block.builders.init.addBlock(deindent`
|
||||
if (~(${current_block_type_index} = ${select_block_type}(ctx))) {
|
||||
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, ctx);
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const mountOrIntro = branches[0].hasIntroMethod ? 'i' : 'm';
|
||||
const initialMountNode = parentNode || '#target';
|
||||
const anchorNode = parentNode ? 'null' : 'anchor';
|
||||
|
||||
block.builders.mount.addLine(
|
||||
`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].${mountOrIntro}(${initialMountNode}, ${anchorNode});`
|
||||
);
|
||||
|
||||
const updateMountNode = this.getUpdateMountNode(anchor);
|
||||
|
||||
const destroyOldBlock = deindent`
|
||||
${name}.o(function() {
|
||||
${if_blocks}[ ${previous_block_index} ].u();
|
||||
${if_blocks}[ ${previous_block_index} ].d();
|
||||
${if_blocks}[ ${previous_block_index} ] = null;
|
||||
});
|
||||
`;
|
||||
|
||||
const createNewBlock = deindent`
|
||||
${name} = ${if_blocks}[${current_block_type_index}];
|
||||
if (!${name}) {
|
||||
${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#component, ctx);
|
||||
${name}.c();
|
||||
}
|
||||
${name}.${mountOrIntro}(${updateMountNode}, ${anchor});
|
||||
`;
|
||||
|
||||
const changeBlock = hasElse
|
||||
? deindent`
|
||||
${destroyOldBlock}
|
||||
|
||||
${createNewBlock}
|
||||
`
|
||||
: deindent`
|
||||
if (${name}) {
|
||||
${destroyOldBlock}
|
||||
}
|
||||
|
||||
if (~${current_block_type_index}) {
|
||||
${createNewBlock}
|
||||
} else {
|
||||
${name} = null;
|
||||
}
|
||||
`;
|
||||
|
||||
if (dynamic) {
|
||||
block.builders.update.addBlock(deindent`
|
||||
var ${previous_block_index} = ${current_block_type_index};
|
||||
${current_block_type_index} = ${select_block_type}(ctx);
|
||||
if (${current_block_type_index} === ${previous_block_index}) {
|
||||
${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, ctx);
|
||||
} else {
|
||||
${changeBlock}
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.builders.update.addBlock(deindent`
|
||||
var ${previous_block_index} = ${current_block_type_index};
|
||||
${current_block_type_index} = ${select_block_type}(ctx);
|
||||
if (${current_block_type_index} !== ${previous_block_index}) {
|
||||
${changeBlock}
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
block.builders.destroy.addLine(deindent`
|
||||
${if_current_block_type_index}{
|
||||
${if_blocks}[${current_block_type_index}].u();
|
||||
${if_blocks}[${current_block_type_index}].d();
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
buildSimple(
|
||||
block: Block,
|
||||
parentNode: string,
|
||||
parentNodes: string,
|
||||
branch,
|
||||
dynamic,
|
||||
{ name, anchor, if_name }
|
||||
) {
|
||||
block.builders.init.addBlock(deindent`
|
||||
var ${name} = (${branch.condition}) && ${branch.block}(#component, ctx);
|
||||
`);
|
||||
|
||||
const mountOrIntro = branch.hasIntroMethod ? 'i' : 'm';
|
||||
const initialMountNode = parentNode || '#target';
|
||||
const anchorNode = parentNode ? 'null' : 'anchor';
|
||||
|
||||
block.builders.mount.addLine(
|
||||
`if (${name}) ${name}.${mountOrIntro}(${initialMountNode}, ${anchorNode});`
|
||||
);
|
||||
|
||||
const updateMountNode = this.getUpdateMountNode(anchor);
|
||||
|
||||
const enter = dynamic
|
||||
? branch.hasIntroMethod
|
||||
? deindent`
|
||||
if (${name}) {
|
||||
${name}.p(changed, ctx);
|
||||
} else {
|
||||
${name} = ${branch.block}(#component, ctx);
|
||||
if (${name}) ${name}.c();
|
||||
}
|
||||
|
||||
${name}.i(${updateMountNode}, ${anchor});
|
||||
`
|
||||
: deindent`
|
||||
if (${name}) {
|
||||
${name}.p(changed, ctx);
|
||||
} else {
|
||||
${name} = ${branch.block}(#component, ctx);
|
||||
${name}.c();
|
||||
${name}.m(${updateMountNode}, ${anchor});
|
||||
}
|
||||
`
|
||||
: branch.hasIntroMethod
|
||||
? deindent`
|
||||
if (!${name}) {
|
||||
${name} = ${branch.block}(#component, ctx);
|
||||
${name}.c();
|
||||
}
|
||||
${name}.i(${updateMountNode}, ${anchor});
|
||||
`
|
||||
: deindent`
|
||||
if (!${name}) {
|
||||
${name} = ${branch.block}(#component, ctx);
|
||||
${name}.c();
|
||||
${name}.m(${updateMountNode}, ${anchor});
|
||||
}
|
||||
`;
|
||||
|
||||
// no `p()` here — we don't want to update outroing nodes,
|
||||
// as that will typically result in glitching
|
||||
const exit = branch.hasOutroMethod
|
||||
? deindent`
|
||||
${name}.o(function() {
|
||||
${name}.u();
|
||||
${name}.d();
|
||||
${name} = null;
|
||||
});
|
||||
`
|
||||
: deindent`
|
||||
${name}.u();
|
||||
${name}.d();
|
||||
${name} = null;
|
||||
`;
|
||||
|
||||
block.builders.update.addBlock(deindent`
|
||||
if (${branch.condition}) {
|
||||
${enter}
|
||||
} else if (${name}) {
|
||||
${exit}
|
||||
}
|
||||
`);
|
||||
|
||||
block.builders.unmount.addLine(`${if_name}${name}.u();`);
|
||||
|
||||
block.builders.destroy.addLine(`${if_name}${name}.d();`);
|
||||
}
|
||||
|
||||
getBranches(
|
||||
block: Block,
|
||||
parentNode: string,
|
||||
parentNodes: string,
|
||||
node: IfBlock
|
||||
) {
|
||||
const branches = [
|
||||
{
|
||||
condition: node.expression.snippet,
|
||||
block: node.block.name,
|
||||
hasUpdateMethod: node.block.hasUpdateMethod,
|
||||
hasIntroMethod: node.block.hasIntroMethod,
|
||||
hasOutroMethod: node.block.hasOutroMethod,
|
||||
},
|
||||
];
|
||||
|
||||
this.visitChildren(block, node);
|
||||
|
||||
if (isElseIf(node.else)) {
|
||||
branches.push(
|
||||
...this.getBranches(block, parentNode, parentNodes, node.else.children[0])
|
||||
);
|
||||
} else {
|
||||
branches.push({
|
||||
condition: null,
|
||||
block: node.else ? node.else.block.name : null,
|
||||
hasUpdateMethod: node.else ? node.else.block.hasUpdateMethod : false,
|
||||
hasIntroMethod: node.else ? node.else.block.hasIntroMethod : false,
|
||||
hasOutroMethod: node.else ? node.else.block.hasOutroMethod : false,
|
||||
});
|
||||
|
||||
if (node.else) {
|
||||
this.visitChildren(block, node.else);
|
||||
}
|
||||
}
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
ssr() {
|
||||
const { compiler } = this;
|
||||
const { snippet } = this.expression;
|
||||
|
||||
compiler.target.append('${ ' + snippet + ' ? `');
|
||||
|
||||
this.children.forEach((child: Node) => {
|
||||
child.ssr();
|
||||
});
|
||||
|
||||
compiler.target.append('` : `');
|
||||
|
||||
if (this.else) {
|
||||
this.else.children.forEach((child: Node) => {
|
||||
child.ssr();
|
||||
});
|
||||
}
|
||||
|
||||
compiler.target.append('` }');
|
||||
}
|
||||
|
||||
visitChildren(block: Block, node: Node) {
|
||||
node.children.forEach((child: Node) => {
|
||||
child.build(node.block, null, 'nodes');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
import Compiler from './../../Compiler';
|
||||
import Block from '../../dom/Block';
|
||||
import { trimStart, trimEnd } from '../../../utils/trim';
|
||||
|
||||
export default class Node {
|
||||
readonly start: number;
|
||||
readonly end: number;
|
||||
readonly compiler: Compiler;
|
||||
readonly parent: Node;
|
||||
readonly type: string;
|
||||
|
||||
prev?: Node;
|
||||
next?: Node;
|
||||
|
||||
canUseInnerHTML: boolean;
|
||||
var: string;
|
||||
|
||||
constructor(compiler: Compiler, parent, scope, info: any) {
|
||||
this.start = info.start;
|
||||
this.end = info.end;
|
||||
this.type = info.type;
|
||||
|
||||
// this makes properties non-enumerable, which makes logging
|
||||
// bearable. might have a performance cost. TODO remove in prod?
|
||||
Object.defineProperties(this, {
|
||||
compiler: {
|
||||
value: compiler
|
||||
},
|
||||
parent: {
|
||||
value: parent
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cannotUseInnerHTML() {
|
||||
if (this.canUseInnerHTML !== false) {
|
||||
this.canUseInnerHTML = false;
|
||||
if (this.parent) this.parent.cannotUseInnerHTML();
|
||||
}
|
||||
}
|
||||
|
||||
init(
|
||||
block: Block,
|
||||
stripWhitespace: boolean,
|
||||
nextSibling: Node
|
||||
) {
|
||||
// implemented by subclasses
|
||||
}
|
||||
|
||||
initChildren(
|
||||
block: Block,
|
||||
stripWhitespace: boolean,
|
||||
nextSibling: Node
|
||||
) {
|
||||
// glue text nodes together
|
||||
const cleaned: Node[] = [];
|
||||
let lastChild: Node;
|
||||
|
||||
let windowComponent;
|
||||
|
||||
this.children.forEach((child: Node) => {
|
||||
if (child.type === 'Comment') return;
|
||||
|
||||
// special case — this is an easy way to remove whitespace surrounding
|
||||
// <svelte:window/>. lil hacky but it works
|
||||
if (child.type === 'Window') {
|
||||
windowComponent = child;
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.type === 'Text' && lastChild && lastChild.type === 'Text') {
|
||||
lastChild.data += child.data;
|
||||
lastChild.end = child.end;
|
||||
} else {
|
||||
if (child.type === 'Text' && stripWhitespace && cleaned.length === 0) {
|
||||
child.data = trimStart(child.data);
|
||||
if (child.data) cleaned.push(child);
|
||||
} else {
|
||||
cleaned.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
lastChild = child;
|
||||
});
|
||||
|
||||
lastChild = null;
|
||||
|
||||
cleaned.forEach((child: Node, i: number) => {
|
||||
child.canUseInnerHTML = !this.compiler.options.hydratable;
|
||||
|
||||
child.init(block, stripWhitespace, cleaned[i + 1] || nextSibling);
|
||||
|
||||
if (child.shouldSkip) return;
|
||||
|
||||
if (lastChild) lastChild.next = child;
|
||||
child.prev = lastChild;
|
||||
|
||||
lastChild = child;
|
||||
});
|
||||
|
||||
// We want to remove trailing whitespace inside an element/component/block,
|
||||
// *unless* there is no whitespace between this node and its next sibling
|
||||
if (stripWhitespace && lastChild && lastChild.type === 'Text') {
|
||||
const shouldTrim = (
|
||||
nextSibling ? (nextSibling.type === 'Text' && /^\s/.test(nextSibling.data)) : !this.hasAncestor('EachBlock')
|
||||
);
|
||||
|
||||
if (shouldTrim) {
|
||||
lastChild.data = trimEnd(lastChild.data);
|
||||
if (!lastChild.data) {
|
||||
cleaned.pop();
|
||||
lastChild = cleaned[cleaned.length - 1];
|
||||
lastChild.next = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.children = cleaned;
|
||||
if (windowComponent) cleaned.unshift(windowComponent);
|
||||
}
|
||||
|
||||
build(
|
||||
block: Block,
|
||||
parentNode: string,
|
||||
parentNodes: string
|
||||
) {
|
||||
// implemented by subclasses
|
||||
}
|
||||
|
||||
isDomNode() {
|
||||
return this.type === 'Element' || this.type === 'Text' || this.type === 'MustacheTag';
|
||||
}
|
||||
|
||||
hasAncestor(type: string) {
|
||||
return this.parent ?
|
||||
this.parent.type === type || this.parent.hasAncestor(type) :
|
||||
false;
|
||||
}
|
||||
|
||||
findNearest(selector: RegExp) {
|
||||
if (selector.test(this.type)) return this;
|
||||
if (this.parent) return this.parent.findNearest(selector);
|
||||
}
|
||||
|
||||
getOrCreateAnchor(block: Block, parentNode: string, parentNodes: string) {
|
||||
// TODO use this in EachBlock and IfBlock — tricky because
|
||||
// children need to be created first
|
||||
const needsAnchor = this.next ? !this.next.isDomNode() : !parentNode || !this.parent.isDomNode();
|
||||
const anchor = needsAnchor
|
||||
? block.getUniqueName(`${this.var}_anchor`)
|
||||
: (this.next && this.next.var) || 'null';
|
||||
|
||||
if (needsAnchor) {
|
||||
block.addElement(
|
||||
anchor,
|
||||
`@createComment()`,
|
||||
parentNodes && `@createComment()`,
|
||||
parentNode
|
||||
);
|
||||
}
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
getUpdateMountNode(anchor: string) {
|
||||
return this.parent.isDomNode() ? this.parent.var : `${anchor}.parentNode`;
|
||||
}
|
||||
|
||||
remount(name: string) {
|
||||
return `${this.var}.m(${name}._slotted.default, null);`;
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
import deindent from '../../utils/deindent';
|
||||
import Compiler from '../Compiler';
|
||||
import Stats from '../../Stats';
|
||||
import Stylesheet from '../../css/Stylesheet';
|
||||
import { removeNode, removeObjectKey } from '../../utils/removeNode';
|
||||
import getName from '../../utils/getName';
|
||||
import globalWhitelist from '../../utils/globalWhitelist';
|
||||
import { Ast, Node, CompileOptions } from '../../interfaces';
|
||||
import { AppendTarget } from '../../interfaces';
|
||||
import { stringify } from '../../utils/stringify';
|
||||
|
||||
export class SsrTarget {
|
||||
bindings: string[];
|
||||
renderCode: string;
|
||||
appendTargets: AppendTarget[];
|
||||
|
||||
constructor() {
|
||||
this.bindings = [];
|
||||
this.renderCode = '';
|
||||
this.appendTargets = [];
|
||||
}
|
||||
|
||||
append(code: string) {
|
||||
if (this.appendTargets.length) {
|
||||
const appendTarget = this.appendTargets[this.appendTargets.length - 1];
|
||||
const slotName = appendTarget.slotStack[appendTarget.slotStack.length - 1];
|
||||
appendTarget.slots[slotName] += code;
|
||||
} else {
|
||||
this.renderCode += code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function ssr(
|
||||
ast: Ast,
|
||||
source: string,
|
||||
stylesheet: Stylesheet,
|
||||
options: CompileOptions,
|
||||
stats: Stats
|
||||
) {
|
||||
const format = options.format || 'cjs';
|
||||
|
||||
const target = new SsrTarget();
|
||||
const compiler = new Compiler(ast, source, options.name || 'SvelteComponent', stylesheet, options, stats, false, target);
|
||||
|
||||
const { computations, name, templateProperties } = compiler;
|
||||
|
||||
// create main render() function
|
||||
trim(compiler.fragment.children).forEach((node: Node) => {
|
||||
node.ssr();
|
||||
});
|
||||
|
||||
const css = compiler.customElement ?
|
||||
{ code: null, map: null } :
|
||||
compiler.stylesheet.render(options.filename, true);
|
||||
|
||||
// generate initial state object
|
||||
const expectedProperties = Array.from(compiler.expectedProperties);
|
||||
const globals = expectedProperties.filter(prop => globalWhitelist.has(prop));
|
||||
const storeProps = expectedProperties.filter(prop => prop[0] === '$');
|
||||
|
||||
const initialState = [];
|
||||
if (globals.length > 0) {
|
||||
initialState.push(`{ ${globals.map(prop => `${prop} : ${prop}`).join(', ')} }`);
|
||||
}
|
||||
|
||||
if (storeProps.length > 0) {
|
||||
const initialize = `_init([${storeProps.map(prop => `"${prop.slice(1)}"`)}])`
|
||||
initialState.push(`options.store.${initialize}`);
|
||||
}
|
||||
|
||||
if (templateProperties.data) {
|
||||
initialState.push(`%data()`);
|
||||
} else if (globals.length === 0 && storeProps.length === 0) {
|
||||
initialState.push('{}');
|
||||
}
|
||||
|
||||
initialState.push('ctx');
|
||||
|
||||
const helpers = new Set();
|
||||
|
||||
// TODO concatenate CSS maps
|
||||
const result = deindent`
|
||||
${compiler.javascript}
|
||||
|
||||
var ${name} = {};
|
||||
|
||||
${options.filename && `${name}.filename = ${stringify(options.filename)}`};
|
||||
|
||||
${name}.data = function() {
|
||||
return ${templateProperties.data ? `%data()` : `{}`};
|
||||
};
|
||||
|
||||
${name}.render = function(state, options = {}) {
|
||||
var components = new Set();
|
||||
|
||||
function addComponent(component) {
|
||||
components.add(component);
|
||||
}
|
||||
|
||||
var result = { head: '', addComponent };
|
||||
var html = ${name}._render(result, state, options);
|
||||
|
||||
var cssCode = Array.from(components).map(c => c.css && c.css.code).filter(Boolean).join('\\n');
|
||||
|
||||
return {
|
||||
html,
|
||||
head: result.head,
|
||||
css: { code: cssCode, map: null },
|
||||
toString() {
|
||||
return html;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
${name}._render = function(__result, ctx, options) {
|
||||
${templateProperties.store && `options.store = %store();`}
|
||||
__result.addComponent(${name});
|
||||
|
||||
ctx = Object.assign(${initialState.join(', ')});
|
||||
|
||||
${computations.map(
|
||||
({ key, deps }) =>
|
||||
`ctx.${key} = %computed-${key}(ctx);`
|
||||
)}
|
||||
|
||||
${target.bindings.length &&
|
||||
deindent`
|
||||
var settled = false;
|
||||
var tmp;
|
||||
|
||||
while (!settled) {
|
||||
settled = true;
|
||||
|
||||
${target.bindings.join('\n\n')}
|
||||
}
|
||||
`}
|
||||
|
||||
return \`${target.renderCode}\`;
|
||||
};
|
||||
|
||||
${name}.css = {
|
||||
code: ${css.code ? stringify(css.code) : `''`},
|
||||
map: ${css.map ? stringify(css.map.toString()) : 'null'}
|
||||
};
|
||||
|
||||
var warned = false;
|
||||
|
||||
${templateProperties.preload && `${name}.preload = %preload;`}
|
||||
`;
|
||||
|
||||
return compiler.generate(result, options, { name, format });
|
||||
}
|
||||
|
||||
function trim(nodes) {
|
||||
let start = 0;
|
||||
for (; start < nodes.length; start += 1) {
|
||||
const node = nodes[start];
|
||||
if (node.type !== 'Text') break;
|
||||
|
||||
node.data = node.data.replace(/^\s+/, '');
|
||||
if (node.data) break;
|
||||
}
|
||||
|
||||
let end = nodes.length;
|
||||
for (; end > start; end -= 1) {
|
||||
const node = nodes[end - 1];
|
||||
if (node.type !== 'Text') break;
|
||||
|
||||
node.data = node.data.replace(/\s+$/, '');
|
||||
if (node.data) break;
|
||||
}
|
||||
|
||||
return nodes.slice(start, end);
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
// this file is auto-generated, do not edit it
|
||||
const shared: Record<string, string> = {
|
||||
"appendNode": "function appendNode(node, target) {\n\ttarget.appendChild(node);\n}",
|
||||
"insertNode": "function insertNode(node, target, anchor) {\n\ttarget.insertBefore(node, anchor);\n}",
|
||||
"detachNode": "function detachNode(node) {\n\tnode.parentNode.removeChild(node);\n}",
|
||||
"detachBetween": "function detachBetween(before, after) {\n\twhile (before.nextSibling && before.nextSibling !== after) {\n\t\tbefore.parentNode.removeChild(before.nextSibling);\n\t}\n}",
|
||||
"detachBefore": "function detachBefore(after) {\n\twhile (after.previousSibling) {\n\t\tafter.parentNode.removeChild(after.previousSibling);\n\t}\n}",
|
||||
"detachAfter": "function detachAfter(before) {\n\twhile (before.nextSibling) {\n\t\tbefore.parentNode.removeChild(before.nextSibling);\n\t}\n}",
|
||||
"reinsertBetween": "function reinsertBetween(before, after, target) {\n\twhile (before.nextSibling && before.nextSibling !== after) {\n\t\ttarget.appendChild(before.parentNode.removeChild(before.nextSibling));\n\t}\n}",
|
||||
"reinsertChildren": "function reinsertChildren(parent, target) {\n\twhile (parent.firstChild) target.appendChild(parent.firstChild);\n}",
|
||||
"reinsertAfter": "function reinsertAfter(before, target) {\n\twhile (before.nextSibling) target.appendChild(before.nextSibling);\n}",
|
||||
"reinsertBefore": "function reinsertBefore(after, target) {\n\tvar parent = after.parentNode;\n\twhile (parent.firstChild !== after) target.appendChild(parent.firstChild);\n}",
|
||||
"destroyEach": "function destroyEach(iterations) {\n\tfor (var i = 0; i < iterations.length; i += 1) {\n\t\tif (iterations[i]) iterations[i].d();\n\t}\n}",
|
||||
"createFragment": "function createFragment() {\n\treturn document.createDocumentFragment();\n}",
|
||||
"createElement": "function createElement(name) {\n\treturn document.createElement(name);\n}",
|
||||
"createSvgElement": "function createSvgElement(name) {\n\treturn document.createElementNS('http://www.w3.org/2000/svg', name);\n}",
|
||||
"createText": "function createText(data) {\n\treturn document.createTextNode(data);\n}",
|
||||
"createComment": "function createComment() {\n\treturn document.createComment('');\n}",
|
||||
"addListener": "function addListener(node, event, handler) {\n\tnode.addEventListener(event, handler, false);\n}",
|
||||
"removeListener": "function removeListener(node, event, handler) {\n\tnode.removeEventListener(event, handler, false);\n}",
|
||||
"setAttribute": "function setAttribute(node, attribute, value) {\n\tnode.setAttribute(attribute, value);\n}",
|
||||
"setAttributes": "function setAttributes(node, attributes) {\n\tfor (var key in attributes) {\n\t\tif (key in node) {\n\t\t\tnode[key] = attributes[key];\n\t\t} else {\n\t\t\tif (attributes[key] === undefined) removeAttribute(node, key);\n\t\t\telse setAttribute(node, key, attributes[key]);\n\t\t}\n\t}\n}",
|
||||
"removeAttribute": "function removeAttribute(node, attribute) {\n\tnode.removeAttribute(attribute);\n}",
|
||||
"setXlinkAttribute": "function setXlinkAttribute(node, attribute, value) {\n\tnode.setAttributeNS('http://www.w3.org/1999/xlink', attribute, value);\n}",
|
||||
"getBindingGroupValue": "function getBindingGroupValue(group) {\n\tvar value = [];\n\tfor (var i = 0; i < group.length; i += 1) {\n\t\tif (group[i].checked) value.push(group[i].__value);\n\t}\n\treturn value;\n}",
|
||||
"toNumber": "function toNumber(value) {\n\treturn value === '' ? undefined : +value;\n}",
|
||||
"timeRangesToArray": "function timeRangesToArray(ranges) {\n\tvar array = [];\n\tfor (var i = 0; i < ranges.length; i += 1) {\n\t\tarray.push({ start: ranges.start(i), end: ranges.end(i) });\n\t}\n\treturn array;\n}",
|
||||
"children": "function children (element) {\n\treturn Array.from(element.childNodes);\n}",
|
||||
"claimElement": "function claimElement (nodes, name, attributes, svg) {\n\tfor (var i = 0; i < nodes.length; i += 1) {\n\t\tvar node = nodes[i];\n\t\tif (node.nodeName === name) {\n\t\t\tfor (var j = 0; j < node.attributes.length; j += 1) {\n\t\t\t\tvar attribute = node.attributes[j];\n\t\t\t\tif (!attributes[attribute.name]) node.removeAttribute(attribute.name);\n\t\t\t}\n\t\t\treturn nodes.splice(i, 1)[0]; // TODO strip unwanted attributes\n\t\t}\n\t}\n\n\treturn svg ? createSvgElement(name) : createElement(name);\n}",
|
||||
"claimText": "function claimText (nodes, data) {\n\tfor (var i = 0; i < nodes.length; i += 1) {\n\t\tvar node = nodes[i];\n\t\tif (node.nodeType === 3) {\n\t\t\tnode.data = data;\n\t\t\treturn nodes.splice(i, 1)[0];\n\t\t}\n\t}\n\n\treturn createText(data);\n}",
|
||||
"setInputType": "function setInputType(input, type) {\n\ttry {\n\t\tinput.type = type;\n\t} catch (e) {}\n}",
|
||||
"setStyle": "function setStyle(node, key, value) {\n\tnode.style.setProperty(key, value);\n}",
|
||||
"selectOption": "function selectOption(select, value) {\n\tfor (var i = 0; i < select.options.length; i += 1) {\n\t\tvar option = select.options[i];\n\n\t\tif (option.__value === value) {\n\t\t\toption.selected = true;\n\t\t\treturn;\n\t\t}\n\t}\n}",
|
||||
"selectOptions": "function selectOptions(select, value) {\n\tfor (var i = 0; i < select.options.length; i += 1) {\n\t\tvar option = select.options[i];\n\t\toption.selected = ~value.indexOf(option.__value);\n\t}\n}",
|
||||
"selectValue": "function selectValue(select) {\n\tvar selectedOption = select.querySelector(':checked') || select.options[0];\n\treturn selectedOption && selectedOption.__value;\n}",
|
||||
"selectMultipleValue": "function selectMultipleValue(select) {\n\treturn [].map.call(select.querySelectorAll(':checked'), function(option) {\n\t\treturn option.__value;\n\t});\n}",
|
||||
"blankObject": "function blankObject() {\n\treturn Object.create(null);\n}",
|
||||
"destroy": "function destroy(detach) {\n\tthis.destroy = noop;\n\tthis.fire('destroy');\n\tthis.set = noop;\n\n\tif (detach !== false) this._fragment.u();\n\tthis._fragment.d();\n\tthis._fragment = null;\n\tthis._state = {};\n}",
|
||||
"destroyDev": "function destroyDev(detach) {\n\tdestroy.call(this, detach);\n\tthis.destroy = function() {\n\t\tconsole.warn('Component was already destroyed');\n\t};\n}",
|
||||
"_differs": "function _differs(a, b) {\n\treturn a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');\n}",
|
||||
"_differsImmutable": "function _differsImmutable(a, b) {\n\treturn a != a ? b == b : a !== b;\n}",
|
||||
"fire": "function fire(eventName, data) {\n\tvar handlers =\n\t\teventName in this._handlers && this._handlers[eventName].slice();\n\tif (!handlers) return;\n\n\tfor (var i = 0; i < handlers.length; i += 1) {\n\t\tvar handler = handlers[i];\n\n\t\tif (!handler.__calling) {\n\t\t\thandler.__calling = true;\n\t\t\thandler.call(this, data);\n\t\t\thandler.__calling = false;\n\t\t}\n\t}\n}",
|
||||
"get": "function get() {\n\treturn this._state;\n}",
|
||||
"init": "function init(component, options) {\n\tcomponent._handlers = blankObject();\n\tcomponent._bind = options._bind;\n\n\tcomponent.options = options;\n\tcomponent.root = options.root || component;\n\tcomponent.store = component.root.store || options.store;\n}",
|
||||
"on": "function on(eventName, handler) {\n\tvar handlers = this._handlers[eventName] || (this._handlers[eventName] = []);\n\thandlers.push(handler);\n\n\treturn {\n\t\tcancel: function() {\n\t\t\tvar index = handlers.indexOf(handler);\n\t\t\tif (~index) handlers.splice(index, 1);\n\t\t}\n\t};\n}",
|
||||
"run": "function run(fn) {\n\tfn();\n}",
|
||||
"set": "function set(newState) {\n\tthis._set(assign({}, newState));\n\tif (this.root._lock) return;\n\tthis.root._lock = true;\n\tcallAll(this.root._beforecreate);\n\tcallAll(this.root._oncreate);\n\tcallAll(this.root._aftercreate);\n\tthis.root._lock = false;\n}",
|
||||
"_set": "function _set(newState) {\n\tvar oldState = this._state,\n\t\tchanged = {},\n\t\tdirty = false;\n\n\tfor (var key in newState) {\n\t\tif (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;\n\t}\n\tif (!dirty) return;\n\n\tthis._state = assign(assign({}, oldState), newState);\n\tthis._recompute(changed, this._state);\n\tif (this._bind) this._bind(changed, this._state);\n\n\tif (this._fragment) {\n\t\tthis.fire(\"state\", { changed: changed, current: this._state, previous: oldState });\n\t\tthis._fragment.p(changed, this._state);\n\t\tthis.fire(\"update\", { changed: changed, current: this._state, previous: oldState });\n\t}\n}",
|
||||
"setDev": "function setDev(newState) {\n\tif (typeof newState !== 'object') {\n\t\tthrow new Error(\n\t\t\tthis._debugName + '.set was called without an object of data key-values to update.'\n\t\t);\n\t}\n\n\tthis._checkReadOnly(newState);\n\tset.call(this, newState);\n}",
|
||||
"callAll": "function callAll(fns) {\n\twhile (fns && fns.length) fns.shift()();\n}",
|
||||
"_mount": "function _mount(target, anchor) {\n\tthis._fragment[this._fragment.i ? 'i' : 'm'](target, anchor || null);\n}",
|
||||
"_unmount": "function _unmount() {\n\tif (this._fragment) this._fragment.u();\n}",
|
||||
"isPromise": "function isPromise(value) {\n\treturn value && typeof value.then === 'function';\n}",
|
||||
"PENDING": "{}",
|
||||
"SUCCESS": "{}",
|
||||
"FAILURE": "{}",
|
||||
"removeFromStore": "function removeFromStore() {\n\tthis.store._remove(this);\n}",
|
||||
"proto": "{\n\tdestroy,\n\tget,\n\tfire,\n\ton,\n\tset,\n\t_recompute: noop,\n\t_set,\n\t_mount,\n\t_unmount,\n\t_differs\n}",
|
||||
"protoDev": "{\n\tdestroy: destroyDev,\n\tget,\n\tfire,\n\ton,\n\tset: setDev,\n\t_recompute: noop,\n\t_set,\n\t_mount,\n\t_unmount,\n\t_differs\n}",
|
||||
"destroyBlock": "function destroyBlock(block, lookup) {\n\tblock.u();\n\tblock.d();\n\tlookup[block.key] = null;\n}",
|
||||
"outroAndDestroyBlock": "function outroAndDestroyBlock(block, lookup) {\n\tblock.o(function() {\n\t\tdestroyBlock(block, lookup);\n\t});\n}",
|
||||
"updateKeyedEach": "function updateKeyedEach(old_blocks, component, changed, key_prop, dynamic, list, lookup, node, has_outro, create_each_block, intro_method, next, get_context) {\n\tvar o = old_blocks.length;\n\tvar n = list.length;\n\n\tvar i = o;\n\tvar old_indexes = {};\n\twhile (i--) old_indexes[old_blocks[i].key] = i;\n\n\tvar new_blocks = [];\n\tvar new_lookup = {};\n\tvar deltas = {};\n\n\tvar i = n;\n\twhile (i--) {\n\t\tvar key = list[i][key_prop];\n\t\tvar block = lookup[key];\n\n\t\tif (!block) {\n\t\t\tblock = create_each_block(component, key, get_context(i));\n\t\t\tblock.c();\n\t\t} else if (dynamic) {\n\t\t\tblock.p(changed, get_context(i));\n\t\t}\n\n\t\tnew_blocks[i] = new_lookup[key] = block;\n\n\t\tif (key in old_indexes) deltas[key] = Math.abs(i - old_indexes[key]);\n\t}\n\n\tvar will_move = {};\n\tvar did_move = {};\n\n\tvar destroy = has_outro ? outroAndDestroyBlock : destroyBlock;\n\n\tfunction insert(block) {\n\t\tblock[intro_method](node, next);\n\t\tlookup[block.key] = block;\n\t\tnext = block.first;\n\t\tn--;\n\t}\n\n\twhile (o && n) {\n\t\tvar new_block = new_blocks[n - 1];\n\t\tvar old_block = old_blocks[o - 1];\n\t\tvar new_key = new_block.key;\n\t\tvar old_key = old_block.key;\n\n\t\tif (new_block === old_block) {\n\t\t\t// do nothing\n\t\t\tnext = new_block.first;\n\t\t\to--;\n\t\t\tn--;\n\t\t}\n\n\t\telse if (!new_lookup[old_key]) {\n\t\t\t// remove old block\n\t\t\tdestroy(old_block, lookup);\n\t\t\to--;\n\t\t}\n\n\t\telse if (!lookup[new_key] || will_move[new_key]) {\n\t\t\tinsert(new_block);\n\t\t}\n\n\t\telse if (did_move[old_key]) {\n\t\t\to--;\n\n\t\t} else if (deltas[new_key] > deltas[old_key]) {\n\t\t\tdid_move[new_key] = true;\n\t\t\tinsert(new_block);\n\n\t\t} else {\n\t\t\twill_move[old_key] = true;\n\t\t\to--;\n\t\t}\n\t}\n\n\twhile (o--) {\n\t\tvar old_block = old_blocks[o];\n\t\tif (!new_lookup[old_block.key]) destroy(old_block, lookup);\n\t}\n\n\twhile (n) insert(new_blocks[n - 1]);\n\n\treturn new_blocks;\n}",
|
||||
"getSpreadUpdate": "function getSpreadUpdate(levels, updates) {\n\tvar update = {};\n\n\tvar to_null_out = {};\n\tvar accounted_for = {};\n\n\tvar i = levels.length;\n\twhile (i--) {\n\t\tvar o = levels[i];\n\t\tvar n = updates[i];\n\n\t\tif (n) {\n\t\t\tfor (var key in o) {\n\t\t\t\tif (!(key in n)) to_null_out[key] = 1;\n\t\t\t}\n\n\t\t\tfor (var key in n) {\n\t\t\t\tif (!accounted_for[key]) {\n\t\t\t\t\tupdate[key] = n[key];\n\t\t\t\t\taccounted_for[key] = 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlevels[i] = n;\n\t\t} else {\n\t\t\tfor (var key in o) {\n\t\t\t\taccounted_for[key] = 1;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (var key in to_null_out) {\n\t\tif (!(key in update)) update[key] = undefined;\n\t}\n\n\treturn update;\n}",
|
||||
"spread": "function spread(args) {\n\tconst attributes = Object.assign({}, ...args);\n\tlet str = '';\n\n\tObject.keys(attributes).forEach(name => {\n\t\tconst value = attributes[name];\n\t\tif (value === undefined) return;\n\t\tif (value === true) str += \" \" + name;\n\t\tstr += \" \" + name + \"=\" + JSON.stringify(value);\n\t});\n\n\treturn str;\n}",
|
||||
"escaped": "{\n\t'\"': '"',\n\t\"'\": ''',\n\t'&': '&',\n\t'<': '<',\n\t'>': '>'\n}",
|
||||
"escape": "function escape(html) {\n\treturn String(html).replace(/[\"'&<>]/g, match => escaped[match]);\n}",
|
||||
"each": "function each(items, assign, fn) {\n\tlet str = '';\n\tfor (let i = 0; i < items.length; i += 1) {\n\t\tstr += fn(assign(items[i], i));\n\t}\n\treturn str;\n}",
|
||||
"missingComponent": "{\n\t_render: () => ''\n}",
|
||||
"linear": "function linear(t) {\n\treturn t;\n}",
|
||||
"generateRule": "function generateRule(\n\ta,\n\tb,\n\tdelta,\n\tduration,\n\tease,\n\tfn\n) {\n\tvar keyframes = '{\\n';\n\n\tfor (var p = 0; p <= 1; p += 16.666 / duration) {\n\t\tvar t = a + delta * ease(p);\n\t\tkeyframes += p * 100 + '%{' + fn(t) + '}\\n';\n\t}\n\n\treturn keyframes + '100% {' + fn(b) + '}\\n}';\n}",
|
||||
"hash": "function hash(str) {\n\tvar hash = 5381;\n\tvar i = str.length;\n\n\twhile (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);\n\treturn hash >>> 0;\n}",
|
||||
"wrapTransition": "function wrapTransition(component, node, fn, params, intro, outgroup) {\n\tvar obj = fn(node, params);\n\tvar duration = obj.duration || 300;\n\tvar ease = obj.easing || linear;\n\tvar cssText;\n\n\t// TODO share <style> tag between all transitions?\n\tif (obj.css && !transitionManager.stylesheet) {\n\t\tvar style = createElement('style');\n\t\tdocument.head.appendChild(style);\n\t\ttransitionManager.stylesheet = style.sheet;\n\t}\n\n\tif (intro) {\n\t\tif (obj.css && obj.delay) {\n\t\t\tcssText = node.style.cssText;\n\t\t\tnode.style.cssText += obj.css(0);\n\t\t}\n\n\t\tif (obj.tick) obj.tick(0);\n\t}\n\n\treturn {\n\t\tt: intro ? 0 : 1,\n\t\trunning: false,\n\t\tprogram: null,\n\t\tpending: null,\n\t\trun: function(intro, callback) {\n\t\t\tvar program = {\n\t\t\t\tstart: window.performance.now() + (obj.delay || 0),\n\t\t\t\tintro: intro,\n\t\t\t\tcallback: callback\n\t\t\t};\n\n\t\t\tif (obj.delay) {\n\t\t\t\tthis.pending = program;\n\t\t\t} else {\n\t\t\t\tthis.start(program);\n\t\t\t}\n\n\t\t\tif (!this.running) {\n\t\t\t\tthis.running = true;\n\t\t\t\ttransitionManager.add(this);\n\t\t\t}\n\t\t},\n\t\tstart: function(program) {\n\t\t\tcomponent.fire(program.intro ? 'intro.start' : 'outro.start', { node: node });\n\n\t\t\tprogram.a = this.t;\n\t\t\tprogram.b = program.intro ? 1 : 0;\n\t\t\tprogram.delta = program.b - program.a;\n\t\t\tprogram.duration = duration * Math.abs(program.b - program.a);\n\t\t\tprogram.end = program.start + program.duration;\n\n\t\t\tif (obj.css) {\n\t\t\t\tif (obj.delay) node.style.cssText = cssText;\n\n\t\t\t\tprogram.rule = generateRule(\n\t\t\t\t\tprogram.a,\n\t\t\t\t\tprogram.b,\n\t\t\t\t\tprogram.delta,\n\t\t\t\t\tprogram.duration,\n\t\t\t\t\tease,\n\t\t\t\t\tobj.css\n\t\t\t\t);\n\n\t\t\t\ttransitionManager.addRule(program.rule, program.name = '__svelte_' + hash(program.rule));\n\n\t\t\t\tnode.style.animation = (node.style.animation || '')\n\t\t\t\t\t.split(', ')\n\t\t\t\t\t.filter(function(anim) {\n\t\t\t\t\t\t// when introing, discard old animations if there are any\n\t\t\t\t\t\treturn anim && (program.delta < 0 || !/__svelte/.test(anim));\n\t\t\t\t\t})\n\t\t\t\t\t.concat(program.name + ' ' + program.duration + 'ms linear 1 forwards')\n\t\t\t\t\t.join(', ');\n\t\t\t}\n\n\t\t\tthis.program = program;\n\t\t\tthis.pending = null;\n\t\t},\n\t\tupdate: function(now) {\n\t\t\tvar program = this.program;\n\t\t\tif (!program) return;\n\n\t\t\tvar p = now - program.start;\n\t\t\tthis.t = program.a + program.delta * ease(p / program.duration);\n\t\t\tif (obj.tick) obj.tick(this.t);\n\t\t},\n\t\tdone: function() {\n\t\t\tvar program = this.program;\n\t\t\tthis.t = program.b;\n\t\t\tif (obj.tick) obj.tick(this.t);\n\t\t\tif (obj.css) transitionManager.deleteRule(node, program.name);\n\t\t\tprogram.callback();\n\t\t\tprogram = null;\n\t\t\tthis.running = !!this.pending;\n\t\t},\n\t\tabort: function() {\n\t\t\tif (obj.tick) obj.tick(1);\n\t\t\tif (obj.css) transitionManager.deleteRule(node, this.program.name);\n\t\t\tthis.program = this.pending = null;\n\t\t\tthis.running = false;\n\t\t}\n\t};\n}",
|
||||
"transitionManager": "{\n\trunning: false,\n\ttransitions: [],\n\tbound: null,\n\tstylesheet: null,\n\tactiveRules: {},\n\n\tadd: function(transition) {\n\t\tthis.transitions.push(transition);\n\n\t\tif (!this.running) {\n\t\t\tthis.running = true;\n\t\t\trequestAnimationFrame(this.bound || (this.bound = this.next.bind(this)));\n\t\t}\n\t},\n\n\taddRule: function(rule, name) {\n\t\tif (!this.activeRules[name]) {\n\t\t\tthis.activeRules[name] = true;\n\t\t\tthis.stylesheet.insertRule('@keyframes ' + name + ' ' + rule, this.stylesheet.cssRules.length);\n\t\t}\n\t},\n\n\tnext: function() {\n\t\tthis.running = false;\n\n\t\tvar now = window.performance.now();\n\t\tvar i = this.transitions.length;\n\n\t\twhile (i--) {\n\t\t\tvar transition = this.transitions[i];\n\n\t\t\tif (transition.program && now >= transition.program.end) {\n\t\t\t\ttransition.done();\n\t\t\t}\n\n\t\t\tif (transition.pending && now >= transition.pending.start) {\n\t\t\t\ttransition.start(transition.pending);\n\t\t\t}\n\n\t\t\tif (transition.running) {\n\t\t\t\ttransition.update(now);\n\t\t\t\tthis.running = true;\n\t\t\t} else if (!transition.pending) {\n\t\t\t\tthis.transitions.splice(i, 1);\n\t\t\t}\n\t\t}\n\n\t\tif (this.running) {\n\t\t\trequestAnimationFrame(this.bound);\n\t\t} else if (this.stylesheet) {\n\t\t\tvar i = this.stylesheet.cssRules.length;\n\t\t\twhile (i--) this.stylesheet.deleteRule(i);\n\t\t\tthis.activeRules = {};\n\t\t}\n\t},\n\n\tdeleteRule: function(node, name) {\n\t\tnode.style.animation = node.style.animation\n\t\t\t.split(', ')\n\t\t\t.filter(function(anim) {\n\t\t\t\treturn anim.indexOf(name) === -1;\n\t\t\t})\n\t\t\t.join(', ');\n\t}\n}",
|
||||
"noop": "function noop() {}",
|
||||
"assign": "function assign(tar, src) {\n\tfor (var k in src) tar[k] = src[k];\n\treturn tar;\n}",
|
||||
"assignTrue": "function assignTrue(tar, src) {\n\tfor (var k in src) tar[k] = 1;\n\treturn tar;\n}"
|
||||
};
|
||||
|
||||
export default shared;
|
Loading…
Reference in new issue