text: // Convert straight quotation marks to typographic ones
  //
  'use strict';
  
  
  var QUOTE_TEST_RE = /['"]/;
  var QUOTE_RE = /['"]/g;
  var PUNCT_RE = /[-\s()\[\]]/;
  var APOSTROPHE = '’';
  
  // This function returns true if the character at `pos`
  // could be inside a word.
  function isLetter(str, pos) {
    if (pos < 0 || pos >= str.length) { return false; }
    return !PUNCT_RE.test(str[pos]);
  }
  
  
  function replaceAt(str, index, ch) {
    return str.substr(0, index) + ch + str.substr(index + 1);
  }
  
  
  module.exports = function smartquotes(state) {
    /*eslint max-depth:0*/
    var i, token, text, t, pos, max, thisLevel, lastSpace, nextSpace, item,
        canOpen, canClose, j, isSingle, blkIdx, tokens,
        stack;
  
    if (!state.options.typographer) { return; }
  
    stack = [];
  
    for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) {
  
      if (state.tokens[blkIdx].type !== 'inline') { continue; }
  
      tokens = state.tokens[blkIdx].children;
      stack.length = 0;
  
      for (i = 0; i < tokens.length; i++) {
        token = tokens[i];
  
        if (token.type !== 'text' || QUOTE_TEST_RE.test(token.text)) { continue; }
  
        thisLevel = tokens[i].level;
  
        for (j = stack.length - 1; j >= 0; j--) {
          if (stack[j].level <= thisLevel) { break; }
        }
        stack.length = j + 1;
  
        text = token.content;
        pos = 0;
        max = text.length;
  
        /*eslint no-labels:0,block-scoped-var:0*/
        OUTER:
        while (pos < max) {
          QUOTE_RE.lastIndex = pos;
          t = QUOTE_RE.exec(text);
          if (!t) { break; }
  
          lastSpace = !isLetter(text, t.index - 1);
          pos = t.index + 1;
          isSingle = (t[0] === "'");
          nextSpace = !isLetter(text, pos);
  
          if (!nextSpace && !lastSpace) {
            // middle of word
            if (isSingle) {
              token.content = replaceAt(token.content, t.index, APOSTROPHE);
            }
            continue;
          }
  
          canOpen = !nextSpace;
          canClose = !lastSpace;
  
          if (canClose) {
            // this could be a closing quote, rewind the stack to get a match
            for (j = stack.length - 1; j >= 0; j--) {
              item = stack[j];
              if (stack[j].level < thisLevel) { break; }
              if (item.single === isSingle && stack[j].level === thisLevel) {
                item = stack[j];
                if (isSingle) {
                  tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, state.options.quotes[2]);
                  token.content = replaceAt(token.content, t.index, state.options.quotes[3]);
                } else {
                  tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, state.options.quotes[0]);
                  token.content = replaceAt(token.content, t.index, state.options.quotes[1]);
                }
                stack.length = j;
                continue OUTER;
              }
            }
          }
  
          if (canOpen) {
            stack.push({
              token: i,
              pos: t.index,
              single: isSingle,
              level: thisLevel
            });
          } else if (canClose && isSingle) {
            token.content = replaceAt(token.content, t.index, APOSTROPHE);
          }
        }
      }
    }
  };
  