var module = angular.module('plunker', [
  "ui.bootstrap",
  "ui.router",
  
  "plunker.controllers.editor"
]);

module.config(["$locationProvider", "$urlRouterProvider", function($locationProvider, $urlRouterProvider){
  //$locationProvider.html5Mode(true);
  
  $urlRouterProvider.otherwise("/edit");
}]);
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <link data-require="bootstrap-css@3.x" data-semver="3.1.0" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css" />
    <link data-require="font-awesome@*" data-semver="4.0.3" rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" />
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <link rel="stylesheet" href="fileTree.css" />
    <link rel="stylesheet" href="vendor/faBorderLayout.css" />
    <script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.12/angular.js" data-semver="1.2.12"></script>
    <script data-require="ui-bootstrap@*" data-semver="0.10.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.10.0.js"></script>
    <script data-require="ui-router@*" data-semver="0.2.8" src="http://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
    <script data-require="lodash.js@*" data-semver="2.4.1" src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js"></script>
    <script data-require="ace@*" data-semver="1.1.01" src="http://ace.c9.io/build/src-min-noconflict/ace.js"></script>
    <script data-require="firebase@*" data-semver="1.0.2" src="https://cdn.firebase.com/js/client/1.0.2/firebase.js"></script>
    <script src="vendor/faBorderLayout.js"></script>
    <script src="vendor/ottypes.js"></script>
    <script src="vendor/bundle.js"></script>
    <script src="directives/tree.js"></script>
    <script src="directives/editor.js"></script>
    <script src="controllers/editor.js"></script>
    <script src="services/genid.js"></script>
    <script src="services/commands.js"></script>
    <script src="services/workspace.js"></script>
    <script src="services/project.js"></script>
    <script src="services/textops.js"></script>
    <script src="services/ottext.js"></script>
    <script src="services/opstream.js"></script>
    <script src="app.js"></script>
  </head>

  <body ui-view=""></body>

</html>
/* Put your css in here */

.fa-pane-handle {
  background: #ccc;
}

.fa-pane:hover>.fa-pane-handle>.fa-pane-toggle {
  background: #666;
}

.fa-pane-resizing>.fa-pane-handle, .fa-pane-handle:hover {
  background-color: #C4E1A4;
}

.fa-pane-scroller {
}

.fa-pane-parent>.fa-pane-scroller {
  border: none;
}

.fa-pane-constrained.fa-pane-resizing>.fa-pane-handle {
  background-color: #e1c1a4;
}


.pane-toolbar {
  background-color: #33454d;
  color: #f5f5f5;
}

.brand {
  font-size: 20px;
  font-weight: 200;
  color: #f5f5f5;
  text-shadow: 0 1px 0 #999999;
  line-height: 26px;
  margin: 0 0 0 1em;
  padding: 0;
}

.pane-toolbar>.fa-pane-scroller {
  overflow: visible;
}


.pane-hierarchy .fa-pane-scroller {
  border: 4px solid #526e7b;
}

.pane-hierarchy .fa-pane-scroller.active {
  border: 4px solid #3a7793;
}

.pane-hierarchy .fa-pane-parent>.fa-pane-scroller {
  border: none;
}
.pane-header {
  position: absolute;
  top: 0; right: 0; left: 0;
  height: 20px;
  background-color: #526e7b;
  color: #f3f3f3;
}

.fa-pane-scroller.active .pane-header {
  background-color: #3a7793;
}
.pane-actions {
  position: absolute;
  top: 0; right: 1px;
  width: auto;
  height: auto;
  line-height: 1;
}
.pane-actions .btn-xs {
  font-size: 8px;
  width: 17px;
}
.pane-content {
  position: absolute;
  top: 20px; right: 0; bottom: 0; left: 0;
}

.ace_editor {
  position: absolute !important;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

@keyframes fadeIn {
	0% {opacity: 0;}
	100% {opacity: 1;}
}

.fadeIn {
	animation-name: fadeIn;
}
var module = angular.module("plunker.service.commands", [
]);


module.factory("commands", ["$rootScope", "$injector", function($rootScope, $injector){
  var commands = {}
  
  $rootScope.$command = execCommand;
  
  return {
    register: registerCommand,
    getById: getCommandById,
    exec: execCommand
  };
  
  function registerCommand(commandId, handler, defaults) {
    commands[commandId] = {
      id: commandId,
      handler: handler || angular.noop,
      defaults: angular.isObject(defaults) ? defaults : {}
    };
  }
  
  function getCommandById(commandId) {
    return commands[commandId];
  }
  
  function execCommand(commandId, locals) {
    var command = getCommandById(commandId)
      , ret;
    
    locals = (locals === void 0) ? {} : locals;
    
    if (command) {
      ret = $injector.invoke(command.handler, {}, _.defaults(locals, command.defaults));
      if (!$rootScope.$$phase) $rootScope.$digest();
    } else {
      throw new Error("No such command: " + commandId);
    }
    
    return ret;
  }
}]);
var module = angular.module("plunker.controllers.editor", [
  "ui.router",
  "ui.bootstrap",
  
  "fa.directive.borderLayout",

  "plunker.directive.tree",
  
  "plunker.service.commands",
  "plunker.service.workspace",
  "plunker.service.project"
]);

module.config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider){
  $stateProvider.state("editor", {
    url: "/edit",
    templateUrl: "views/editor.html",
    controller: ["$scope", "commands", "project", function($scope, commands, project){
      commands.exec("project.openDefault");
      commands.exec("file.openDefault");
    }]
  });

  $stateProvider.state("editor.plunk", {
    url: "/:plunkId",
    template: "",
    controller: ["$scope", "$stateParams", "commands", function($scope, $stateParams, commands){
      commands.exec("project.open", $stateParams);
    }]
  });
}]);

module.controller("SidebarController", ["$scope", "project", function($scope, project){
  $scope.tree = [project.root];
  $scope.open = function(entry) {
    
  }
}])
<div fa-pane="container">
  <div fa-pane="toolbar" pane-anchor="north" pane-size="30px">
    <h4 class="brand">Plunker</h4>
  </div>
  
  <div fa-pane="sidebar" pane-anchor="west" pane-size="250px" pane-min="200px" pane-max="300px" pane-handle="4px" ng-controller="SidebarController">
    <file-tree tree="tree"></file-tree>
  </div>
  
  <div fa-pane="editor">
    <pane-hierarchy></pane-hierarchy>
  </div>
</div>

<div ui-view></div>
var module = angular.module("plunker.service.workspace", [
  "plunker.service.commands",
  "plunker.service.project",
  
  "plunker.directive.editor"
]);

module.factory("workspace", ["commands", "project", function(commands, project){
  var nextPaneId = 0
    , active = {row: 0, col: 0}
    , layout = []
    , panes = {}
    , renderers = {};

  split(active);
  
  commands.register("space.activate", activate);
  commands.register("space.close", closeByCoords);
  commands.register("space.split", split);
    
  commands.register("file.open", fileOpen, {options: {}});
  commands.register("file.openDefault", fileOpenDefault, {options: {}});

  return {
    getActivePaneCoords: getActivePaneCoords,
    getActivePaneId: getActivePaneId,
    getActivePaneData: getActivePaneData,
    getPane: getPane,
    getPaneById: getPane,
    getPaneCoords: getPaneCoords,
    getPaneData: getPaneData,
    getPaneIdByCoords: getPaneIdByCoords,
    getPanes: function(){return panes;},
    getLayout: function(){return layout;},
    isActiveById: isPaneActiveById,
    isOpenById: isPaneOpenById
  };

  function activate(coords) {
    active.col = Math.max(0, Math.min(layout.length - 1, coords.col));
    active.row = Math.max(0, Math.min(layout[active.col].length - 1, coords.row));
  }
  
  function getActivePaneCoords() {
    return active;
  }
  
  function getActivePaneId() {
    return getPaneIdByCoords(getActivePaneCoords());
  }
  
  function getPane(paneId) {
    return panes[paneId];
  }
  
  function getActivePaneData() {
    return getPaneData(getActivePaneId());
  }
  
  function getPaneByCoords(coords) {
    if (layout[coords.col]) {
      return getPane(layout[coords.col][coords.row]);
    }
  }
  
  function getPaneCoords(paneId) {
    for (var col = 0; col < layout.length; col++) {
      rows = layout[col];
      
      for (var row = 0; row < rows.length; row++) {
        if (rows[row] === paneId) return {
          row: row,
          col: col
        };
      }
    }
  }
  
  function getPaneData(paneId) {
    return getPane(paneId).data;
  }
  
  function getPaneIdByCoords(coords) {
    if (layout[coords.col]) return layout[coords.col][coords.row];
  }
  
  function isPaneActiveById(paneId) {
    return getActivePaneId() === paneId;
  }
  
  function isPaneOpenById(paneId) {
    return !!panes[paneId];
  }
  
  function split(coords, direction) {
    var paneDef = {
      id: nextPaneId++,
      title: "Empty",
      type: "empty"
    };
    
    direction || (direction = "horizontal");
    
    coords.row || (coords.row = 0);
    coords.col || (coords.col = 0);
    
    if (direction[0] === "h") {
      coords.row = 0;
      coords.col += 1;
      
      layout.splice(coords.col, 0, [paneDef.id]);

    } else if (direction[0] === "v") {
      coords.row += 1;
      coords.col = Math.max(0, Math.min(layout.length - 1, coords.col));
      
      layout[coords.col].splice(coords.row, 0, paneDef.id);
    }

    panes[paneDef.id] = paneDef;
    activate(coords);
    
    return coords;
  }

  function closeByCoords(coords) {
    if (layout.length <= coords.col || layout[coords.col].length <= coords.row) return;
    
    layout[coords.col].splice(coords.row, 1);
    
    if (!layout[coords.col].length) layout.splice(coords.col, 1);
    
    if (coords.row <= active.row) active.row = Math.max(0, active.row - 1);
    if (coords.col <= active.col) active.col = Math.max(0, active.col - 1);
    
    if (!layout.length) {
      split(active, "h");
    }
  }
  
  function fileOpenDefault() {
    var entry =_.find(project.entries, function(entry) {
      return 0 <= ["index.html"].indexOf(entry.filename);
    });
    
    if (entry) commands.exec("file.open", {entry: entry});
  }

  function fileOpen(entry, options) {
    var coords = null
      , paneDef = null;
      
    options || (options = {});
    
    if (angular.isString(entry)) {
      entry = project.getEntryById(entry);
    }
    
    if (!entry) return;
    
    coords = getPaneCoords(entry.id);
    
    if (coords) {
      activate(coords);
      return;
    }
    
    if (options.target === "col") {
      coords = split(getActivePaneCoords(), "v")
    } else if (options.target === "row") {
      coords = split(getActivePaneCoords(), "h")
    } else {
      coords = options.coords || getActivePaneCoords(); 
    }
    
    delete panes[getPaneByCoords(coords).id];
    
    layout[coords.col][coords.row] = entry.id

    panes[entry.id] = {
      id: entry.id,
      type: "code",
      title: entry.filename,
      data: entry
    };    
  }
}]);


module.directive("paneHierarchy", ["workspace", function(workspace){
  return {
    restrict: "E",
    replace: true,
    templateUrl: "views/paneHierarchy.html",
    controller: ["$scope", function($scope){
      $scope.layout = workspace.getLayout();
      $scope.panes = workspace.getPanes();
      $scope.active = workspace.active;
      $scope.getPane = workspace.getPane;
      $scope.isActive = workspace.isActiveById;
    }]
  };
}]);

module.directive("paneViewer", ["$compile", "workspace", function($compile, workspace){
  return {
    restrict: "A",
    replace: true,
    template: "<div></div>",
    link: function($scope, $element, $attrs){
      var $childScope
        , pane = null
        , markup = "";
      
      $scope.$watch(isActive, function(active) {
        if (active) $scope.$broadcast("pane-active");
      });
      
      function isActive() {
        return pane && workspace.isActiveById(pane.id);
      }
      
      $attrs.$observe("paneViewer", function(paneId){
        pane = workspace.getPaneById(paneId);
        
        if (!pane || !pane.type) return;
        
        switch (pane.type) {
          case 'code':
            markup = '<ace-editor></ace-editor>';
            break;
          default:
            markup = '<strong>EMPTY</strong>';
        }
        
        var tree = angular.element(markup);
        
        if ($childScope) $childScope.$destroy();
        $element.empty();
        
        $childScope = $scope.$new();
        $childScope.pane = pane;
        
        $compile(tree)($childScope);
        $element.append(tree);
      });
    }
  };
}]);
module = angular.module "fa.directive.borderLayout", [
]

module.factory "paneManager", ->
  panes: {}
  get: (paneId) -> @panes[paneId]
  set: (paneId, pane) -> @panes[paneId] = pane
  remove: (paneId) -> delete @panes[paneId]
  

module.directive "faPane", [ "$window", "$rootScope", "paneManager", ($window, $rootScope, paneManager) ->
  
  class Region
    constructor: (@width = 0, @height = 0, @top = 0, @right = 0, @bottom = 0, @left = 0) ->

    clone: -> new Region(@width, @height, @top, @right, @bottom, @left)
      
    calculateSize: (orientation, target = 0) ->
      total = @getSize(orientation)
      available = @getAvailableSize(orientation)
      
      if angular.isNumber(target)
        if target >= 1 then return Math.round(target)
        if target >= 0 then return Math.round(target * total)
        
        return 0
      
      # Kill whitespace
      target = target.replace /\s+/mg, ""
      
      # Allow for complex sizes, e.g.: 50% - 4px
      if (terms = target.split("-")).length > 1 then return @calculateSize(orientation, terms.shift()) - @calculateSize(orientation, terms.join("+"))
      if (terms = target.split("+")).length > 1 then return @calculateSize(orientation, terms.shift()) + @calculateSize(orientation, terms.join("+"))
        
      if matches = target.match /^(\d+)px$/ then return parseInt(matches[1], 10)
      if matches = target.match /^(\d+(?:\.\d+)?)&$/ then return Math.round(available * parseFloat(matches[1]) / 100)
      if matches = target.match /^(\d+(?:\.\d+)?)%$/ then return Math.round(total * parseFloat(matches[1]) / 100)
      
      throw new Error("Unsupported size: #{target}")
    
    consume: (anchor, size = 0) ->
      switch anchor
        when "north"
          style = { top: "#{@top}px", right: "#{@right}px", bottom: "auto", left: "#{@left}px", height: "#{size}px", width: "auto" }
          @top += size
        when "east"
          style = { top: "#{@top}px", right: "#{@right}px", bottom: "#{@bottom}px", left: "auto", width: "#{size}px", height: "auto" }
          @right += size
        when "south"
          style = { top: "auto", right: "#{@right}px", bottom: "#{@bottom}px", left: "#{@left}px", height: "#{size}px", width: "auto" }
          @bottom += size
        when "west"
          style = { top: "#{@top}px", right: "auto", bottom: "#{@bottom}px", left: "#{@left}px", width: "#{size}px", height: "auto" }
          @left += size
      
      style.display = "none" if size is 0
        
      style
      
    getInnerRegion: ->
      new Region @width - @right - @left, @height - @top - @bottom
    
    getSize: (orientation) ->
      switch orientation
        when "vertical" then @height
        when "horizontal" then @width
    
    getAvailableSize: (orientation) ->
      switch orientation
        when "vertical" then @height - @top - @bottom
        when "horizontal" then @width - @right - @left
    
    toString: -> "{#{@top}, #{@right}, #{@bottom}, #{@left}}, {#{@width}, #{@height}}"  
  
  
  
  
  ## BEGIN UTILITY FUNCTIONS
  
  getOrientation = (anchor) ->
    switch anchor
      when "north", "south" then "vertical"
      when "east", "west" then "horizontal"  
      
  getScrollerStyle = (anchor, size) ->
    style =
      top: 0
      right: 0
      bottom: 0
      left: 0
    
    if size
      switch anchor
        when "north"
          style.bottom = "auto"
          style.height = "#{size}px"
        when "east"
          style.left = "auto"
          style.width = "#{size}px"
        when "south"
          style.top = "auto"
          style.height = "#{size}px"
        when "west"
          style.right = "auto"
          style.width = "#{size}px"
    
    style
      
  getHandleStyle = (anchor, region, handleSize) ->
    
    switch anchor
      when "north"
        height: "#{region.calculateSize('vertical', handleSize)}px"
        right: 0
        left: 0
        bottom: 0
      when "south" 
        height: "#{region.calculateSize('vertical', handleSize)}px"
        right: 0
        left: 0
        top: 0
      when "east"
        width: "#{region.calculateSize('horizontal', handleSize)}px"
        top: 0
        bottom: 0
        left: 0
      when "west" 
        width: "#{region.calculateSize('horizontal', handleSize)}px"
        top: 0
        bottom: 0
        right: 0
  
  generateSerialId = do ->
    counter = 0
    fun = -> counter++
    fun.peek = -> counter
    fun
  
      
  ## BEGIN DIRECTIVE DECLARATION
  
  restrict: "A"
  replace: true
  require: "faPane"
  priority: 1
  transclude: "element"
  scope:
    anchor: "@paneAnchor"
    paneId: "@faPane"
    size: "@paneSize"
    min: "@paneMin"
    max: "@paneMax"
    handle: "@paneHandle"
    closed: "=paneClosed"
    order: "@paneOrder"
    noToggle: "@paneNoToggle"
  template: """
    <div class="fa-pane pane-{{$pane.id}}">
      <div class="fa-pane-overlay"></div>
      <div class="fa-pane-handle" fa-pane-resizer>
        <div ng-if="!$pane.noToggle" class="fa-pane-toggle" ng-click="$pane.toggle()"></div>
      </div>
    </div>
  """
  controllerAs: "$pane"
  controller: ->
    $pane = new class Pane
      constructor: ->
        @children = []
        
        @closed = false
        @noToggle = false
        @max = Number.MAX_VALUE
        @min = 0

      
      # Schedule a reflow later in the digest cycle, but do not reflow more than
      # necessary
      $scheduleReflow: ->
        if $pane.parent then $pane.parent.$scheduleReflow()
        else unless $pane.$reflowScheduled
          $pane.$reflowScheduled = true
          
          $rootScope.$evalAsync ->
            if $pane.$reflowScheduled then $pane.reflow()
            
            $pane.$reflowScheduled = false
      
      getOptions: ->
        anchor: @anchor
        targetSize: @targetSize
        size: @size
        min: @min
        max: @max
        order: @order or 0
        handle:
          open: @handleSizeOpen or 0
          closed: @handleSizeClosed or 0
        noToggle: !!@noToggle
        closed: @closed
      
      setOptions: (options = {}) ->
        @setAnchor(options.anchor) if options.anchor?
        @setTargetSize(options.size) if options.size?
        @setMinSize(options.min) if options.min?
        @setMaxSize(options.max) if options.max?
        @setHandleSize(options.handle) if options.handle?
        @setOrder(options.order) if options.order?
        @setNoToggle(options.noToggle) if options.noToggle?
        @toggle(!options.closed) if options.closed?
        
      setAnchor: (@anchor) -> @$scheduleReflow()
      setTargetSize: (@targetSize) -> @$scheduleReflow()
      setMinSize: (@min) -> @$scheduleReflow()
      setMaxSize: (@max) -> @$scheduleReflow()
      setOrder: (@order) -> @$scheduleReflow()
      setNoToggle: (@noToggle) -> @$scheduleReflow()
      setHandleSize: (handleSize) ->
        if handleSize?.open or handleSize?.closed
          @handleSizeOpen = handleSize.open or 0
          @handleSizeClosed = handleSize.closed or 0
        else
          @handleSizeOpen = @handleSizeClosed = parseInt(handleSize, 10)
        @$scheduleReflow()

      
      addChild: (child) ->
        child.parent = $pane
        @children.push child
        
        $pane.$containerEl.addClass("fa-pane-parent") if @children.length
        
        $pane.$scheduleReflow()
      
      getOrientation: -> getOrientation($pane.anchor)
      onHandleDown: -> $pane.$containerEl.addClass("active")
      onHandleUp: ->
        $pane.$containerEl.removeClass("active")
        $pane.$scheduleReflow()
        
      removeChild: (child) ->
        @children.splice idx, 1 unless 0 > idx = @children.indexOf(child)
        
        $pane.$containerEl.removeClass("fa-pane-parent") unless @children.length
        
        $pane.$scheduleReflow()
      
      reflow: (region) ->
        width = $pane.$containerEl[0].offsetWidth
        height = $pane.$containerEl[0].offsetHeight
      
        region ||= new Region(width, height)
        
        if $pane.anchor in ["north", "east", "south", "west"]
          $pane.$containerEl.removeClass("fa-pane-orientation-vertical")
          $pane.$containerEl.removeClass("fa-pane-orientation-horizontal")
          
          orientation = getOrientation($pane.anchor)
  
          $pane.$containerEl.addClass("fa-pane-orientation-#{orientation}")

          handleSize = region.calculateSize(orientation, !$pane.closed and $pane.handleSizeOpen or $pane.handleSizeClosed)
          
          if $pane.closed
            size = handleSize
          else
            size = region.calculateSize(orientation, !$pane.closed and $pane.targetSize or handleSize)
            
            size = Math.min(size, region.calculateSize(orientation, $pane.max))
            size = Math.max(size, region.calculateSize(orientation, $pane.min))
            size = Math.min(size, region.getAvailableSize(orientation))
            size = Math.max(size, handleSize)
          
          @size = size
          
          styleContainer = region.consume($pane.anchor, size)
          styleScroller = getScrollerStyle($pane.anchor, size - handleSize)
          styleHandle = getHandleStyle($pane.anchor, region, handleSize)
         
          $pane.$containerEl.attr("style", "").css(styleContainer)
          $pane.$overlayEl.attr("style", "").css(styleScroller)
          $pane.$handleEl.attr("style", "").css(styleHandle)
          $pane.$scrollerEl.attr("style", "").css(styleScroller)

        else
          $pane.$containerEl.css
            top: "#{region.top}px"
            right: "#{region.right}px"
            bottom: "#{region.bottom}px"
            left: "#{region.left}px"
            width: "auto"
            height: "auto"
            
          
        $pane.$region = region.clone()
        $pane.reflowChildren region.getInnerRegion()
      
      reflowChildren: (region) ->
        region ||= $pane.$region
        
        $pane.children.sort (a, b) -> a.order - b.order
        child.reflow(region) for child in $pane.children

      # Attempt to resize the 
      resize: (size = $pane.targetSize) ->
        $pane.targetSize = size
        $pane.parent.reflowChildren($pane.parent.$region.getInnerRegion())
        
        if size != $pane.size then $pane.$containerEl.addClass("fa-pane-constrained")
        else $pane.$containerEl.removeClass("fa-pane-constrained")
      
      toggle: (open = !!$pane.closed) ->
        $pane.closed = !open
        
        reflow = ->
          if $pane.parent then $pane.parent.$scheduleReflow() 
          else $pane.$scheduleReflow()
        
        if $pane.closed then $pane.$containerEl.addClass "fa-pane-closed"
        else $pane.$containerEl.removeClass "fa-pane-closed"
        
        reflow()

  compile: ($el, $attrs, $transclude) ->
    # Tool used to force elements into their compile order
    serialId = generateSerialId()

    link = ($scope, $el, $attrs, $pane) ->
      $directiveScope = $scope.$parent.$new()
      $directiveScope.$pane = $scope.$pane = $pane
      
      $transcludeScope = $directiveScope.$new()
      
      $pane.order ?= serialId
      
      $pane.$isolateScope = $scope
      $pane.$directiveScope = $directiveScope
      $pane.$transcludeScope = $transcludeScope
      
      $transclude $transcludeScope, (clone) ->
        clone.addClass("fa-pane-scroller")
        
        $el.append(clone)
        
        $pane.$containerEl = $el
        $pane.$overlayEl = $el.children().eq(0)
        $pane.$handleEl = $el.children().eq(1)
        $pane.$scrollerEl = $el.children().eq(2)

        $scope.$watch "anchor", (anchor) -> $pane.setAnchor(anchor)
        $scope.$watch "size", (targetSize) -> $pane.setTargetSize(targetSize)
        $scope.$watch "closed", (closed) -> $pane.toggle(!closed)
        $scope.$watch "min", (min) -> $pane.setMinSize(if min? then min else 0)
        $scope.$watch "max", (max) -> $pane.setMaxSize(if max? then max else Number.MAX_VALUE)
        $scope.$watch "order", (order) -> $pane.setOrder(order)
        $scope.$watch "noToggle", (noToggle) -> $pane.setNoToggle(!!noToggle)
        $scope.$watch "paneId", (paneId, prevPaneId) ->
          paneManager.remove(prevPaneId) if prevPaneId
          paneManager.set(paneId, $pane)

          $pane.id = paneId
        
        $scope.$watch "handle", (handle) -> $pane.setHandleSize(handle)
        
        $pane.$directiveScope.$on "fa-pane-attach", (e, child) ->
          if child != $pane
            e.stopPropagation()
            
            $pane.addChild(child)
        
        $pane.$directiveScope.$on "fa-pane-detach", (e, child) ->
          if child != $pane
            e.stopPropagation()
            
            $pane.removeChild(child)
        
        $window.addEventListener "resize", (e) ->
          e.stopPropagation()
          $pane.$scheduleReflow()
          
        $pane.$directiveScope.$on "$stateChangeSuccess", ->
          $pane.$scheduleReflow()
          

        $pane.$directiveScope.$emit "fa-pane-attach", $pane
        $pane.$directiveScope.$on "$destroy", ->
          $pane.$directiveScope.$emit "fa-pane-detach", $pane
]

