"use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.splitStyles = exports.detokenize = exports.clearStyles = exports.loadTheme = exports.flush = exports.configureRunMode = exports.configureLoadStyles = exports.loadStyles = void 0; // Store the theming state in __themeState__ global scope for reuse in the case of duplicate // load-themed-styles hosted on the page. var _root = typeof window === 'undefined' ? global : window; // eslint-disable-line @typescript-eslint/no-explicit-any // Nonce string to inject into script tag if one provided. This is used in CSP (Content Security Policy). var _styleNonce = _root && _root.CSPSettings && _root.CSPSettings.nonce; var _themeState = initializeThemeState(); /** * Matches theming tokens. For example, "[theme: themeSlotName, default: #FFF]" (including the quotes). */ var _themeTokenRegex = /[\'\"]\[theme:\s*(\w+)\s*(?:\,\s*default:\s*([\\"\']?[\.\,\(\)\#\-\s\w]*[\.\,\(\)\#\-\w][\"\']?))?\s*\][\'\"]/g; var now = function () { return typeof performance !== 'undefined' && !!performance.now ? performance.now() : Date.now(); }; function measure(func) { var start = now(); func(); var end = now(); _themeState.perf.duration += end - start; } /** * initialize global state object */ function initializeThemeState() { var state = _root.__themeState__ || { theme: undefined, lastStyleElement: undefined, registeredStyles: [] }; if (!state.runState) { state = __assign(__assign({}, state), { perf: { count: 0, duration: 0 }, runState: { flushTimer: 0, mode: 0 /* Mode.sync */, buffer: [] } }); } if (!state.registeredThemableStyles) { state = __assign(__assign({}, state), { registeredThemableStyles: [] }); } _root.__themeState__ = state; return state; } /** * Loads a set of style text. If it is registered too early, we will register it when the window.load * event is fired. * @param {string | ThemableArray} styles Themable style text to register. * @param {boolean} loadAsync When true, always load styles in async mode, irrespective of current sync mode. */ function loadStyles(styles, loadAsync) { if (loadAsync === void 0) { loadAsync = false; } measure(function () { var styleParts = Array.isArray(styles) ? styles : splitStyles(styles); var _a = _themeState.runState, mode = _a.mode, buffer = _a.buffer, flushTimer = _a.flushTimer; if (loadAsync || mode === 1 /* Mode.async */) { buffer.push(styleParts); if (!flushTimer) { _themeState.runState.flushTimer = asyncLoadStyles(); } } else { applyThemableStyles(styleParts); } }); } exports.loadStyles = loadStyles; /** * Allows for customizable loadStyles logic. e.g. for server side rendering application * @param {(processedStyles: string, rawStyles?: string | ThemableArray) => void} * a loadStyles callback that gets called when styles are loaded or reloaded */ function configureLoadStyles(loadStylesFn) { _themeState.loadStyles = loadStylesFn; } exports.configureLoadStyles = configureLoadStyles; /** * Configure run mode of load-themable-styles * @param mode load-themable-styles run mode, async or sync */ function configureRunMode(mode) { _themeState.runState.mode = mode; } exports.configureRunMode = configureRunMode; /** * external code can call flush to synchronously force processing of currently buffered styles */ function flush() { measure(function () { var styleArrays = _themeState.runState.buffer.slice(); _themeState.runState.buffer = []; var mergedStyleArray = [].concat.apply([], styleArrays); if (mergedStyleArray.length > 0) { applyThemableStyles(mergedStyleArray); } }); } exports.flush = flush; /** * register async loadStyles */ function asyncLoadStyles() { return setTimeout(function () { _themeState.runState.flushTimer = 0; flush(); }, 0); } /** * Loads a set of style text. If it is registered too early, we will register it when the window.load event * is fired. * @param {string} styleText Style to register. * @param {IStyleRecord} styleRecord Existing style record to re-apply. */ function applyThemableStyles(stylesArray, styleRecord) { if (_themeState.loadStyles) { _themeState.loadStyles(resolveThemableArray(stylesArray).styleString, stylesArray); } else { registerStyles(stylesArray); } } /** * Registers a set theme tokens to find and replace. If styles were already registered, they will be * replaced. * @param {theme} theme JSON object of theme tokens to values. */ function loadTheme(theme) { _themeState.theme = theme; // reload styles. reloadStyles(); } exports.loadTheme = loadTheme; /** * Clear already registered style elements and style records in theme_State object * @param option - specify which group of registered styles should be cleared. * Default to be both themable and non-themable styles will be cleared */ function clearStyles(option) { if (option === void 0) { option = 3 /* ClearStyleOptions.all */; } if (option === 3 /* ClearStyleOptions.all */ || option === 2 /* ClearStyleOptions.onlyNonThemable */) { clearStylesInternal(_themeState.registeredStyles); _themeState.registeredStyles = []; } if (option === 3 /* ClearStyleOptions.all */ || option === 1 /* ClearStyleOptions.onlyThemable */) { clearStylesInternal(_themeState.registeredThemableStyles); _themeState.registeredThemableStyles = []; } } exports.clearStyles = clearStyles; function clearStylesInternal(records) { records.forEach(function (styleRecord) { var styleElement = styleRecord && styleRecord.styleElement; if (styleElement && styleElement.parentElement) { styleElement.parentElement.removeChild(styleElement); } }); } /** * Reloads styles. */ function reloadStyles() { if (_themeState.theme) { var themableStyles = []; for (var _i = 0, _a = _themeState.registeredThemableStyles; _i < _a.length; _i++) { var styleRecord = _a[_i]; themableStyles.push(styleRecord.themableStyle); } if (themableStyles.length > 0) { clearStyles(1 /* ClearStyleOptions.onlyThemable */); applyThemableStyles([].concat.apply([], themableStyles)); } } } /** * Find theme tokens and replaces them with provided theme values. * @param {string} styles Tokenized styles to fix. */ function detokenize(styles) { if (styles) { styles = resolveThemableArray(splitStyles(styles)).styleString; } return styles; } exports.detokenize = detokenize; /** * Resolves ThemingInstruction objects in an array and joins the result into a string. * @param {ThemableArray} splitStyleArray ThemableArray to resolve and join. */ function resolveThemableArray(splitStyleArray) { var theme = _themeState.theme; var themable = false; // Resolve the array of theming instructions to an array of strings. // Then join the array to produce the final CSS string. var resolvedArray = (splitStyleArray || []).map(function (currentValue) { var themeSlot = currentValue.theme; if (themeSlot) { themable = true; // A theming annotation. Resolve it. var themedValue = theme ? theme[themeSlot] : undefined; var defaultValue = currentValue.defaultValue || 'inherit'; // Warn to console if we hit an unthemed value even when themes are provided, but only if "DEBUG" is true. // Allow the themedValue to be undefined to explicitly request the default value. if (theme && !themedValue && console && !(themeSlot in theme) && typeof DEBUG !== 'undefined' && DEBUG) { console.warn("Theming value not provided for \"".concat(themeSlot, "\". Falling back to \"").concat(defaultValue, "\".")); } return themedValue || defaultValue; } else { // A non-themable string. Preserve it. return currentValue.rawString; } }); return { styleString: resolvedArray.join(''), themable: themable }; } /** * Split tokenized CSS into an array of strings and theme specification objects * @param {string} styles Tokenized styles to split. */ function splitStyles(styles) { var result = []; if (styles) { var pos = 0; // Current position in styles. var tokenMatch = void 0; while ((tokenMatch = _themeTokenRegex.exec(styles))) { var matchIndex = tokenMatch.index; if (matchIndex > pos) { result.push({ rawString: styles.substring(pos, matchIndex) }); } result.push({ theme: tokenMatch[1], defaultValue: tokenMatch[2] // May be undefined }); // index of the first character after the current match pos = _themeTokenRegex.lastIndex; } // Push the rest of the string after the last match. result.push({ rawString: styles.substring(pos) }); } return result; } exports.splitStyles = splitStyles; /** * Registers a set of style text. If it is registered too early, we will register it when the * window.load event is fired. * @param {ThemableArray} styleArray Array of IThemingInstruction objects to register. * @param {IStyleRecord} styleRecord May specify a style Element to update. */ function registerStyles(styleArray) { if (typeof document === 'undefined') { return; } var head = document.getElementsByTagName('head')[0]; var styleElement = document.createElement('style'); var _a = resolveThemableArray(styleArray), styleString = _a.styleString, themable = _a.themable; styleElement.setAttribute('data-load-themed-styles', 'true'); if (_styleNonce) { styleElement.setAttribute('nonce', _styleNonce); } styleElement.appendChild(document.createTextNode(styleString)); _themeState.perf.count++; head.appendChild(styleElement); var ev = document.createEvent('HTMLEvents'); ev.initEvent('styleinsert', true /* bubbleEvent */, false /* cancelable */); ev.args = { newStyle: styleElement }; document.dispatchEvent(ev); var record = { styleElement: styleElement, themableStyle: styleArray }; if (themable) { _themeState.registeredThemableStyles.push(record); } else { _themeState.registeredStyles.push(record); } } //# sourceMappingURL=index.js.map