CasperSecurity

Current Path : /var/www/hrms.uiet.co.in/node_modules/svgo/plugins/
Upload File :
Current File : /var/www/hrms.uiet.co.in/node_modules/svgo/plugins/inlineStyles.js

'use strict';

/**
 * @typedef {import('../lib/types').Specificity} Specificity
 * @typedef {import('../lib/types').XastElement} XastElement
 * @typedef {import('../lib/types').XastParent} XastParent
 */

const csstree = require('css-tree');
// @ts-ignore not defined in @types/csso
const specificity = require('csso/lib/restructure/prepare/specificity');
const stable = require('stable');
const {
  visitSkip,
  querySelectorAll,
  detachNodeFromParent,
} = require('../lib/xast.js');

exports.type = 'visitor';
exports.name = 'inlineStyles';
exports.active = true;
exports.description = 'inline styles (additional options)';

/**
 * Compares two selector specificities.
 * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
 *
 * @type {(a: Specificity, b: Specificity) => number}
 */
const compareSpecificity = (a, b) => {
  for (var i = 0; i < 4; i += 1) {
    if (a[i] < b[i]) {
      return -1;
    } else if (a[i] > b[i]) {
      return 1;
    }
  }
  return 0;
};

/**
 * Moves + merges styles from style elements to element styles
 *
 * Options
 *   onlyMatchedOnce (default: true)
 *     inline only selectors that match once
 *
 *   removeMatchedSelectors (default: true)
 *     clean up matched selectors,
 *     leave selectors that hadn't matched
 *
 *   useMqs (default: ['', 'screen'])
 *     what media queries to be used
 *     empty string element for styles outside media queries
 *
 *   usePseudos (default: [''])
 *     what pseudo-classes/-elements to be used
 *     empty string element for all non-pseudo-classes and/or -elements
 *
 * @author strarsis <strarsis@gmail.com>
 *
 * @type {import('../lib/types').Plugin<{
 *   onlyMatchedOnce?: boolean,
 *   removeMatchedSelectors?: boolean,
 *   useMqs?: Array<string>,
 *   usePseudos?: Array<string>
 * }>}
 */
