"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.assign = void 0;
var _postcss = _interopRequireDefault(require("postcss"));
var _postcss_plugin = _interopRequireDefault(require("../../helpers/postcss_plugin"));
var _plugin = _interopRequireDefault(require("../../plugin"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** @module */

const uniqKeyChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const uniqKeyCharsLength = uniqKeyChars.length;
const generateScopeAttr = uniqKey => `data-marpit-scope-${uniqKey}`;
const generateUniqKey = (length = 8) => {
  let ret = '';
  for (let i = 0; i < length; i += 1) ret += uniqKeyChars[Math.floor(Math.random() * uniqKeyCharsLength)];
  return ret;
};
const injectScopePostCSSplugin = (0, _postcss_plugin.default)('marpit-style-assign-postcss-inject-scope', (key, keyframeSet) => css => css.each(function inject(node) {
  const {
    type,
    name
  } = node;
  if (type === 'atrule') {
    if (name === 'keyframes' && node.params) {
      keyframeSet.add(node.params);
      node.params += `-${key}`;
    } else if (name === 'media' || name === 'supports') {
      node.each(inject);
    }
  } else if (type === 'rule') {
    node.selectors = node.selectors.map(selector => {
      const injectSelector = /^section(?![\w-])/.test(selector) ? selector.slice(7) : ` ${selector}`;
      return `section[${generateScopeAttr(key)}]${injectSelector}`;
    });
  }
}));
const scopeKeyframesPostCSSPlugin = (0, _postcss_plugin.default)('marpit-style-assign-postcss-scope-keyframes', (key, keyframeSet) => css => {
  if (keyframeSet.size === 0) return;
  const keyframeMatcher = new RegExp(`\\b(${[...keyframeSet.values()].map(kf => kf.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&')).join('|')})(?!\\()\\b`);
  css.walkDecls(/^animation(-name)?$/, decl => {
    decl.value = decl.value.replace(keyframeMatcher, kf => `${kf}-${key}`);
  });
});

/**
 * Marpit style assign plugin.
 *
 * Assign style global directive and parsed styles to Marpit instance's
 * `lastStyles' property.
 *
 * @function assign
 * @param {MarkdownIt} md markdown-it instance.
 */
function _assign(md) {
  const {
    marpit
  } = md;
  md.core.ruler.after('marpit_slide', 'marpit_style_assign', state => {
    if (state.inlineMode) return;
    const directives = marpit.lastGlobalDirectives || {};
    marpit.lastStyles = directives.style ? [directives.style] : [];
    let current;
    for (const token of state.tokens) {
      if (token.meta && token.meta.marpitSlideElement === 1) {
        current = token;
      } else if (token.meta && token.meta.marpitSlideElement === -1) {
        if (current.meta && current.meta.marpitStyleScoped) {
          const {
            key,
            keyframeSet,
            styles
          } = current.meta.marpitStyleScoped;

          // Rewrite keyframes name in animation decls
          const processor = (0, _postcss.default)([scopeKeyframesPostCSSPlugin(key, keyframeSet)]);
          current.meta.marpitStyleScoped.styles = styles.map(style => {
            try {
              return processor.process(style).css;
            } catch {
              return style;
            }
          });

          // Assign scoped styles
          marpit.lastStyles.push(...current.meta.marpitStyleScoped.styles);
        }
        current = undefined;
      } else if (token.type === 'marpit_style') {
        const {
          content
        } = token;

        // Scoped style
        const {
          marpitStyleScoped
        } = token.meta || {};
        if (current && marpitStyleScoped) {
          current.meta = current.meta || {};
          current.meta.marpitStyleScoped = current.meta.marpitStyleScoped || {};
          let {
            key
          } = current.meta.marpitStyleScoped;
          if (!key) {
            key = generateUniqKey();
            current.meta.marpitStyleScoped.key = key;
            current.attrSet(generateScopeAttr(key), '');
          }
          current.meta.marpitStyleScoped.styles = current.meta.marpitStyleScoped.styles || [];
          current.meta.marpitStyleScoped.keyframeSet = current.meta.marpitStyleScoped.keyframeSet || new Set();
          const processor = (0, _postcss.default)([injectScopePostCSSplugin(key, current.meta.marpitStyleScoped.keyframeSet)]);
          try {
            current.meta.marpitStyleScoped.styles.push(processor.process(content).css);
          } catch {
            // No ops
          }
        } else if (content) {
          // Global style
          marpit.lastStyles.push(content);
        }
      }
    }
  });
}
const assign = exports.assign = (0, _plugin.default)(_assign);
var _default = exports.default = assign;