module.directive "faPaneToggle", [ "paneManager", (paneManager) ->
  link: ($scope, $el, $attrs) ->
    $attrs.$observe "faPaneToggle", (paneId) ->
]

module.directive "faPaneResizer", [ "$window", ($window) ->
  throttle = (delay, fn) ->
    throttled = false
    ->
      return if throttled
      
      throttled = true
      setTimeout ->
        throttled = false
      , delay
      
      fn.call(@, arguments...)


  restrict: "A"
  #require: "^faPane"
  link: ($scope, $element, $attrs, $pane) ->
    #return unless $pane
    
    $pane ||= $scope.$pane
    
    el = $element[0]
    
    clickRadius = 5
    clickTime = 300
    
    $scope.$watch ( -> $pane.getOrientation() ), (orientation) ->
      $element.removeClass("vertical")
      $element.removeClass("horizontal")
      
      switch orientation
        when "vertical" then $element.addClass("vertical")
        when "horizontal" then $element.addClass("horizontal")
    
    el.addEventListener "mousedown", (e) ->
      return unless e.button is 0
      
      anchor = $pane.anchor
      
      if anchor in ["north", "south"] then coord = "screenY"
      else if anchor in ["west", "east"] then coord = "screenX"

      if anchor in ["north", "west"] then scale = 1
      else if anchor in ["south", "east"] then scale = -1
    
      startPos = {x: e.screenX, y: e.screenY}
      startCoord = e[coord]
      startSize = $pane.size
      startTime = Date.now()
      
      ##pane.onHandleDown()
      
      # Not sure if this really adds value, but added for compatibility
      el.unselectable = "on"
      el.onselectstart = -> false
      el.style.userSelect = el.style.MozUserSelect = "none"
      
      # Null out the event to re-use e and prevent memory leaks
      #e.setCapture()
      e.preventDefault()
      e.defaultPrevented = true
      e = null

      handleMouseMove = (e) ->
        $pane.$containerEl.addClass("fa-pane-resizing")
      
        # Inside Angular's digest, determine the ideal size of the element
        # according to movements then determine if those movements have been
        # constrained by boundaries, other panes or min/max clauses
        $scope.$apply ->
          targetSize = startSize + scale * (e[coord] - startCoord)
          
          $pane.resize targetSize

        # Null out the event in case of memory leaks
        #e.setCapture()
        e.preventDefault()
        e.defaultPrevented = true
        e = null
        
      handleMouseUp = (e) ->
        displacementSq = Math.pow(e.screenX - startPos.x, 2) + Math.pow(e.screenY - startPos.y, 2)
        timeElapsed = Date.now() - startTime

        $window.removeEventListener "mousemove", handleMouseMoveThrottled, true
        $window.removeEventListener "mouseup", handleMouseUp, true
        
        cleanup = ->
          $pane.$containerEl.removeClass("fa-pane-resizing")
        
          # Null out the event in case of memory leaks
          #e.releaseCapture()
          e.preventDefault()
          e.defaultPrevented = true
          e = null
        
        if displacementSq <= Math.pow(clickRadius, 2) and timeElapsed <= clickTime
          cleanup()
          return
          
        # In case the mouse is released at the end of a throttle period
        handleMouseMove(e)
        
        cleanup()

      
      # Prevent the reflow logic from happening too often
      handleMouseMoveThrottled = throttle(10, handleMouseMove)
    
      $window.addEventListener "mouseup", handleMouseUp, true
      $window.addEventListener "mousemove", handleMouseMoveThrottled, true
]

.fa-pane {
  overflow: visible;
}

.fa-pane, .fa-pane-overlay, .fa-pane-handle, .fa-pane-scroller {
  display: block;
  position: absolute;
  box-sizing: border-box;
}

