1/// BareSpecifier=lit-html/lib/shady-render 2 3/** 4 * @license 5 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. 6 * This code may only be used under the BSD style license found at 7 * http://polymer.github.io/LICENSE.txt 8 * The complete set of authors may be found at 9 * http://polymer.github.io/AUTHORS.txt 10 * The complete set of contributors may be found at 11 * http://polymer.github.io/CONTRIBUTORS.txt 12 * Code distributed by Google as part of the polymer project is also 13 * subject to an additional IP rights grant found at 14 * http://polymer.github.io/PATENTS.txt 15 */ 16 17/** 18 * Module to add shady DOM/shady CSS polyfill support to lit-html template 19 * rendering. See the [[render]] method for details. 20 * 21 * @packageDocumentation 22 */ 23 24/** 25 * Do not remove this comment; it keeps typedoc from misplacing the module 26 * docs. 27 */ 28import { removeNodes } from "./dom.js?version=aa97bd14"; 29import { insertNodeIntoTemplate, removeNodesFromTemplate } from "./modify-template.js?version=aa97bd14"; 30import { parts, render as litRender } from "./render.js?version=aa97bd14"; 31import { templateCaches } from "./template-factory.js?version=aa97bd14"; 32import { TemplateInstance } from "./template-instance.js?version=aa97bd14"; 33import { marker, Template } from "./template.js?version=aa97bd14"; 34export { html, svg, TemplateResult } from "../lit-html.js?version=aa97bd14"; // Get a key to lookup in `templateCaches`. 35 36const getTemplateCacheKey = (type, scopeName) => `${type}--${scopeName}`; 37 38let compatibleShadyCSSVersion = true; 39 40if (typeof window.ShadyCSS === 'undefined') { 41 compatibleShadyCSSVersion = false; 42} else if (typeof window.ShadyCSS.prepareTemplateDom === 'undefined') { 43 console.warn(`Incompatible ShadyCSS version detected. ` + `Please update to at least @webcomponents/[email protected] and ` + `@webcomponents/[email protected].`); 44 compatibleShadyCSSVersion = false; 45} 46/** 47 * Template factory which scopes template DOM using ShadyCSS. 48 * @param scopeName {string} 49 */ 50 51 52export const shadyTemplateFactory = scopeName => result => { 53 const cacheKey = getTemplateCacheKey(result.type, scopeName); 54 let templateCache = templateCaches.get(cacheKey); 55 56 if (templateCache === undefined) { 57 templateCache = { 58 stringsArray: new WeakMap(), 59 keyString: new Map() 60 }; 61 templateCaches.set(cacheKey, templateCache); 62 } 63 64 let template = templateCache.stringsArray.get(result.strings); 65 66 if (template !== undefined) { 67 return template; 68 } 69 70 const key = result.strings.join(marker); 71 template = templateCache.keyString.get(key); 72 73 if (template === undefined) { 74 const element = result.getTemplateElement(); 75 76 if (compatibleShadyCSSVersion) { 77 window.ShadyCSS.prepareTemplateDom(element, scopeName); 78 } 79 80 template = new Template(result, element); 81 templateCache.keyString.set(key, template); 82 } 83 84 templateCache.stringsArray.set(result.strings, template); 85 return template; 86}; 87const TEMPLATE_TYPES = ['html', 'svg']; 88/** 89 * Removes all style elements from Templates for the given scopeName. 90 */ 91 92const removeStylesFromLitTemplates = scopeName => { 93 TEMPLATE_TYPES.forEach(type => { 94 const templates = templateCaches.get(getTemplateCacheKey(type, scopeName)); 95 96 if (templates !== undefined) { 97 templates.keyString.forEach(template => { 98 const { 99 element: { 100 content 101 } 102 } = template; // IE 11 doesn't support the iterable param Set constructor 103 104 const styles = new Set(); 105 Array.from(content.querySelectorAll('style')).forEach(s => { 106 styles.add(s); 107 }); 108 removeNodesFromTemplate(template, styles); 109 }); 110 } 111 }); 112}; 113 114const shadyRenderSet = new Set(); 115/** 116 * For the given scope name, ensures that ShadyCSS style scoping is performed. 117 * This is done just once per scope name so the fragment and template cannot 118 * be modified. 119 * (1) extracts styles from the rendered fragment and hands them to ShadyCSS 120 * to be scoped and appended to the document 121 * (2) removes style elements from all lit-html Templates for this scope name. 122 *
123 * Note, <style> elements can only be placed into templates for the 124 * initial rendering of the scope. If <style> elements are included in templates 125 * dynamically rendered to the scope (after the first scope render), they will 126 * not be scoped and the <style> will be left in the template and rendered 127 * output. 128 */ 129 130const prepareTemplateStyles = (scopeName, renderedDOM, template) => { 131 shadyRenderSet.add(scopeName); // If `renderedDOM` is stamped from a Template, then we need to edit that 132 // Template's underlying template element. Otherwise, we create one here 133 // to give to ShadyCSS, which still requires one while scoping. 134 135 const templateElement = !!template ? template.element : document.createElement('template'); // Move styles out of rendered DOM and store. 136 137 const styles = renderedDOM.querySelectorAll('style'); 138 const { 139 length 140 } = styles; // If there are no styles, skip unnecessary work 141 142 if (length === 0) { 143 // Ensure prepareTemplateStyles is called to support adding 144 // styles via `prepareAdoptedCssText` since that requires that 145 // `prepareTemplateStyles` is called. 146 // 147 // ShadyCSS will only update styles containing @apply in the template 148 // given to `prepareTemplateStyles`. If no lit Template was given, 149 // ShadyCSS will not be able to update uses of @apply in any relevant 150 // template. However, this is not a problem because we only create the 151 // template for the purpose of supporting `prepareAdoptedCssText`, 152 // which doesn't support @apply at all. 153 window.ShadyCSS.prepareTemplateStyles(templateElement, scopeName); 154 return; 155 } 156 157 const condensedStyle = document.createElement('style'); // Collect styles into a single style. This helps us make sure ShadyCSS 158 // manipulations will not prevent us from being able to fix up template 159 // part indices. 160 // NOTE: collecting styles is inefficient for browsers but ShadyCSS 161 // currently does this anyway. When it does not, this should be changed. 162 163 for (let i = 0; i < length; i++) { 164 const style = styles[i]; 165 style.parentNode.removeChild(style); 166 condensedStyle.textContent += style.textContent; 167 } // Remove styles from nested templates in this scope. 168 169 170 removeStylesFromLitTemplates(scopeName); // And then put the condensed style into the "root" template passed in as 171 // `template`. 172 173 const content = templateElement.content; 174 175 if (!!template) { 176 insertNodeIntoTemplate(template, condensedStyle, content.firstChild); 177 } else { 178 content.insertBefore(condensedStyle, content.firstChild); 179 } // Note, it's important that ShadyCSS gets the template that `lit-html` 180 // will actually render so that it can update the style inside when 181 // needed (e.g. @apply native Shadow DOM case). 182 183 184 window.ShadyCSS.prepareTemplateStyles(templateElement, scopeName); 185 const style = content.querySelector('style'); 186 187 if (window.ShadyCSS.nativeShadow && style !== null) { 188 // When in native Shadow DOM, ensure the style created by ShadyCSS is 189 // included in initially rendered output (`renderedDOM`). 190 renderedDOM.insertBefore(style.cloneNode(true), renderedDOM.firstChild); 191 } else if (!!template) { 192 // When no style is left in the template, parts will be broken as a 193 // result. To fix this, we put back the style node ShadyCSS removed 194 // and then tell lit to remove that node from the template. 195 // There can be no style in the template in 2 cases (1) when Shady DOM 196 // is in use, ShadyCSS removes all styles, (2) when native Shadow DOM 197 // is in use ShadyCSS removes the style if it contains no content. 198 // NOTE, ShadyCSS creates its own style so we can safely add/remove 199 // `condensedStyle` here. 200 content.insertBefore(condensedStyle, content.firstChild); 201 const removes = new Set(); 202 removes.add(condensedStyle); 203 removeNodesFromTemplate(template, removes); 204 } 205}; 206/** 207 * Extension to the standard `render` method which supports rendering 208 * to ShadowRoots when the ShadyDOM (https://github.com/webcomponents/shadydom) 209 * and ShadyCSS (https://github.com/webcomponents/shadycss) polyfills are used 210 * or when the webcomponentsjs 211 * (https://github.com/webcomponents/webcomponentsjs) polyfill is used. 212 * 213 * Adds a `scopeName` option which is used to scope element DOM and stylesheets 214 * when native ShadowDOM is unavailable. The `scopeName` will be added to 215 * the class attribute of all rendered DOM. In addition, any style elements will 216 * be automatically re-written with this `scopeName` selector and moved out 217 * of the rendered DOM and into the document `<head>`. 218 * 219 * It is common to use this render method in conjunction with a custom element 220 * which renders a shadowRoot. When this is done, typically the element's 221 * `localName` should be used as the `scopeName`. 222 * 223 * In addition to DOM scoping, ShadyCSS also supports a basic shim for css 224 * custom properties (needed only on older browsers like IE11) and a shim for 225 * a deprecated feature called `@apply` that supports applying a set of css 226 * custom properties to a given location. 227 * 228 * Usage considerations: 229 * 230 * * Part values in `<style>` elements are only applied the first time a given 231 * `scopeName` renders. Subsequent changes to parts in style elements will have 232 * no effect. Because of this, parts in style elements should only be used for 233 * values that will never change, for example parts that set scope-wide theme 234 * values or parts which render shared style elements. 235 * 236 * * Note, due to a limitation of the ShadyDOM polyfill, rendering in a 237 * custom element's `constructor` is not supported. Instead rendering should 238 * either done asynchronously, for example at microtask timing (for example 239 * `Promise.resolve()`), or be deferred until the first time the element's 240 * `connectedCallback` runs. 241 * 242 * Usage considerations when using shimmed custom properties or `@apply`: 243 * 244 * * Whenever any dynamic changes are made which affect 245 * css custom properties, `ShadyCSS.styleElement(element)` must be called 246 * to update the element. There are two cases when this is needed: 247 * (1) the element is connected to a new parent, (2) a class is added to the 248 * element that causes it to match different custom properties. 249 * To address the first case when rendering a custom element, `styleElement` 250 * should be called in the element's `connectedCallback`. 251 * 252 * * Shimmed custom properties may only be defined either for an entire 253 * shadowRoot (for example, in a `:host` rule) or via a rule that directly 254 * matches an element with a shadowRoot. In other words, instead of flowing from 255 * parent to child as do native css custom properties, shimmed custom properties 256 * flow only from shadowRoots to nested shadowRoots. 257 * 258 * * When using `@apply` mixing css shorthand property names with 259 * non-shorthand names (for example `border` and `border-width`) is not 260 * supported. 261 */ 262 263 264export const render = (result, container, options) => { 265 if (!options || typeof options !== 'object' || !options.scopeName) { 266 throw new Error('The `scopeName` option is required.'); 267 } 268 269 const scopeName = options.scopeName; 270 const hasRendered = parts.has(container); 271 const needsScoping = compatibleShadyCSSVersion && container.nodeType === 11 272 /* Node.DOCUMENT_FRAGMENT_NODE */ 273 && !!container.host; // Handle first render to a scope specially... 274
275 const firstScopeRender = needsScoping && !shadyRenderSet.has(scopeName); // On first scope render, render into a fragment; this cannot be a single 276 // fragment that is reused since nested renders can occur synchronously. 277 278 const renderContainer = firstScopeRender ? document.createDocumentFragment() : container; 279 litRender(result, renderContainer, Object.assign({ 280 templateFactory: shadyTemplateFactory(scopeName) 281 }, options)); // When performing first scope render, 282 // (1) We've rendered into a fragment so that there's a chance to 283 // `prepareTemplateStyles` before sub-elements hit the DOM 284 // (which might cause them to render based on a common pattern of 285 // rendering in a custom element's `connectedCallback`); 286 // (2) Scope the template with ShadyCSS one time only for this scope. 287 // (3) Render the fragment into the container and make sure the 288 // container knows its `part` is the one we just rendered. This ensures 289 // DOM will be re-used on subsequent renders. 290 291 if (firstScopeRender) { 292 const part = parts.get(renderContainer); 293 parts.delete(renderContainer); // ShadyCSS might have style sheets (e.g. from `prepareAdoptedCssText`) 294 // that should apply to `renderContainer` even if the rendered value is 295 // not a TemplateInstance. However, it will only insert scoped styles 296 // into the document if `prepareTemplateStyles` has already been called 297 // for the given scope name. 298 299 const template = part.value instanceof TemplateInstance ? part.value.template : undefined; 300 prepareTemplateStyles(scopeName, renderContainer, template); 301 removeNodes(container, container.firstChild); 302 container.appendChild(renderContainer); 303 parts.set(container, part); 304 } // After elements have hit the DOM, update styling if this is the 305 // initial render to this container. 306 // This is needed whenever dynamic changes are made so it would be 307 // safest to do every render; however, this would regress performance 308 // so we leave it up to the user to call `ShadyCSS.styleElement` 309 // for dynamic changes. 310 311 312 if (!hasRendered && needsScoping) { 313 window.ShadyCSS.styleElement(container.host); 314 } 315};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.