exports.fn = (root, params) => {
  const {
    onlyMatchedOnce = true,
    removeMatchedSelectors = true,
    useMqs = ['', 'screen'],
    usePseudos = [''],
  } = params;

  /**
   * @type {Array<{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }>}
   */
  const styles = [];
  /**
   * @type {Array<{
   *   node: csstree.Selector,
   *   item: csstree.ListItem<csstree.CssNode>,
   *   rule: csstree.Rule,
   *   matchedElements?: Array<XastElement>
   * }>}
   */
  let selectors = [];

  return {
    element: {
      enter: (node, parentNode) => {
        // skip <foreignObject /> content
        if (node.name === 'foreignObject') {
          return visitSkip;
        }
        // collect only non-empty <style /> elements
        if (node.name !== 'style' || node.children.length === 0) {
          return;
        }
        // values other than the empty string or text/css are not used
        if (
          node.attributes.type != null &&
          node.attributes.type !== '' &&
          node.attributes.type !== 'text/css'
        ) {
          return;
        }
        // parse css in style element
        let cssText = '';
        for (const child of node.children) {
          if (child.type === 'text' || child.type === 'cdata') {
            cssText += child.value;
          }
        }
        /**
         * @type {null | csstree.CssNode}
         */
        let cssAst = null;
        try {
          cssAst = csstree.parse(cssText, {
            parseValue: false,
            parseCustomProperty: false,
          });
        } catch {
          return;
        }
        if (cssAst.type === 'StyleSheet') {
          styles.push({ node, parentNode, cssAst });
        }

        // collect selectors
        csstree.walk(cssAst, {
          visit: 'Selector',
          enter(node, item) {
            const atrule = this.atrule;
            const rule = this.rule;
            if (rule == null) {
              return;
            }

            // skip media queries not included into useMqs param
            let mq = '';
            if (atrule != null) {
              mq = atrule.name;
              if (atrule.prelude != null) {
                mq += ` ${csstree.generate(atrule.prelude)}`;
              }
            }
            if (useMqs.includes(mq) === false) {
              return;
            }

            /**
             * @type {Array<{
             *   item: csstree.ListItem<csstree.CssNode>,
             *   list: csstree.List<csstree.CssNode>
             * }>}
             */
            const pseudos = [];
            if (node.type === 'Selector') {
              node.children.each((childNode, childItem, childList) => {
                if (
                  childNode.type === 'PseudoClassSelector' ||
                  childNode.type === 'PseudoElementSelector'
                ) {
                  pseudos.push({ item: childItem, list: childList });
                }
              });
            }

            // skip pseudo classes and pseudo elements not includes into usePseudos param
            const pseudoSelectors = csstree.generate({
              type: 'Selector',
              children: new csstree.List().fromArray(
                pseudos.map((pseudo) => pseudo.item.data)
              ),
            });
            if (usePseudos.includes(pseudoSelectors) === false) {
              return;
            }

            // remove pseudo classes and elements to allow querySelector match elements
            // TODO this is not very accurate since some pseudo classes like first-child
            // are used for selection
            for (const pseudo of pseudos) {
              pseudo.list.remove(pseudo.item);
            }

            selectors.push({ node, item, rule });
          },
        });
      },
    },

    root: {
      exit: () => {
        if (styles.length === 0) {
          return;
        }
        // stable sort selectors
        const sortedSelectors = stable(selectors, (a, b) => {
          const aSpecificity = specificity(a.item.data);
          const bSpecificity = specificity(b.item.data);
          return compareSpecificity(aSpecificity, bSpecificity);
        }).reverse();

        for (const selector of sortedSelectors) {
          // match selectors
          const selectorText = csstree.generate(selector.item.data);
          /**
           * @type {Array<XastElement>}
           */
          const matchedElements = [];
          try {
            for (const node of querySelectorAll(root, selectorText)) {
              if (node.type === 'element') {
                matchedElements.push(node);
              }
            }
          } catch (selectError) {
            continue;
          }
          // nothing selected
          if (matchedElements.length === 0) {
            continue;
          }

          // apply styles to matched elements
          // skip selectors that match more than once if option onlyMatchedOnce is enabled
          if (onlyMatchedOnce && matchedElements.length > 1) {
            continue;
          }

          // apply <style/> to matched elements
          for (const selectedEl of matchedElements) {
            const styleDeclarationList = csstree.parse(
              selectedEl.attributes.style == null
                ? ''
                : selectedEl.attributes.style,
              {
                context: 'declarationList',
                parseValue: false,
              }
            );
            if (styleDeclarationList.type !== 'DeclarationList') {
              continue;
            }
            const styleDeclarationItems = new Map();
            csstree.walk(styleDeclarationList, {
              visit: 'Declaration',
              enter(node, item) {
                styleDeclarationItems.set(node.property, item);
              },
            });
            // merge declarations
            csstree.walk(selector.rule, {
              visit: 'Declaration',
              enter(ruleDeclaration) {
                // existing inline styles have higher priority
                // no inline styles, external styles,                                    external styles used
                // inline styles,    external styles same   priority as inline styles,   inline   styles used
                // inline styles,    external styles higher priority than inline styles, external styles used
                const matchedItem = styleDeclarationItems.get(
                  ruleDeclaration.property
                );
                const ruleDeclarationItem =
                  styleDeclarationList.children.createItem(ruleDeclaration);
                if (matchedItem == null) {
                  styleDeclarationList.children.append(ruleDeclarationItem);
                } else if (
                  matchedItem.data.important !== true &&
                  ruleDeclaration.important === true
                ) {
                  styleDeclarationList.children.replace(
                    matchedItem,
                    ruleDeclarationItem
                  );
                  styleDeclarationItems.set(
                    ruleDeclaration.property,
                    ruleDeclarationItem
                  );
                }
              },
            });
            selectedEl.attributes.style =
              csstree.generate(styleDeclarationList);
          }

          if (
            removeMatchedSelectors &&
            matchedElements.length !== 0 &&
            selector.rule.prelude.type === 'SelectorList'
          ) {
            // clean up matching simple selectors if option removeMatchedSelectors is enabled
            selector.rule.prelude.children.remove(selector.item);
          }
          selector.matchedElements = matchedElements;
        }

        // no further processing required
        if (removeMatchedSelectors === false) {
          return;
        }

        // clean up matched class + ID attribute values
        for (const selector of sortedSelectors) {
          if (selector.matchedElements == null) {
            continue;
          }

          if (onlyMatchedOnce && selector.matchedElements.length > 1) {
            // skip selectors that match more than once if option onlyMatchedOnce is enabled
            continue;
          }

          for (const selectedEl of selector.matchedElements) {
            // class
            const classList = new Set(
              selectedEl.attributes.class == null
                ? null
                : selectedEl.attributes.class.split(' ')
            );
            const firstSubSelector = selector.node.children.first();
            if (
              firstSubSelector != null &&
              firstSubSelector.type === 'ClassSelector'
            ) {
              classList.delete(firstSubSelector.name);
            }
            if (classList.size === 0) {
              delete selectedEl.attributes.class;
            } else {
              selectedEl.attributes.class = Array.from(classList).join(' ');
            }

            // ID
            if (
              firstSubSelector != null &&
              firstSubSelector.type === 'IdSelector'
            ) {
              if (selectedEl.attributes.id === firstSubSelector.name) {
                delete selectedEl.attributes.id;
              }
            }
          }
        }

        for (const style of styles) {
          csstree.walk(style.cssAst, {
            visit: 'Rule',
            enter: function (node, item, list) {
              // clean up <style/> rulesets without any css selectors left
              if (
                node.type === 'Rule' &&
                node.prelude.type === 'SelectorList' &&
                node.prelude.children.isEmpty()
              ) {
                list.remove(item);
              }
            },
          });

          if (style.cssAst.children.isEmpty()) {
            // remove emtpy style element
            detachNodeFromParent(style.node, style.parentNode);
          } else {
            // update style element if any styles left
            const firstChild = style.node.children[0];
            if (firstChild.type === 'text' || firstChild.type === 'cdata') {
              firstChild.value = csstree.generate(style.cssAst);
            }
          }
        }
      },
    },
  };
};
Hacker Blog, Shell İndir, Sql İnjection, XSS Attacks, LFI Attacks, Social Hacking, Exploit Bot, Proxy Tools, Web Shell, PHP Shell, Alfa Shell İndir, Hacking Training Set, DDoS Script, Denial Of Service, Botnet, RFI Attacks, Encryption
Telegram @BIBIL_0DAY