text: var typecastArrayValues = require('../../utils/typecast/arrayValues');
  
  /**
   * A pattern lexer heavily influenced by Crossroads.js by Miller Medeiros (https://github.com/millermedeiros/crossroads.js)
   * date - Jul 29, 2012
   * crossroads.js commit - 3b413b0b506b0c04f80b03194d4c1abaeccc9574
   * @type {Class}
   */
  
      //match chars that should be escaped on string regexp
  var ESCAPE_CHARS_REGEXP = /[\\.+*?\^$\[\](){}\/'#]/g,
  
      //trailing slashes (begin/end of string)
      LOOSE_SLASHES_REGEXP = /^\/|\/$/g,
      LEGACY_SLASHES_REGEXP = /\/$/g,
  
      //params - everything between `{ }` or `: :`
      PARAMS_REGEXP = /(?:\{|:)([^}:]+)(?:\}|:)/g,
  
      //used to save params during compile (avoid escaping things that
      //shouldn't be escaped).
      TOKENS = {
          'OS' : {
              //optional slashes
              //slash between `::` or `}:` or `\w:` or `:{?` or `}{?` or `\w{?`
              rgx : /([:}]|\w(?=\/))\/?(:|(?:\{\?))/g,
              save : '$1{{id}}$2',
              res : '\\/?'
          },
          'RS' : {
              //required slashes
              //used to insert slash between `:{` and `}{`
              rgx : /([:}])\/?(\{)/g,
              save : '$1{{id}}$2',
              res : '\\/'
          },
          'RQ' : {
              //required query string - everything in between `{? }`
              rgx : /\{\?([^}]+)\}/g,
              //everything from `?` till `#` or end of string
              res : '\\?([^#]+)'
          },
          'OQ' : {
              //optional query string - everything in between `:? :`
              rgx : /:\?([^:]+):/g,
              //everything from `?` till `#` or end of string
              res : '(?:\\?([^#]*))?'
          },
          'OR' : {
              //optional rest - everything in between `: *:`
              rgx : /:([^:]+)\*:/g,
              res : '(.*)?' // optional group to avoid passing empty string as captured
          },
          'RR' : {
              //rest param - everything in between `{ *}`
              rgx : /\{([^}]+)\*\}/g,
              res : '(.+)'
          },
          // required/optional params should come after rest segments
          'RP' : {
              //required params - everything between `{ }`
              rgx : /\{([^}]+)\}/g,
              res : '([^\\/?]+)'
          },
          'OP' : {
              //optional params - everything between `: :`
              rgx : /:([^:]+):/g,
              res : '([^\\/?]+)?\/?'
          }
      },
  
      LOOSE_SLASH = 1,
      STRICT_SLASH = 2,
      LEGACY_SLASH = 3,
  
      _slashMode = LOOSE_SLASH;
  
  
  function precompileTokens(){
      var key, cur;
      for (key in TOKENS) {
          if (TOKENS.hasOwnProperty(key)) {
              cur = TOKENS[key];
              cur.id = '__CR_'+ key +'__';
              cur.save = ('save' in cur)? cur.save.replace('{{id}}', cur.id) : cur.id;
              cur.rRestore = new RegExp(cur.id, 'g');
          }
      }
  }
  precompileTokens();
  
  
  function captureVals(regex, pattern) {
      var vals = [], match;
      // very important to reset lastIndex since RegExp can have "g" flag
      // and multiple runs might affect the result, specially if matching
      // same string multiple times on IE 7-8
      regex.lastIndex = 0;
      while (match = regex.exec(pattern)) {
          vals.push(match[1]);
      }
      return vals;
  }
  
  function getParamIds(pattern) {
      return captureVals(PARAMS_REGEXP, pattern);
  }
  
  function getOptionalParamsIds(pattern) {
      return captureVals(TOKENS.OP.rgx, pattern);
  }
  
  function compilePattern(pattern) {
      pattern = pattern || '';
  
      if(pattern){
          if (_slashMode === LOOSE_SLASH) {
              pattern = pattern.replace(LOOSE_SLASHES_REGEXP, '');
          }
          else if (_slashMode === LEGACY_SLASH) {
              pattern = pattern.replace(LEGACY_SLASHES_REGEXP, '');
          }
  
          //save tokens
          pattern = replaceTokens(pattern, 'rgx', 'save');
          //regexp escape
          pattern = pattern.replace(ESCAPE_CHARS_REGEXP, '\\$&');
          //restore tokens
          pattern = replaceTokens(pattern, 'rRestore', 'res');
  
          if (_slashMode === LOOSE_SLASH) {
              pattern = '\\/?'+ pattern;
          }
      }
  
      if (_slashMode !== STRICT_SLASH) {
          //single slash is treated as empty and end slash is optional
          pattern += '\\/?';
      }
      return new RegExp('^'+ pattern + '$');
  }
  
  function replaceTokens(pattern, regexpName, replaceName) {
      var cur, key;
      for (key in TOKENS) {
          if (TOKENS.hasOwnProperty(key)) {
              cur = TOKENS[key];
              pattern = pattern.replace(cur[regexpName], cur[replaceName]);
          }
      }
      return pattern;
  }
  
  function getParamValues(request, regexp, shouldTypecast) {
      var vals = regexp.exec(request);
      if (vals) {
          vals.shift();
          if (shouldTypecast) {
              vals = typecastArrayValues(vals);
          }
      }
      return vals;
  }
  
  function interpolate(pattern, replacements) {
      if (typeof pattern !== 'string') {
          throw new Error('Route pattern should be a string.');
      }
  
      var replaceFn = function(match, prop){
          var val;
          if (prop in replacements) {
              // make sure value is a string see #gh-54
              val = String(replacements[prop]);
              if (match.indexOf('*') === -1 && val.indexOf('/') !== -1) {
                  throw new Error('Invalid value "'+ val +'" for segment "'+ match +'".');
              }
          }
          else if (match.indexOf('{') !== -1) {
              throw new Error('The segment '+ match +' is required.');
          }
          else {
              val = '';
          }
          return val;
      };
  
      if (! TOKENS.OS.trail) {
          TOKENS.OS.trail = new RegExp('(?:'+ TOKENS.OS.id +')+$');
      }
  
      return pattern
          .replace(TOKENS.OS.rgx, TOKENS.OS.save)
          .replace(PARAMS_REGEXP, replaceFn)
          .replace(TOKENS.OS.trail, '') // remove trailing
          .replace(TOKENS.OS.rRestore, '/'); // add slash between segments
  }
  
  //API
  exports = module.exports = {
      strict : function(){
          _slashMode = STRICT_SLASH;
      },
      loose : function(){
          _slashMode = LOOSE_SLASH;
      },
      legacy : function(){
          _slashMode = LEGACY_SLASH;
      },
      getParamIds : getParamIds,
      getOptionalParamsIds : getOptionalParamsIds,
      getParamValues : getParamValues,
      compilePattern : compilePattern,
      interpolate : interpolate
  };