1/**
2@license
3Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7Code distributed by Google as part of the polymer project is also
8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9*/
10
11'use strict';
12import templateMap from './template-map.js';
13import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars
14
15/*
16 * Utilities for handling invalidating apply-shim mixins for a given template.
17 *
18 * The invalidation strategy involves keeping track of the "current" version of a template's mixins, and updating that count when a mixin is invalidated.
19 * The template
20 */
21
22/** @const {string} */
23const CURRENT_VERSION = '_applyShimCurrentVersion';
24
25/** @const {string} */
26const NEXT_VERSION = '_applyShimNextVersion';
27
28/** @const {string} */
29const VALIDATING_VERSION = '_applyShimValidatingVersion';
30
31/**
32 * @const {Promise<void>}
33 */
34const promise = Promise.resolve();
35
36/**
37 * @param {string} elementName
38 */
39export function invalidate(elementName){
40  let template = templateMap[elementName];
41  if (template) {
42    invalidateTemplate(template);
43  }
44}
45
46/**
47 * This function can be called multiple times to mark a template invalid
48 * and signal that the style inside must be regenerated.
49 *
50 * Use `startValidatingTemplate` to begin an asynchronous validation cycle.
51 * During that cycle, call `templateIsValidating` to see if the template must
52 * be revalidated
53 * @param {HTMLTemplateElement} template
54 */
55export function invalidateTemplate(template) {
56  // default the current version to 0
57  template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0;
58  // ensure the "validating for" flag exists
59  template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0;
60  // increment the next version
61  template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1;
62}
63
64/**
65 * @param {string} elementName
66 * @return {boolean}
67 */
68export function isValid(elementName) {
69  let template = templateMap[elementName];
70  if (template) {
71    return templateIsValid(template);
72  }
73  return true;
74}
75
76/**
77 * @param {HTMLTemplateElement} template
78 * @return {boolean}
79 */
80export function templateIsValid(template) {
81  return template[CURRENT_VERSION] === template[NEXT_VERSION];
82}
83
84/**
85 * @param {string} elementName
86 * @return {boolean}
87 */
88export function isValidating(elementName) {
89  let template = templateMap[elementName];
90  if (template) {
91    return templateIsValidating(template);
92  }
93  return false;
94}
95
96/**
97 * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation.
98 * If false, the template must be validated.
99 * @param {HTMLTemplateElement} template
100 * @return {boolean}
101 */
102export function templateIsValidating(template) {
103  return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION];
104}
105
106/**
107 * the template is marked as `validating` for one microtask so that all instances
108 * found in the tree crawl of `applyStyle` will update themselves,
109 * but the template will only be updated once.
110 * @param {string} elementName
111*/
112export function startValidating(elementName) {
113  let template = templateMap[elementName];
114  startValidatingTemplate(template);
115}
116
117/**
118 * Begin an asynchronous invalidation cycle.
119 * This should be called after every validation of a template
120 *
121 * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate`
122 * @param {HTMLTemplateElement} template
123 */
124export function startValidatingTemplate(template) {
125  // remember that the current "next version" is the reason for this validation cycle
126  template[VALIDATING_VERSION] = template[NEXT_VERSION];
127  // however, there only needs to be one async task to clear the counters
128  if (!template._validating) {
129    template._validating = true;
130    promise.then(function() {
131      // sync the current version to let future invalidations cause a refresh cycle
132      template[CURRENT_VERSION] = template[NEXT_VERSION];
133      template._validating = false;
134    });
135  }
136}
137
138/**
139 * @return {boolean}
140 */
141export function elementsAreInvalid() {
142  for (let elementName in templateMap) {
143    let template = templateMap[elementName];
144    if (!templateIsValid(template)) {
145      return true;
146    }
147  }
148  return false;
149}
150