| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 | 'use strict';Object.defineProperty(exports, "__esModule", {  value: true});exports.parseMediaFeature = parseMediaFeature;exports.parseMediaQuery = parseMediaQuery;exports.parseMediaList = parseMediaList;var _Node = require('./nodes/Node');var _Node2 = _interopRequireDefault(_Node);var _Container = require('./nodes/Container');var _Container2 = _interopRequireDefault(_Container);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }/** * Parses a media feature expression, e.g. `max-width: 10px`, `(color)` * * @param {string} string - the source expression string, can be inside parens * @param {Number} index - the index of `string` in the overall input * * @return {Array} an array of Nodes, the first element being a media feature, *    the secont - its value (may be missing) */function parseMediaFeature(string) {  var index = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];  var modesEntered = [{    mode: 'normal',    character: null  }];  var result = [];  var lastModeIndex = 0;  var mediaFeature = '';  var colon = null;  var mediaFeatureValue = null;  var indexLocal = index;  var stringNormalized = string;  // Strip trailing parens (if any), and correct the starting index  if (string[0] === '(' && string[string.length - 1] === ')') {    stringNormalized = string.substring(1, string.length - 1);    indexLocal++;  }  for (var i = 0; i < stringNormalized.length; i++) {    var character = stringNormalized[i];    // If entering/exiting a string    if (character === '\'' || character === '"') {      if (modesEntered[lastModeIndex].isCalculationEnabled === true) {        modesEntered.push({          mode: 'string',          isCalculationEnabled: false,          character: character        });        lastModeIndex++;      } else if (modesEntered[lastModeIndex].mode === 'string' && modesEntered[lastModeIndex].character === character && stringNormalized[i - 1] !== '\\') {        modesEntered.pop();        lastModeIndex--;      }    }    // If entering/exiting interpolation    if (character === '{') {      modesEntered.push({        mode: 'interpolation',        isCalculationEnabled: true      });      lastModeIndex++;    } else if (character === '}') {      modesEntered.pop();      lastModeIndex--;    }    // If a : is met outside of a string, function call or interpolation, than    // this : separates a media feature and a value    if (modesEntered[lastModeIndex].mode === 'normal' && character === ':') {      var mediaFeatureValueStr = stringNormalized.substring(i + 1);      mediaFeatureValue = {        type: 'value',        before: /^(\s*)/.exec(mediaFeatureValueStr)[1],        after: /(\s*)$/.exec(mediaFeatureValueStr)[1],        value: mediaFeatureValueStr.trim()      };      // +1 for the colon      mediaFeatureValue.sourceIndex = mediaFeatureValue.before.length + i + 1 + indexLocal;      colon = {        type: 'colon',        sourceIndex: i + indexLocal,        after: mediaFeatureValue.before,        value: ':' };      break;    }    mediaFeature += character;  }  // Forming a media feature node  mediaFeature = {    type: 'media-feature',    before: /^(\s*)/.exec(mediaFeature)[1],    after: /(\s*)$/.exec(mediaFeature)[1],    value: mediaFeature.trim()  };  mediaFeature.sourceIndex = mediaFeature.before.length + indexLocal;  result.push(mediaFeature);  if (colon !== null) {    colon.before = mediaFeature.after;    result.push(colon);  }  if (mediaFeatureValue !== null) {    result.push(mediaFeatureValue);  }  return result;}/** * Parses a media query, e.g. `screen and (color)`, `only tv` * * @param {string} string - the source media query string * @param {Number} index - the index of `string` in the overall input * * @return {Array} an array of Nodes and Containers */function parseMediaQuery(string) {  var index = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];  var result = [];  // How many timies the parser entered parens/curly braces  var localLevel = 0;  // Has any keyword, media type, media feature expression or interpolation  // ('element' hereafter) started  var insideSomeValue = false;  var node = void 0;  function resetNode() {    return {      before: '',      after: '',      value: ''    };  }  node = resetNode();  for (var i = 0; i < string.length; i++) {    var character = string[i];    // If not yet entered any element    if (!insideSomeValue) {      if (character.search(/\s/) !== -1) {        // A whitespace        // Don't form 'after' yet; will do it later        node.before += character;      } else {        // Not a whitespace - entering an element        // Expression start        if (character === '(') {          node.type = 'media-feature-expression';          localLevel++;        }        node.value = character;        node.sourceIndex = index + i;        insideSomeValue = true;      }    } else {      // Already in the middle of some alement      node.value += character;      // Here parens just increase localLevel and don't trigger a start of      // a media feature expression (since they can't be nested)      // Interpolation start      if (character === '{' || character === '(') {        localLevel++;      }      // Interpolation/function call/media feature expression end      if (character === ')' || character === '}') {        localLevel--;      }    }    // If exited all parens/curlies and the next symbol    if (insideSomeValue && localLevel === 0 && (character === ')' || i === string.length - 1 || string[i + 1].search(/\s/) !== -1)) {      if (['not', 'only', 'and'].indexOf(node.value) !== -1) {        node.type = 'keyword';      }      // if it's an expression, parse its contents      if (node.type === 'media-feature-expression') {        node.nodes = parseMediaFeature(node.value, node.sourceIndex);      }      result.push(Array.isArray(node.nodes) ? new _Container2.default(node) : new _Node2.default(node));      node = resetNode();      insideSomeValue = false;    }  }  // Now process the result array - to specify undefined types of the nodes  // and specify the `after` prop  for (var _i = 0; _i < result.length; _i++) {    node = result[_i];    if (_i > 0) {      result[_i - 1].after = node.before;    }    // Node types. Might not be set because contains interpolation/function    // calls or fully consists of them    if (node.type === undefined) {      if (_i > 0) {        // only `and` can follow an expression        if (result[_i - 1].type === 'media-feature-expression') {          node.type = 'keyword';          continue;        }        // Anything after 'only|not' is a media type        if (result[_i - 1].value === 'not' || result[_i - 1].value === 'only') {          node.type = 'media-type';          continue;        }        // Anything after 'and' is an expression        if (result[_i - 1].value === 'and') {          node.type = 'media-feature-expression';          continue;        }        if (result[_i - 1].type === 'media-type') {          // if it is the last element - it might be an expression          // or 'and' depending on what is after it          if (!result[_i + 1]) {            node.type = 'media-feature-expression';          } else {            node.type = result[_i + 1].type === 'media-feature-expression' ? 'keyword' : 'media-feature-expression';          }        }      }      if (_i === 0) {        // `screen`, `fn( ... )`, `#{ ... }`. Not an expression, since then        // its type would have been set by now        if (!result[_i + 1]) {          node.type = 'media-type';          continue;        }        // `screen and` or `#{...} (max-width: 10px)`        if (result[_i + 1] && (result[_i + 1].type === 'media-feature-expression' || result[_i + 1].type === 'keyword')) {          node.type = 'media-type';          continue;        }        if (result[_i + 2]) {          // `screen and (color) ...`          if (result[_i + 2].type === 'media-feature-expression') {            node.type = 'media-type';            result[_i + 1].type = 'keyword';            continue;          }          // `only screen and ...`          if (result[_i + 2].type === 'keyword') {            node.type = 'keyword';            result[_i + 1].type = 'media-type';            continue;          }        }        if (result[_i + 3]) {          // `screen and (color) ...`          if (result[_i + 3].type === 'media-feature-expression') {            node.type = 'keyword';            result[_i + 1].type = 'media-type';            result[_i + 2].type = 'keyword';            continue;          }        }      }    }  }  return result;}/** * Parses a media query list. Takes a possible `url()` at the start into * account, and divides the list into media queries that are parsed separately * * @param {string} string - the source media query list string * * @return {Array} an array of Nodes/Containers */function parseMediaList(string) {  var result = [];  var interimIndex = 0;  var levelLocal = 0;  // Check for a `url(...)` part (if it is contents of an @import rule)  var doesHaveUrl = /^(\s*)url\s*\(/.exec(string);  if (doesHaveUrl !== null) {    var i = doesHaveUrl[0].length;    var parenthesesLv = 1;    while (parenthesesLv > 0) {      var character = string[i];      if (character === '(') {        parenthesesLv++;      }      if (character === ')') {        parenthesesLv--;      }      i++;    }    result.unshift(new _Node2.default({      type: 'url',      value: string.substring(0, i).trim(),      sourceIndex: doesHaveUrl[1].length,      before: doesHaveUrl[1],      after: /^(\s*)/.exec(string.substring(i))[1]    }));    interimIndex = i;  }  // Start processing the media query list  for (var _i2 = interimIndex; _i2 < string.length; _i2++) {    var _character = string[_i2];    // Dividing the media query list into comma-separated media queries    // Only count commas that are outside of any parens    // (i.e., not part of function call params list, etc.)    if (_character === '(') {      levelLocal++;    }    if (_character === ')') {      levelLocal--;    }    if (levelLocal === 0 && _character === ',') {      var _mediaQueryString = string.substring(interimIndex, _i2);      var _spaceBefore = /^(\s*)/.exec(_mediaQueryString)[1];      result.push(new _Container2.default({        type: 'media-query',        value: _mediaQueryString.trim(),        sourceIndex: interimIndex + _spaceBefore.length,        nodes: parseMediaQuery(_mediaQueryString, interimIndex),        before: _spaceBefore,        after: /(\s*)$/.exec(_mediaQueryString)[1]      }));      interimIndex = _i2 + 1;    }  }  var mediaQueryString = string.substring(interimIndex);  var spaceBefore = /^(\s*)/.exec(mediaQueryString)[1];  result.push(new _Container2.default({    type: 'media-query',    value: mediaQueryString.trim(),    sourceIndex: interimIndex + spaceBefore.length,    nodes: parseMediaQuery(mediaQueryString, interimIndex),    before: spaceBefore,    after: /(\s*)$/.exec(mediaQueryString)[1]  }));  return result;}
 |