text: '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 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 === ':') {
        mediaFeatureValue = stringNormalized.substring(i + 1);
        break;
      }
  
      mediaFeature += character;
    }
  
    // Forming a media feature node
    result.push({
      type: 'media-feature',
      before: /^(\s*)/.exec(mediaFeature)[1],
      after: /(\s*)$/.exec(mediaFeature)[1],
      value: mediaFeature.trim()
    });
    result[0].sourceIndex = result[0].before.length + indexLocal;
  
    if (mediaFeatureValue !== null) {
      result.push({
        type: 'value',
        before: /^(\s*)/.exec(mediaFeatureValue)[1],
        after: /(\s*)$/.exec(mediaFeatureValue)[1],
        value: mediaFeatureValue.trim()
      });
      result[1].sourceIndex = result[1].before.length + mediaFeature.length + indexLocal + 1;
    }
  
    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;
  }