| /** | |
| * @license | |
| * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. | |
| * This code may only be used under the BSD style license found at | |
| * http://polymer.github.io/LICENSE.txt | |
| * The complete set of authors may be found at | |
| * http://polymer.github.io/AUTHORS.txt | |
| * The complete set of contributors may be found at | |
| * http://polymer.github.io/CONTRIBUTORS.txt | |
| * Code distributed by Google as part of the polymer project is also | |
| * subject to an additional IP rights grant found at | |
| * http://polymer.github.io/PATENTS.txt | |
| */ | |
| import {TemplateInstance} from '../lib/template-instance.js'; | |
| import {Template} from '../lib/template.js'; | |
| import {directive, NodePart, Part, reparentNodes, TemplateResult} from '../lit-html.js'; | |
| interface CachedTemplate { | |
| readonly instance: TemplateInstance; | |
| readonly nodes: DocumentFragment; | |
| } | |
| const templateCaches = | |
| new WeakMap<NodePart, WeakMap<Template, CachedTemplate>>(); | |
| /** | |
| * Enables fast switching between multiple templates by caching the DOM nodes | |
| * and TemplateInstances produced by the templates. | |
| * | |
| * Example: | |
| * | |
| * ``` | |
| * let checked = false; | |
| * | |
| * html` | |
| * ${cache(checked ? html`input is checked` : html`input is not checked`)} | |
| * ` | |
| * ``` | |
| */ | |
| export const cache = directive((value: unknown) => (part: Part) => { | |
| if (!(part instanceof NodePart)) { | |
| throw new Error('cache can only be used in text bindings'); | |
| } | |
| let templateCache = templateCaches.get(part); | |
| if (templateCache === undefined) { | |
| templateCache = new WeakMap(); | |
| templateCaches.set(part, templateCache); | |
| } | |
| const previousValue = part.value; | |
| // First, can we update the current TemplateInstance, or do we need to move | |
| // the current nodes into the cache? | |
| if (previousValue instanceof TemplateInstance) { | |
| if (value instanceof TemplateResult && | |
| previousValue.template === part.options.templateFactory(value)) { | |
| // Same Template, just trigger an update of the TemplateInstance | |
| part.setValue(value); | |
| return; | |
| } else { | |
| // Not the same Template, move the nodes from the DOM into the cache. | |
| let cachedTemplate = templateCache.get(previousValue.template); | |
| if (cachedTemplate === undefined) { | |
| cachedTemplate = { | |
| instance: previousValue, | |
| nodes: document.createDocumentFragment(), | |
| }; | |
| templateCache.set(previousValue.template, cachedTemplate); | |
| } | |
| reparentNodes( | |
| cachedTemplate.nodes, part.startNode.nextSibling, part.endNode); | |
| } | |
| } | |
| // Next, can we reuse nodes from the cache? | |
| if (value instanceof TemplateResult) { | |
| const template = part.options.templateFactory(value); | |
| const cachedTemplate = templateCache.get(template); | |
| if (cachedTemplate !== undefined) { | |
| // Move nodes out of cache | |
| part.setValue(cachedTemplate.nodes); | |
| part.commit(); | |
| // Set the Part value to the TemplateInstance so it'll update it. | |
| part.value = cachedTemplate.instance; | |
| } | |
| } | |
| part.setValue(value); | |
| }); |