.fa-pane, .fa-pane-overlay, .fa-pane-scroller {
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.fa-pane-scroller {
  overflow: auto;
}

.fa-pane.fa-pane-closed>.fa-pane-scroller {
  display: none;
}

.fa-pane-overlay {
  display: none;
  z-index: 9999;
}

.fa-pane-resizing>.fa-pane-overlay {
  display: block;
}

.fa-pane-orientation-vertical>.fa-pane-handle {
  cursor: row-resize;
}

.fa-pane-orientation-horizontal>.fa-pane-handle {
  cursor: col-resize;
}

.fa-pane-orientation-horizontal>.fa-pane-handle>.fa-pane-toggle {
  margin: auto;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  height: 100px;
  max-height: 20%;
  cursor: pointer;
}

.fa-pane-orientation-vertical>.fa-pane-handle>.fa-pane-toggle {
  margin: auto;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100px;
  max-width: 20%;
  cursor: pointer;
}

.fa-pane-closed>.fa-pane-scroller {
  display: none;
}
<div class="pane-hierarchy">
  <div fa-pane>
    <div fa-pane pane-anchor="west" pane-size="{{100/(layout.length - $index) + '&'}}" pane-min="80px" pane-handle="{{$index!=layout.length-1&&4}}px" pane-no-toggle="true" pane-order="{{colIndex}}" ng-repeat="(colIndex, verticalSplits) in layout">
      <div class="pane-container" pane-min="80px" ng-class="{active: isActive(paneId)}" fa-pane pane-anchor="north" pane-size="{{100/(verticalSplits.length-$index) + '&'}}" pane-handle="{{$index!=verticalSplits.length-1&&4}}px" pane-no-toggle="true" pane-order="{{rowIndex}}" ng-repeat="(rowIndex, paneId) in verticalSplits track by paneId">
        <div class="pane-header">
          <div class="pane-title" ng-click="$command('space.activate', {coords: {row: rowIndex, col: colIndex}})" ng-bind="getPane(paneId).title"></div>
          <div class="pane-actions">
            <button class="btn btn-xs btn-primary" ng-click="$command('space.split', {direction: 'h', coords: {row: rowIndex, col: colIndex}})" tooltip="Split horizontally" tooltip-placement="bottom" tooltip-append-to-body="true">|</button>
            <button class="btn btn-xs btn-primary" ng-click="$command('space.split', {direction: 'v', coords: {row: rowIndex, col: colIndex}})" tooltip="Split vertically" tooltip-placement="bottom" tooltip-append-to-body="true">-</button>
            <button class="btn btn-xs btn-danger" ng-click="$command('space.close', {coords: {row: rowIndex, col: colIndex}})" tooltip="Close" tooltip-placement="bottom" tooltip-append-to-body="true">X</button>
          </div>
        </div>
        <div class="pane-content" ng-click="$command('space.activate', {coords: {row: rowIndex, col: colIndex}})" pane-viewer="{{paneId}}"></div>
      </div>
    </div>
  </div>
</div>
var module = angular.module("plunker.service.keybindings", [
  "plunker.service.commands"
]);

module.directive("keybindings", ["keybindings", function(keybindings){
  return {
    link: function($scope, $el){
      keybindings.attachTo($el[0]);
    }
  };
}]);

module.service("keybindings", ["$injector", "commands", function($injector, commands){
  var KeyBinding = ace.require("ace/keyboard/keybinding").KeyBinding
    , HashHandler = ace.require("ace/keyboard/hash_handler").HashHandler
    , Event = ace.require("ace/lib/event");
  
  function KeyBindings() {
    var self = this;
    
    // Set up this class so that it will work with ace's KeyBinding class out of the box
    this.commands = new HashHandler();
    this.commands.exec = function(command){
      command.exec();
      return true;
    };

    this.keyBinding = new KeyBinding(this);
  }

  KeyBindings.prototype.attachTo = function(el){
    Event.addCommandKeyListener(el, this.keyBinding.onCommandKey.bind(this.keyBinding));
  };
  
  KeyBindings.prototype.bindKey = function(bindKey, commandId){
    this.commands.bindKey(bindKey, _.bind(commands.exec, commands, commandId));
  };
  
  return KeyBindings;
}]);
var stream = require("stream")
  , through = require("through");

var module = angular.module("plunker.service.project", [
  "plunker.service.commands",
  "plunker.service.genid",
  "plunker.service.textops"
]);

module.value("defaultProjectFiles", [{
    type: "file",
    filename: "index.html",
    contents: "<!html>\n\n  <head>\n  </head>\n\n  <body>\n  </body>\n\n</html>"
  },{
    type: "directory",
    filename: "lib",
    children: [{
      type: "file",
      filename: "style.css",
      contents: "h1 {\n  color: red;\n}"
    },{
      type: "file",
      filename: "script.js",
      contents: "// comment"
    }]
  }]
);

module.factory("project", ["$rootScope", "$http", "$q", "commands", "genid", "textops", "defaultProjectFiles", function($rootScope, $http, $q, commands, genid, textops, defaultProjectFiles){
  var EditSession = ace.require("ace/edit_session").EditSession;
    
  var plunk = []
    , entries = {}
    , root = {
      type: "directory",
      filename: "Project",
      path: "",
      children: []
    };
  
  commands.register("project.reset", projectReset);
  commands.register("project.openDefault", projectOpenDefault);
  commands.register("project.open", projectOpen);
  commands.register("project.import", projectImport);

  commands.register("directory.create", directoryCreate, {parent: root});
  commands.register("directory.remove", directoryRemove, {parent: root});
  commands.register("directory.rename", directoryRename, {parent: root});

  commands.register("file.create", fileCreate, {parent: root});
  commands.register("file.remove", fileRemove, {parent: root});
  commands.register("file.rename", fileRename, {parent: root});

  commands.register("text.insert", textInsert);
  commands.register("text.remove", textRemove);
  
  
  return {
    createStream: createStream,
    entries: entries,
    root: root,
    getEditSessionByFilename: getEditSessionByFilename,
    getEditSessionById: getEditSessionById,
    getEntryByFilename: getEntryByFilename,
    getEntryById: getEntryById,
  };
  
  
  function fileCreate(parent, filename) {
    var entry = {
      id: genid(),
      type: "file",
      filename: filename,
      path: parent.path + filename,
      contents: "",
      editSession: new EditSession("")
    };

    $rootScope.$watch(function() {
      return entry.filename;
    }, function(){
      withModule("ace/ext/modelist").then(function(modelist){
        entry.editSession.setMode(modelist.getModeForPath(entry.filename).mode);
      });
    });
    
    parent.children.push(entry);
    entries[entry.id] = entry;
    
    return entry;
  }
  
  function fileRemove(parent, entryId) {
    var entry = entries[entryId];
    
    if (entry) {
      var idx = parent.children.indexOf(entry);
      
      if (idx >= 0) parent.children.splice(idx, 1);
      
      delete entries[entryId];
    }
  }

  function fileRename(parent, entryId, filename) {
    var entry = entries[entryId];
    
    if (entry) {
      //entry.path = Path.dirname(entry.path) + '/' + filename;
      entry.filename = filename;
    }
    
    return entry;
  }
  
  function directoryCreate(parent, filename) {
    var entry = {
      id: genid(),
      type: "directory",
      filename: filename,
      path: parent.path + '/' + filename,
      children: []
    };
    
    parent.children.push(entry);
    entries[entry.id] = entry;
    
    return entry;
  }
  
  function directoryRemove(parent, entryId) {
  }

  function directoryRename(parent, entryId, filename) {
    
  }
  
  function createStream(options) {
    events = stream.Duplex({objectMode: true});
    events._read = angular.noop;
    
    _(entries).filter({type: 'file'}).forEach(function(entry) {
      // Send the initial value
      if (!options || !options.keepLocalState) {
        events.push({
          type: "text",
          source: entry.id,
          op: textops.createInsertOp(0, entry.contents)
        });
      }
      
      entry.editStream.pipe(through(function(data){
        this.queue({
          type: "text",
          source: entry.id,
          op: data
        });
      })).pipe(events);
      
      events.pipe(through(function(data){
        if (data.type === "text" && data.source === entry.id) {
          this.queue(data);
        }
      })).pipe(entry.editStream);
    });
    
    return events;
  }
  
  function projectReset() {
    // Reset entry containers
    root.children.length = 0;
    angular.copy({}, entries);
  }
  
  function projectOpen(plunkId) {
    $http.get("https://plunker_api-c9-ggoodman.c9.io/plunks/" + plunkId).then(function(response) {
      var plunk = response.data
        , lastRevision = response.data.revisions[response.data.revisions.length - 1];
      
      return $http.get("https://plunker_api-c9-ggoodman.c9.io/trees/" + lastRevision.tree).then(function(response){
        commands.exec('tree.reset', {tree: response.data});
        
        trackEntries(tree);
      });
    });
  }
  
  function projectOpenDefault() {
    commands.exec('project.reset');
    
    treeReset(defaultProjectFiles);
  }
  
  function projectImport() {
    
  }
  
  function textInsert(entryId, offset, text) {
    var entry = entries[entryId]
      , doc;
    
    if (entry && entry.editSession && (doc = entry.editSession.doc)) {
      doc.insert(doc.indexToPosition(offset), text);
    }
  }
  
  function textRemove(entryId, offset, text) {
    var entry = entries[entryId]
      , doc
      , range;
    
    if (entry && entry.editSession && (doc = entry.editSession.doc)) {
      range = Range.fromPoints(doc.indexToPosition(offset), doc.indexToPosition(offset + text.length));
      
      doc.remove(range);
    }
  }
  
  function treeReset(tree) {
    addChildren(tree, root);
    
    function addChildren(children, parent) {
      for (var i = 0; i < children.length; i++) {
        var entry = children[i];
        
        if (entry.type === "file") {
          var file = commands.exec("file.create", {
            parent: parent,
            filename: entry.filename
          });
          
          commands.exec("text.insert", {
            entryId: file.id,
            offset: 0,
            text: entry.contents
          });
          
        } else if (entry.type === "directory") {
          var dir = commands.exec("directory.create", {
            parent: parent,
            filename: entry.filename
          });
          
          addChildren(entry.children, dir);
        }
      }
    }
  }
  
  function trackFile(entry) {
    var localEvent = false;
    
    entry.editSession = new EditSession(entry.contents);
    entry.editStream = stream.Duplex({objectMode: true});

    entry.editStream._read = function(size){
      console.log("editStream._read", arguments);
    };
    entry.editStream._write = function(chunk, enc, next){
      console.log("editStream._write", arguments);
      //entry.contents = textops.apply(entry.contents, chunk);
      next();
    };
    
    // Keep the session's mode in sync with the entry's filename
    $rootScope.$watch(function() {
      return entry.filename;
    }, function(){
      withModule("ace/ext/modelist").then(function(modelist){
        entry.editSession.setMode(modelist.getModeForPath(entry.filename).mode);
      });
    });

    // Listen to the EditSession's change events and push them across the Duplex
    // stream as OT ops.
    entry.editSession.on("change", onLocalChange);
      
    function onLocalChange(e) {
      if (localEvent) return;
      
      localEvent = true;
      if (!$rootScope.$$phase) {
        $rootScope.$apply(function() {
          var op = null
            , doc = entry.editSession.doc;
          
          switch (e.data.action) {
            case "insertText": op = textops.createInsertOp(doc.positionToIndex(e.data.range.start), e.data.text); break;
            case "insertLines": op = textops.createInsertOp(doc.positionToIndex(e.data.range.start), e.data.lines.join(e.data.nl) + e.data.nl); break;
            case "removeText": op = textops.createRemoveOp(doc.positionToIndex(e.data.range.start), e.data.text); break;
            case "removeLines": op = textops.createRemoveOp(doc.positionToIndex(e.data.range.start), e.data.lines.join(e.data.nl) + e.data.nl); break;
          }
          
          entry.editStream.push(op);
        });
      }
      
      entry.contents = entry.editSession.getValue();
      
      localEvent = false;
    }
  }
  
  function trackEntries(tree) {
    for (var i = 0; i < tree.length; i++) {
      (function(entry) {
        entry.id = genid();
        
        if (entry.type === "file") {
          trackFile(entry);
        }
      
        entries[entry.id] = entry;
        
        if (entry.children) trackEntries(entry.children);
      }(tree[i]));
    }
  }
  
  function getEditSessionByFilename(filename) {
    var entry = getEntryByFilename(filename);
    
    if (entry) return entry.editSession;
  }
  
  function getEditSessionById(entryId) {
    var entry = getEntryById(entryId);
    
    if (entry) return entry.editSession;
  }
  
  function getEntryByFilename(filename) {
    return _.find(entries, {filename: filename});
  }
  
  function getEntryById(entryId) {
    return entries[entryId];
  }

  // Private interfaces
  
  function withModule(moduleName) {
    var dfd = $q.defer();
    
    ace.config.loadModule(moduleName, function(module){
      if (module) { dfd.resolve(module); }
      else { dfd.reject("Failed to load module") }
    });
    
    return dfd.promise;
  }

}]);
var module = angular.module("plunker.directive.tree", [
  "ui.bootstrap",
  
  "plunker.service.commands"
]);


module.directive("fileTree", ["commands", "workspace", function(commands, workspace){
  return {
    restrict: "E",
    replace: true,
    scope: {
      tree: "=",
      closed: "@"
    },
    templateUrl: "views/fileTree.html",
    link: function($scope, $element, $attrs){
      $scope.open = function(entry, options){
        commands.exec("file.open", {entry: entry, options: options});
      };
      
      $scope.isActive = function(entry) {
        return workspace.isActiveById(entry.id);
      };
      
      $scope.isOpen = function(entry) {
        return workspace.isOpenById(entry.id);
      };
    }
  };
}]);

module.directive("fileTreeRecurse", ["$compile", function($compile){
  return {
    restrict: "E",
    replace: true,
    template: "<div></div>",
    scope: {
      tree: "="
    },
    link: function($scope, $element, $attrs){
      var tree = angular.element('<file-tree tree="tree"></file-tree>');
      
      $compile(tree)($scope);
      $element.replaceWith(tree);
    }
  };
}]);
<ul class="file-tree fa-ul">
  <li ng-repeat="entry in tree" ng-switch="entry.type">
    <div class="tree-entry tree-folder" ng-switch-when="directory" ng-class="{collapsed: collapsed}">
      <div class="tree-inner" ng-click="collapsed=!collapsed">
        <i class="tree-icon fa-li fa fa-folder{{!collapsed && '-open' || ''}}"></i>
        <span class="tree-filename" >{{entry.filename}} <span class="label label-default" ng-show="collapsed" ng-bind="entry.children.length"></span></span>
      </div>
      <div class="tree-bg"></div>
      <file-tree-recurse tree="entry.children"></file-tree-recurse>
    </div>
    <div class="tree-entry tree-file" ng-switch-when="file" ng-class="{active: isActive(entry), open: isOpen(entry)}">
      <div class="tree-inner" ng-click="open(entry)">
        <i class="tree-icon fa-li fa fa-file-text"></i>
        <div class="tree-filename" ng-bind="entry.filename"></div>
        <div class="tree-actions dropdown">
          <button class="btn btn-xs btn-default" ng-disabled="isActive(entry)" tooltip="Open in new space below" tooltip-append-to-body="true" ng-click="open(entry, {target: 'col'})"><i class="fa fa-caret-square-o-down"></i></button>
          <button class="btn btn-xs btn-default" ng-disabled="isActive(entry)" tooltip="Open in new space to the right" tooltip-append-to-body="true" ng-click="open(entry, {target: 'row'})"><i class="fa fa-caret-square-o-right"></i></button>
          <button class="btn btn-xs btn-default dropdown-toggle"><i class="fa fa-bars"></i></button>
          <ul class="dropdown-menu">
            <li><a ng-click="promptRename(entry)">Rename...</a></li>
            <li class="divider"></li>
            <li><a class="text-danger" ng-click="promptDelete(entry, tree)">Delete</a></li>
          </ul>
        </div>
      </div>
      <div class="tree-bg"></div>
    </div>
  </li>
</ul>
var stream = require('stream');


var module = angular.module("plunker.directive.editor", [
  "plunker.service.textops",
  "plunker.service.opstream"
]);

module.directive("aceEditor", ["$rootScope", "$q", "textops", "opstream", function($rootScope, $q, textops, opstream){
  var AceEditor = ace.require("ace/editor").Editor
    , Renderer = ace.require("ace/virtual_renderer").VirtualRenderer
    , EditSession = ace.require("ace/edit_session").EditSession
    , UndoManager = ace.require("ace/undomanager").UndoManager
    , Range = ace.require("ace/range").Range;

  return {
    restrict: "E",
    replace: true,
    template: "<div></div>",
    link: function($scope, $element, $attrs){
      var editor = new AceEditor(new Renderer($element[0], "ace/theme/textmate"));
      
      editor.setSession($scope.pane.data.editSession);
      
      withModule("ace/ext/language_tools").then(function() {
        editor.setOptions({
          enableBasicAutocompletion: true,
          enableSnippets: true      
        });
      });

      $scope.$on("pane-active", function(e){
        editor.focus();
      });
    }
  };
  
  function withModule(moduleName) {
    var dfd = $q.defer();
    
    ace.config.loadModule(moduleName, function(module){
      if (module) { dfd.resolve(module); }
      else { dfd.reject("Failed to load module") }
    });
    
    return dfd.promise;
  }
}]);
var module = angular.module("plunker.service.genid", []);

module.factory("genid", function(){
  return genid;
  
  function genid(len, prefix, keyspace) {
    if (len == null) {
      len = 32;
    }
    if (prefix == null) {
      prefix = "";
    }
    if (keyspace == null) {
      keyspace = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    }
    while (len-- > 0) {
      prefix += keyspace.charAt(Math.floor(Math.random() * keyspace.length));
    }
    return prefix;
  }
});
var module = angular.module("plunker.service.textops", []);

module.factory("textops", [function(){
  return {
    apply: applyOp,
    createInsertOp: createInsertOp,
    createRemoveOp: createRemoveOp
  };
  
  function applyOp(snapshot, op) {
    return ottext.ot.apply(snapshot, op);
  }
  
  function createInsertOp(position, text) {
    return [{p: position, i: text}];
  }
  
  function createRemoveOp(position, text) {
    return [{p: position, d: text}];
  }
  
  function toSimpleOps(ops) {
    return _.map(ops, function(op) {
      if (op.d) return {op: 'remove', args: [op.p, op.d]};
      if (op.i) return {op: 'insert', args: [op.p, op.i]};
    });
  }
}]);
// This is a prelude which comes before the JS blob of each JS type for the web.
(function() {
  var module = {
    exports: {}
  };
  var exports = window.ottypes = module.exports;

  // These methods let you build a transform function from a transformComponent
  // function for OT types like JSON0 in which operations are lists of components
  // and transforming them reqreuires N^2 work. I find it kind of nasty that I need
  // this, but I'm not really sure what a better solution is. Maybe I should do
  // this automatically to types that don't have a compose function defined.

  // Add transform and transformX functions for an OT type which has
  // transformComponent defined.  transformComponent(destination array,
  // component, other component, side)
  exports._bootstrapTransform = function(type, transformComponent, checkValidOp, append) {
    var transformComponentX = function(left, right, destLeft, destRight) {
      transformComponent(destLeft, left, right, 'left');
      transformComponent(destRight, right, left, 'right');
    };

    var transformX = type.transformX = function(leftOp, rightOp) {
      checkValidOp(leftOp);
      checkValidOp(rightOp);
      var newRightOp = [];

      for (var i = 0; i < rightOp.length; i++) {
        var rightComponent = rightOp[i];

        // Generate newLeftOp by composing leftOp by rightComponent
        var newLeftOp = [];
        var k = 0;
        while (k < leftOp.length) {
          var nextC = [];
          transformComponentX(leftOp[k], rightComponent, newLeftOp, nextC);
          k++;

          if (nextC.length === 1) {
            rightComponent = nextC[0];
          } else if (nextC.length === 0) {
            for (var j = k; j < leftOp.length; j++) {
              append(newLeftOp, leftOp[j]);
            }
            rightComponent = null;
            break;
          } else {
            // Recurse.
            var pair = transformX(leftOp.slice(k), nextC);
            for (var l = 0; l < pair[0].length; l++) {
              append(newLeftOp, pair[0][l]);
            }
            for (var r = 0; r < pair[1].length; r++) {
              append(newRightOp, pair[1][r]);
            }
            rightComponent = null;
            break;
          }
        }

        if (rightComponent != null) {
          append(newRightOp, rightComponent);
        }
        leftOp = newLeftOp;
      }
      return [leftOp, newRightOp];
    };

    // Transforms op with specified type ('left' or 'right') by otherOp.
    type.transform = type['transform'] = function(op, otherOp, type) {
      if (!(type === 'left' || type === 'right'))
        throw new Error("type must be 'left' or 'right'");

      if (otherOp.length === 0) return op;

      if (op.length === 1 && otherOp.length === 1)
        return transformComponent([], op[0], otherOp[0], type);

      if (type === 'left')
        return transformX(op, otherOp)[0];
      else
        return transformX(otherOp, op)[1];
    };
  };
  // DEPRECATED!
  //
  // This type works, but is not exported, and will be removed in a future version of this library.


  // A simple text implementation
  //
  // Operations are lists of components.
  // Each component either inserts or deletes at a specified position in the document.
  //
  // Components are either:
  //  {i:'str', p:100}: Insert 'str' at position 100 in the document
  //  {d:'str', p:100}: Delete 'str' at position 100 in the document
  //
  // Components in an operation are executed sequentially, so the position of components
  // assumes previous components have already executed.
  //
  // Eg: This op:
  //   [{i:'abc', p:0}]
  // is equivalent to this op:
  //   [{i:'a', p:0}, {i:'b', p:1}, {i:'c', p:2}]

  // NOTE: The global scope here is shared with other sharejs files when built with closure.
  // Be careful what ends up in your namespace.

  var text = module.exports.text0 = {
    name: 'text0',
    uri: 'http://sharejs.org/types/textv0',
    create: function(initial) {
      if ((initial != null) && typeof initial !== 'string') {
        throw new Error('Initial data must be a string');
      }
      return initial || '';
    }
  };

  /** Insert s2 into s1 at pos. */
  var strInject = function(s1, pos, s2) {
    return s1.slice(0, pos) + s2 + s1.slice(pos);
  };

  /** Check that an operation component is valid. Throws if its invalid. */
  var checkValidComponent = function(c) {
    if (typeof c.p !== 'number')
      throw new Error('component missing position field');

    if ((typeof c.i === 'string') === (typeof c.d === 'string'))
      throw new Error('component needs an i or d field');

    if (c.p < 0)
      throw new Error('position cannot be negative');
  };

  /** Check that an operation is valid */
  var checkValidOp = function(op) {
    for (var i = 0; i < op.length; i++) {
      checkValidComponent(op[i]);
    }
  };

  /** Apply op to snapshot */
  text.apply = function(snapshot, op) {
    var deleted;

    checkValidOp(op);
    for (var i = 0; i < op.length; i++) {
      var component = op[i];
      if (component.i != null) {
        snapshot = strInject(snapshot, component.p, component.i);
      } else {
        deleted = snapshot.slice(component.p, component.p + component.d.length);
        if (component.d !== deleted)
          throw new Error("Delete component '" + component.d + "' does not match deleted text '" + deleted + "'");

        snapshot = snapshot.slice(0, component.p) + snapshot.slice(component.p + component.d.length);
      }
    }
    return snapshot;
  };

  /**
   * Append a component to the end of newOp. Exported for use by the random op
   * generator and the JSON0 type.
   */
  var append = text._append = function(newOp, c) {
    if (c.i === '' || c.d === '') return;

    if (newOp.length === 0) {
      newOp.push(c);
    } else {
      var last = newOp[newOp.length - 1];

      if (last.i != null && c.i != null && last.p <= c.p && c.p <= last.p + last.i.length) {
        // Compose the insert into the previous insert
        newOp[newOp.length - 1] = {
          i: strInject(last.i, c.p - last.p, c.i),
          p: last.p
        };

      } else if (last.d != null && c.d != null && c.p <= last.p && last.p <= c.p + c.d.length) {
        // Compose the deletes together
        newOp[newOp.length - 1] = {
          d: strInject(c.d, last.p - c.p, last.d),
          p: c.p
        };

      } else {
        newOp.push(c);
      }
    }
  };

  /** Compose op1 and op2 together */
  text.compose = function(op1, op2) {
    checkValidOp(op1);
    checkValidOp(op2);
    var newOp = op1.slice();
    for (var i = 0; i < op2.length; i++) {
      append(newOp, op2[i]);
    }
    return newOp;
  };

  /** Clean up an op */
  text.normalize = function(op) {
    var newOp = [];

    // Normalize should allow ops which are a single (unwrapped) component:
    // {i:'asdf', p:23}.
    // There's no good way to test if something is an array:
    // http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
    // so this is probably the least bad solution.
    if (op.i != null || op.p != null) op = [op];

    for (var i = 0; i < op.length; i++) {
      var c = op[i];
      if (c.p == null) c.p = 0;

      append(newOp, c);
    }

    return newOp;
  };

  // This helper method transforms a position by an op component.
  //
  // If c is an insert, insertAfter specifies whether the transform
  // is pushed after the insert (true) or before it (false).
  //
  // insertAfter is optional for deletes.
  var transformPosition = function(pos, c, insertAfter) {
    // This will get collapsed into a giant ternary by uglify.
    if (c.i != null) {
      if (c.p < pos || (c.p === pos && insertAfter)) {
        return pos + c.i.length;
      } else {
        return pos;
      }
    } else {
      // I think this could also be written as: Math.min(c.p, Math.min(c.p -
      // otherC.p, otherC.d.length)) but I think its harder to read that way, and
      // it compiles using ternary operators anyway so its no slower written like
      // this.
      if (pos <= c.p) {
        return pos;
      } else if (pos <= c.p + c.d.length) {
        return c.p;
      } else {
        return pos - c.d.length;
      }
    }
  };

  // Helper method to transform a cursor position as a result of an op.
  //
  // Like transformPosition above, if c is an insert, insertAfter specifies
  // whether the cursor position is pushed after an insert (true) or before it
  // (false).
  text.transformCursor = function(position, op, side) {
    var insertAfter = side === 'right';
    for (var i = 0; i < op.length; i++) {
      position = transformPosition(position, op[i], insertAfter);
    }

    return position;
  };

  // Transform an op component by another op component. Asymmetric.
  // The result will be appended to destination.
  //
  // exported for use in JSON type
  var transformComponent = text._tc = function(dest, c, otherC, side) {
    //var cIntersect, intersectEnd, intersectStart, newC, otherIntersect, s;

    checkValidComponent(c);
    checkValidComponent(otherC);

    if (c.i != null) {
      // Insert.
      append(dest, {
        i: c.i,
        p: transformPosition(c.p, otherC, side === 'right')
      });
    } else {
      // Delete
      if (otherC.i != null) {
        // Delete vs insert
        var s = c.d;
        if (c.p < otherC.p) {
          append(dest, {
            d: s.slice(0, otherC.p - c.p),
            p: c.p
          });
          s = s.slice(otherC.p - c.p);
        }
        if (s !== '')
          append(dest, {
            d: s,
            p: c.p + otherC.i.length
          });

      } else {
        // Delete vs delete
        if (c.p >= otherC.p + otherC.d.length)
          append(dest, {
            d: c.d,
            p: c.p - otherC.d.length
          });
        else if (c.p + c.d.length <= otherC.p)
          append(dest, c);
        else {
          // They overlap somewhere.
          var newC = {
            d: '',
            p: c.p
          };

          if (c.p < otherC.p)
            newC.d = c.d.slice(0, otherC.p - c.p);

          if (c.p + c.d.length > otherC.p + otherC.d.length)
            newC.d += c.d.slice(otherC.p + otherC.d.length - c.p);

          // This is entirely optional - I'm just checking the deleted text in
          // the two ops matches
          var intersectStart = Math.max(c.p, otherC.p);
          var intersectEnd = Math.min(c.p + c.d.length, otherC.p + otherC.d.length);
          var cIntersect = c.d.slice(intersectStart - c.p, intersectEnd - c.p);
          var otherIntersect = otherC.d.slice(intersectStart - otherC.p, intersectEnd - otherC.p);
          if (cIntersect !== otherIntersect)
            throw new Error('Delete ops delete different text in the same region of the document');

          if (newC.d !== '') {
            newC.p = transformPosition(newC.p, otherC);
            append(dest, newC);
          }
        }
      }
    }

    return dest;
  };

  var invertComponent = function(c) {
    return (c.i != null) ? {
      d: c.i,
      p: c.p
    } : {
      i: c.d,
      p: c.p
    };
  };

  // No need to use append for invert, because the components won't be able to
  // cancel one another.
  text.invert = function(op) {
    // Shallow copy & reverse that sucka.
    op = op.slice().reverse();
    for (var i = 0; i < op.length; i++) {
      op[i] = invertComponent(op[i]);
    }
    return op;
  };

  exports._bootstrapTransform(text, transformComponent, checkValidOp, append);

  /*
 This is the implementation of the JSON OT type.

 Spec is here: https://github.com/josephg/ShareJS/wiki/JSON-Operations

 Note: This is being made obsolete. It will soon be replaced by the JSON2 type.
*/

  /**
   * UTILITY FUNCTIONS
   */

  /**
   * Checks if the passed object is an Array instance. Can't use Array.isArray
   * yet because its not supported on IE8.
   *
   * @param obj
   * @returns {boolean}
   */
  var isArray = function(obj) {
    return Object.prototype.toString.call(obj) == '[object Array]';
  };

  /**
   * Clones the passed object using JSON serialization (which is slow).
   *
   * hax, copied from test/types/json. Apparently this is still the fastest way
   * to deep clone an object, assuming we have browser support for JSON.  @see
   * http://jsperf.com/cloning-an-object/12
   */
  var clone = function(o) {
    return JSON.parse(JSON.stringify(o));
  };

  /**
   * Reference to the Text OT type. This is used for the JSON String operations.
   * @type {*}
   */
  if (typeof text === 'undefined')
    var text = window.ottypes.text;

  /**
   * JSON OT Type
   * @type {*}
   */
  var json = module.exports.json0 = {
    name: 'json0',
    uri: 'http://sharejs.org/types/JSONv0'
  };

  json.create = function(data) {
    // Null instead of undefined if you don't pass an argument.
    return data === undefined ? null : data;
  };

  json.invertComponent = function(c) {
    var c_ = {
      p: c.p
    };

    if (c.si !== void 0) c_.sd = c.si;
    if (c.sd !== void 0) c_.si = c.sd;
    if (c.oi !== void 0) c_.od = c.oi;
    if (c.od !== void 0) c_.oi = c.od;
    if (c.li !== void 0) c_.ld = c.li;
    if (c.ld !== void 0) c_.li = c.ld;
    if (c.na !== void 0) c_.na = -c.na;

    if (c.lm !== void 0) {
      c_.lm = c.p[c.p.length - 1];
      c_.p = c.p.slice(0, c.p.length - 1).concat([c.lm]);
    }

    return c_;
  };

  json.invert = function(op) {
    var op_ = op.slice().reverse();
    var iop = [];
    for (var i = 0; i < op_.length; i++) {
      iop.push(json.invertComponent(op_[i]));
    }
    return iop;
  };

  json.checkValidOp = function(op) {
    for (var i = 0; i < op.length; i++) {
      if (!isArray(op[i].p))
        throw new Error('Missing path');
    }
  };

  json.checkList = function(elem) {
    if (!isArray(elem))
      throw new Error('Referenced element not a list');
  };

  json.checkObj = function(elem) {
    if (elem.constructor !== Object) {
      throw new Error("Referenced element not an object (it was " + JSON.stringify(elem) + ")");
    }
  };

  json.apply = function(snapshot, op) {
    json.checkValidOp(op);

    op = clone(op);

    var container = {
      data: snapshot
    };

    for (var i = 0; i < op.length; i++) {
      var c = op[i];

      var parent = null;
      var parentKey = null;
      var elem = container;
      var key = 'data';

      for (var j = 0; j < c.p.length; j++) {
        var p = c.p[j];

        parent = elem;
        parentKey = key;
        elem = elem[key];
        key = p;

        if (parent == null)
          throw new Error('Path invalid');
      }

      // Number add
      if (c.na !== void 0) {
        if (typeof elem[key] != 'number')
          throw new Error('Referenced element not a number');

        elem[key] += c.na;
      }

      // String insert
      else if (c.si !== void 0) {
        if (typeof elem != 'string')
          throw new Error('Referenced element not a string (it was ' + JSON.stringify(elem) + ')');

        parent[parentKey] = elem.slice(0, key) + c.si + elem.slice(key);
      }

      // String delete
      else if (c.sd !== void 0) {
        if (typeof elem != 'string')
          throw new Error('Referenced element not a string');

        if (elem.slice(key, key + c.sd.length) !== c.sd)
          throw new Error('Deleted string does not match');

        parent[parentKey] = elem.slice(0, key) + elem.slice(key + c.sd.length);
      }

      // List replace
      else if (c.li !== void 0 && c.ld !== void 0) {
        json.checkList(elem);
        // Should check the list element matches c.ld
        elem[key] = c.li;
      }

      // List insert
      else if (c.li !== void 0) {
        json.checkList(elem);
        elem.splice(key, 0, c.li);
      }

      // List delete
      else if (c.ld !== void 0) {
        json.checkList(elem);
        // Should check the list element matches c.ld here too.
        elem.splice(key, 1);
      }

      // List move
      else if (c.lm !== void 0) {
        json.checkList(elem);
        if (c.lm != key) {
          var e = elem[key];
          // Remove it...
          elem.splice(key, 1);
          // And insert it back.
          elem.splice(c.lm, 0, e);
        }
      }

      // Object insert / replace
      else if (c.oi !== void 0) {
        json.checkObj(elem);

        // Should check that elem[key] == c.od
        elem[key] = c.oi;
      }

      // Object delete
      else if (c.od !== void 0) {
        json.checkObj(elem);

        // Should check that elem[key] == c.od
        delete elem[key];
      } else {
        throw new Error('invalid / missing instruction in op');
      }
    }

    return container.data;
  };

  // Helper for incrementally applying an operation to a snapshot. Calls yield
  // after each op component has been applied.
  json.incrementalApply = function(snapshot, op, _yield) {
    for (var i = 0; i < op.length; i++) {
      var smallOp = [op[i]];
      snapshot = json.apply(snapshot, smallOp);
      // I'd just call this yield, but thats a reserved keyword. Bah!
      _yield(smallOp, snapshot);
    }

    return snapshot;
  };

  // Checks if two paths, p1 and p2 match.
  var pathMatches = json.pathMatches = function(p1, p2, ignoreLast) {
    if (p1.length != p2.length)
      return false;

    for (var i = 0; i < p1.length; i++) {
      if (p1[i] !== p2[i] && (!ignoreLast || i !== p1.length - 1))
        return false;
    }

    return true;
  };

  var _convertToTextComponent = function(component) {
    var newC = {
      p: component.p[component.p.length - 1]
    };
    if (component.si != null) {
      newC.i = component.si;
    } else {
      newC.d = component.sd;
    }
    return newC;
  };

  json.append = function(dest, c) {
    c = clone(c);

    var last;

    if (dest.length != 0 && pathMatches(c.p, (last = dest[dest.length - 1]).p)) {
      if (last.na != null && c.na != null) {
        dest[dest.length - 1] = {
          p: last.p,
          na: last.na + c.na
        };
      } else if (last.li !== undefined && c.li === undefined && c.ld === last.li) {
        // insert immediately followed by delete becomes a noop.
        if (last.ld !== undefined) {
          // leave the delete part of the replace
          delete last.li;
        } else {
          dest.pop();
        }
      } else if (last.od !== undefined && last.oi === undefined && c.oi !== undefined && c.od === undefined) {
        last.oi = c.oi;
      } else if (last.oi !== undefined && c.od !== undefined) {
        // The last path component inserted something that the new component deletes (or replaces).
        // Just merge them.
        if (c.oi !== undefined) {
          last.oi = c.oi;
        } else if (last.od !== undefined) {
          delete last.oi;
        } else {
          // An insert directly followed by a delete turns into a no-op and can be removed.
          dest.pop();
        }
      } else if (c.lm !== undefined && c.p[c.p.length - 1] === c.lm) {
        // don't do anything
      } else {
        dest.push(c);
      }
    } else if (dest.length != 0 && pathMatches(c.p, last.p, true)) {
      if ((c.si != null || c.sd != null) && (last.si != null || last.sd != null)) {
        // Try to compose the string ops together using text's equivalent methods
        var textOp = [_convertToTextComponent(last)];
        text._append(textOp, _convertToTextComponent(c));

        // Then convert back.
        if (textOp.length !== 1) {
          dest.push(c);
        } else {
          var textC = textOp[0];
          last.p[last.p.length - 1] = textC.p;
          if (textC.i != null)
            last.si = textC.i;
          else
            last.sd = textC.d;
        }
      } else {
        dest.push(c);
      }
    } else {
      dest.push(c);
    }
  };

  json.compose = function(op1, op2) {
    json.checkValidOp(op1);
    json.checkValidOp(op2);

    var newOp = clone(op1);

    for (var i = 0; i < op2.length; i++) {
      json.append(newOp, op2[i]);
    }

    return newOp;
  };

  json.normalize = function(op) {
    var newOp = [];

    op = isArray(op) ? op : [op];

    for (var i = 0; i < op.length; i++) {
      var c = op[i];
      if (c.p == null) c.p = [];

      json.append(newOp, c);
    }

    return newOp;
  };

  // Returns true if an op at otherPath may affect an op at path
  json.canOpAffectOp = function(otherPath, path) {
    if (otherPath.length === 0) return true;
    if (path.length === 0) return false;

    path = path.slice(0, path.length - 1);
    otherPath = otherPath.slice(0, otherPath.length - 1);

    for (var i = 0; i < otherPath.length; i++) {
      var p = otherPath[i];
      if (i >= path.length || p != path[i]) return false;
    }

    // Same
    return true;
  };

  // transform c so it applies to a document with otherC applied.
  json.transformComponent = function(dest, c, otherC, type) {
    c = clone(c);

    if (c.na !== void 0)
      c.p.push(0);

    if (otherC.na !== void 0)
      otherC.p.push(0);

    var common;
    if (json.canOpAffectOp(otherC.p, c.p))
      common = otherC.p.length - 1;

    var common2;
    if (json.canOpAffectOp(c.p, otherC.p))
      common2 = c.p.length - 1;

    var cplength = c.p.length;
    var otherCplength = otherC.p.length;

    if (c.na !== void 0) // hax
      c.p.pop();

    if (otherC.na !== void 0)
      otherC.p.pop();

    if (otherC.na) {
      if (common2 != null && otherCplength >= cplength && otherC.p[common2] == c.p[common2]) {
        if (c.ld !== void 0) {
          var oc = clone(otherC);
          oc.p = oc.p.slice(cplength);
          c.ld = json.apply(clone(c.ld), [oc]);
        } else if (c.od !== void 0) {
          var oc = clone(otherC);
          oc.p = oc.p.slice(cplength);
          c.od = json.apply(clone(c.od), [oc]);
        }
      }
      json.append(dest, c);
      return dest;
    }

    // if c is deleting something, and that thing is changed by otherC, we need to
    // update c to reflect that change for invertibility.
    // TODO this is probably not needed since we don't have invertibility
    if (common2 != null && otherCplength > cplength && c.p[common2] == otherC.p[common2]) {
      if (c.ld !== void 0) {
        var oc = clone(otherC);
        oc.p = oc.p.slice(cplength);
        c.ld = json.apply(clone(c.ld), [oc]);
      } else if (c.od !== void 0) {
        var oc = clone(otherC);
        oc.p = oc.p.slice(cplength);
        c.od = json.apply(clone(c.od), [oc]);
      }
    }

    if (common != null) {
      var commonOperand = cplength == otherCplength;

      // transform based on otherC
      if (otherC.na !== void 0) {
        // this case is handled above due to icky path hax
      } else if (otherC.si !== void 0 || otherC.sd !== void 0) {
        // String op vs string op - pass through to text type
        if (c.si !== void 0 || c.sd !== void 0) {
          if (!commonOperand) throw new Error('must be a string?');

          // Convert an op component to a text op component so we can use the
          // text type's transform function
          var tc1 = _convertToTextComponent(c);
          var tc2 = _convertToTextComponent(otherC);

          var res = [];

          // actually transform
          text._tc(res, tc1, tc2, type);

          // .... then convert the result back into a JSON op again.
          for (var i = 0; i < res.length; i++) {
            // Text component
            var tc = res[i];
            // JSON component
            var jc = {
              p: c.p.slice(0, common)
            };
            jc.p.push(tc.p);

            if (tc.i != null) jc.si = tc.i;
            if (tc.d != null) jc.sd = tc.d;
            json.append(dest, jc);
          }
          return dest;
        }
      } else if (otherC.li !== void 0 && otherC.ld !== void 0) {
        if (otherC.p[common] === c.p[common]) {
          // noop

          if (!commonOperand) {
            return dest;
          } else if (c.ld !== void 0) {
            // we're trying to delete the same element, -> noop
            if (c.li !== void 0 && type === 'left') {
              // we're both replacing one element with another. only one can survive
              c.ld = clone(otherC.li);
            } else {
              return dest;
            }
          }
        }
      } else if (otherC.li !== void 0) {
        if (c.li !== void 0 && c.ld === undefined && commonOperand && c.p[common] === otherC.p[common]) {
          // in li vs. li, left wins.
          if (type === 'right')
            c.p[common]++;
        } else if (otherC.p[common] <= c.p[common]) {
          c.p[common]++;
        }

        if (c.lm !== void 0) {
          if (commonOperand) {
            // otherC edits the same list we edit
            if (otherC.p[common] <= c.lm)
              c.lm++;
            // changing c.from is handled above.
          }
        }
      } else if (otherC.ld !== void 0) {
        if (c.lm !== void 0) {
          if (commonOperand) {
            if (otherC.p[common] === c.p[common]) {
              // they deleted the thing we're trying to move
              return dest;
            }
            // otherC edits the same list we edit
            var p = otherC.p[common];
            var from = c.p[common];
            var to = c.lm;
            if (p < to || (p === to && from < to))
              c.lm--;

          }
        }

        if (otherC.p[common] < c.p[common]) {
          c.p[common]--;
        } else if (otherC.p[common] === c.p[common]) {
          if (otherCplength < cplength) {
            // we're below the deleted element, so -> noop
            return dest;
          } else if (c.ld !== void 0) {
            if (c.li !== void 0) {
              // we're replacing, they're deleting. we become an insert.
              delete c.ld;
            } else {
              // we're trying to delete the same element, -> noop
              return dest;
            }
          }
        }

      } else if (otherC.lm !== void 0) {
        if (c.lm !== void 0 && cplength === otherCplength) {
          // lm vs lm, here we go!
          var from = c.p[common];
          var to = c.lm;
          var otherFrom = otherC.p[common];
          var otherTo = otherC.lm;
          if (otherFrom !== otherTo) {
            // if otherFrom == otherTo, we don't need to change our op.

            // where did my thing go?
            if (from === otherFrom) {
              // they moved it! tie break.
              if (type === 'left') {
                c.p[common] = otherTo;
                if (from === to) // ugh
                  c.lm = otherTo;
              } else {
                return dest;
              }
            } else {
              // they moved around it
              if (from > otherFrom) c.p[common]--;
              if (from > otherTo) c.p[common]++;
              else if (from === otherTo) {
                if (otherFrom > otherTo) {
                  c.p[common]++;
                  if (from === to) // ugh, again
                    c.lm++;
                }
              }

              // step 2: where am i going to put it?
              if (to > otherFrom) {
                c.lm--;
              } else if (to === otherFrom) {
                if (to > from)
                  c.lm--;
              }
              if (to > otherTo) {
                c.lm++;
              } else if (to === otherTo) {
                // if we're both moving in the same direction, tie break
                if ((otherTo > otherFrom && to > from) ||
                  (otherTo < otherFrom && to < from)) {
                  if (type === 'right') c.lm++;
                } else {
                  if (to > from) c.lm++;
                  else if (to === otherFrom) c.lm--;
                }
              }
            }
          }
        } else if (c.li !== void 0 && c.ld === undefined && commonOperand) {
          // li
          var from = otherC.p[common];
          var to = otherC.lm;
          p = c.p[common];
          if (p > from) c.p[common]--;
          if (p > to) c.p[common]++;
        } else {
          // ld, ld+li, si, sd, na, oi, od, oi+od, any li on an element beneath
          // the lm
          //
          // i.e. things care about where their item is after the move.
          var from = otherC.p[common];
          var to = otherC.lm;
          p = c.p[common];
          if (p === from) {
            c.p[common] = to;
          } else {
            if (p > from) c.p[common]--;
            if (p > to) c.p[common]++;
            else if (p === to && from > to) c.p[common]++;
          }
        }
      } else if (otherC.oi !== void 0 && otherC.od !== void 0) {
        if (c.p[common] === otherC.p[common]) {
          if (c.oi !== void 0 && commonOperand) {
            // we inserted where someone else replaced
            if (type === 'right') {
              // left wins
              return dest;
            } else {
              // we win, make our op replace what they inserted
              c.od = otherC.oi;
            }
          } else {
            // -> noop if the other component is deleting the same object (or any parent)
            return dest;
          }
        }
      } else if (otherC.oi !== void 0) {
        if (c.oi !== void 0 && c.p[common] === otherC.p[common]) {
          // left wins if we try to insert at the same place
          if (type === 'left') {
            json.append(dest, {
              p: c.p,
              od: otherC.oi
            });
          } else {
            return dest;
          }
        }
      } else if (otherC.od !== void 0) {
        if (c.p[common] == otherC.p[common]) {
          if (!commonOperand)
            return dest;
          if (c.oi !== void 0) {
            delete c.od;
          } else {
            return dest;
          }
        }
      }
    }

    json.append(dest, c);
    return dest;
  };

  exports._bootstrapTransform(json, json.transformComponent, json.checkValidOp, json.append);
})();
var stream = require("stream");

var module = angular.module("plunker.service.opstream", []);

module.factory("opstream", [function(){
  var opstream = stream.Duplex({objectMode: true});
  
  opstream._read = angular.noop;
  
  opstream._write = function(chunk, enc, next) {
    console.log("opstream._write", chunk);
    next();
  };

  return opstream;
}]);
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"mDjLDU":[function(require,module,exports){
(function (process){var Stream = require('stream')

// through
//
// a stream that does nothing but re-emit the input.
// useful for aggregating a series of changing but not ending streams into one stream)

exports = module.exports = through
through.through = through

//create a readable writable stream.

function through (write, end, opts) {
  write = write || function (data) { this.queue(data) }
  end = end || function () { this.queue(null) }

  var ended = false, destroyed = false, buffer = [], _ended = false
  var stream = new Stream()
  stream.readable = stream.writable = true
  stream.paused = false

//  stream.autoPause   = !(opts && opts.autoPause   === false)
  stream.autoDestroy = !(opts && opts.autoDestroy === false)

  stream.write = function (data) {
    write.call(this, data)
    return !stream.paused
  }

  function drain() {
    while(buffer.length && !stream.paused) {
      var data = buffer.shift()
      if(null === data)
        return stream.emit('end')
      else
        stream.emit('data', data)
    }
  }

  stream.queue = stream.push = function (data) {
//    console.error(ended)
    if(_ended) return stream
    if(data == null) _ended = true
    buffer.push(data)
    drain()
    return stream
  }

  //this will be registered as the first 'end' listener
  //must call destroy next tick, to make sure we're after any
  //stream piped from here.
  //this is only a problem if end is not emitted synchronously.
  //a nicer way to do this is to make sure this is the last listener for 'end'

  stream.on('end', function () {
    stream.readable = false
    if(!stream.writable && stream.autoDestroy)
      process.nextTick(function () {
        stream.destroy()
      })
  })

  function _end () {
    stream.writable = false
    end.call(stream)
    if(!stream.readable && stream.autoDestroy)
      stream.destroy()
  }

  stream.end = function (data) {
    if(ended) return
    ended = true
    if(arguments.length) stream.write(data)
    _end() // will emit or queue
    return stream
  }

  stream.destroy = function () {
    if(destroyed) return
    destroyed = true
    ended = true
    buffer.length = 0
    stream.writable = stream.readable = false
    stream.emit('close')
    return stream
  }

  stream.pause = function () {
    if(stream.paused) return
    stream.paused = true
    return stream
  }

  stream.resume = function () {
    if(stream.paused) {
      stream.paused = false
      stream.emit('resume')
    }
    drain()
    //may have become paused again,
    //as drain emits 'data'.
    if(!stream.paused)
      stream.emit('drain')
    return stream
  }
  return stream
}

}).call(this,require("/var/lib/stickshift/05be99daaebd4e358a8cb0c60e267bb1/app-root/data/lib/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"))
},{"/var/lib/stickshift/05be99daaebd4e358a8cb0c60e267bb1/app-root/data/lib/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":5,"stream":"VDsrfn"}],"through":[function(require,module,exports){
module.exports=require('mDjLDU');
},{}],3:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

function EventEmitter() {
  this._events = this._events || {};
  this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!isNumber(n) || n < 0 || isNaN(n))
    throw TypeError('n must be a positive number');
  this._maxListeners = n;
  return this;
};

