Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { uniq, reduce, map, filter, debounce, sortBy, difference } from 'lodash';
- /**
- * Watches for new stylesheets for a period of time, returns a unique list of
- * pattern match captures from CSS selectors. Designed to handle a single
- * capture group per match, so the resulting array of matches will always be
- * strings. The observer is a singleton, callers should pass callbacks that will
- * be called when new matches are found. Uses `MutationObserver` to watch for
- * stylesheets in the document head. Accepts RegExp `pattern` and an options
- * hash for `debounceLength`/`lifespan` in milliseconds, defaulting to 5000 and
- * 20000 respectively, and `matchIndex`, defaulting to 1.
- *
- * Designed to detect CodeMirror themes by checking for a telltale selector
- * across all stylesheets and parsing the unique theme name from the selector.
- * Note that I ultimately never used it, as observing and processing style
- * changes is costly for performance, and the potential for problems wasn't
- * worth it.
- *
- * Regexp for parsing CodeMirror themes in case its useful:
- * /^\.cm-s-([\w-]+)\.CodeMirror$/
- *
- * To use with a React app, call **once per component** that requires the data:
- *
- * ```js
- * componentDidMount() {
- * const cb = themes => this.setState({ themes });
- * const pattern = /^\.cm-s-([\w-]+)\.CodeMirror$/;
- * observeStyles(cb, pattern);
- * }
- *
- * Once the observer expires (20s from init by default), further calls to
- * observeStyles are safe and will do nothing.
- */
- let headObserver;
- let matches = [];
- const headObserverCallbacks = [];
- export default function observeStyles(cb, pattern, opts = {}) {
- const { debounceLength = 5000, lifespan = 20000, matchIndex = 1 } = opts;
- if (headObserver === undefined) {
- headObserver = new MutationObserver(debounce(
- () => onObserveStyles(pattern),
- debounceLength,
- { leading: true },
- ));
- headObserver.observe(document.head, { childList: true });
- setTimeout(() => {
- headObserver.disconnect();
- headObserver = null;
- }, lifespan);
- } else if (headObserver !== null) {
- if (matches.length > 0) {
- cb(matches);
- }
- headObserverCallbacks.push(cb);
- } else {
- cb(matches);
- }
- }
- function onObserveStyles(pattern, matchIndex) {
- const parsedMatches = parseMatchesFromStyles(pattern, matchIndex);
- if (difference(parsedMatches, matches).length > 0) {
- matches = parsedMatches;
- headObserverCallbacks.forEach(cb => cb(matches));
- }
- }
- function parseMatchesFromStyles(pattern, matchIndex) {
- const matches = reduce(document.styleSheets, (acc, { cssRules }) => {
- const matches = map(cssRules, ({ selectorText }) => {
- return selectorText?.match(pattern)?.[matchIndex];
- });
- return acc.concat(matches);
- }, []);
- return sortBy(uniq(filter(matches, v => v)));
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement