[
  {
    "path": ".gitignore",
    "content": "dist\nnode_modules\n"
  },
  {
    "path": "README.md",
    "content": "# live coding react components\n\nThis repo provides one webpage and three components.\n\n## index.html\n\nA simple example of `<LiveEditor>` in use.\n\n## code-mirror-editor.js\n\nA code mirror react component (called `<CodeMirrorEditor>` since code mirror took the name `CodeMirror`).\n\n## live-compile.js\n\nDynamic compilation of react components.\n\n## live-editor.js\n\nCombination of `<CodeMirrorEditor>` and `<ComponentPreview>` which live-compiles what you type!\n\n# Running the demo\n\n```\n> webpack\n> open index.html\n```\n\nYou should see something like this:\n\n![](live-editor.gif)\n"
  },
  {
    "path": "code-mirror-editor.js",
    "content": "var React = require(\"react\");\nvar CodeMirror = require('codemirror');\nrequire('codemirror/mode/javascript/javascript');\nrequire('codemirror/addon/fold/foldcode');\nimport CodeMirrorHighlight from './code-mirror-highlight';\n\n\nvar IS_MOBILE = (\n  navigator.userAgent.match(/Android/i)\n    || navigator.userAgent.match(/webOS/i)\n    || navigator.userAgent.match(/iPhone/i)\n    || navigator.userAgent.match(/iPad/i)\n    || navigator.userAgent.match(/iPod/i)\n    || navigator.userAgent.match(/BlackBerry/i)\n    || navigator.userAgent.match(/Windows Phone/i)\n);\n\nvar OPEN_MARK = /{{{/;\nvar CLOSE_MARK = /}}}/;\n\nCodeMirror.registerGlobalHelper('fold', 'marked',\n    function(mode, mirror) {\n        return mode.name === 'javascript';\n    },\n    function(mirror, start) {\n        var lineNo = start.line;\n        var lineText = mirror.getLine(lineNo);\n        var lineCount = mirror.lineCount();\n\n        var openMatch = OPEN_MARK.exec(lineText);\n        var closeMatch = CLOSE_MARK.exec(lineText);\n\n        if (openMatch) {\n            // search forwards\n            for (var i = lineNo; i < lineCount; i++) {\n                closeMatch = CLOSE_MARK.exec(mirror.getLine(i));\n                if (closeMatch) {\n                    return {\n                        from: CodeMirror.Pos(lineNo, openMatch.index),\n                        to: CodeMirror.Pos(i, closeMatch.index + 3)\n                    };\n                }\n            }\n\n        } else if (closeMatch) {\n            // search backwards\n            for (var i = lineNo; i >= 0; i--) {\n                openMatch = OPEN_MARK.exec(mirror.getLine(i));\n                if (openMatch) {\n                    return {\n                        from: CodeMirror.Pos(i, openMatch.index),\n                        to: CodeMirror.Pos(lineNo, closeMatch.index + 3)\n                    };\n                }\n            }\n        }\n    }\n);\n\nvar CodeMirrorEditor = React.createClass({\n  getDefaultProps() {\n    return {\n      renderType: IS_MOBILE ? 'pre' : 'textarea',\n    }\n  },\n\n  componentDidMount: function() {\n    if (this.props.renderType === 'textarea') {\n      this.instantiateTextarea();\n    }\n  },\n\n  instantiateTextarea() {\n    this.editor = CodeMirror.fromTextArea(this.refs.editor, {\n      mode: 'javascript',\n      lineNumbers: false,\n      lineWrapping: true,\n      smartIndent: false,  // javascript mode does bad things with jsx indents\n      matchBrackets: true,\n      theme: 'solarized-light',\n      readOnly: this.props.readOnly\n    });\n    this.editor.foldCode(0, { widget: '...' });\n    this.editor.on('change', this.handleChange);\n\n    this.editor.on('beforeSelectionChange', (instance, obj) => {\n        // why is ranges plural?\n        var selection = obj.ranges ?\n            obj.ranges[0] :\n            obj;\n\n        var noRange = selection.anchor.ch === selection.head.ch &&\n                      selection.anchor.line === selection.head.line;\n        if (!noRange) {\n            return;\n        }\n\n        var cursor = selection.anchor;\n        var line = instance.getLine(cursor.line);\n        var match = OPEN_MARK.exec(line) || CLOSE_MARK.exec(line);\n\n            // the opening or closing mark appears on this line\n        if (match &&\n            // and the cursor is on it\n            // (this is buggy if both occur on the same line)\n            cursor.ch >= match.index &&\n            cursor.ch < match.index + 3) {\n\n                // TODO(joel) - figure out why this doesn't fold although it\n                // seems like it should work.\n                instance.foldCode(cursor, { widget: '...' });\n        }\n    });\n  },\n\n  componentDidUpdate: function() {\n    if (this.props.readOnly) {\n      this.editor.setValue(this.props.codeText);\n    }\n  },\n\n  handleChange: function() {\n    if (!this.props.readOnly && this.props.onChange) {\n      this.props.onChange(this.editor.getValue());\n    }\n  },\n\n  render: function() {\n    // wrap in a div to fully contain CodeMirror\n    var editor;\n\n    if (this.props.renderType === 'pre') {\n      editor = <CodeMirrorHighlight codeText={this.props.codeText} />;\n    } else {\n      editor = <textarea ref=\"editor\" defaultValue={this.props.codeText} />;\n    }\n\n    return (\n      <div style={this.props.style} className={this.props.className}>\n        {editor}\n      </div>\n    );\n  }\n});\n\nmodule.exports = CodeMirrorEditor;\n"
  },
  {
    "path": "code-mirror-highlight.js",
    "content": "import React from 'react';\nimport CodeMirror from 'codemirror';\nimport JSParser from './code-mirror-highlighting/jsparser';\nimport stringStream from './code-mirror-highlighting/stringstream';\n\nconst indentUnit = 2;\n\nfunction normaliseString(string) {\n  var tab = \"\";\n  for (var i = 0; i < indentUnit; i++) tab += \" \";\n\n  string = string.replace(/\\t/g, tab).replace(/\\u00a0/g, \" \").replace(/\\r\\n?/g, \"\\n\");\n  var pos = 0, parts = [], lines = string.split(\"\\n\");\n  for (var line = 0; line < lines.length; line++) {\n    if (line != 0) parts.push(\"\\n\");\n    parts.push(lines[line]);\n  }\n\n  return {\n    next: function() {\n      if (pos < parts.length) return parts[pos++];\n      else throw StopIteration;\n    }\n  };\n}\n\nfunction highlightText(string, callback) {\n  const parser = JSParser.make(stringStream(normaliseString(string)));\n  const result = [];\n  let i = 0;\n  try {\n    while (true) {\n      var token = parser.next();\n      result.push(\n        token.value === '\\n'\n          ? <br key={i} />\n          : <span key={i} className={token.style}>{token.value}</span>\n      );\n      i++;\n    }\n  }\n  catch (e) {\n    if (e != StopIteration) throw e;\n  }\n  return result;\n}\n\nexport default function CodeMirrorHighlight({codeText}) {\n  return (\n    <pre\n      style={{overflow: 'scroll'}}\n      className=\"CodeMirror cm-s-solarized-light\"\n    >\n      {highlightText(codeText)}\n    </pre>\n  );\n}\n"
  },
  {
    "path": "code-mirror-highlighting/jsparser.js",
    "content": "/* Parse function for JavaScript. Makes use of the tokenizer from\n * tokenizejavascript.js. Note that your parsers do not have to be\n * this complicated -- if you don't want to recognize local variables,\n * in many languages it is enough to just look for braces, semicolons,\n * parentheses, etc, and know when you are inside a string or comment.\n *\n * See manual.html for more info about the parser interface.\n */\nvar tokenizeJavaScript = require('./tokenizejs').default;\n\nvar indentUnit = 2;\n\nvar JSParser = (function() {\n  // Token types that can be considered to be atoms.\n  var atomicTypes = {\"atom\": true, \"number\": true, \"variable\": true, \"string\": true, \"regexp\": true};\n  // Setting that can be used to have JSON data indent properly.\n  var json = false;\n  // Constructor for the lexical context objects.\n  function JSLexical(indented, column, type, align, prev, info) {\n    // indentation at start of this line\n    this.indented = indented;\n    // column at which this scope was opened\n    this.column = column;\n    // type of scope ('vardef', 'stat' (statement), 'form' (special form), '[', '{', or '(')\n    this.type = type;\n    // '[', '{', or '(' blocks that have any text after their opening\n    // character are said to be 'aligned' -- any lines below are\n    // indented all the way to the opening character.\n    if (align != null)\n      this.align = align;\n    // Parent scope, if any.\n    this.prev = prev;\n    this.info = info;\n  }\n\n  // My favourite JavaScript indentation rules.\n  function indentJS(lexical) {\n    return function(firstChars) {\n      var firstChar = firstChars && firstChars.charAt(0), type = lexical.type;\n      var closing = firstChar == type;\n      if (type == \"vardef\")\n        return lexical.indented + 4;\n      else if (type == \"form\" && firstChar == \"{\")\n        return lexical.indented;\n      else if (type == \"stat\" || type == \"form\")\n        return lexical.indented + indentUnit;\n      else if (lexical.info == \"switch\" && !closing)\n        return lexical.indented + (/^(?:case|default)\\b/.test(firstChars) ? indentUnit : 2 * indentUnit);\n      else if (lexical.align)\n        return lexical.column - (closing ? 1 : 0);\n      else\n        return lexical.indented + (closing ? 0 : indentUnit);\n    };\n  }\n\n  // The parser-iterator-producing function itself.\n  function parseJS(input, basecolumn) {\n    // Wrap the input in a token stream\n    var tokens = tokenizeJavaScript(input);\n    // The parser state. cc is a stack of actions that have to be\n    // performed to finish the current statement. For example we might\n    // know that we still need to find a closing parenthesis and a\n    // semicolon. Actions at the end of the stack go first. It is\n    // initialized with an infinitely looping action that consumes\n    // whole statements.\n    var cc = [json ? expressions : statements];\n    // Context contains information about the current local scope, the\n    // variables defined in that, and the scopes above it.\n    var context = null;\n    // The lexical scope, used mostly for indentation.\n    var lexical = new JSLexical((basecolumn || 0) - indentUnit, 0, \"block\", false);\n    // Current column, and the indentation at the start of the current\n    // line. Used to create lexical scope objects.\n    var column = 0;\n    var indented = 0;\n    // Variables which are used by the mark, cont, and pass functions\n    // below to communicate with the driver loop in the 'next'\n    // function.\n    var consume, marked;\n\n    // The iterator object.\n    var parser = {next: next, copy: copy};\n\n    function next(){\n      // Start by performing any 'lexical' actions (adjusting the\n      // lexical variable), or the operations below will be working\n      // with the wrong lexical state.\n      while(cc[cc.length - 1].lex)\n        cc.pop()();\n\n      // Fetch a token.\n      var token = tokens.next();\n\n      // Adjust column and indented.\n      if (token.type == \"whitespace\" && column == 0)\n        indented = token.value.length;\n      column += token.value.length;\n      if (token.content == \"\\n\"){\n        indented = column = 0;\n        // If the lexical scope's align property is still undefined at\n        // the end of the line, it is an un-aligned scope.\n        if (!(\"align\" in lexical))\n          lexical.align = false;\n        // Newline tokens get an indentation function associated with\n        // them.\n        token.indentation = indentJS(lexical);\n      }\n      // No more processing for meaningless tokens.\n      if (token.type == \"whitespace\" || token.type == \"comment\")\n        return token;\n      // When a meaningful token is found and the lexical scope's\n      // align is undefined, it is an aligned scope.\n      if (!(\"align\" in lexical))\n        lexical.align = true;\n\n      // Execute actions until one 'consumes' the token and we can\n      // return it.\n      while(true) {\n        consume = marked = false;\n        // Take and execute the topmost action.\n        cc.pop()(token.type, token.content);\n        if (consume){\n          // Marked is used to change the style of the current token.\n          if (marked)\n            token.style = marked;\n          // Here we differentiate between local and global variables.\n          else if (token.type == \"variable\" && inScope(token.content))\n            token.style = \"cm-localvariable\";\n          return token;\n        }\n      }\n    }\n\n    // This makes a copy of the parser state. It stores all the\n    // stateful variables in a closure, and returns a function that\n    // will restore them when called with a new input stream. Note\n    // that the cc array has to be copied, because it is contantly\n    // being modified. Lexical objects are not mutated, and context\n    // objects are not mutated in a harmful way, so they can be shared\n    // between runs of the parser.\n    function copy(){\n      var _context = context, _lexical = lexical, _cc = cc.concat([]), _tokenState = tokens.state;\n\n      return function copyParser(input){\n        context = _context;\n        lexical = _lexical;\n        cc = _cc.concat([]); // copies the array\n        column = indented = 0;\n        tokens = tokenizeJavaScript(input, _tokenState);\n        return parser;\n      };\n    }\n\n    // Helper function for pushing a number of actions onto the cc\n    // stack in reverse order.\n    function push(fs){\n      for (var i = fs.length - 1; i >= 0; i--)\n        cc.push(fs[i]);\n    }\n    // cont and pass are used by the action functions to add other\n    // actions to the stack. cont will cause the current token to be\n    // consumed, pass will leave it for the next action.\n    function cont(){\n      push(arguments);\n      consume = true;\n    }\n    function pass(){\n      push(arguments);\n      consume = false;\n    }\n    // Used to change the style of the current token.\n    function mark(style){\n      marked = style;\n    }\n\n    // Push a new scope. Will automatically link the current scope.\n    function pushcontext(){\n      context = {prev: context, vars: {\"this\": true, \"arguments\": true}};\n    }\n    // Pop off the current scope.\n    function popcontext(){\n      context = context.prev;\n    }\n    // Register a variable in the current scope.\n    function register(varname){\n      if (context){\n        mark(\"cm-variabledef\");\n        context.vars[varname] = true;\n      }\n    }\n    // Check whether a variable is defined in the current scope.\n    function inScope(varname){\n      var cursor = context;\n      while (cursor) {\n        if (cursor.vars[varname])\n          return true;\n        cursor = cursor.prev;\n      }\n      return false;\n    }\n\n    // Push a new lexical context of the given type.\n    function pushlex(type, info) {\n      var result = function(){\n        lexical = new JSLexical(indented, column, type, null, lexical, info)\n      };\n      result.lex = true;\n      return result;\n    }\n    // Pop off the current lexical context.\n    function poplex(){\n      if (lexical.type == \")\")\n        indented = lexical.indented;\n      lexical = lexical.prev;\n    }\n    poplex.lex = true;\n    // The 'lex' flag on these actions is used by the 'next' function\n    // to know they can (and have to) be ran before moving on to the\n    // next token.\n\n    // Creates an action that discards tokens until it finds one of\n    // the given type.\n    function expect(wanted){\n      return function expecting(type){\n        if (type == wanted) cont();\n        else if (wanted == \";\") pass();\n        else cont(expecting);\n        // else cont(arguments.callee);\n      };\n    }\n\n    // Looks for a statement, and then calls itself.\n    function statements(type){\n      return pass(statement, statements);\n    }\n    function expressions(type){\n      return pass(expression, expressions);\n    }\n    // Dispatches various types of statements based on the type of the\n    // current token.\n    function statement(type){\n      if (type == \"var\") cont(pushlex(\"vardef\"), vardef1, expect(\";\"), poplex);\n      else if (type == \"keyword a\") cont(pushlex(\"form\"), expression, statement, poplex);\n      else if (type == \"keyword b\") cont(pushlex(\"form\"), statement, poplex);\n      else if (type == \"{\") cont(pushlex(\"}\"), block, poplex);\n      else if (type == \";\") cont();\n      else if (type == \"function\") cont(functiondef);\n      else if (type == \"for\") cont(pushlex(\"form\"), expect(\"(\"), pushlex(\")\"), forspec1, expect(\")\"), poplex, statement, poplex);\n      else if (type == \"variable\") cont(pushlex(\"stat\"), maybelabel);\n      else if (type == \"switch\") cont(pushlex(\"form\"), expression, pushlex(\"}\", \"switch\"), expect(\"{\"), block, poplex, poplex);\n      else if (type == \"case\") cont(expression, expect(\":\"));\n      else if (type == \"default\") cont(expect(\":\"));\n      else if (type == \"catch\") cont(pushlex(\"form\"), pushcontext, expect(\"(\"), funarg, expect(\")\"), statement, poplex, popcontext);\n      else pass(pushlex(\"stat\"), expression, expect(\";\"), poplex);\n    }\n    // Dispatch expression types.\n    function expression(type){\n      if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator);\n      else if (type == \"function\") cont(functiondef);\n      else if (type == \"keyword c\") cont(expression);\n      else if (type == \"(\") cont(pushlex(\")\"), expression, expect(\")\"), poplex, maybeoperator);\n      else if (type == \"operator\") cont(expression);\n      else if (type == \"[\") cont(pushlex(\"]\"), commasep(expression, \"]\"), poplex, maybeoperator);\n      else if (type == \"{\") cont(pushlex(\"}\"), commasep(objprop, \"}\"), poplex, maybeoperator);\n      else cont();\n    }\n    // Called for places where operators, function calls, or\n    // subscripts are valid. Will skip on to the next action if none\n    // is found.\n    function maybeoperator(type, value){\n      if (type == \"operator\" && /\\+\\+|--/.test(value)) cont(maybeoperator);\n      else if (type == \"operator\") cont(expression);\n      else if (type == \";\") pass();\n      else if (type == \"(\") cont(pushlex(\")\"), commasep(expression, \")\"), poplex, maybeoperator);\n      else if (type == \".\") cont(property, maybeoperator);\n      else if (type == \"[\") cont(pushlex(\"]\"), expression, expect(\"]\"), poplex, maybeoperator);\n    }\n    // When a statement starts with a variable name, it might be a\n    // label. If no colon follows, it's a regular statement.\n    function maybelabel(type){\n      if (type == \":\") cont(poplex, statement);\n      else pass(maybeoperator, expect(\";\"), poplex);\n    }\n    // Property names need to have their style adjusted -- the\n    // tokenizer thinks they are variables.\n    function property(type){\n      if (type == \"variable\") {mark(\"cm-property\"); cont();}\n    }\n    // This parses a property and its value in an object literal.\n    function objprop(type){\n      if (type == \"variable\") mark(\"cm-property\");\n      if (atomicTypes.hasOwnProperty(type)) cont(expect(\":\"), expression);\n    }\n    // Parses a comma-separated list of the things that are recognized\n    // by the 'what' argument.\n    function commasep(what, end){\n      function proceed(type) {\n        if (type == \",\") cont(what, proceed);\n        else if (type == end) cont();\n        else cont(expect(end));\n      }\n      return function commaSeparated(type) {\n        if (type == end) cont();\n        else pass(what, proceed);\n      };\n    }\n    // Look for statements until a closing brace is found.\n    function block(type){\n      if (type == \"}\") cont();\n      else pass(statement, block);\n    }\n    // Variable definitions are split into two actions -- 1 looks for\n    // a name or the end of the definition, 2 looks for an '=' sign or\n    // a comma.\n    function vardef1(type, value){\n      if (type == \"variable\"){register(value); cont(vardef2);}\n      else cont();\n    }\n    function vardef2(type, value){\n      if (value == \"=\") cont(expression, vardef2);\n      else if (type == \",\") cont(vardef1);\n    }\n    // For loops.\n    function forspec1(type){\n      if (type == \"var\") cont(vardef1, forspec2);\n      else if (type == \";\") pass(forspec2);\n      else if (type == \"variable\") cont(formaybein);\n      else pass(forspec2);\n    }\n    function formaybein(type, value){\n      if (value == \"in\") cont(expression);\n      else cont(maybeoperator, forspec2);\n    }\n    function forspec2(type, value){\n      if (type == \";\") cont(forspec3);\n      else if (value == \"in\") cont(expression);\n      else cont(expression, expect(\";\"), forspec3);\n    }\n    function forspec3(type) {\n      if (type == \")\") pass();\n      else cont(expression);\n    }\n    // A function definition creates a new context, and the variables\n    // in its argument list have to be added to this context.\n    function functiondef(type, value){\n      if (type == \"variable\"){register(value); cont(functiondef);}\n      else if (type == \"(\") cont(pushlex(\")\"), pushcontext, commasep(funarg, \")\"), poplex, statement, popcontext);\n    }\n    function funarg(type, value){\n      if (type == \"variable\"){register(value); cont();}\n    }\n\n    return parser;\n  }\n\n  return {\n    make: parseJS,\n    electricChars: \"{}:\",\n    configure: function(obj) {\n      if (obj.json != null) json = obj.json;\n    }\n  };\n})();\n\nmodule.exports = JSParser;\n"
  },
  {
    "path": "code-mirror-highlighting/stringstream.js",
    "content": "/* String streams are the things fed to parsers (which can feed them\n * to a tokenizer if they want). They provide peek and next methods\n * for looking at the current character (next 'consumes' this\n * character, peek does not), and a get method for retrieving all the\n * text that was consumed since the last time get was called.\n *\n * An easy mistake to make is to let a StopIteration exception finish\n * the token stream while there are still characters pending in the\n * string stream (hitting the end of the buffer while parsing a\n * token). To make it easier to detect such errors, the stringstreams\n * throw an exception when this happens.\n */\n\nwindow.StopIteration = {toString: function() {return \"StopIteration\"}};\n\n// Make a stringstream stream out of an iterator that returns strings.\n// This is applied to the result of traverseDOM (see codemirror.js),\n// and the resulting stream is fed to the parser.\nvar stringStream = function(source){\n  // String that's currently being iterated over.\n  var current = \"\";\n  // Position in that string.\n  var pos = 0;\n  // Accumulator for strings that have been iterated over but not\n  // get()-ed yet.\n  var accum = \"\";\n  // Make sure there are more characters ready, or throw\n  // StopIteration.\n  function ensureChars() {\n    while (pos == current.length) {\n      accum += current;\n      current = \"\"; // In case source.next() throws\n      pos = 0;\n      try {current = source.next();}\n      catch (e) {\n        if (e != StopIteration) throw e;\n        else return false;\n      }\n    }\n    return true;\n  }\n\n  return {\n    // peek: -> character\n    // Return the next character in the stream.\n    peek: function() {\n      if (!ensureChars()) return null;\n      return current.charAt(pos);\n    },\n    // next: -> character\n    // Get the next character, throw StopIteration if at end, check\n    // for unused content.\n    next: function() {\n      if (!ensureChars()) {\n        if (accum.length > 0)\n          throw \"End of stringstream reached without emptying buffer ('\" + accum + \"').\";\n        else\n          throw StopIteration;\n      }\n      return current.charAt(pos++);\n    },\n    // get(): -> string\n    // Return the characters iterated over since the last call to\n    // .get().\n    get: function() {\n      var temp = accum;\n      accum = \"\";\n      if (pos > 0){\n        temp += current.slice(0, pos);\n        current = current.slice(pos);\n        pos = 0;\n      }\n      return temp;\n    },\n    // Push a string back into the stream.\n    push: function(str) {\n      current = current.slice(0, pos) + str + current.slice(pos);\n    },\n    lookAhead: function(str, consume, skipSpaces, caseInsensitive) {\n      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}\n      str = cased(str);\n      var found = false;\n\n      var _accum = accum, _pos = pos;\n      if (skipSpaces) this.nextWhileMatches(/[\\s\\u00a0]/);\n\n      while (true) {\n        var end = pos + str.length, left = current.length - pos;\n        if (end <= current.length) {\n          found = str == cased(current.slice(pos, end));\n          pos = end;\n          break;\n        }\n        else if (str.slice(0, left) == cased(current.slice(pos))) {\n          accum += current; current = \"\";\n          try {current = source.next();}\n          catch (e) {if (e != StopIteration) throw e; break;}\n          pos = 0;\n          str = str.slice(left);\n        }\n        else {\n          break;\n        }\n      }\n\n      if (!(found && consume)) {\n        current = accum.slice(_accum.length) + current;\n        pos = _pos;\n        accum = _accum;\n      }\n\n      return found;\n    },\n    // Wont't match past end of line.\n    lookAheadRegex: function(regex, consume) {\n      if (regex.source.charAt(0) != \"^\")\n        throw new Error(\"Regexps passed to lookAheadRegex must start with ^\");\n\n      // Fetch the rest of the line\n      while (current.indexOf(\"\\n\", pos) == -1) {\n        try {current += source.next();}\n        catch (e) {if (e != StopIteration) throw e; break;}\n      }\n      var matched = current.slice(pos).match(regex);\n      if (matched && consume) pos += matched[0].length;\n      return matched;\n    },\n\n    // Utils built on top of the above\n    // more: -> boolean\n    // Produce true if the stream isn't empty.\n    more: function() {\n      return this.peek() !== null;\n    },\n    applies: function(test) {\n      var next = this.peek();\n      return (next !== null && test(next));\n    },\n    nextWhile: function(test) {\n      var next;\n      while ((next = this.peek()) !== null && test(next))\n        this.next();\n    },\n    matches: function(re) {\n      var next = this.peek();\n      return (next !== null && re.test(next));\n    },\n    nextWhileMatches: function(re) {\n      var next;\n      while ((next = this.peek()) !== null && re.test(next))\n        this.next();\n    },\n    equals: function(ch) {\n      return ch === this.peek();\n    },\n    endOfLine: function() {\n      var next = this.peek();\n      return next == null || next == \"\\n\";\n    }\n  };\n};\n\nexport default stringStream;\n"
  },
  {
    "path": "code-mirror-highlighting/tokenizejs.js",
    "content": "/* Tokenizer for JavaScript code */\nimport tokenizer from './tokenizer';\n\nvar tokenizeJavaScript = (function() {\n  // Advance the stream until the given character (not preceded by a\n  // backslash) is encountered, or the end of the line is reached.\n  function nextUntilUnescaped(source, end) {\n    var escaped = false;\n    while (!source.endOfLine()) {\n      var next = source.next();\n      if (next == end && !escaped)\n        return false;\n      escaped = !escaped && next == \"\\\\\";\n    }\n    return escaped;\n  }\n\n  // A map of JavaScript's keywords. The a/b/c keyword distinction is\n  // very rough, but it gives the parser enough information to parse\n  // correct code correctly (we don't care that much how we parse\n  // incorrect code). The style information included in these objects\n  // is used by the highlighter to pick the correct CSS style for a\n  // token.\n  var keywords = function(){\n    function result(type, style){\n      return {type: type, style: \"cm-\" + style};\n    }\n    // keywords that take a parenthised expression, and then a\n    // statement (if)\n    var keywordA = result(\"keyword a\", \"keyword\");\n    // keywords that take just a statement (else)\n    var keywordB = result(\"keyword b\", \"keyword\");\n    // keywords that optionally take an expression, and form a\n    // statement (return)\n    var keywordC = result(\"keyword c\", \"keyword\");\n    var operator = result(\"operator\", \"keyword\");\n    var atom = result(\"atom\", \"atom\");\n    return {\n      \"if\": keywordA, \"while\": keywordA, \"with\": keywordA,\n      \"else\": keywordB, \"do\": keywordB, \"try\": keywordB, \"finally\": keywordB,\n      \"return\": keywordC, \"break\": keywordC, \"continue\": keywordC, \"new\": keywordC, \"delete\": keywordC, \"throw\": keywordC,\n      \"in\": operator, \"typeof\": operator, \"instanceof\": operator,\n      \"var\": result(\"var\", \"keyword\"), \"function\": result(\"function\", \"keyword\"), \"catch\": result(\"catch\", \"keyword\"),\n      \"for\": result(\"for\", \"keyword\"), \"switch\": result(\"switch\", \"keyword\"),\n      \"case\": result(\"case\", \"keyword\"), \"default\": result(\"default\", \"keyword\"),\n      \"true\": atom, \"false\": atom, \"null\": atom, \"undefined\": atom, \"NaN\": atom, \"Infinity\": atom\n    };\n  }();\n\n  // Some helper regexps\n  var isOperatorChar = /[+\\-*&%=<>!?|]/;\n  var isHexDigit = /[0-9A-Fa-f]/;\n  var isWordChar = /[\\w\\$_]/;\n\n  // Wrapper around jsToken that helps maintain parser state (whether\n  // we are inside of a multi-line comment and whether the next token\n  // could be a regular expression).\n  function jsTokenState(inside, regexp) {\n    return function(source, setState) {\n      var newInside = inside;\n      var type = jsToken(inside, regexp, source, function(c) {newInside = c;});\n      var newRegexp = type.type == \"operator\" || type.type == \"keyword c\" || type.type.match(/^[\\[{}\\(,;:]$/);\n      if (newRegexp != regexp || newInside != inside)\n        setState(jsTokenState(newInside, newRegexp));\n      return type;\n    };\n  }\n\n  // The token reader, intended to be used by the tokenizer from\n  // tokenize.js (through jsTokenState). Advances the source stream\n  // over a token, and returns an object containing the type and style\n  // of that token.\n  function jsToken(inside, regexp, source, setInside) {\n    function readHexNumber(){\n      source.next(); // skip the 'x'\n      source.nextWhileMatches(isHexDigit);\n      return {type: \"number\", style: \"cm-atom\"};\n    }\n\n    function readNumber() {\n      source.nextWhileMatches(/[0-9]/);\n      if (source.equals(\".\")){\n        source.next();\n        source.nextWhileMatches(/[0-9]/);\n      }\n      if (source.equals(\"e\") || source.equals(\"E\")){\n        source.next();\n        if (source.equals(\"-\"))\n          source.next();\n        source.nextWhileMatches(/[0-9]/);\n      }\n      return {type: \"number\", style: \"cm-atom\"};\n    }\n    // Read a word, look it up in keywords. If not found, it is a\n    // variable, otherwise it is a keyword of the type found.\n    function readWord() {\n      source.nextWhileMatches(isWordChar);\n      var word = source.get();\n      var known = keywords.hasOwnProperty(word) && keywords.propertyIsEnumerable(word) && keywords[word];\n      return known ? {type: known.type, style: known.style, content: word} :\n      {type: \"variable\", style: \"cm-variable\", content: word};\n    }\n    function readRegexp() {\n      nextUntilUnescaped(source, \"/\");\n      source.nextWhileMatches(/[gimy]/); // 'y' is \"sticky\" option in Mozilla\n      return {type: \"regexp\", style: \"cm-string\"};\n    }\n    // Mutli-line comments are tricky. We want to return the newlines\n    // embedded in them as regular newline tokens, and then continue\n    // returning a comment token for every line of the comment. So\n    // some state has to be saved (inside) to indicate whether we are\n    // inside a /* */ sequence.\n    function readMultilineComment(start){\n      var newInside = \"/*\";\n      var maybeEnd = (start == \"*\");\n      while (true) {\n        if (source.endOfLine())\n          break;\n        var next = source.next();\n        if (next == \"/\" && maybeEnd){\n          newInside = null;\n          break;\n        }\n        maybeEnd = (next == \"*\");\n      }\n      setInside(newInside);\n      return {type: \"comment\", style: \"cm-comment\"};\n    }\n    function readOperator() {\n      source.nextWhileMatches(isOperatorChar);\n      return {type: \"operator\", style: \"cm-operator\"};\n    }\n    function readString(quote) {\n      var endBackSlash = nextUntilUnescaped(source, quote);\n      setInside(endBackSlash ? quote : null);\n      return {type: \"string\", style: \"cm-string\"};\n    }\n\n    // Fetch the next token. Dispatches on first character in the\n    // stream, or first two characters when the first is a slash.\n    if (inside == \"\\\"\" || inside == \"'\")\n      return readString(inside);\n    var ch = source.next();\n    if (inside == \"/*\")\n      return readMultilineComment(ch);\n    else if (ch == \"\\\"\" || ch == \"'\")\n      return readString(ch);\n    // with punctuation, the type of the token is the symbol itself\n    else if (/[\\[\\]{}\\(\\),;\\:\\.]/.test(ch))\n      return {type: ch, style: \"cm-punctuation\"};\n    else if (ch == \"0\" && (source.equals(\"x\") || source.equals(\"X\")))\n      return readHexNumber();\n    else if (/[0-9]/.test(ch))\n      return readNumber();\n    else if (ch == \"/\"){\n      if (source.equals(\"*\"))\n      { source.next(); return readMultilineComment(ch); }\n      else if (source.equals(\"/\"))\n      { nextUntilUnescaped(source, null); return {type: \"comment\", style: \"cm-comment\"};}\n      else if (regexp)\n        return readRegexp();\n      else\n        return readOperator();\n    }\n    else if (isOperatorChar.test(ch))\n      return readOperator();\n    else\n      return readWord();\n  }\n\n  // The external interface to the tokenizer.\n  return function(source, startState) {\n    return tokenizer(source, startState || jsTokenState(false, true));\n  };\n})();\n\nexport default tokenizeJavaScript;\n"
  },
  {
    "path": "code-mirror-highlighting/tokenizer.js",
    "content": "// A framework for simple tokenizers. Takes care of newlines and\n// white-space, and of getting the text from the source stream into\n// the token object. A state is a function of two arguments -- a\n// string stream and a setState function. The second can be used to\n// change the tokenizer's state, and can be ignored for stateless\n// tokenizers. This function should advance the stream over a token\n// and return a string or object containing information about the next\n// token, or null to pass and have the (new) state be called to finish\n// the token. When a string is given, it is wrapped in a {style, type}\n// object. In the resulting object, the characters consumed are stored\n// under the content property. Any whitespace following them is also\n// automatically consumed, and added to the value property. (Thus,\n// content is the actual meaningful part of the token, while value\n// contains all the text it spans.)\n\nexport default function tokenizer(source, state) {\n  // Newlines are always a separate token.\n  function isWhiteSpace(ch) {\n    // The messy regexp is because IE's regexp matcher is of the\n    // opinion that non-breaking spaces are no whitespace.\n    return ch != \"\\n\" && /^[\\s\\u00a0]*$/.test(ch);\n  }\n\n  var tokenizer = {\n    state: state,\n\n    take: function(type) {\n      if (typeof(type) == \"string\")\n        type = {style: type, type: type};\n\n      type.content = (type.content || \"\") + source.get();\n      if (!/\\n$/.test(type.content))\n        source.nextWhile(isWhiteSpace);\n      type.value = type.content + source.get();\n      return type;\n    },\n\n    next: function () {\n      if (!source.more()) throw StopIteration;\n\n      var type;\n      if (source.equals(\"\\n\")) {\n        source.next();\n        return this.take(\"whitespace\");\n      }\n\n      if (source.applies(isWhiteSpace))\n        type = \"whitespace\";\n      else\n        while (!type)\n          type = this.state(source, function(s) {tokenizer.state = s;});\n\n      return this.take(type);\n    }\n  };\n  return tokenizer;\n}\n"
  },
  {
    "path": "edit.js",
    "content": "var React = require('react');\nvar ReactDOM = require(\"react-dom\");\nvar ReactPlayground = require('./live-editor');\n\nvar HELLO_COMPONENT = \"\\\n\\/\\/ {{{\\n\\\nvar HelloMessage = React.createClass({\\n\\\n  render: function() {\\n\\\n    return <div>Hello {this.props.name}</div>;\\n\\\n  }\\n\\\n});\\n\\\n\\/\\/ }}}\\n\\\n\\n\\\nexport default <HelloMessage name=\\\"John\\\" />;\\\n\";\n\nReactDOM.render(\n  <ReactPlayground codeText={HELLO_COMPONENT} />,\n  document.getElementById('inject')\n);\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset='utf-8'>\n        <title>jsx compiler</title>\n        <link rel=\"stylesheet\" href=\"http://facebook.github.io/react/css/syntax.css\">\n        <link rel=\"stylesheet\" href=\"http://facebook.github.io/react/css/codemirror.css\">\n        <link rel=\"stylesheet\" href=\"http://facebook.github.io/react/css/react.css\">\n        <meta name=\"author\" content=\"Joel Burget (joelburget@gmail.com)\">\n        <meta name=\"copyright\" content=\"Copyright &copy; Joel Burget\">\n        <meta name=\"keywords\" content=\"babel,javascript,react\">\n    </head>\n    <body>\n      <div id=\"inject\"></div>\n      <script type=\"text/javascript\" src=\"dist/bundle.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "index.js",
    "content": "import CodeMirrorEditor from './code-mirror-editor';\nimport CodeMirrorHighlight from './code-mirror-highlight';\nimport LiveCompile from './live-compile';\nimport LiveEditor from './live-editor';\n\nexport default {\n  CodeMirrorEditor,\n  CodeMirrorHighlight,\n  LiveCompile,\n  LiveEditor,\n};\n"
  },
  {
    "path": "live-compile.js",
    "content": "var React = require(\"react\");\nvar ReactDOM = require(\"react-dom\");\nvar babel = require('babel-core');\n\nvar selfCleaningTimeout = {\n  componentDidUpdate: function() {\n    clearTimeout(this.timeoutID);\n  },\n\n  setTimeout: function() {\n    clearTimeout(this.timeoutID);\n    this.timeoutID = setTimeout.apply(null, arguments);\n  }\n};\n\nvar ComponentPreview = React.createClass({\n    propTypes: {\n      code: React.PropTypes.string.isRequired\n    },\n\n    mixins: [selfCleaningTimeout],\n\n    render: function() {\n        return <div ref=\"mount\" />;\n    },\n\n    componentDidMount: function() {\n      this.executeCode();\n    },\n\n    componentDidUpdate: function(prevProps) {\n      // execute code only when the state's not being updated by switching tab\n      // this avoids re-displaying the error, which comes after a certain delay\n      if (this.props.code !== prevProps.code) {\n        this.executeCode();\n      }\n    },\n\n    compileCode: function() {\n      return babel.transform(\n        this.props.code,\n        { presets:\n          [ require('babel-preset-es2015')\n          , require('babel-preset-react')\n          ]\n        }\n      ).code;\n    },\n\n    executeCode: function() {\n      var mountNode = this.refs.mount;\n\n      try {\n        ReactDOM.unmountComponentAtNode(mountNode);\n      } catch (e) { }\n\n      try {\n        var compiledCode = this.compileCode();\n        ReactDOM.render(\n          eval(compiledCode),\n          mountNode\n        );\n      } catch (err) {\n        this.setTimeout(function() {\n          ReactDOM.render(\n            <div className=\"playgroundError\">{err.toString()}</div>,\n            mountNode\n          );\n        }, 500);\n      }\n    }\n});\n\nmodule.exports = ComponentPreview;\n"
  },
  {
    "path": "live-editor.js",
    "content": "var React = require(\"react\");\n\nvar CodeMirrorEditor = require(\"./code-mirror-editor\");\nvar ComponentPreview = require(\"./live-compile\");\n\nvar ReactPlayground = React.createClass({\n  propTypes: {\n    codeText: React.PropTypes.string.isRequired\n  },\n\n  getInitialState: function() {\n    return {\n      code: this.props.codeText\n    };\n  },\n\n  handleCodeChange: function(code) {\n    this.setState({ code });\n  },\n\n  render: function() {\n    const {code} = this.state;\n\n    return <div className=\"playground\">\n      <div className=\"playgroundCode\">\n        <CodeMirrorEditor\n          onChange={this.handleCodeChange}\n          className=\"playgroundStage\"\n          codeText={code}\n        />\n      </div>\n      <div className=\"playgroundPreview\">\n        <ComponentPreview code={code} />\n      </div>\n    </div>;\n  },\n});\n\nmodule.exports = ReactPlayground;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-live-editor\",\n  \"version\": \"5.2.1\",\n  \"description\": \"Live editor for React components\",\n  \"files\": [\n    \"index.js\",\n    \"code-mirror-editor.js\",\n    \"live-compile.js\",\n    \"live-editor.js\",\n    \"code-mirror-highlight.js\",\n    \"code-mirror-highlighting/jsparser.js\",\n    \"code-mirror-highlighting/stringstream.js\",\n    \"code-mirror-highlighting/tokenizejs.js\",\n    \"code-mirror-highlighting/tokenizer.js\"\n  ],\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"prepublish\": \"babel --presets es2015,react . -d . --ignore node_modules\",\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/joelburget/react-live-editor\"\n  },\n  \"keywords\": [\n    \"react-component\",\n    \"react\",\n    \"editor\",\n    \"component\"\n  ],\n  \"author\": \"Joel Burget\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"babel-core\": \"^6.10.4\",\n    \"babel-preset-es2015\": \"^6.9.0\",\n    \"babel-preset-react\": \"^6.11.1\",\n    \"babel-template\": \"^6.9.0\",\n    \"codemirror\": \"^5.16.0\"\n  },\n  \"devDependencies\": {\n    \"babel-loader\": \"^6.2.4\",\n    \"json-loader\": \"^0.5.4\",\n    \"react\": \"^15.2.1\",\n    \"react-dom\": \"^15.2.1\",\n    \"webpack\": \"^1.13.1\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^15.2.1\",\n    \"react-dom\": \"^15.2.1\"\n  },\n  \"babel\": {\n    \"presets\": [\n      \"es2015\",\n      \"react\"\n    ]\n  }\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n  devtool: 'eval',\n  entry: './edit',\n  output: {\n    path: path.join(__dirname, 'dist'),\n    filename: 'bundle.js',\n    publicPath: '/dist/'\n  },\n  module: {\n    loaders: [\n      {\n        test: /\\.js$/,\n        loaders: ['babel'],\n        exclude: path.join(__dirname, 'node_modules')\n      },\n      { test: /\\.json$/, loader: 'json-loader' }\n    ]\n  },\n  node: {\n    fs: 'empty',\n    net: 'empty',\n    module: 'empty',\n  }\n};\n"
  }
]