EventEmitter.prototype.emit = function(type) {
  var er, handler, len, args, i, listeners;

  if (!this._events)
    this._events = {};

  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events.error ||
        (isObject(this._events.error) && !this._events.error.length)) {
      er = arguments[1];
      if (er instanceof Error) {
        throw er; // Unhandled 'error' event
      } else {
        throw TypeError('Uncaught, unspecified "error" event.');
      }
      return false;
    }
  }

  handler = this._events[type];

  if (isUndefined(handler))
    return false;

  if (isFunction(handler)) {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        len = arguments.length;
        args = new Array(len - 1);
        for (i = 1; i < len; i++)
          args[i - 1] = arguments[i];
        handler.apply(this, args);
    }
  } else if (isObject(handler)) {
    len = arguments.length;
    args = new Array(len - 1);
    for (i = 1; i < len; i++)
      args[i - 1] = arguments[i];

    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++)
      listeners[i].apply(this, args);
  }

  return true;
};

EventEmitter.prototype.addListener = function(type, listener) {
  var m;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events)
    this._events = {};

  // To avoid recursion in the case that type === "newListener"! Before
  // adding it to the listeners, first emit "newListener".
  if (this._events.newListener)
    this.emit('newListener', type,
              isFunction(listener.listener) ?
              listener.listener : listener);

  if (!this._events[type])
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  else if (isObject(this._events[type]))
    // If we've already got an array, just append.
    this._events[type].push(listener);
  else
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];

  // Check for listener leak
  if (isObject(this._events[type]) && !this._events[type].warned) {
    var m;
    if (!isUndefined(this._maxListeners)) {
      m = this._maxListeners;
    } else {
      m = EventEmitter.defaultMaxListeners;
    }

    if (m && m > 0 && this._events[type].length > m) {
      this._events[type].warned = true;
      console.error('(node) warning: possible EventEmitter memory ' +
                    'leak detected. %d listeners added. ' +
                    'Use emitter.setMaxListeners() to increase limit.',
                    this._events[type].length);
      console.trace();
    }
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  var fired = false;

  function g() {
    this.removeListener(type, g);

    if (!fired) {
      fired = true;
      listener.apply(this, arguments);
    }
  }

  g.listener = listener;
  this.on(type, g);

  return this;
};

// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
  var list, position, length, i;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events || !this._events[type])
    return this;

  list = this._events[type];
  length = list.length;
  position = -1;

  if (list === listener ||
      (isFunction(list.listener) && list.listener === listener)) {
    delete this._events[type];
    if (this._events.removeListener)
      this.emit('removeListener', type, listener);

  } else if (isObject(list)) {
    for (i = length; i-- > 0;) {
      if (list[i] === listener ||
          (list[i].listener && list[i].listener === listener)) {
        position = i;
        break;
      }
    }

    if (position < 0)
      return this;

    if (list.length === 1) {
      list.length = 0;
      delete this._events[type];
    } else {
      list.splice(position, 1);
    }

    if (this._events.removeListener)
      this.emit('removeListener', type, listener);
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  var key, listeners;

  if (!this._events)
    return this;

  // not listening for removeListener, no need to emit
  if (!this._events.removeListener) {
    if (arguments.length === 0)
      this._events = {};
    else if (this._events[type])
      delete this._events[type];
    return this;
  }

  // emit removeListener for all listeners on all events
  if (arguments.length === 0) {
    for (key in this._events) {
      if (key === 'removeListener') continue;
      this.removeAllListeners(key);
    }
    this.removeAllListeners('removeListener');
    this._events = {};
    return this;
  }

  listeners = this._events[type];

  if (isFunction(listeners)) {
    this.removeListener(type, listeners);
  } else {
    // LIFO order
    while (listeners.length)
      this.removeListener(type, listeners[listeners.length - 1]);
  }
  delete this._events[type];

  return this;
};

EventEmitter.prototype.listeners = function(type) {
  var ret;
  if (!this._events || !this._events[type])
    ret = [];
  else if (isFunction(this._events[type]))
    ret = [this._events[type]];
  else
    ret = this._events[type].slice();
  return ret;
};

EventEmitter.listenerCount = function(emitter, type) {
  var ret;
  if (!emitter._events || !emitter._events[type])
    ret = 0;
  else if (isFunction(emitter._events[type]))
    ret = 1;
  else
    ret = emitter._events[type].length;
  return ret;
};

function isFunction(arg) {
  return typeof arg === 'function';
}

function isNumber(arg) {
  return typeof arg === 'number';
}

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}

function isUndefined(arg) {
  return arg === void 0;
}

},{}],4:[function(require,module,exports){
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
}

},{}],5:[function(require,module,exports){
// shim for using process in browser

var process = module.exports = {};

process.nextTick = (function () {
    var canSetImmediate = typeof window !== 'undefined'
    && window.setImmediate;
    var canPost = typeof window !== 'undefined'
    && window.postMessage && window.addEventListener
    ;

    if (canSetImmediate) {
        return function (f) { return window.setImmediate(f) };
    }

    if (canPost) {
        var queue = [];
        window.addEventListener('message', function (ev) {
            var source = ev.source;
            if ((source === window || source === null) && ev.data === 'process-tick') {
                ev.stopPropagation();
                if (queue.length > 0) {
                    var fn = queue.shift();
                    fn();
                }
            }
        }, true);

        return function nextTick(fn) {
            queue.push(fn);
            window.postMessage('process-tick', '*');
        };
    }

    return function nextTick(fn) {
        setTimeout(fn, 0);
    };
})();

process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];

process.binding = function (name) {
    throw new Error('process.binding is not supported');
}

// TODO(shtylman)
process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};

},{}],6:[function(require,module,exports){
var base64 = require('base64-js')
var ieee754 = require('ieee754')

exports.Buffer = Buffer
exports.SlowBuffer = Buffer
exports.INSPECT_MAX_BYTES = 50
Buffer.poolSize = 8192

/**
 * If `Buffer._useTypedArrays`:
 *   === true    Use Uint8Array implementation (fastest)
 *   === false   Use Object implementation (compatible down to IE6)
 */
Buffer._useTypedArrays = (function () {
   // Detect if browser supports Typed Arrays. Supported browsers are IE 10+,
   // Firefox 4+, Chrome 7+, Safari 5.1+, Opera 11.6+, iOS 4.2+.
   if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined')
      return false

  // Does the browser support adding properties to `Uint8Array` instances? If
  // not, then that's the same as no `Uint8Array` support. We need to be able to
  // add all the node Buffer API methods.
  // Relevant Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=695438
  try {
    var arr = new Uint8Array(0)
    arr.foo = function () { return 42 }
    return 42 === arr.foo() &&
        typeof arr.subarray === 'function' // Chrome 9-10 lack `subarray`
  } catch (e) {
    return false
  }
})()

/**
 * Class: Buffer
 * =============
 *
 * The Buffer constructor returns instances of `Uint8Array` that are augmented
 * with function properties for all the node `Buffer` API functions. We use
 * `Uint8Array` so that square bracket notation works as expected -- it returns
 * a single octet.
 *
 * By augmenting the instances, we can avoid modifying the `Uint8Array`
 * prototype.
 */
function Buffer (subject, encoding, noZero) {
  if (!(this instanceof Buffer))
    return new Buffer(subject, encoding, noZero)

  var type = typeof subject

  // Workaround: node's base64 implementation allows for non-padded strings
  // while base64-js does not.
  if (encoding === 'base64' && type === 'string') {
    subject = stringtrim(subject)
    while (subject.length % 4 !== 0) {
      subject = subject + '='
    }
  }

  // Find the length
  var length
  if (type === 'number')
    length = coerce(subject)
  else if (type === 'string')
    length = Buffer.byteLength(subject, encoding)
  else if (type === 'object')
    length = coerce(subject.length) // Assume object is an array
  else
    throw new Error('First argument needs to be a number, array or string.')

  var buf
  if (Buffer._useTypedArrays) {
    // Preferred: Return an augmented `Uint8Array` instance for best performance
    buf = augment(new Uint8Array(length))
  } else {
    // Fallback: Return THIS instance of Buffer (created by `new`)
    buf = this
    buf.length = length
    buf._isBuffer = true
  }

  var i
  if (Buffer._useTypedArrays && typeof Uint8Array === 'function' &&
      subject instanceof Uint8Array) {
    // Speed optimization -- use set if we're copying from a Uint8Array
    buf._set(subject)
  } else if (isArrayish(subject)) {
    // Treat array-ish objects as a byte array
    for (i = 0; i < length; i++) {
      if (Buffer.isBuffer(subject))
        buf[i] = subject.readUInt8(i)
      else
        buf[i] = subject[i]
    }
  } else if (type === 'string') {
    buf.write(subject, 0, encoding)
  } else if (type === 'number' && !Buffer._useTypedArrays && !noZero) {
    for (i = 0; i < length; i++) {
      buf[i] = 0
    }
  }

  return buf
}

// STATIC METHODS
// ==============

Buffer.isEncoding = function (encoding) {
  switch (String(encoding).toLowerCase()) {
    case 'hex':
    case 'utf8':
    case 'utf-8':
    case 'ascii':
    case 'binary':
    case 'base64':
    case 'raw':
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      return true
    default:
      return false
  }
}

Buffer.isBuffer = function (b) {
  return !!(b !== null && b !== undefined && b._isBuffer)
}

Buffer.byteLength = function (str, encoding) {
  var ret
  str = str + ''
  switch (encoding || 'utf8') {
    case 'hex':
      ret = str.length / 2
      break
    case 'utf8':
    case 'utf-8':
      ret = utf8ToBytes(str).length
      break
    case 'ascii':
    case 'binary':
    case 'raw':
      ret = str.length
      break
    case 'base64':
      ret = base64ToBytes(str).length
      break
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      ret = str.length * 2
      break
    default:
      throw new Error('Unknown encoding')
  }
  return ret
}

Buffer.concat = function (list, totalLength) {
  assert(isArray(list), 'Usage: Buffer.concat(list, [totalLength])\n' +
      'list should be an Array.')

  if (list.length === 0) {
    return new Buffer(0)
  } else if (list.length === 1) {
    return list[0]
  }

  var i
  if (typeof totalLength !== 'number') {
    totalLength = 0
    for (i = 0; i < list.length; i++) {
      totalLength += list[i].length
    }
  }

  var buf = new Buffer(totalLength)
  var pos = 0
  for (i = 0; i < list.length; i++) {
    var item = list[i]
    item.copy(buf, pos)
    pos += item.length
  }
  return buf
}

// BUFFER INSTANCE METHODS
// =======================

function _hexWrite (buf, string, offset, length) {
  offset = Number(offset) || 0
  var remaining = buf.length - offset
  if (!length) {
    length = remaining
  } else {
    length = Number(length)
    if (length > remaining) {
      length = remaining
    }
  }

  // must be an even number of digits
  var strLen = string.length
  assert(strLen % 2 === 0, 'Invalid hex string')

  if (length > strLen / 2) {
    length = strLen / 2
  }
  for (var i = 0; i < length; i++) {
    var byte = parseInt(string.substr(i * 2, 2), 16)
    assert(!isNaN(byte), 'Invalid hex string')
    buf[offset + i] = byte
  }
  Buffer._charsWritten = i * 2
  return i
}

function _utf8Write (buf, string, offset, length) {
  var charsWritten = Buffer._charsWritten =
    blitBuffer(utf8ToBytes(string), buf, offset, length)
  return charsWritten
}

function _asciiWrite (buf, string, offset, length) {
  var charsWritten = Buffer._charsWritten =
    blitBuffer(asciiToBytes(string), buf, offset, length)
  return charsWritten
}

function _binaryWrite (buf, string, offset, length) {
  return _asciiWrite(buf, string, offset, length)
}

function _base64Write (buf, string, offset, length) {
  var charsWritten = Buffer._charsWritten =
    blitBuffer(base64ToBytes(string), buf, offset, length)
  return charsWritten
}

Buffer.prototype.write = function (string, offset, length, encoding) {
  // Support both (string, offset, length, encoding)
  // and the legacy (string, encoding, offset, length)
  if (isFinite(offset)) {
    if (!isFinite(length)) {
      encoding = length
      length = undefined
    }
  } else {  // legacy
    var swap = encoding
    encoding = offset
    offset = length
    length = swap
  }

  offset = Number(offset) || 0
  var remaining = this.length - offset
  if (!length) {
    length = remaining
  } else {
    length = Number(length)
    if (length > remaining) {
      length = remaining
    }
  }
  encoding = String(encoding || 'utf8').toLowerCase()

  switch (encoding) {
    case 'hex':
      return _hexWrite(this, string, offset, length)
    case 'utf8':
    case 'utf-8':
    case 'ucs2': // TODO: No support for ucs2 or utf16le encodings yet
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      return _utf8Write(this, string, offset, length)
    case 'ascii':
      return _asciiWrite(this, string, offset, length)
    case 'binary':
      return _binaryWrite(this, string, offset, length)
    case 'base64':
      return _base64Write(this, string, offset, length)
    default:
      throw new Error('Unknown encoding')
  }
}

Buffer.prototype.toString = function (encoding, start, end) {
  var self = this

  encoding = String(encoding || 'utf8').toLowerCase()
  start = Number(start) || 0
  end = (end !== undefined)
    ? Number(end)
    : end = self.length

  // Fastpath empty strings
  if (end === start)
    return ''

  switch (encoding) {
    case 'hex':
      return _hexSlice(self, start, end)
    case 'utf8':
    case 'utf-8':
    case 'ucs2': // TODO: No support for ucs2 or utf16le encodings yet
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      return _utf8Slice(self, start, end)
    case 'ascii':
      return _asciiSlice(self, start, end)
    case 'binary':
      return _binarySlice(self, start, end)
    case 'base64':
      return _base64Slice(self, start, end)
    default:
      throw new Error('Unknown encoding')
  }
}

Buffer.prototype.toJSON = function () {
  return {
    type: 'Buffer',
    data: Array.prototype.slice.call(this._arr || this, 0)
  }
}

// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function (target, target_start, start, end) {
  var source = this

  if (!start) start = 0
  if (!end && end !== 0) end = this.length
  if (!target_start) target_start = 0

  // Copy 0 bytes; we're done
  if (end === start) return
  if (target.length === 0 || source.length === 0) return

  // Fatal error conditions
  assert(end >= start, 'sourceEnd < sourceStart')
  assert(target_start >= 0 && target_start < target.length,
      'targetStart out of bounds')
  assert(start >= 0 && start < source.length, 'sourceStart out of bounds')
  assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds')

  // Are we oob?
  if (end > this.length)
    end = this.length
  if (target.length - target_start < end - start)
    end = target.length - target_start + start

  // copy!
  for (var i = 0; i < end - start; i++)
    target[i + target_start] = this[i + start]
}

function _base64Slice (buf, start, end) {
  if (start === 0 && end === buf.length) {
    return base64.fromByteArray(buf)
  } else {
    return base64.fromByteArray(buf.slice(start, end))
  }
}

function _utf8Slice (buf, start, end) {
  var res = ''
  var tmp = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; i++) {
    if (buf[i] <= 0x7F) {
      res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
      tmp = ''
    } else {
      tmp += '%' + buf[i].toString(16)
    }
  }

  return res + decodeUtf8Char(tmp)
}

