whole_func_string: function filterUnusedRules(pages, css, ignore, usedSelectors) {
      let ignoreNextRule = false,
          ignoreNextRulesStart = false,
          unusedRules = [],
          unusedRuleSelectors,
          usedRuleSelectors;
      /* Rule format:
       *  { selectors: [ '...', '...' ],
       *    declarations: [ { property: '...', value: '...' } ]
       *  },.
       * Two steps: filter the unused selectors for each rule,
       *            filter the rules with no selectors
       */
      ignoreNextRule = false;
      css.walk((rule) => {
          if (rule.type === 'comment') {
              if (/^!?\s?uncss:ignore start\s?$/.test(rule.text)) { // ignore next rules while using comment `/* uncss:ignore start */`
                  ignoreNextRulesStart = true;
              } else if (/^!?\s?uncss:ignore end\s?$/.test(rule.text)) { // until `/* uncss:ignore end */` was found
                  ignoreNextRulesStart = false;
              } else if (/^!?\s?uncss:ignore\s?$/.test(rule.text)) { // ignore next rule while using comment `/* uncss:ignore */`
                  ignoreNextRule = true;
              }
          } else if (rule.type === 'rule') {
              if (rule.parent.type === 'atrule' && _.endsWith(rule.parent.name, 'keyframes')) {
                  // Don't remove animation keyframes that have selector names of '30%' or 'to'
                  return;
              }
              if (ignoreNextRulesStart) {
                  ignore = ignore.concat(rule.selectors);
              } else if (ignoreNextRule) {
                  ignoreNextRule = false;
                  ignore = ignore.concat(rule.selectors);
              }
  
              usedRuleSelectors = filterUnusedSelectors(
                  rule.selectors,
                  ignore,
                  usedSelectors
              );
              unusedRuleSelectors = rule.selectors.filter((selector) => usedRuleSelectors.indexOf(selector) < 0);
              if (unusedRuleSelectors && unusedRuleSelectors.length) {
                  unusedRules.push({
                      type: 'rule',
                      selectors: unusedRuleSelectors,
                      position: rule.source
                  });
              }
              if (usedRuleSelectors.length === 0) {
                  rule.remove();
              } else {
                  rule.selectors = usedRuleSelectors;
              }
          }
      });
  
      /* Filter the @media rules with no rules */
      filterEmptyAtRules(css);
  
      /* Filter unused @keyframes */
      filterKeyframes(css, unusedRules);
  
      return css;
  }
func_code_string: function filterUnusedRules(pages, css, ignore, usedSelectors) {
      let ignoreNextRule = false,
          ignoreNextRulesStart = false,
          unusedRules = [],
          unusedRuleSelectors,
          usedRuleSelectors;
      /* Rule format:
       *  { selectors: [ '...', '...' ],
       *    declarations: [ { property: '...', value: '...' } ]
       *  },.
       * Two steps: filter the unused selectors for each rule,
       *            filter the rules with no selectors
       */
      ignoreNextRule = false;
      css.walk((rule) => {
          if (rule.type === 'comment') {
              if (/^!?\s?uncss:ignore start\s?$/.test(rule.text)) { // ignore next rules while using comment `/* uncss:ignore start */`
                  ignoreNextRulesStart = true;
              } else if (/^!?\s?uncss:ignore end\s?$/.test(rule.text)) { // until `/* uncss:ignore end */` was found
                  ignoreNextRulesStart = false;
              } else if (/^!?\s?uncss:ignore\s?$/.test(rule.text)) { // ignore next rule while using comment `/* uncss:ignore */`
                  ignoreNextRule = true;
              }
          } else if (rule.type === 'rule') {
              if (rule.parent.type === 'atrule' && _.endsWith(rule.parent.name, 'keyframes')) {
                  // Don't remove animation keyframes that have selector names of '30%' or 'to'
                  return;
              }
              if (ignoreNextRulesStart) {
                  ignore = ignore.concat(rule.selectors);
              } else if (ignoreNextRule) {
                  ignoreNextRule = false;
                  ignore = ignore.concat(rule.selectors);
              }
  
              usedRuleSelectors = filterUnusedSelectors(
                  rule.selectors,
                  ignore,
                  usedSelectors
              );
              unusedRuleSelectors = rule.selectors.filter((selector) => usedRuleSelectors.indexOf(selector) < 0);
              if (unusedRuleSelectors && unusedRuleSelectors.length) {
                  unusedRules.push({
                      type: 'rule',
                      selectors: unusedRuleSelectors,
                      position: rule.source
                  });
              }
              if (usedRuleSelectors.length === 0) {
                  rule.remove();
              } else {
                  rule.selectors = usedRuleSelectors;
              }
          }
      });
  
      /* Filter the @media rules with no rules */
      filterEmptyAtRules(css);
  
      /* Filter unused @keyframes */
      filterKeyframes(css, unusedRules);
  
      return css;
  }
func_documentation_string: Remove css rules not used in the dom
  @param  {Array}  pages           List of jsdom pages
  @param  {Object} css             The postcss.Root node
  @param  {Array}  ignore          List of selectors to be ignored
  @param  {Array}  usedSelectors   List of selectors that are found in {pages}
  @return {Object}                 A css_parse-compatible stylesheet