function sanitizeCssSelectors(selectors, suffix) {
  // Produce two distinct lists of selectors to sequester selectors that are
  // history sensitive (:visited), so that we can disallow properties in the
  // property groups for the history sensitive ones.
  var historySensitiveSelectors = [];
  var historyInsensitiveSelectors = [];

  // Remove any spaces that are not operators.
  var k = 0, i;
  for (i = 0; i < selectors.length; ++i) {
    if (!(selectors[i] == ' '
          && (selectors[i-1] == '>' || selectors[i+1] == '>'))) {
      selectors[k++] = selectors[i];
    }
  }
  selectors.length = k;

  // Split around commas.  If there is an error in one of the comma separated
  // bits, we throw the whole away, but the failure of one selector does not
  // affect others.
  var n = selectors.length, start = 0;
  for (i = 0; i < n; ++i) {
    if (selectors[i] == ',') {
      processSelector(start, i);
      start = i+1;
    }
  }
  processSelector(start, n);


  function processSelector(start, end) {
    var historySensitive = false;

    // Space around commas is not an operator.
    if (selectors[start] === ' ') { ++start; }
    if (end-1 !== start && selectors[end] === ' ') { --end; }

    // Split the selector into element selectors, content around
    // space (ancestor operator) and '>' (descendant operator).
    var out = [];
    var lastOperator = start;
    var elSelector = '';
    for (var i = start; i < end; ++i) {
      var tok = selectors[i];
      var isChild = (tok === '>');
      if (isChild || tok === ' ') {
        // We've found the end of a single link in the selector chain.
        // We disallow absolute positions relative to html.
        elSelector = processElementSelector(lastOperator, i, false);
        if (!elSelector || (isChild && /^html/i.test(elSelector))) {
          return;
        }
        lastOperator = i+1;
        out.push(elSelector, isChild ? ' > ' : ' ');
      }
    }
    elSelector = processElementSelector(lastOperator, end, true);
    if (!elSelector) { return; }
    out.push(elSelector);

    function processElementSelector(start, end, last) {
      var debugStart = start, debugEnd = end;

      // Split the element selector into three parts.
      // DIV.foo#bar:hover
      //    ^       ^
      // el classes pseudo
      var element, classId, pseudoSelector, tok, elType;
      element = '';
      if (start < end) {
        tok = selectors[start].toLowerCase();
        if (tok === '*'
            || (tok === 'body' && start+1 !== end && !last)
            || ('number' === typeof (elType = html4.ELEMENTS[tok])
                && !(elType & html4.eflags.UNSAFE))) {
          ++start;
          element = tok;
        }
      }
      classId = '';
      while (start < end) {
        tok = selectors[start];
        if (tok.charAt(0) === '#') {
          if (/^#_|__$|[^#0-9A-Za-z:_\-]/.test(tok)) { return null; }
          // Rewrite ID elements to include the suffix.
          classId += tok + '-' + suffix;
        } else if (tok === '.') {
          if (++start < end
              && /^[0-9A-Za-z:_\-]+$/.test(tok = selectors[start])
              && !/^_|__$/.test(tok)) {
            classId += '.' + tok;
          } else {
            return null;
          }
        } else {
          break;
        }
        ++start;
      }
      pseudoSelector = '';
      if (start < end && selectors[start] === ':') {
        tok = selectors[++start];
        if (tok === 'visited' || tok === 'link') {
          if (!/^[a*]?$/.test(element)) {
            return null;
          }
          historySensitive = true;
          pseudoSelector = ':' + tok;
          element = 'a';
          ++start;
        }
      }
      if (start === end) {
        return element + classId + pseudoSelector;
      }
      return null;
    }


    var safeSelector = out.join('');
    if (/^body\b/.test(safeSelector)) {
      // Substitute the class that is attached to pseudo body elements for
      // the body element.
      safeSelector = '.vdoc-body___.' + suffix + safeSelector.substring(4);
    } else {
      // Namespace the selector so that it only matches under
      // a node with suffix in its CLASS attribute.
      safeSelector = '.' + suffix + ' ' + safeSelector;
    }

    (historySensitive
     ? historySensitiveSelectors
     : historyInsensitiveSelectors).push(safeSelector);
  }

  return [historyInsensitiveSelectors, historySensitiveSelectors];
}
/**
 * Given a series of tokens, returns two lists of sanitized selectors.
 * @param {Array.<string>} selectors In the form produces by csslexer.js.
 * @param {string} suffix a suffix that is added to all IDs and which is
 *    used as a CLASS names so that the returned selectors will only match
 *    nodes under one with suffix as a class name.
 *    If suffix is {@code "sfx"}, the selector
 *    {@code ["a", "#foo", " ", "b", ".bar"]} will be namespaced to
 *    {@code [".sfx", " ", "a", "#foo-sfx", " ", "b", ".bar"]}.
 * @return {Array.<Array.<string>>} an array of length 2 where the zeroeth
 *    element contains history-insensitive selectors and the first element
 *    contains history-sensitive selectors.
 */
Given a series of tokens, returns two lists of sanitized selectors.
@param {Array.} selectors In the form produces by csslexer.js.
@param {string} suffix a suffix that is added to all IDs and which is
used as a CLASS names so that the returned selectors will only match
nodes under one with suffix as a class name.
Given a series of tokens returns two lists of sanitized selectors . @param { Array . } selectors In the form produces by csslexer . js . @param { string } suffix a suffix that is added to all IDs and which is used as a CLASS names so that the returned selectors will only match nodes under one with suffix as a class name .
function sanitizeCssSelectors(selectors, suffix) {
  var historySensitiveSelectors = [];
  var historyInsensitiveSelectors = [];
  var k = 0, i;
  for (i = 0; i < selectors.length; ++i) {
    if (!(selectors[i] == ' '
          && (selectors[i-1] == '>' || selectors[i+1] == '>'))) {
      selectors[k++] = selectors[i];
    }
  }
  selectors.length = k;
  var n = selectors.length, start = 0;
  for (i = 0; i < n; ++i) {
    if (selectors[i] == ',') {
      processSelector(start, i);
      start = i+1;
    }
  }
  processSelector(start, n);
  function processSelector(start, end) {
    var historySensitive = false;
    if (selectors[start] === ' ') { ++start; }
    if (end-1 !== start && selectors[end] === ' ') { --end; }
    var out = [];
    var lastOperator = start;
    var elSelector = '';
    for (var i = start; i < end; ++i) {
      var tok = selectors[i];
      var isChild = (tok === '>');
      if (isChild || tok === ' ') {
        elSelector = processElementSelector(lastOperator, i, false);
        if (!elSelector || (isChild && /^html/i.test(elSelector))) {
          return;
        }
        lastOperator = i+1;
        out.push(elSelector, isChild ? ' > ' : ' ');
      }
    }
    elSelector = processElementSelector(lastOperator, end, true);
    if (!elSelector) { return; }
    out.push(elSelector);
    function processElementSelector(start, end, last) {
      var debugStart = start, debugEnd = end;
      var element, classId, pseudoSelector, tok, elType;
      element = '';
      if (start < end) {
        tok = selectors[start].toLowerCase();
        if (tok === '*'
            || (tok === 'body' && start+1 !== end && !last)
            || ('number' === typeof (elType = html4.ELEMENTS[tok])
                && !(elType & html4.eflags.UNSAFE))) {
          ++start;
          element = tok;
        }
      }
      classId = '';
      while (start < end) {
        tok = selectors[start];
        if (tok.charAt(0) === '#') {
          if (/^#_|__$|[^#0-9A-Za-z:_\-]/.test(tok)) { return null; }
          classId += tok + '-' + suffix;
        } else if (tok === '.') {
          if (++start < end
              && /^[0-9A-Za-z:_\-]+$/.test(tok = selectors[start])
              && !/^_|__$/.test(tok)) {
            classId += '.' + tok;
          } else {
            return null;
          }
        } else {
          break;
        }
        ++start;
      }
      pseudoSelector = '';
      if (start < end && selectors[start] === ':') {
        tok = selectors[++start];
        if (tok === 'visited' || tok === 'link') {
          if (!/^[a*]?$/.test(element)) {
            return null;
          }
          historySensitive = true;
          pseudoSelector = ':' + tok;
          element = 'a';
          ++start;
        }
      }
      if (start === end) {
        return element + classId + pseudoSelector;
      }
      return null;
    }
    var safeSelector = out.join('');
    if (/^body\b/.test(safeSelector)) {
      safeSelector = '.vdoc-body___.' + suffix + safeSelector.substring(4);
    } else {
      safeSelector = '.' + suffix + ' ' + safeSelector;
    }
    (historySensitive
     ? historySensitiveSelectors
     : historyInsensitiveSelectors).push(safeSelector);
  }
  return [historyInsensitiveSelectors, historySensitiveSelectors];
}
function sanitizeCssSelectors ( selectors , suffix ) { var historySensitiveSelectors = [ ] ; var historyInsensitiveSelectors = [ ] ; var k = 0 , i ; for ( i = 0 ; i < selectors . length ; ++ i ) { if ( ! ( selectors [ i ] == ' ' && ( selectors [ i - 1 ] == '>' || selectors [ i + 1 ] == '>' ) ) ) { selectors [ k ++ ] = selectors [ i ] ; } } selectors . length = k ; var n = selectors . length , start = 0 ; for ( i = 0 ; i < n ; ++ i ) { if ( selectors [ i ] == ',' ) { processSelector ( start , i ) ; start = i + 1 ; } } processSelector ( start , n ) ; function processSelector ( start , end ) { var historySensitive = false ; if ( selectors [ start ] === ' ' ) { ++ start ; } if ( end - 1 !== start && selectors [ end ] === ' ' ) { -- end ; } var out = [ ] ; var lastOperator = start ; var elSelector = '' ; for ( var i = start ; i < end ; ++ i ) { var tok = selectors [ i ] ; var isChild = ( tok === '>' ) ; if ( isChild || tok === ' ' ) { elSelector = processElementSelector ( lastOperator , i , false ) ; if ( ! elSelector || ( isChild && / ^html / i . test ( elSelector ) ) ) { return ; } lastOperator = i + 1 ; out . push ( elSelector , isChild ? ' > ' : ' ' ) ; } } elSelector = processElementSelector ( lastOperator , end , true ) ; if ( ! elSelector ) { return ; } out . push ( elSelector ) ; function processElementSelector ( start , end , last ) { var debugStart = start , debugEnd = end ; var element , classId , pseudoSelector , tok , elType ; element = '' ; if ( start < end ) { tok = selectors [ start ] . toLowerCase ( ) ; if ( tok === '*' || ( tok === 'body' && start + 1 !== end && ! last ) || ( 'number' === typeof ( elType = html4 . ELEMENTS [ tok ] ) && ! ( elType & html4 . eflags . UNSAFE ) ) ) { ++ start ; element = tok ; } } classId = '' ; while ( start < end ) { tok = selectors [ start ] ; if ( tok . charAt ( 0 ) === '#' ) { if ( / ^#_|__$|[^#0-9A-Za-z:_\-] / . test ( tok ) ) { return null ; } classId += tok + '-' + suffix ; } else if ( tok === '.' ) { if ( ++ start < end && / ^[0-9A-Za-z:_\-]+$ / . test ( tok = selectors [ start ] ) && ! / ^_|__$ / . test ( tok ) ) { classId += '.' + tok ; } else { return null ; } } else { break ; } ++ start ; } pseudoSelector = '' ; if ( start < end && selectors [ start ] === ':' ) { tok = selectors [ ++ start ] ; if ( tok === 'visited' || tok === 'link' ) { if ( ! / ^[a*]?$ / . test ( element ) ) { return null ; } historySensitive = true ; pseudoSelector = ':' + tok ; element = 'a' ; ++ start ; } } if ( start === end ) { return element + classId + pseudoSelector ; } return null ; } var safeSelector = out . join ( '' ) ; if ( / ^body\b / . test ( safeSelector ) ) { safeSelector = '.vdoc-body___.' + suffix + safeSelector . substring ( 4 ) ; } else { safeSelector = '.' + suffix + ' ' + safeSelector ; } ( historySensitive ? historySensitiveSelectors : historyInsensitiveSelectors ) . push ( safeSelector ) ; } return [ historyInsensitiveSelectors , historySensitiveSelectors ] ; }
Given a series of tokens, returns two lists of sanitized selectors.
Given a series of tokens returns two lists of sanitized selectors .
// Produce two distinct lists of selectors to sequester selectors that are // history sensitive (:visited), so that we can disallow properties in the // property groups for the history sensitive ones. // Remove any spaces that are not operators. // Split around commas.  If there is an error in one of the comma separated // bits, we throw the whole away, but the failure of one selector does not // affect others. // Space around commas is not an operator. // Split the selector into element selectors, content around // space (ancestor operator) and '>' (descendant operator). // We've found the end of a single link in the selector chain. // We disallow absolute positions relative to html. // Split the element selector into three parts. // DIV.foo#bar:hover //    ^       ^ // el classes pseudo // Rewrite ID elements to include the suffix. // Substitute the class that is attached to pseudo body elements for // the body element. // Namespace the selector so that it only matches under // a node with suffix in its CLASS attribute.