function _asciiSlice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; i++)
    ret += String.fromCharCode(buf[i])
  return ret
}

function _binarySlice (buf, start, end) {
  return _asciiSlice(buf, start, end)
}

function _hexSlice (buf, start, end) {
  var len = buf.length

  if (!start || start < 0) start = 0
  if (!end || end < 0 || end > len) end = len

  var out = ''
  for (var i = start; i < end; i++) {
    out += toHex(buf[i])
  }
  return out
}

// http://nodejs.org/api/buffer.html#buffer_buf_slice_start_end
Buffer.prototype.slice = function (start, end) {
  var len = this.length
  start = clamp(start, len, 0)
  end = clamp(end, len, len)

  if (Buffer._useTypedArrays) {
    return augment(this.subarray(start, end))
  } else {
    var sliceLen = end - start
    var newBuf = new Buffer(sliceLen, undefined, true)
    for (var i = 0; i < sliceLen; i++) {
      newBuf[i] = this[i + start]
    }
    return newBuf
  }
}

// `get` will be removed in Node 0.13+
Buffer.prototype.get = function (offset) {
  console.log('.get() is deprecated. Access using array indexes instead.')
  return this.readUInt8(offset)
}

// `set` will be removed in Node 0.13+
Buffer.prototype.set = function (v, offset) {
  console.log('.set() is deprecated. Access using array indexes instead.')
  return this.writeUInt8(v, offset)
}

Buffer.prototype.readUInt8 = function (offset, noAssert) {
  if (!noAssert) {
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset < this.length, 'Trying to read beyond buffer length')
  }

  if (offset >= this.length)
    return

  return this[offset]
}

function _readUInt16 (buf, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
  }

  var len = buf.length
  if (offset >= len)
    return

  var val
  if (littleEndian) {
    val = buf[offset]
    if (offset + 1 < len)
      val |= buf[offset + 1] << 8
  } else {
    val = buf[offset] << 8
    if (offset + 1 < len)
      val |= buf[offset + 1]
  }
  return val
}

Buffer.prototype.readUInt16LE = function (offset, noAssert) {
  return _readUInt16(this, offset, true, noAssert)
}

Buffer.prototype.readUInt16BE = function (offset, noAssert) {
  return _readUInt16(this, offset, false, noAssert)
}

function _readUInt32 (buf, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
  }

  var len = buf.length
  if (offset >= len)
    return

  var val
  if (littleEndian) {
    if (offset + 2 < len)
      val = buf[offset + 2] << 16
    if (offset + 1 < len)
      val |= buf[offset + 1] << 8
    val |= buf[offset]
    if (offset + 3 < len)
      val = val + (buf[offset + 3] << 24 >>> 0)
  } else {
    if (offset + 1 < len)
      val = buf[offset + 1] << 16
    if (offset + 2 < len)
      val |= buf[offset + 2] << 8
    if (offset + 3 < len)
      val |= buf[offset + 3]
    val = val + (buf[offset] << 24 >>> 0)
  }
  return val
}

Buffer.prototype.readUInt32LE = function (offset, noAssert) {
  return _readUInt32(this, offset, true, noAssert)
}

Buffer.prototype.readUInt32BE = function (offset, noAssert) {
  return _readUInt32(this, offset, false, noAssert)
}

Buffer.prototype.readInt8 = function (offset, noAssert) {
  if (!noAssert) {
    assert(offset !== undefined && offset !== null,
        'missing offset')
    assert(offset < this.length, 'Trying to read beyond buffer length')
  }

  if (offset >= this.length)
    return

  var neg = this[offset] & 0x80
  if (neg)
    return (0xff - this[offset] + 1) * -1
  else
    return this[offset]
}

function _readInt16 (buf, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 1 < buf.length, 'Trying to read beyond buffer length')
  }

  var len = buf.length
  if (offset >= len)
    return

  var val = _readUInt16(buf, offset, littleEndian, true)
  var neg = val & 0x8000
  if (neg)
    return (0xffff - val + 1) * -1
  else
    return val
}

Buffer.prototype.readInt16LE = function (offset, noAssert) {
  return _readInt16(this, offset, true, noAssert)
}

Buffer.prototype.readInt16BE = function (offset, noAssert) {
  return _readInt16(this, offset, false, noAssert)
}

function _readInt32 (buf, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
  }

  var len = buf.length
  if (offset >= len)
    return

  var val = _readUInt32(buf, offset, littleEndian, true)
  var neg = val & 0x80000000
  if (neg)
    return (0xffffffff - val + 1) * -1
  else
    return val
}

Buffer.prototype.readInt32LE = function (offset, noAssert) {
  return _readInt32(this, offset, true, noAssert)
}

Buffer.prototype.readInt32BE = function (offset, noAssert) {
  return _readInt32(this, offset, false, noAssert)
}

function _readFloat (buf, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset + 3 < buf.length, 'Trying to read beyond buffer length')
  }

  return ieee754.read(buf, offset, littleEndian, 23, 4)
}

Buffer.prototype.readFloatLE = function (offset, noAssert) {
  return _readFloat(this, offset, true, noAssert)
}

Buffer.prototype.readFloatBE = function (offset, noAssert) {
  return _readFloat(this, offset, false, noAssert)
}

function _readDouble (buf, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset + 7 < buf.length, 'Trying to read beyond buffer length')
  }

  return ieee754.read(buf, offset, littleEndian, 52, 8)
}

Buffer.prototype.readDoubleLE = function (offset, noAssert) {
  return _readDouble(this, offset, true, noAssert)
}

Buffer.prototype.readDoubleBE = function (offset, noAssert) {
  return _readDouble(this, offset, false, noAssert)
}

Buffer.prototype.writeUInt8 = function (value, offset, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset < this.length, 'trying to write beyond buffer length')
    verifuint(value, 0xff)
  }

  if (offset >= this.length) return

  this[offset] = value
}

function _writeUInt16 (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 1 < buf.length, 'trying to write beyond buffer length')
    verifuint(value, 0xffff)
  }

  var len = buf.length
  if (offset >= len)
    return

  for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) {
    buf[offset + i] =
        (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
            (littleEndian ? i : 1 - i) * 8
  }
}

Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) {
  _writeUInt16(this, value, offset, true, noAssert)
}

Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) {
  _writeUInt16(this, value, offset, false, noAssert)
}

function _writeUInt32 (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 3 < buf.length, 'trying to write beyond buffer length')
    verifuint(value, 0xffffffff)
  }

  var len = buf.length
  if (offset >= len)
    return

  for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) {
    buf[offset + i] =
        (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
  }
}

Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) {
  _writeUInt32(this, value, offset, true, noAssert)
}

Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) {
  _writeUInt32(this, value, offset, false, noAssert)
}

Buffer.prototype.writeInt8 = function (value, offset, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset < this.length, 'Trying to write beyond buffer length')
    verifsint(value, 0x7f, -0x80)
  }

  if (offset >= this.length)
    return

  if (value >= 0)
    this.writeUInt8(value, offset, noAssert)
  else
    this.writeUInt8(0xff + value + 1, offset, noAssert)
}

function _writeInt16 (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 1 < buf.length, 'Trying to write beyond buffer length')
    verifsint(value, 0x7fff, -0x8000)
  }

  var len = buf.length
  if (offset >= len)
    return

  if (value >= 0)
    _writeUInt16(buf, value, offset, littleEndian, noAssert)
  else
    _writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert)
}

Buffer.prototype.writeInt16LE = function (value, offset, noAssert) {
  _writeInt16(this, value, offset, true, noAssert)
}

Buffer.prototype.writeInt16BE = function (value, offset, noAssert) {
  _writeInt16(this, value, offset, false, noAssert)
}

function _writeInt32 (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
    verifsint(value, 0x7fffffff, -0x80000000)
  }

  var len = buf.length
  if (offset >= len)
    return

  if (value >= 0)
    _writeUInt32(buf, value, offset, littleEndian, noAssert)
  else
    _writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert)
}

Buffer.prototype.writeInt32LE = function (value, offset, noAssert) {
  _writeInt32(this, value, offset, true, noAssert)
}

Buffer.prototype.writeInt32BE = function (value, offset, noAssert) {
  _writeInt32(this, value, offset, false, noAssert)
}

function _writeFloat (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 3 < buf.length, 'Trying to write beyond buffer length')
    verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38)
  }

  var len = buf.length
  if (offset >= len)
    return

  ieee754.write(buf, value, offset, littleEndian, 23, 4)
}

Buffer.prototype.writeFloatLE = function (value, offset, noAssert) {
  _writeFloat(this, value, offset, true, noAssert)
}

Buffer.prototype.writeFloatBE = function (value, offset, noAssert) {
  _writeFloat(this, value, offset, false, noAssert)
}

function _writeDouble (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    assert(value !== undefined && value !== null, 'missing value')
    assert(typeof littleEndian === 'boolean', 'missing or invalid endian')
    assert(offset !== undefined && offset !== null, 'missing offset')
    assert(offset + 7 < buf.length,
        'Trying to write beyond buffer length')
    verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308)
  }

  var len = buf.length
  if (offset >= len)
    return

  ieee754.write(buf, value, offset, littleEndian, 52, 8)
}

Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) {
  _writeDouble(this, value, offset, true, noAssert)
}

Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) {
  _writeDouble(this, value, offset, false, noAssert)
}

// fill(value, start=0, end=buffer.length)
Buffer.prototype.fill = function (value, start, end) {
  if (!value) value = 0
  if (!start) start = 0
  if (!end) end = this.length

  if (typeof value === 'string') {
    value = value.charCodeAt(0)
  }

  assert(typeof value === 'number' && !isNaN(value), 'value is not a number')
  assert(end >= start, 'end < start')

  // Fill 0 bytes; we're done
  if (end === start) return
  if (this.length === 0) return

  assert(start >= 0 && start < this.length, 'start out of bounds')
  assert(end >= 0 && end <= this.length, 'end out of bounds')

  for (var i = start; i < end; i++) {
    this[i] = value
  }
}

Buffer.prototype.inspect = function () {
  var out = []
  var len = this.length
  for (var i = 0; i < len; i++) {
    out[i] = toHex(this[i])
    if (i === exports.INSPECT_MAX_BYTES) {
      out[i + 1] = '...'
      break
    }
  }
  return '<Buffer ' + out.join(' ') + '>'
}

/**
 * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
 * Added in Node 0.12. Only available in browsers that support ArrayBuffer.
 */
Buffer.prototype.toArrayBuffer = function () {
  if (typeof Uint8Array === 'function') {
    if (Buffer._useTypedArrays) {
      return (new Buffer(this)).buffer
    } else {
      var buf = new Uint8Array(this.length)
      for (var i = 0, len = buf.length; i < len; i += 1)
        buf[i] = this[i]
      return buf.buffer
    }
  } else {
    throw new Error('Buffer.toArrayBuffer not supported in this browser')
  }
}

// HELPER FUNCTIONS
// ================

function stringtrim (str) {
  if (str.trim) return str.trim()
  return str.replace(/^\s+|\s+$/g, '')
}

var BP = Buffer.prototype

/**
 * Augment the Uint8Array *instance* (not the class!) with Buffer methods
 */
function augment (arr) {
  arr._isBuffer = true

  // save reference to original Uint8Array get/set methods before overwriting
  arr._get = arr.get
  arr._set = arr.set

  // deprecated, will be removed in node 0.13+
  arr.get = BP.get
  arr.set = BP.set

  arr.write = BP.write
  arr.toString = BP.toString
  arr.toLocaleString = BP.toString
  arr.toJSON = BP.toJSON
  arr.copy = BP.copy
  arr.slice = BP.slice
  arr.readUInt8 = BP.readUInt8
  arr.readUInt16LE = BP.readUInt16LE
  arr.readUInt16BE = BP.readUInt16BE
  arr.readUInt32LE = BP.readUInt32LE
  arr.readUInt32BE = BP.readUInt32BE
  arr.readInt8 = BP.readInt8
  arr.readInt16LE = BP.readInt16LE
  arr.readInt16BE = BP.readInt16BE
  arr.readInt32LE = BP.readInt32LE
  arr.readInt32BE = BP.readInt32BE
  arr.readFloatLE = BP.readFloatLE
  arr.readFloatBE = BP.readFloatBE
  arr.readDoubleLE = BP.readDoubleLE
  arr.readDoubleBE = BP.readDoubleBE
  arr.writeUInt8 = BP.writeUInt8
  arr.writeUInt16LE = BP.writeUInt16LE
  arr.writeUInt16BE = BP.writeUInt16BE
  arr.writeUInt32LE = BP.writeUInt32LE
  arr.writeUInt32BE = BP.writeUInt32BE
  arr.writeInt8 = BP.writeInt8
  arr.writeInt16LE = BP.writeInt16LE
  arr.writeInt16BE = BP.writeInt16BE
  arr.writeInt32LE = BP.writeInt32LE
  arr.writeInt32BE = BP.writeInt32BE
  arr.writeFloatLE = BP.writeFloatLE
  arr.writeFloatBE = BP.writeFloatBE
  arr.writeDoubleLE = BP.writeDoubleLE
  arr.writeDoubleBE = BP.writeDoubleBE
  arr.fill = BP.fill
  arr.inspect = BP.inspect
  arr.toArrayBuffer = BP.toArrayBuffer

  return arr
}

// slice(start, end)
function clamp (index, len, defaultValue) {
  if (typeof index !== 'number') return defaultValue
  index = ~~index;  // Coerce to integer.
  if (index >= len) return len
  if (index >= 0) return index
  index += len
  if (index >= 0) return index
  return 0
}

function coerce (length) {
  // Coerce length to a number (possibly NaN), round up
  // in case it's fractional (e.g. 123.456) then do a
  // double negate to coerce a NaN to 0. Easy, right?
  length = ~~Math.ceil(+length)
  return length < 0 ? 0 : length
}

function isArray (subject) {
  return (Array.isArray || function (subject) {
    return Object.prototype.toString.call(subject) === '[object Array]'
  })(subject)
}

function isArrayish (subject) {
  return isArray(subject) || Buffer.isBuffer(subject) ||
      subject && typeof subject === 'object' &&
      typeof subject.length === 'number'
}

function toHex (n) {
  if (n < 16) return '0' + n.toString(16)
  return n.toString(16)
}

function utf8ToBytes (str) {
  var byteArray = []
  for (var i = 0; i < str.length; i++) {
    var b = str.charCodeAt(i)
    if (b <= 0x7F)
      byteArray.push(str.charCodeAt(i))
    else {
      var start = i
      if (b >= 0xD800 && b <= 0xDFFF) i++
      var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%')
      for (var j = 0; j < h.length; j++)
        byteArray.push(parseInt(h[j], 16))
    }
  }
  return byteArray
}

function asciiToBytes (str) {
  var byteArray = []
  for (var i = 0; i < str.length; i++) {
    // Node's code seems to be doing this and not & 0x7F..
    byteArray.push(str.charCodeAt(i) & 0xFF)
  }
  return byteArray
}

function base64ToBytes (str) {
  return base64.toByteArray(str)
}

function blitBuffer (src, dst, offset, length) {
  var pos
  for (var i = 0; i < length; i++) {
    if ((i + offset >= dst.length) || (i >= src.length))
      break
    dst[i + offset] = src[i]
  }
  return i
}

function decodeUtf8Char (str) {
  try {
    return decodeURIComponent(str)
  } catch (err) {
    return String.fromCharCode(0xFFFD) // UTF 8 invalid char
  }
}

/*
 * We have to make sure that the value is a valid integer. This means that it
 * is non-negative. It has no fractional component and that it does not
 * exceed the maximum allowed value.
 */
function verifuint (value, max) {
  assert(typeof value == 'number', 'cannot write a non-number as a number')
  assert(value >= 0,
      'specified a negative value for writing an unsigned value')
  assert(value <= max, 'value is larger than maximum value for type')
  assert(Math.floor(value) === value, 'value has a fractional component')
}

function verifsint(value, max, min) {
  assert(typeof value == 'number', 'cannot write a non-number as a number')
  assert(value <= max, 'value larger than maximum allowed value')
  assert(value >= min, 'value smaller than minimum allowed value')
  assert(Math.floor(value) === value, 'value has a fractional component')
}

function verifIEEE754(value, max, min) {
  assert(typeof value == 'number', 'cannot write a non-number as a number')
  assert(value <= max, 'value larger than maximum allowed value')
  assert(value >= min, 'value smaller than minimum allowed value')
}

function assert (test, message) {
  if (!test) throw new Error(message || 'Failed assertion')
}

},{"base64-js":7,"ieee754":8}],7:[function(require,module,exports){
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

;(function (exports) {
	'use strict';

  var Arr = (typeof Uint8Array !== 'undefined')
    ? Uint8Array
    : Array

	var ZERO   = '0'.charCodeAt(0)
	var PLUS   = '+'.charCodeAt(0)
	var SLASH  = '/'.charCodeAt(0)
	var NUMBER = '0'.charCodeAt(0)
	var LOWER  = 'a'.charCodeAt(0)
	var UPPER  = 'A'.charCodeAt(0)

	function decode (elt) {
		var code = elt.charCodeAt(0)
		if (code === PLUS)
			return 62 // '+'
		if (code === SLASH)
			return 63 // '/'
		if (code < NUMBER)
			return -1 //no match
		if (code < NUMBER + 10)
			return code - NUMBER + 26 + 26
		if (code < UPPER + 26)
			return code - UPPER
		if (code < LOWER + 26)
			return code - LOWER + 26
	}

	function b64ToByteArray (b64) {
		var i, j, l, tmp, placeHolders, arr

		if (b64.length % 4 > 0) {
			throw new Error('Invalid string. Length must be a multiple of 4')
		}

		// the number of equal signs (place holders)
		// if there are two placeholders, than the two characters before it
		// represent one byte
		// if there is only one, then the three characters before it represent 2 bytes
		// this is just a cheap hack to not do indexOf twice
		var len = b64.length
		placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0

		// base64 is 4/3 + up to two characters of the original data
		arr = new Arr(b64.length * 3 / 4 - placeHolders)

		// if there are placeholders, only get up to the last complete 4 chars
		l = placeHolders > 0 ? b64.length - 4 : b64.length

		var L = 0

		function push (v) {
			arr[L++] = v
		}

		for (i = 0, j = 0; i < l; i += 4, j += 3) {
			tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
			push((tmp & 0xFF0000) >> 16)
			push((tmp & 0xFF00) >> 8)
			push(tmp & 0xFF)
		}

		if (placeHolders === 2) {
			tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
			push(tmp & 0xFF)
		} else if (placeHolders === 1) {
			tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
			push((tmp >> 8) & 0xFF)
			push(tmp & 0xFF)
		}

		return arr
	}

	function uint8ToBase64 (uint8) {
		var i,
			extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
			output = "",
			temp, length

		function encode (num) {
			return lookup.charAt(num)
		}

		function tripletToBase64 (num) {
			return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
		}

		// go through the array every three bytes, we'll deal with trailing stuff later
		for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
			temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
			output += tripletToBase64(temp)
		}

		// pad the end with zeros, but make sure to not forget the extra bytes
		switch (extraBytes) {
			case 1:
				temp = uint8[uint8.length - 1]
				output += encode(temp >> 2)
				output += encode((temp << 4) & 0x3F)
				output += '=='
				break
			case 2:
				temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
				output += encode(temp >> 10)
				output += encode((temp >> 4) & 0x3F)
				output += encode((temp << 2) & 0x3F)
				output += '='
				break
		}

		return output
	}

	module.exports.toByteArray = b64ToByteArray
	module.exports.fromByteArray = uint8ToBase64
}())

},{}],8:[function(require,module,exports){
exports.read = function(buffer, offset, isLE, mLen, nBytes) {
  var e, m,
      eLen = nBytes * 8 - mLen - 1,
      eMax = (1 << eLen) - 1,
      eBias = eMax >> 1,
      nBits = -7,
      i = isLE ? (nBytes - 1) : 0,
      d = isLE ? -1 : 1,
      s = buffer[offset + i];

  i += d;

  e = s & ((1 << (-nBits)) - 1);
  s >>= (-nBits);
  nBits += eLen;
  for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);

  m = e & ((1 << (-nBits)) - 1);
  e >>= (-nBits);
  nBits += mLen;
  for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);

  if (e === 0) {
    e = 1 - eBias;
  } else if (e === eMax) {
    return m ? NaN : ((s ? -1 : 1) * Infinity);
  } else {
    m = m + Math.pow(2, mLen);
    e = e - eBias;
  }
  return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
};

exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
  var e, m, c,
      eLen = nBytes * 8 - mLen - 1,
      eMax = (1 << eLen) - 1,
      eBias = eMax >> 1,
      rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
      i = isLE ? 0 : (nBytes - 1),
      d = isLE ? 1 : -1,
      s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;

  value = Math.abs(value);

  if (isNaN(value) || value === Infinity) {
    m = isNaN(value) ? 1 : 0;
    e = eMax;
  } else {
    e = Math.floor(Math.log(value) / Math.LN2);
    if (value * (c = Math.pow(2, -e)) < 1) {
      e--;
      c *= 2;
    }
    if (e + eBias >= 1) {
      value += rt / c;
    } else {
      value += rt * Math.pow(2, 1 - eBias);
    }
    if (value * c >= 2) {
      e++;
      c /= 2;
    }

    if (e + eBias >= eMax) {
      m = 0;
      e = eMax;
    } else if (e + eBias >= 1) {
      m = (value * c - 1) * Math.pow(2, mLen);
      e = e + eBias;
    } else {
      m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
      e = 0;
    }
  }

  for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);

  e = (e << mLen) | m;
  eLen += mLen;
  for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);

  buffer[offset + i - d] |= s * 128;
};

},{}],9:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.

module.exports = Duplex;
var inherits = require('inherits');
var setImmediate = require('process/browser.js').nextTick;
var Readable = require('./readable.js');
var Writable = require('./writable.js');

inherits(Duplex, Readable);

Duplex.prototype.write = Writable.prototype.write;
Duplex.prototype.end = Writable.prototype.end;
Duplex.prototype._write = Writable.prototype._write;

function Duplex(options) {
  if (!(this instanceof Duplex))
    return new Duplex(options);

  Readable.call(this, options);
  Writable.call(this, options);

  if (options && options.readable === false)
    this.readable = false;

  if (options && options.writable === false)
    this.writable = false;

  this.allowHalfOpen = true;
  if (options && options.allowHalfOpen === false)
    this.allowHalfOpen = false;

  this.once('end', onend);
}

// the no-half-open enforcer
function onend() {
  // if we allow half-open state, or if the writable side ended,
  // then we're ok.
  if (this.allowHalfOpen || this._writableState.ended)
    return;

  // no more data can be written.
  // But allow more writes to happen in this tick.
  var self = this;
  setImmediate(function () {
    self.end();
  });
}

},{"./readable.js":14,"./writable.js":16,"inherits":4,"process/browser.js":12}],"stream":[function(require,module,exports){
module.exports=require('VDsrfn');
},{}],"VDsrfn":[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

module.exports = Stream;

var EE = require('events').EventEmitter;
var inherits = require('inherits');

inherits(Stream, EE);
Stream.Readable = require('./readable.js');
Stream.Writable = require('./writable.js');
Stream.Duplex = require('./duplex.js');
Stream.Transform = require('./transform.js');
Stream.PassThrough = require('./passthrough.js');

// Backwards-compat with node 0.4.x
Stream.Stream = Stream;



// old-style streams.  Note that the pipe method (the only relevant
// part of this class) is overridden in the Readable class.

function Stream() {
  EE.call(this);
}

Stream.prototype.pipe = function(dest, options) {
  var source = this;

  function ondata(chunk) {
    if (dest.writable) {
      if (false === dest.write(chunk) && source.pause) {
        source.pause();
      }
    }
  }

  source.on('data', ondata);

  function ondrain() {
    if (source.readable && source.resume) {
      source.resume();
    }
  }

  dest.on('drain', ondrain);

  // If the 'end' option is not supplied, dest.end() will be called when
  // source gets the 'end' or 'close' events.  Only dest.end() once.
  if (!dest._isStdio && (!options || options.end !== false)) {
    source.on('end', onend);
    source.on('close', onclose);
  }

  var didOnEnd = false;
  function onend() {
    if (didOnEnd) return;
    didOnEnd = true;

    dest.end();
  }


  function onclose() {
    if (didOnEnd) return;
    didOnEnd = true;

    if (typeof dest.destroy === 'function') dest.destroy();
  }

  // don't leave dangling pipes when there are errors.
  function onerror(er) {
    cleanup();
    if (EE.listenerCount(this, 'error') === 0) {
      throw er; // Unhandled stream error in pipe.
    }
  }

  source.on('error', onerror);
  dest.on('error', onerror);

  // remove all the event listeners that were added.
  function cleanup() {
    source.removeListener('data', ondata);
    dest.removeListener('drain', ondrain);

    source.removeListener('end', onend);
    source.removeListener('close', onclose);

    source.removeListener('error', onerror);
    dest.removeListener('error', onerror);

    source.removeListener('end', cleanup);
    source.removeListener('close', cleanup);

    dest.removeListener('close', cleanup);
  }

  source.on('end', cleanup);
  source.on('close', cleanup);

  dest.on('close', cleanup);

  dest.emit('pipe', source);

  // Allow for unix-like usage: A.pipe(B).pipe(C)
  return dest;
};

},{"./duplex.js":9,"./passthrough.js":13,"./readable.js":14,"./transform.js":15,"./writable.js":16,"events":3,"inherits":4}],12:[function(require,module,exports){
module.exports=require(5)
},{}],13:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.

module.exports = PassThrough;

var Transform = require('./transform.js');
var inherits = require('inherits');
inherits(PassThrough, Transform);

function PassThrough(options) {
  if (!(this instanceof PassThrough))
    return new PassThrough(options);

  Transform.call(this, options);
}

PassThrough.prototype._transform = function(chunk, encoding, cb) {
  cb(null, chunk);
};

},{"./transform.js":15,"inherits":4}],14:[function(require,module,exports){
(function (process){// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

module.exports = Readable;
Readable.ReadableState = ReadableState;

var EE = require('events').EventEmitter;
var Stream = require('./index.js');
var Buffer = require('buffer').Buffer;
var setImmediate = require('process/browser.js').nextTick;
var StringDecoder;

var inherits = require('inherits');
inherits(Readable, Stream);

function ReadableState(options, stream) {
  options = options || {};

  // the point at which it stops calling _read() to fill the buffer
  // Note: 0 is a valid value, means "don't call _read preemptively ever"
  var hwm = options.highWaterMark;
  this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;

  // cast to ints.
  this.highWaterMark = ~~this.highWaterMark;

  this.buffer = [];
  this.length = 0;
  this.pipes = null;
  this.pipesCount = 0;
  this.flowing = false;
  this.ended = false;
  this.endEmitted = false;
  this.reading = false;

  // In streams that never have any data, and do push(null) right away,
  // the consumer can miss the 'end' event if they do some I/O before
  // consuming the stream.  So, we don't emit('end') until some reading
  // happens.
  this.calledRead = false;

  // a flag to be able to tell if the onwrite cb is called immediately,
  // or on a later tick.  We set this to true at first, becuase any
  // actions that shouldn't happen until "later" should generally also
  // not happen before the first write call.
  this.sync = true;

  // whenever we return null, then we set a flag to say
  // that we're awaiting a 'readable' event emission.
  this.needReadable = false;
  this.emittedReadable = false;
  this.readableListening = false;


  // object stream flag. Used to make read(n) ignore n and to
  // make all the buffer merging and length checks go away
  this.objectMode = !!options.objectMode;

  // Crypto is kind of old and crusty.  Historically, its default string
  // encoding is 'binary' so we have to make this configurable.
  // Everything else in the universe uses 'utf8', though.
  this.defaultEncoding = options.defaultEncoding || 'utf8';

  // when piping, we only care about 'readable' events that happen
  // after read()ing all the bytes and not getting any pushback.
  this.ranOut = false;

  // the number of writers that are awaiting a drain event in .pipe()s
  this.awaitDrain = 0;

  // if true, a maybeReadMore has been scheduled
  this.readingMore = false;

  this.decoder = null;
  this.encoding = null;
  if (options.encoding) {
    if (!StringDecoder)
      StringDecoder = require('string_decoder').StringDecoder;
    this.decoder = new StringDecoder(options.encoding);
    this.encoding = options.encoding;
  }
}

function Readable(options) {
  if (!(this instanceof Readable))
    return new Readable(options);

  this._readableState = new ReadableState(options, this);

  // legacy
  this.readable = true;

  Stream.call(this);
}

// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function(chunk, encoding) {
  var state = this._readableState;

  if (typeof chunk === 'string' && !state.objectMode) {
    encoding = encoding || state.defaultEncoding;
    if (encoding !== state.encoding) {
      chunk = new Buffer(chunk, encoding);
      encoding = '';
    }
  }

  return readableAddChunk(this, state, chunk, encoding, false);
};

// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function(chunk) {
  var state = this._readableState;
  return readableAddChunk(this, state, chunk, '', true);
};

function readableAddChunk(stream, state, chunk, encoding, addToFront) {
  var er = chunkInvalid(state, chunk);
  if (er) {
    stream.emit('error', er);
  } else if (chunk === null || chunk === undefined) {
    state.reading = false;
    if (!state.ended)
      onEofChunk(stream, state);
  } else if (state.objectMode || chunk && chunk.length > 0) {
    if (state.ended && !addToFront) {
      var e = new Error('stream.push() after EOF');
      stream.emit('error', e);
    } else if (state.endEmitted && addToFront) {
      var e = new Error('stream.unshift() after end event');
      stream.emit('error', e);
    } else {
      if (state.decoder && !addToFront && !encoding)
        chunk = state.decoder.write(chunk);

      // update the buffer info.
      state.length += state.objectMode ? 1 : chunk.length;
      if (addToFront) {
        state.buffer.unshift(chunk);
      } else {
        state.reading = false;
        state.buffer.push(chunk);
      }

      if (state.needReadable)
        emitReadable(stream);

      maybeReadMore(stream, state);
    }
  } else if (!addToFront) {
    state.reading = false;
  }

  return needMoreData(state);
}



// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes.  This is to work around cases where hwm=0,
// such as the repl.  Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
  return !state.ended &&
         (state.needReadable ||
          state.length < state.highWaterMark ||
          state.length === 0);
}

// backwards compatibility.
Readable.prototype.setEncoding = function(enc) {
  if (!StringDecoder)
    StringDecoder = require('string_decoder').StringDecoder;
  this._readableState.decoder = new StringDecoder(enc);
  this._readableState.encoding = enc;
};

// Don't raise the hwm > 128MB
var MAX_HWM = 0x800000;
function roundUpToNextPowerOf2(n) {
  if (n >= MAX_HWM) {
    n = MAX_HWM;
  } else {
    // Get the next highest power of 2
    n--;
    for (var p = 1; p < 32; p <<= 1) n |= n >> p;
    n++;
  }
  return n;
}

function howMuchToRead(n, state) {
  if (state.length === 0 && state.ended)
    return 0;

  if (state.objectMode)
    return n === 0 ? 0 : 1;

  if (isNaN(n) || n === null) {
    // only flow one buffer at a time
    if (state.flowing && state.buffer.length)
      return state.buffer[0].length;
    else
      return state.length;
  }

  if (n <= 0)
    return 0;

  // If we're asking for more than the target buffer level,
  // then raise the water mark.  Bump up to the next highest
  // power of 2, to prevent increasing it excessively in tiny
  // amounts.
  if (n > state.highWaterMark)
    state.highWaterMark = roundUpToNextPowerOf2(n);

  // don't have that much.  return null, unless we've ended.
  if (n > state.length) {
    if (!state.ended) {
      state.needReadable = true;
      return 0;
    } else
      return state.length;
  }

  return n;
}

// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function(n) {
  var state = this._readableState;
  state.calledRead = true;
  var nOrig = n;

  if (typeof n !== 'number' || n > 0)
    state.emittedReadable = false;

  // if we're doing read(0) to trigger a readable event, but we
  // already have a bunch of data in the buffer, then just trigger
  // the 'readable' event and move on.
  if (n === 0 &&
      state.needReadable &&
      (state.length >= state.highWaterMark || state.ended)) {
    emitReadable(this);
    return null;
  }

  n = howMuchToRead(n, state);

  // if we've ended, and we're now clear, then finish it up.
  if (n === 0 && state.ended) {
    if (state.length === 0)
      endReadable(this);
    return null;
  }

  // All the actual chunk generation logic needs to be
  // *below* the call to _read.  The reason is that in certain
  // synthetic stream cases, such as passthrough streams, _read
  // may be a completely synchronous operation which may change
  // the state of the read buffer, providing enough data when
  // before there was *not* enough.
  //
  // So, the steps are:
  // 1. Figure out what the state of things will be after we do
  // a read from the buffer.
  //
  // 2. If that resulting state will trigger a _read, then call _read.
  // Note that this may be asynchronous, or synchronous.  Yes, it is
  // deeply ugly to write APIs this way, but that still doesn't mean
  // that the Readable class should behave improperly, as streams are
  // designed to be sync/async agnostic.
  // Take note if the _read call is sync or async (ie, if the read call
  // has returned yet), so that we know whether or not it's safe to emit
  // 'readable' etc.
  //
  // 3. Actually pull the requested chunks out of the buffer and return.

  // if we need a readable event, then we need to do some reading.
  var doRead = state.needReadable;

  // if we currently have less than the highWaterMark, then also read some
  if (state.length - n <= state.highWaterMark)
    doRead = true;

  // however, if we've ended, then there's no point, and if we're already
  // reading, then it's unnecessary.
  if (state.ended || state.reading)
    doRead = false;

  if (doRead) {
    state.reading = true;
    state.sync = true;
    // if the length is currently zero, then we *need* a readable event.
    if (state.length === 0)
      state.needReadable = true;
    // call internal read method
    this._read(state.highWaterMark);
    state.sync = false;
  }

  // If _read called its callback synchronously, then `reading`
  // will be false, and we need to re-evaluate how much data we
  // can return to the user.
  if (doRead && !state.reading)
    n = howMuchToRead(nOrig, state);

  var ret;
  if (n > 0)
    ret = fromList(n, state);
  else
    ret = null;

  if (ret === null) {
    state.needReadable = true;
    n = 0;
  }

  state.length -= n;

  // If we have nothing in the buffer, then we want to know
  // as soon as we *do* get something into the buffer.
  if (state.length === 0 && !state.ended)
    state.needReadable = true;

  // If we happened to read() exactly the remaining amount in the
  // buffer, and the EOF has been seen at this point, then make sure
  // that we emit 'end' on the very next tick.
  if (state.ended && !state.endEmitted && state.length === 0)
    endReadable(this);

  return ret;
};

function chunkInvalid(state, chunk) {
  var er = null;
  if (!Buffer.isBuffer(chunk) &&
      'string' !== typeof chunk &&
      chunk !== null &&
      chunk !== undefined &&
      !state.objectMode &&
      !er) {
    er = new TypeError('Invalid non-string/buffer chunk');
  }
  return er;
}


function onEofChunk(stream, state) {
  if (state.decoder && !state.ended) {
    var chunk = state.decoder.end();
    if (chunk && chunk.length) {
      state.buffer.push(chunk);
      state.length += state.objectMode ? 1 : chunk.length;
    }
  }
  state.ended = true;

  // if we've ended and we have some data left, then emit
  // 'readable' now to make sure it gets picked up.
  if (state.length > 0)
    emitReadable(stream);
  else
    endReadable(stream);
}

// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow.  This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
  var state = stream._readableState;
  state.needReadable = false;
  if (state.emittedReadable)
    return;

  state.emittedReadable = true;
  if (state.sync)
    setImmediate(function() {
      emitReadable_(stream);
    });
  else
    emitReadable_(stream);
}

function emitReadable_(stream) {
  stream.emit('readable');
}


// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data.  that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
  if (!state.readingMore) {
    state.readingMore = true;
    setImmediate(function() {
      maybeReadMore_(stream, state);
    });
  }
}

function maybeReadMore_(stream, state) {
  var len = state.length;
  while (!state.reading && !state.flowing && !state.ended &&
         state.length < state.highWaterMark) {
    stream.read(0);
    if (len === state.length)
      // didn't get any data, stop spinning.
      break;
    else
      len = state.length;
  }
  state.readingMore = false;
}

// abstract method.  to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function(n) {
  this.emit('error', new Error('not implemented'));
};

Readable.prototype.pipe = function(dest, pipeOpts) {
  var src = this;
  var state = this._readableState;

  switch (state.pipesCount) {
    case 0:
      state.pipes = dest;
      break;
    case 1:
      state.pipes = [state.pipes, dest];
      break;
    default:
      state.pipes.push(dest);
      break;
  }
  state.pipesCount += 1;

  var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
              dest !== process.stdout &&
              dest !== process.stderr;

  var endFn = doEnd ? onend : cleanup;
  if (state.endEmitted)
    setImmediate(endFn);
  else
    src.once('end', endFn);

  dest.on('unpipe', onunpipe);
  function onunpipe(readable) {
    if (readable !== src) return;
    cleanup();
  }

  function onend() {
    dest.end();
  }

  // when the dest drains, it reduces the awaitDrain counter
  // on the source.  This would be more elegant with a .once()
  // handler in flow(), but adding and removing repeatedly is
  // too slow.
  var ondrain = pipeOnDrain(src);
  dest.on('drain', ondrain);

  function cleanup() {
    // cleanup event handlers once the pipe is broken
    dest.removeListener('close', onclose);
    dest.removeListener('finish', onfinish);
    dest.removeListener('drain', ondrain);
    dest.removeListener('error', onerror);
    dest.removeListener('unpipe', onunpipe);
    src.removeListener('end', onend);
    src.removeListener('end', cleanup);

    // if the reader is waiting for a drain event from this
    // specific writer, then it would cause it to never start
    // flowing again.
    // So, if this is awaiting a drain, then we just call it now.
    // If we don't know, then assume that we are waiting for one.
    if (!dest._writableState || dest._writableState.needDrain)
      ondrain();
  }

  // if the dest has an error, then stop piping into it.
  // however, don't suppress the throwing behavior for this.
  // check for listeners before emit removes one-time listeners.
  var errListeners = EE.listenerCount(dest, 'error');
  function onerror(er) {
    unpipe();
    if (errListeners === 0 && EE.listenerCount(dest, 'error') === 0)
      dest.emit('error', er);
  }
  dest.once('error', onerror);

  // Both close and finish should trigger unpipe, but only once.
  function onclose() {
    dest.removeListener('finish', onfinish);
    unpipe();
  }
  dest.once('close', onclose);
  function onfinish() {
    dest.removeListener('close', onclose);
    unpipe();
  }
  dest.once('finish', onfinish);

  function unpipe() {
    src.unpipe(dest);
  }

  // tell the dest that it's being piped to
  dest.emit('pipe', src);

  // start the flow if it hasn't been started already.
  if (!state.flowing) {
    // the handler that waits for readable events after all
    // the data gets sucked out in flow.
    // This would be easier to follow with a .once() handler
    // in flow(), but that is too slow.
    this.on('readable', pipeOnReadable);

    state.flowing = true;
    setImmediate(function() {
      flow(src);
    });
  }

  return dest;
};

function pipeOnDrain(src) {
  return function() {
    var dest = this;
    var state = src._readableState;
    state.awaitDrain--;
    if (state.awaitDrain === 0)
      flow(src);
  };
}

function flow(src) {
  var state = src._readableState;
  var chunk;
  state.awaitDrain = 0;

  function write(dest, i, list) {
    var written = dest.write(chunk);
    if (false === written) {
      state.awaitDrain++;
    }
  }

  while (state.pipesCount && null !== (chunk = src.read())) {

    if (state.pipesCount === 1)
      write(state.pipes, 0, null);
    else
      forEach(state.pipes, write);

    src.emit('data', chunk);

    // if anyone needs a drain, then we have to wait for that.
    if (state.awaitDrain > 0)
      return;
  }

  // if every destination was unpiped, either before entering this
  // function, or in the while loop, then stop flowing.
  //
  // NB: This is a pretty rare edge case.
  if (state.pipesCount === 0) {
    state.flowing = false;

    // if there were data event listeners added, then switch to old mode.
    if (EE.listenerCount(src, 'data') > 0)
      emitDataEvents(src);
    return;
  }

  // at this point, no one needed a drain, so we just ran out of data
  // on the next readable event, start it over again.
  state.ranOut = true;
}

function pipeOnReadable() {
  if (this._readableState.ranOut) {
    this._readableState.ranOut = false;
    flow(this);
  }
}


Readable.prototype.unpipe = function(dest) {
  var state = this._readableState;

  // if we're not piping anywhere, then do nothing.
  if (state.pipesCount === 0)
    return this;

  // just one destination.  most common case.
  if (state.pipesCount === 1) {
    // passed in one, but it's not the right one.
    if (dest && dest !== state.pipes)
      return this;

    if (!dest)
      dest = state.pipes;

    // got a match.
    state.pipes = null;
    state.pipesCount = 0;
    this.removeListener('readable', pipeOnReadable);
    state.flowing = false;
    if (dest)
      dest.emit('unpipe', this);
    return this;
  }

  // slow case. multiple pipe destinations.

  if (!dest) {
    // remove all.
    var dests = state.pipes;
    var len = state.pipesCount;
    state.pipes = null;
    state.pipesCount = 0;
    this.removeListener('readable', pipeOnReadable);
    state.flowing = false;

    for (var i = 0; i < len; i++)
      dests[i].emit('unpipe', this);
    return this;
  }

  // try to find the right one.
  var i = indexOf(state.pipes, dest);
  if (i === -1)
    return this;

  state.pipes.splice(i, 1);
  state.pipesCount -= 1;
  if (state.pipesCount === 1)
    state.pipes = state.pipes[0];

  dest.emit('unpipe', this);

  return this;
};

// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function(ev, fn) {
  var res = Stream.prototype.on.call(this, ev, fn);

  if (ev === 'data' && !this._readableState.flowing)
    emitDataEvents(this);

  if (ev === 'readable' && this.readable) {
    var state = this._readableState;
    if (!state.readableListening) {
      state.readableListening = true;
      state.emittedReadable = false;
      state.needReadable = true;
      if (!state.reading) {
        this.read(0);
      } else if (state.length) {
        emitReadable(this, state);
      }
    }
  }

  return res;
};
Readable.prototype.addListener = Readable.prototype.on;

// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function() {
  emitDataEvents(this);
  this.read(0);
  this.emit('resume');
};

Readable.prototype.pause = function() {
  emitDataEvents(this, true);
  this.emit('pause');
};

function emitDataEvents(stream, startPaused) {
  var state = stream._readableState;

  if (state.flowing) {
    // https://github.com/isaacs/readable-stream/issues/16
    throw new Error('Cannot switch to old mode now.');
  }

  var paused = startPaused || false;
  var readable = false;

  // convert to an old-style stream.
  stream.readable = true;
  stream.pipe = Stream.prototype.pipe;
  stream.on = stream.addListener = Stream.prototype.on;

  stream.on('readable', function() {
    readable = true;

    var c;
    while (!paused && (null !== (c = stream.read())))
      stream.emit('data', c);

    if (c === null) {
      readable = false;
      stream._readableState.needReadable = true;
    }
  });

  stream.pause = function() {
    paused = true;
    this.emit('pause');
  };

  stream.resume = function() {
    paused = false;
    if (readable)
      setImmediate(function() {
        stream.emit('readable');
      });
    else
      this.read(0);
    this.emit('resume');
  };

  // now make it start, just in case it hadn't already.
  stream.emit('readable');
}

// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function(stream) {
  var state = this._readableState;
  var paused = false;

  var self = this;
  stream.on('end', function() {
    if (state.decoder && !state.ended) {
      var chunk = state.decoder.end();
      if (chunk && chunk.length)
        self.push(chunk);
    }

    self.push(null);
  });

  stream.on('data', function(chunk) {
    if (state.decoder)
      chunk = state.decoder.write(chunk);
    if (!chunk || !state.objectMode && !chunk.length)
      return;

    var ret = self.push(chunk);
    if (!ret) {
      paused = true;
      stream.pause();
    }
  });

  // proxy all the other methods.
  // important when wrapping filters and duplexes.
  for (var i in stream) {
    if (typeof stream[i] === 'function' &&
        typeof this[i] === 'undefined') {
      this[i] = function(method) { return function() {
        return stream[method].apply(stream, arguments);
      }}(i);
    }
  }

  // proxy certain important events.
  var events = ['error', 'close', 'destroy', 'pause', 'resume'];
  forEach(events, function(ev) {
    stream.on(ev, function (x) {
      return self.emit.apply(self, ev, x);
    });
  });

  // when we try to consume some more bytes, simply unpause the
  // underlying stream.
  self._read = function(n) {
    if (paused) {
      paused = false;
      stream.resume();
    }
  };

  return self;
};



// exposed for testing purposes only.
Readable._fromList = fromList;

// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
  var list = state.buffer;
  var length = state.length;
  var stringMode = !!state.decoder;
  var objectMode = !!state.objectMode;
  var ret;

  // nothing in the list, definitely empty.
  if (list.length === 0)
    return null;

  if (length === 0)
    ret = null;
  else if (objectMode)
    ret = list.shift();
  else if (!n || n >= length) {
    // read it all, truncate the array.
    if (stringMode)
      ret = list.join('');
    else
      ret = Buffer.concat(list, length);
    list.length = 0;
  } else {
    // read just some of it.
    if (n < list[0].length) {
      // just take a part of the first list item.
      // slice is the same for buffers and strings.
      var buf = list[0];
      ret = buf.slice(0, n);
      list[0] = buf.slice(n);
    } else if (n === list[0].length) {
      // first list is a perfect match
      ret = list.shift();
    } else {
      // complex case.
      // we have enough to cover it, but it spans past the first buffer.
      if (stringMode)
        ret = '';
      else
        ret = new Buffer(n);

      var c = 0;
      for (var i = 0, l = list.length; i < l && c < n; i++) {
        var buf = list[0];
        var cpy = Math.min(n - c, buf.length);

        if (stringMode)
          ret += buf.slice(0, cpy);
        else
          buf.copy(ret, c, 0, cpy);

        if (cpy < buf.length)
          list[0] = buf.slice(cpy);
        else
          list.shift();

        c += cpy;
      }
    }
  }

  return ret;
}

function endReadable(stream) {
  var state = stream._readableState;

  // If we get here before consuming all the bytes, then that is a
  // bug in node.  Should never happen.
  if (state.length > 0)
    throw new Error('endReadable called on non-empty stream');

  if (!state.endEmitted && state.calledRead) {
    state.ended = true;
    setImmediate(function() {
      // Check that we didn't get one last unshift.
      if (!state.endEmitted && state.length === 0) {
        state.endEmitted = true;
        stream.readable = false;
        stream.emit('end');
      }
    });
  }
}

function forEach (xs, f) {
  for (var i = 0, l = xs.length; i < l; i++) {
    f(xs[i], i);
  }
}

function indexOf (xs, x) {
  for (var i = 0, l = xs.length; i < l; i++) {
    if (xs[i] === x) return i;
  }
  return -1;
}
}).call(this,require("/var/lib/stickshift/05be99daaebd4e358a8cb0c60e267bb1/app-root/data/lib/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"))
},{"./index.js":"VDsrfn","/var/lib/stickshift/05be99daaebd4e358a8cb0c60e267bb1/app-root/data/lib/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":5,"buffer":6,"events":3,"inherits":4,"process/browser.js":12,"string_decoder":17}],15:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// a transform stream is a readable/writable stream where you do
// something with the data.  Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored.  (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation.  For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes.  When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up.  When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer.  When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks.  If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk.  However,
// a pathological inflate type of transform can cause excessive buffering
// here.  For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output.  Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output.  In this case, you could write a very small
// amount of input, and end up with a very large amount of output.  In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform.  A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.

module.exports = Transform;

var Duplex = require('./duplex.js');
var inherits = require('inherits');
inherits(Transform, Duplex);


function TransformState(options, stream) {
  this.afterTransform = function(er, data) {
    return afterTransform(stream, er, data);
  };

  this.needTransform = false;
  this.transforming = false;
  this.writecb = null;
  this.writechunk = null;
}

function afterTransform(stream, er, data) {
  var ts = stream._transformState;
  ts.transforming = false;

  var cb = ts.writecb;

  if (!cb)
    return stream.emit('error', new Error('no writecb in Transform class'));

  ts.writechunk = null;
  ts.writecb = null;

  if (data !== null && data !== undefined)
    stream.push(data);

  if (cb)
    cb(er);

  var rs = stream._readableState;
  rs.reading = false;
  if (rs.needReadable || rs.length < rs.highWaterMark) {
    stream._read(rs.highWaterMark);
  }
}


function Transform(options) {
  if (!(this instanceof Transform))
    return new Transform(options);

  Duplex.call(this, options);

  var ts = this._transformState = new TransformState(options, this);

  // when the writable side finishes, then flush out anything remaining.
  var stream = this;

  // start out asking for a readable event once data is transformed.
  this._readableState.needReadable = true;

  // we have implemented the _read method, and done the other things
  // that Readable wants before the first _read call, so unset the
  // sync guard flag.
  this._readableState.sync = false;

  this.once('finish', function() {
    if ('function' === typeof this._flush)
      this._flush(function(er) {
        done(stream, er);
      });
    else
      done(stream);
  });
}

Transform.prototype.push = function(chunk, encoding) {
  this._transformState.needTransform = false;
  return Duplex.prototype.push.call(this, chunk, encoding);
};

// This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side.  You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk.  If you pass
// an error, then that'll put the hurt on the whole operation.  If you
// never call cb(), then you'll never get another chunk.
Transform.prototype._transform = function(chunk, encoding, cb) {
  throw new Error('not implemented');
};

Transform.prototype._write = function(chunk, encoding, cb) {
  var ts = this._transformState;
  ts.writecb = cb;
  ts.writechunk = chunk;
  ts.writeencoding = encoding;
  if (!ts.transforming) {
    var rs = this._readableState;
    if (ts.needTransform ||
        rs.needReadable ||
        rs.length < rs.highWaterMark)
      this._read(rs.highWaterMark);
  }
};

// Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.
Transform.prototype._read = function(n) {
  var ts = this._transformState;

  if (ts.writechunk && ts.writecb && !ts.transforming) {
    ts.transforming = true;
    this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
  } else {
    // mark that we need a transform, so that any data that comes in
    // will get processed, now that we've asked for it.
    ts.needTransform = true;
  }
};


function done(stream, er) {
  if (er)
    return stream.emit('error', er);

  // if there's nothing in the write buffer, then that means
  // that nothing more will ever be provided
  var ws = stream._writableState;
  var rs = stream._readableState;
  var ts = stream._transformState;

  if (ws.length)
    throw new Error('calling transform done when ws.length != 0');

  if (ts.transforming)
    throw new Error('calling transform done when still transforming');

  return stream.push(null);
}

},{"./duplex.js":9,"inherits":4}],16:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// A bit simpler than readable streams.
// Implement an async ._write(chunk, cb), and it'll handle all
// the drain event emission and buffering.

module.exports = Writable;
Writable.WritableState = WritableState;

var isUint8Array = typeof Uint8Array !== 'undefined'
  ? function (x) { return x instanceof Uint8Array }
  : function (x) {
    return x && x.constructor && x.constructor.name === 'Uint8Array'
  }
;
var isArrayBuffer = typeof ArrayBuffer !== 'undefined'
  ? function (x) { return x instanceof ArrayBuffer }
  : function (x) {
    return x && x.constructor && x.constructor.name === 'ArrayBuffer'
  }
;

var inherits = require('inherits');
var Stream = require('./index.js');
var setImmediate = require('process/browser.js').nextTick;
var Buffer = require('buffer').Buffer;

inherits(Writable, Stream);

function WriteReq(chunk, encoding, cb) {
  this.chunk = chunk;
  this.encoding = encoding;
  this.callback = cb;
}

function WritableState(options, stream) {
  options = options || {};

  // the point at which write() starts returning false
  // Note: 0 is a valid value, means that we always return false if
  // the entire buffer is not flushed immediately on write()
  var hwm = options.highWaterMark;
  this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;

  // object stream flag to indicate whether or not this stream
  // contains buffers or objects.
  this.objectMode = !!options.objectMode;

  // cast to ints.
  this.highWaterMark = ~~this.highWaterMark;

  this.needDrain = false;
  // at the start of calling end()
  this.ending = false;
  // when end() has been called, and returned
  this.ended = false;
  // when 'finish' is emitted
  this.finished = false;

  // should we decode strings into buffers before passing to _write?
  // this is here so that some node-core streams can optimize string
  // handling at a lower level.
  var noDecode = options.decodeStrings === false;
  this.decodeStrings = !noDecode;

  // Crypto is kind of old and crusty.  Historically, its default string
  // encoding is 'binary' so we have to make this configurable.
  // Everything else in the universe uses 'utf8', though.
  this.defaultEncoding = options.defaultEncoding || 'utf8';

  // not an actual buffer we keep track of, but a measurement
  // of how much we're waiting to get pushed to some underlying
  // socket or file.
  this.length = 0;

  // a flag to see when we're in the middle of a write.
  this.writing = false;

  // a flag to be able to tell if the onwrite cb is called immediately,
  // or on a later tick.  We set this to true at first, becuase any
  // actions that shouldn't happen until "later" should generally also
  // not happen before the first write call.
  this.sync = true;

  // a flag to know if we're processing previously buffered items, which
  // may call the _write() callback in the same tick, so that we don't
  // end up in an overlapped onwrite situation.
  this.bufferProcessing = false;

  // the callback that's passed to _write(chunk,cb)
  this.onwrite = function(er) {
    onwrite(stream, er);
  };

  // the callback that the user supplies to write(chunk,encoding,cb)
  this.writecb = null;

  // the amount that is being written when _write is called.
  this.writelen = 0;

  this.buffer = [];
}

function Writable(options) {
  // Writable ctor is applied to Duplexes, though they're not
  // instanceof Writable, they're instanceof Readable.
  if (!(this instanceof Writable) && !(this instanceof Stream.Duplex))
    return new Writable(options);

  this._writableState = new WritableState(options, this);

  // legacy.
  this.writable = true;

  Stream.call(this);
}

// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function() {
  this.emit('error', new Error('Cannot pipe. Not readable.'));
};


function writeAfterEnd(stream, state, cb) {
  var er = new Error('write after end');
  // TODO: defer error events consistently everywhere, not just the cb
  stream.emit('error', er);
  setImmediate(function() {
    cb(er);
  });
}

// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
  var valid = true;
  if (!Buffer.isBuffer(chunk) &&
      'string' !== typeof chunk &&
      chunk !== null &&
      chunk !== undefined &&
      !state.objectMode) {
    var er = new TypeError('Invalid non-string/buffer chunk');
    stream.emit('error', er);
    setImmediate(function() {
      cb(er);
    });
    valid = false;
  }
  return valid;
}

Writable.prototype.write = function(chunk, encoding, cb) {
  var state = this._writableState;
  var ret = false;

  if (typeof encoding === 'function') {
    cb = encoding;
    encoding = null;
  }

  if (!Buffer.isBuffer(chunk) && isUint8Array(chunk))
    chunk = new Buffer(chunk);
  if (isArrayBuffer(chunk) && typeof Uint8Array !== 'undefined')
    chunk = new Buffer(new Uint8Array(chunk));
  
  if (Buffer.isBuffer(chunk))
    encoding = 'buffer';
  else if (!encoding)
    encoding = state.defaultEncoding;

  if (typeof cb !== 'function')
    cb = function() {};

  if (state.ended)
    writeAfterEnd(this, state, cb);
  else if (validChunk(this, state, chunk, cb))
    ret = writeOrBuffer(this, state, chunk, encoding, cb);

  return ret;
};

function decodeChunk(state, chunk, encoding) {
  if (!state.objectMode &&
      state.decodeStrings !== false &&
      typeof chunk === 'string') {
    chunk = new Buffer(chunk, encoding);
  }
  return chunk;
}

// if we're already writing something, then just put this
// in the queue, and wait our turn.  Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
  chunk = decodeChunk(state, chunk, encoding);
  var len = state.objectMode ? 1 : chunk.length;

  state.length += len;

  var ret = state.length < state.highWaterMark;
  state.needDrain = !ret;

  if (state.writing)
    state.buffer.push(new WriteReq(chunk, encoding, cb));
  else
    doWrite(stream, state, len, chunk, encoding, cb);

  return ret;
}

function doWrite(stream, state, len, chunk, encoding, cb) {
  state.writelen = len;
  state.writecb = cb;
  state.writing = true;
  state.sync = true;
  stream._write(chunk, encoding, state.onwrite);
  state.sync = false;
}

function onwriteError(stream, state, sync, er, cb) {
  if (sync)
    setImmediate(function() {
      cb(er);
    });
  else
    cb(er);

  stream.emit('error', er);
}

function onwriteStateUpdate(state) {
  state.writing = false;
  state.writecb = null;
  state.length -= state.writelen;
  state.writelen = 0;
}

function onwrite(stream, er) {
  var state = stream._writableState;
  var sync = state.sync;
  var cb = state.writecb;

  onwriteStateUpdate(state);

  if (er)
    onwriteError(stream, state, sync, er, cb);
  else {
    // Check if we're actually ready to finish, but don't emit yet
    var finished = needFinish(stream, state);

    if (!finished && !state.bufferProcessing && state.buffer.length)
      clearBuffer(stream, state);

    if (sync) {
      setImmediate(function() {
        afterWrite(stream, state, finished, cb);
      });
    } else {
      afterWrite(stream, state, finished, cb);
    }
  }
}

function afterWrite(stream, state, finished, cb) {
  if (!finished)
    onwriteDrain(stream, state);
  cb();
  if (finished)
    finishMaybe(stream, state);
}

// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
  if (state.length === 0 && state.needDrain) {
    state.needDrain = false;
    stream.emit('drain');
  }
}


// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
  state.bufferProcessing = true;

  for (var c = 0; c < state.buffer.length; c++) {
    var entry = state.buffer[c];
    var chunk = entry.chunk;
    var encoding = entry.encoding;
    var cb = entry.callback;
    var len = state.objectMode ? 1 : chunk.length;

    doWrite(stream, state, len, chunk, encoding, cb);

    // if we didn't call the onwrite immediately, then
    // it means that we need to wait until it does.
    // also, that means that the chunk and cb are currently
    // being processed, so move the buffer counter past them.
    if (state.writing) {
      c++;
      break;
    }
  }

  state.bufferProcessing = false;
  if (c < state.buffer.length)
    state.buffer = state.buffer.slice(c);
  else
    state.buffer.length = 0;
}

Writable.prototype._write = function(chunk, encoding, cb) {
  cb(new Error('not implemented'));
};

Writable.prototype.end = function(chunk, encoding, cb) {
  var state = this._writableState;

  if (typeof chunk === 'function') {
    cb = chunk;
    chunk = null;
    encoding = null;
  } else if (typeof encoding === 'function') {
    cb = encoding;
    encoding = null;
  }

  if (typeof chunk !== 'undefined' && chunk !== null)
    this.write(chunk, encoding);

  // ignore unnecessary end() calls.
  if (!state.ending && !state.finished)
    endWritable(this, state, cb);
};


function needFinish(stream, state) {
  return (state.ending &&
          state.length === 0 &&
          !state.finished &&
          !state.writing);
}

function finishMaybe(stream, state) {
  var need = needFinish(stream, state);
  if (need) {
    state.finished = true;
    stream.emit('finish');
  }
  return need;
}

function endWritable(stream, state, cb) {
  state.ending = true;
  finishMaybe(stream, state);
  if (cb) {
    if (state.finished)
      setImmediate(cb);
    else
      stream.once('finish', cb);
  }
  state.ended = true;
}

},{"./index.js":"VDsrfn","buffer":6,"inherits":4,"process/browser.js":12}],17:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var Buffer = require('buffer').Buffer;

function assertEncoding(encoding) {
  if (encoding && !Buffer.isEncoding(encoding)) {
    throw new Error('Unknown encoding: ' + encoding);
  }
}

var StringDecoder = exports.StringDecoder = function(encoding) {
  this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, '');
  assertEncoding(encoding);
  switch (this.encoding) {
    case 'utf8':
      // CESU-8 represents each of Surrogate Pair by 3-bytes
      this.surrogateSize = 3;
      break;
    case 'ucs2':
    case 'utf16le':
      // UTF-16 represents each of Surrogate Pair by 2-bytes
      this.surrogateSize = 2;
      this.detectIncompleteChar = utf16DetectIncompleteChar;
      break;
    case 'base64':
      // Base-64 stores 3 bytes in 4 chars, and pads the remainder.
      this.surrogateSize = 3;
      this.detectIncompleteChar = base64DetectIncompleteChar;
      break;
    default:
      this.write = passThroughWrite;
      return;
  }

  this.charBuffer = new Buffer(6);
  this.charReceived = 0;
  this.charLength = 0;
};


StringDecoder.prototype.write = function(buffer) {
  var charStr = '';
  var offset = 0;

  // if our last write ended with an incomplete multibyte character
  while (this.charLength) {
    // determine how many remaining bytes this buffer has to offer for this char
    var i = (buffer.length >= this.charLength - this.charReceived) ?
                this.charLength - this.charReceived :
                buffer.length;

    // add the new bytes to the char buffer
    buffer.copy(this.charBuffer, this.charReceived, offset, i);
    this.charReceived += (i - offset);
    offset = i;

    if (this.charReceived < this.charLength) {
      // still not enough chars in this buffer? wait for more ...
      return '';
    }

    // get the character that was split
    charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding);

    // lead surrogate (D800-DBFF) is also the incomplete character
    var charCode = charStr.charCodeAt(charStr.length - 1);
    if (charCode >= 0xD800 && charCode <= 0xDBFF) {
      this.charLength += this.surrogateSize;
      charStr = '';
      continue;
    }
    this.charReceived = this.charLength = 0;

    // if there are no more bytes in this buffer, just emit our char
    if (i == buffer.length) return charStr;

    // otherwise cut off the characters end from the beginning of this buffer
    buffer = buffer.slice(i, buffer.length);
    break;
  }

  var lenIncomplete = this.detectIncompleteChar(buffer);

  var end = buffer.length;
  if (this.charLength) {
    // buffer the incomplete character bytes we got
    buffer.copy(this.charBuffer, 0, buffer.length - lenIncomplete, end);
    this.charReceived = lenIncomplete;
    end -= lenIncomplete;
  }

  charStr += buffer.toString(this.encoding, 0, end);

  var end = charStr.length - 1;
  var charCode = charStr.charCodeAt(end);
  // lead surrogate (D800-DBFF) is also the incomplete character
  if (charCode >= 0xD800 && charCode <= 0xDBFF) {
    var size = this.surrogateSize;
    this.charLength += size;
    this.charReceived += size;
    this.charBuffer.copy(this.charBuffer, size, 0, size);
    this.charBuffer.write(charStr.charAt(charStr.length - 1), this.encoding);
    return charStr.substring(0, end);
  }

  // or just emit the charStr
  return charStr;
};

StringDecoder.prototype.detectIncompleteChar = function(buffer) {
  // determine how many bytes we have to check at the end of this buffer
  var i = (buffer.length >= 3) ? 3 : buffer.length;

  // Figure out if one of the last i bytes of our buffer announces an
  // incomplete char.
  for (; i > 0; i--) {
    var c = buffer[buffer.length - i];

    // See http://en.wikipedia.org/wiki/UTF-8#Description

    // 110XXXXX
    if (i == 1 && c >> 5 == 0x06) {
      this.charLength = 2;
      break;
    }

    // 1110XXXX
    if (i <= 2 && c >> 4 == 0x0E) {
      this.charLength = 3;
      break;
    }

    // 11110XXX
    if (i <= 3 && c >> 3 == 0x1E) {
      this.charLength = 4;
      break;
    }
  }

  return i;
};

StringDecoder.prototype.end = function(buffer) {
  var res = '';
  if (buffer && buffer.length)
    res = this.write(buffer);

  if (this.charReceived) {
    var cr = this.charReceived;
    var buf = this.charBuffer;
    var enc = this.encoding;
    res += buf.slice(0, cr).toString(enc);
  }

  return res;
};

function passThroughWrite(buffer) {
  return buffer.toString(this.encoding);
}

function utf16DetectIncompleteChar(buffer) {
  var incomplete = this.charReceived = buffer.length % 2;
  this.charLength = incomplete ? 2 : 0;
  return incomplete;
}

function base64DetectIncompleteChar(buffer) {
  var incomplete = this.charReceived = buffer.length % 3;
  this.charLength = incomplete ? 3 : 0;
  return incomplete;
}

},{"buffer":6}],18:[function(require,module,exports){
module.exports = function isBuffer(arg) {
  return arg && typeof arg === 'object'
    && typeof arg.copy === 'function'
    && typeof arg.fill === 'function'
    && typeof arg.readUInt8 === 'function';
}
},{}],"util":[function(require,module,exports){
module.exports=require('yrrLM1');
},{}],"yrrLM1":[function(require,module,exports){
(function (process,global){// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
  if (!isString(f)) {
    var objects = [];
    for (var i = 0; i < arguments.length; i++) {
      objects.push(inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  var i = 1;
  var args = arguments;
  var len = args.length;
  var str = String(f).replace(formatRegExp, function(x) {
    if (x === '%%') return '%';
    if (i >= len) return x;
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j':
        try {
          return JSON.stringify(args[i++]);
        } catch (_) {
          return '[Circular]';
        }
      default:
        return x;
    }
  });
  for (var x = args[i]; i < len; x = args[++i]) {
    if (isNull(x) || !isObject(x)) {
      str += ' ' + x;
    } else {
      str += ' ' + inspect(x);
    }
  }
  return str;
};


// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
  // Allow for deprecating things in the process of starting up.
  if (isUndefined(global.process)) {
    return function() {
      return exports.deprecate(fn, msg).apply(this, arguments);
    };
  }

  if (process.noDeprecation === true) {
    return fn;
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (process.throwDeprecation) {
        throw new Error(msg);
      } else if (process.traceDeprecation) {
        console.trace(msg);
      } else {
        console.error(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
};


var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
  if (isUndefined(debugEnviron))
    debugEnviron = process.env.NODE_DEBUG || '';
  set = set.toUpperCase();
  if (!debugs[set]) {
    if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
      var pid = process.pid;
      debugs[set] = function() {
        var msg = exports.format.apply(exports, arguments);
        console.error('%s %d: %s', set, pid, msg);
      };
    } else {
      debugs[set] = function() {};
    }
  }
  return debugs[set];
};


/**
 * Echos the value of a value. Trys to print the value out
 * in the best way possible given the different types.
 *
 * @param {Object} obj The object to print out.
 * @param {Object} opts Optional options object that alters the output.
 */
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
  // default options
  var ctx = {
    seen: [],
    stylize: stylizeNoColor
  };
  // legacy...
  if (arguments.length >= 3) ctx.depth = arguments[2];
  if (arguments.length >= 4) ctx.colors = arguments[3];
  if (isBoolean(opts)) {
    // legacy...
    ctx.showHidden = opts;
  } else if (opts) {
    // got an "options" object
    exports._extend(ctx, opts);
  }
  // set default options
  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
  if (isUndefined(ctx.depth)) ctx.depth = 2;
  if (isUndefined(ctx.colors)) ctx.colors = false;
  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
  if (ctx.colors) ctx.stylize = stylizeWithColor;
  return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
  'bold' : [1, 22],
  'italic' : [3, 23],
  'underline' : [4, 24],
  'inverse' : [7, 27],
  'white' : [37, 39],
  'grey' : [90, 39],
  'black' : [30, 39],
  'blue' : [34, 39],
  'cyan' : [36, 39],
  'green' : [32, 39],
  'magenta' : [35, 39],
  'red' : [31, 39],
  'yellow' : [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
  'special': 'cyan',
  'number': 'yellow',
  'boolean': 'yellow',
  'undefined': 'grey',
  'null': 'bold',
  'string': 'green',
  'date': 'magenta',
  // "name": intentionally not styling
  'regexp': 'red'
};


function stylizeWithColor(str, styleType) {
  var style = inspect.styles[styleType];

  if (style) {
    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
           '\u001b[' + inspect.colors[style][1] + 'm';
  } else {
    return str;
  }
}


function stylizeNoColor(str, styleType) {
  return str;
}


function arrayToHash(array) {
  var hash = {};

  array.forEach(function(val, idx) {
    hash[val] = true;
  });

  return hash;
}


function formatValue(ctx, value, recurseTimes) {
  // Provide a hook for user-specified inspect functions.
  // Check that value is an object with an inspect function on it
  if (ctx.customInspect &&
      value &&
      isFunction(value.inspect) &&
      // Filter out the util module, it's inspect function is special
      value.inspect !== exports.inspect &&
      // Also filter out any prototype objects using the circular check.
      !(value.constructor && value.constructor.prototype === value)) {
    var ret = value.inspect(recurseTimes, ctx);
    if (!isString(ret)) {
      ret = formatValue(ctx, ret, recurseTimes);
    }
    return ret;
  }

  // Primitive types cannot have properties
  var primitive = formatPrimitive(ctx, value);
  if (primitive) {
    return primitive;
  }

  // Look up the keys of the object.
  var keys = Object.keys(value);
  var visibleKeys = arrayToHash(keys);

  if (ctx.showHidden) {
    keys = Object.getOwnPropertyNames(value);
  }

  // IE doesn't make error fields non-enumerable
  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
  if (isError(value)
      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
    return formatError(value);
  }

  // Some type of object without properties can be shortcutted.
  if (keys.length === 0) {
    if (isFunction(value)) {
      var name = value.name ? ': ' + value.name : '';
      return ctx.stylize('[Function' + name + ']', 'special');
    }
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    }
    if (isDate(value)) {
      return ctx.stylize(Date.prototype.toString.call(value), 'date');
    }
    if (isError(value)) {
      return formatError(value);
    }
  }

  var base = '', array = false, braces = ['{', '}'];

  // Make Array say that they are Array
  if (isArray(value)) {
    array = true;
    braces = ['[', ']'];
  }

  // Make functions say that they are functions
  if (isFunction(value)) {
    var n = value.name ? ': ' + value.name : '';
    base = ' [Function' + n + ']';
  }

  // Make RegExps say that they are RegExps
  if (isRegExp(value)) {
    base = ' ' + RegExp.prototype.toString.call(value);
  }

  // Make dates with properties first say the date
  if (isDate(value)) {
    base = ' ' + Date.prototype.toUTCString.call(value);
  }

  // Make error with message first say the error
  if (isError(value)) {
    base = ' ' + formatError(value);
  }

  if (keys.length === 0 && (!array || value.length == 0)) {
    return braces[0] + base + braces[1];
  }

  if (recurseTimes < 0) {
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    } else {
      return ctx.stylize('[Object]', 'special');
    }
  }

  ctx.seen.push(value);

  var output;
  if (array) {
    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
  } else {
    output = keys.map(function(key) {
      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
    });
  }

  ctx.seen.pop();

  return reduceToSingleString(output, base, braces);
}


function formatPrimitive(ctx, value) {
  if (isUndefined(value))
    return ctx.stylize('undefined', 'undefined');
  if (isString(value)) {
    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                                             .replace(/'/g, "\\'")
                                             .replace(/\\"/g, '"') + '\'';
    return ctx.stylize(simple, 'string');
  }
  if (isNumber(value))
    return ctx.stylize('' + value, 'number');
  if (isBoolean(value))
    return ctx.stylize('' + value, 'boolean');
  // For some reason typeof null is "object", so special case here.
  if (isNull(value))
    return ctx.stylize('null', 'null');
}


function formatError(value) {
  return '[' + Error.prototype.toString.call(value) + ']';
}


function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  var output = [];
  for (var i = 0, l = value.length; i < l; ++i) {
    if (hasOwnProperty(value, String(i))) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          String(i), true));
    } else {
      output.push('');
    }
  }
  keys.forEach(function(key) {
    if (!key.match(/^\d+$/)) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          key, true));
    }
  });
  return output;
}


function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
  var name, str, desc;
  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
  if (desc.get) {
    if (desc.set) {
      str = ctx.stylize('[Getter/Setter]', 'special');
    } else {
      str = ctx.stylize('[Getter]', 'special');
    }
  } else {
    if (desc.set) {
      str = ctx.stylize('[Setter]', 'special');
    }
  }
  if (!hasOwnProperty(visibleKeys, key)) {
    name = '[' + key + ']';
  }
  if (!str) {
    if (ctx.seen.indexOf(desc.value) < 0) {
      if (isNull(recurseTimes)) {
        str = formatValue(ctx, desc.value, null);
      } else {
        str = formatValue(ctx, desc.value, recurseTimes - 1);
      }
      if (str.indexOf('\n') > -1) {
        if (array) {
          str = str.split('\n').map(function(line) {
            return '  ' + line;
          }).join('\n').substr(2);
        } else {
          str = '\n' + str.split('\n').map(function(line) {
            return '   ' + line;
          }).join('\n');
        }
      }
    } else {
      str = ctx.stylize('[Circular]', 'special');
    }
  }
  if (isUndefined(name)) {
    if (array && key.match(/^\d+$/)) {
      return str;
    }
    name = JSON.stringify('' + key);
    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
      name = name.substr(1, name.length - 2);
      name = ctx.stylize(name, 'name');
    } else {
      name = name.replace(/'/g, "\\'")
                 .replace(/\\"/g, '"')
                 .replace(/(^"|"$)/g, "'");
      name = ctx.stylize(name, 'string');
    }
  }

  return name + ': ' + str;
}


function reduceToSingleString(output, base, braces) {
  var numLinesEst = 0;
  var length = output.reduce(function(prev, cur) {
    numLinesEst++;
    if (cur.indexOf('\n') >= 0) numLinesEst++;
    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
  }, 0);

  if (length > 60) {
    return braces[0] +
           (base === '' ? '' : base + '\n ') +
           ' ' +
           output.join(',\n  ') +
           ' ' +
           braces[1];
  }

  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}


// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
  return Array.isArray(ar);
}
exports.isArray = isArray;

function isBoolean(arg) {
  return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;

function isNull(arg) {
  return arg === null;
}
exports.isNull = isNull;

function isNullOrUndefined(arg) {
  return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;

function isNumber(arg) {
  return typeof arg === 'number';
}
exports.isNumber = isNumber;

function isString(arg) {
  return typeof arg === 'string';
}
exports.isString = isString;

function isSymbol(arg) {
  return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;

function isUndefined(arg) {
  return arg === void 0;
}
exports.isUndefined = isUndefined;

function isRegExp(re) {
  return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;

function isDate(d) {
  return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;

function isError(e) {
  return isObject(e) &&
      (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;

function isFunction(arg) {
  return typeof arg === 'function';
}
exports.isFunction = isFunction;

function isPrimitive(arg) {
  return arg === null ||
         typeof arg === 'boolean' ||
         typeof arg === 'number' ||
         typeof arg === 'string' ||
         typeof arg === 'symbol' ||  // ES6 symbol
         typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;

exports.isBuffer = require('./support/isBuffer');

function objectToString(o) {
  return Object.prototype.toString.call(o);
}


function pad(n) {
  return n < 10 ? '0' + n.toString(10) : n.toString(10);
}


var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];

// 26 Feb 16:19:34
function timestamp() {
  var d = new Date();
  var time = [pad(d.getHours()),
              pad(d.getMinutes()),
              pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}


// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be rewritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = require('inherits');

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || !isObject(add)) return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}
}).call(this,require("/var/lib/stickshift/05be99daaebd4e358a8cb0c60e267bb1/app-root/data/lib/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":18,"/var/lib/stickshift/05be99daaebd4e358a8cb0c60e267bb1/app-root/data/lib/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":5,"inherits":4}]},{},[])
var stream = require("stream")
  , util = require("util");

var module = angular.module("plunker.service.ottext", [
  "plunker.service.textops"
]);

module.factory("otuser",
  [function(){
    
  }]
);

module.factory("ottext",
  ["$rootScope", "$q", "$timeout", "textops", "otuser", function($rootScope, $q, $timeout, textops, otuser){
    util.inherits(OTText, stream.Duplex);
    
    /**
     * Interface for enabling OT text bindings on a Firebase reference
     *
     */
    function OTText(ref, options) {
      stream.Duplex.call(this, options);
      
      this.id = ref.name();
      this.ref = ref;
      this.revisionsRef = ref.child("revisions");
      this.checkpointRef = ref.child("snapshots");
      
      this.ot = window.ottypes.text0;
      
      // Flag to get around the fact that Firebase will fire 'child_added' events for local adds
      this.isLocalEvent = false;
      this.isFlushing = false;
      
      // Frequency (number of revisions) that a milestone will be created
      this.checkpointFrequency = 5;
      this.nextRevisionNum = 0;
      this.initialized;
      
      // A queue of pending local operations
      this.pendingOp = [];
      
      this.otuser = otuser;
      
      // Create some scopes that we'll use as event busses.
      this.$events = $rootScope.$new();
      this.$localEvents = this.$events.$new();
      this.$remoteEvents = this.$events.$new();
      
      this.$localEvents.$on("textInsert", function (e, op) {
        
      });
    }
    
    OTText.prototype = {
      _read: function(size) {
        
      },
      _write: function(chunk, encoding, next) {
        // Take events coming from the local instance and queue them for Firebase
        this.queueOperation(chunk);
        
        // No need to defer the next call... buffer events in ottext
        next(); 
      },
      init: function() {
        console.log("ottext:init");
        var ottext = this;
        
        return ottext.initialized = ottext.initialized || this.getRemoteState().then(function (state) {
          console.log("ottext:init:inited", state);
          ottext.nextRevisionNum = revisionFromId(state.nextRevId);
          ottext.val = state.val;
          
          ottext.revisionsRef.startAt(null, revisionToId(ottext.nextRevisionNum)).on("child_added", function (snapshot) {
            console.log("ottext:init:revision", snapshot.name(), snapshot.val(), ottext.isLocalEvent);
            
            $rootScope.$evalAsync(ottext.flushPendingOp.bind(ottext));
            
            // Ignore events originating locally
            if (ottext.isLocalEvent)  {
              // Record milestone
              if (ottext.nextRevisionNum > 0 && 0 === (ottext.nextRevisionNum % ottext.checkpointFrequency)) {
                ottext.checkpointRef.set({
                  val: ottext.val,
                  revId: revisionToId(ottext.nextRevisionNum - 1)
                })
              }
            } else {
              var revision = snapshot.val();
              
              // Update local state
              ottext.nextRevisionNum++;
              ottext.val = textops.apply(ottext.val, revision.op);
              
              // Push the operation to the Duplex stream
              ottext.push({
                type: "text",
                val: ottext.val,
                rev: ottext.nextRevisionNum - 1,
                source: ottext.id,
                op: revision.op
              });
            }
          });
        });;
      },
      
      /**
       * Record an operation in the oplog
       * 
       * To apply an operation, we need to make sure that no remote operation
       * was applied at the same time at the same refId. To prevent this, we use
       * Firebase's transaction support to prevent adding the op if this
       * happened. If that happened, we need to queue the operation for later.
       */
      applyOperation: function (operation) {
        console.log("ottext:applyOperation", operation);
        
        var ottext = this
          , revisionsRef = this.revisionsRef;
        
        return ottext.init().then(function () {
          console.log("ottext:applyOperation:inited");
          
          var dfd = $q.defer()
            , revId = revisionToId(ottext.nextRevisionNum)
            , revision = {op: operation};
          
          // Use Firebase transactions to make sure that the operation does not
          // overwrite the operation added by a different user
          ottext.isLocalEvent = true;
          revisionsRef.child(revId).transaction(function (current) {
            console.log("ottext:applyOperation:transaction", current);
            // Only apply the revision if no one else has created it
            if (null === current) {
              return revision;
            }
          }, function (err, committed, snapshot) {
            console.log("ottext:applyOperation:transaction:completed", err, committed, snapshot.val());
            
            if (committed) {
              ottext.nextRevisionNum++;
              ottext.val = textops.apply(ottext.val, operation);
              
              for (var i = 0; i < operation.length; i++) {
                var component = operation[i];
                
                if (component.i) {
                  ottext.$localEvents.$emit("textInsert", ottext.val, ottext.nextRevisionNum - 1, [component]);
                } else if (component.d) {
                  ottext.$localEvents.$emit("textRemove", ottext.val, ottext.nextRevisionNum - 1, [component]);
                }
              }
              
              ottext.$localEvents.$emit("textChange", ottext.val, ottext.nextRevisionNum - 1, operation);
              
              dfd.resolve(ottext.nextRevisionNum - 1);
            } else {
              dfd.reject(ottext.nextRevisionNum - 1);
            }
            
            ottext.isLocalEvent = false;
            
          }, false);
          
          return dfd.promise;
        });
      },
      
      queueOperation: function(operation) {
        console.log("ottext:queueOperation", operation);
        
        var ottext = this;
        
        // We use angular.copy to ensure referential integrity for the pending
        // operations queue
        angular.copy(this.ot.compose(this.pendingOp, operation), this.pendingOp);
        
        
        this.pendingOpDfd = this.pendingOpDfd || $q.defer();
        
        // Request that the operation queue be flushed on nextTick
        $rootScope.$evalAsync(ottext.flushPendingOp.bind(ottext));
        
        return this.pendingOpDfd.promise;
      },
      
      /**
       * Flush the pending operation to the server
       */
      flushPendingOp: function () {
        console.log("ottext:flushPendingOp", this.isFlushing, this.pendingOp);
        
        var ottext = this
          , revisionsRef = this.revisionsRef
          , dfd = $q.defer();
        
        if (!ottext.pendingOp.length) {
          console.log("ottext:flushPendingOp:empty");
          
          return $q.when(ottext.nextRevisionNum);
        }

        return $q.all(ottext.init(), ottext.flushing).then(function() {
          console.log("ottext:flushPendingOp:inited");
          
          if (!ottext.pendingOp.length) {
            return true
          }
          
          ottext.inFlightOp = angular.copy(ottext.pendingOp);
          ottext.pendingOp.length = 0;
          
          ottext.flushing = ottext.applyOperation(ottext.inFlightOp).then(function(revNum) {
            console.log("ottext:flushPendingOp:applyOperation");
            if (ottext.pendingOpDfd) {
              ottext.pendingOpDfd.resolve(revNum);
              ottext.pendingOpDfd = null;
            }
            
            ottext.pendingOp.length = 0;
            
            return revNum;
          });
          
          ottext.flushing.finally(function() {
            ottext.flushing = null;
          });
          
          return ottext.flushing;
        });
      },
      
      getVersionId: function() {
        return this.getVersionNum(function(revNum) {
          return revisionToId(revNum);
        })
      },
      
      getVersionNum: function() {
        var ottext = this;
        
        return this.init().then(function() {
          return ottext.nextRevisionNum - 1;
        });
      },
      
      /**
       * Get a snapshot object of the current state
       * 
       * @returns Promise that resolves to the value of the file
       */
      getRemoteState: function() {
        var ot = this.ot
          , ottext = this;
          
        var revisionsRef = this.revisionsRef
          , checkpointRef = this.checkpointRef;
        
        return getFirebaseValue(checkpointRef).then(function(checkpoint){
          var nextRevision = 0
            , remoteVal = ot.create()
            , trailingRevisionsRef = revisionsRef;
          
          // A checkpoint was saved
          if (checkpoint && checkpoint.val && checkpoint.opId) {
            nextRevision = revisionFromId(checkpoint.opId) + 1;
            remoteVal = checkpoint.val;

            // Filter the trailing revisions to those after the last checkpoint
            trailingRevisionsRef = trailingRevisionsRef.startAt(null, revisionToId(nextRevision));
          }

          return getFirebaseValue(trailingRevisionsRef).then(function (revisions) {
            console.log("revisions", revisions);
            
            _.each(revisions, function(revision, revId) {
              remoteVal = ot.apply(remoteVal, revision.op);
              nextRevision++;
            });
            
            return {
              nextRevId: revisionToId(nextRevision),
              val: remoteVal
            };
          });
        });
      }
    };
    
    function getFirebaseSnapshot(ref) {
      dfd = $q.defer();
      
      ref.once("value", dfd.resolve);
      
      return dfd.promise;
    }
    
    function getFirebaseValue(ref) {
      return getFirebaseSnapshot(ref).then(function (snapshot) {
        return snapshot.val();
      });
    }

    /**
     * Source: https://github.com/firebase/firepad/blob/master/lib/firebase-adapter.js
     * 
     */
    var characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    function revisionToId(revision) {
      if (revision === 0) {
        return 'A0';
      }
  
      var str = '';
      while (revision > 0) {
        var digit = (revision % characters.length);
        str = characters[digit] + str;
        revision -= digit;
        revision /= characters.length;
      }
  
      // Prefix with length (starting at 'A' for length 1) to ensure the id's sort lexicographically.
      var prefix = characters[str.length + 9];
      return prefix + str;
    }
  
    function revisionFromId(revisionId) {
      var revision = 0;
      for(var i = 1; i < revisionId.length; i++) {
        revision *= characters.length;
        revision += characters.indexOf(revisionId[i]);
      }
      return revision;
    }

    return {
      createStream: function(path, options){
        options || (options = {
          
        })
      },
      connect: function (fileId) {
        return new OTText(new Firebase("https://cocode.firebaseio.com/" + fileId));
      }
    };
  }]
);
.file-tree .file-tree {
  margin-left: 1em;
}

.tree-bg {
  position: absolute;
  top: 0; right: 0; left: -999px;
  height: 26px;
}

.tree-entry {
  .tree-bg {
    position: absolute;
    top: 0;
    right: 0;
    left: -999px;
    height: 26px;
    pointer-events: none;
    z-index: -1;
  }
  
  .tree-inner {
    line-height: 26px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    
    .tree-icon {
      line-height: 22px;
    }
    
    .tree-actions {
      visibility: hidden;
      position: absolute;
      top: -1px;
      right: 0;
      
      .btn > .fa {
        font-size: 1.3em;
        line-height: 1.3em;
      }
    }
    
    &:hover {
      cursor: pointer;
      
      .tree-actions {
        visibility: visible;
      }
    }
  }
  
  .tree-inner:hover ~ .tree-bg {
    background-color: #eeeeee;
  }
  
  &.open {
    & > .tree-inner {
      font-weight: bold;
    }
  }
  
  &.active {
    & > .tree-bg {
      background-color: #0088cc;
    }
    
    & > .tree-inner {
      color: #f5f5f5;
    }
    
    &:hover > .tree-bg {
      background-color: #277ba5;
    }
  }
  
  &.collapsed>.file-tree {
    display: none;
  }
}