function tokenize(input, callback) {

while(input.length > 0) {
  callback(input.match(/^[\/\.\?]|[^\/\.\?]+/)[0]);
  input = input.replace(/^[\/\.\?]|[^\/\.\?]+/, '');
}

}

var graph = d3.select(“#chart-2 svg”); var svg_edges = {}; var svg_nodes = {};

graph.selectAll(“g.edge”).each(function() {

var node  = d3.select(this);
var index = node.select("title").text().split("->");
var left  = parseInt(index[0]);
var right = parseInt(index[1]);

if(!svg_edges[left]) { svg_edges[left] = {} }
svg_edges[left][right] = node;

});

graph.selectAll(“g.node”).each(function() {

var node  = d3.select(this);
var index = parseInt(node.select("title").text());
svg_nodes[index] = node;

});

function reset_graph() {

for(var key in svg_edges) {
  for(var mkey in svg_edges[key]) {
    var node = svg_edges[key][mkey];
    var path = node.select("path");
    var arrow = node.select("polygon");
    path.style("stroke", "black");
    arrow.style("stroke", "black").style("fill", "black");
  }
}

for(var key in svg_nodes) {
  var node = svg_nodes[key];
  node.select('ellipse').style("fill", "white");
  node.select('polygon').style("fill", "white");
}
return false;

}

function highlight_edge(from, to) {

var node = svg_edges[from][to];
var path = node.select("path");
var arrow = node.select("polygon");

path
  .transition().duration(500)
  .style("stroke", "green");

arrow
  .transition().duration(500)
  .style("stroke", "green").style("fill", "green");

}

function highlight_state(index, color) {

if(!color) { color = "green"; }

svg_nodes[index].select('ellipse')
  .style("fill", "white")
  .transition().duration(500)
  .style("fill", color);

}

function highlight_finish(index) {

svg_nodes[index].select('ellipse')
  .style("fill", "while")
  .transition().duration(500)
  .style("fill", "blue");

}

function match(input) {

reset_graph();
var table           = tt();
var states          = [[0, null]];
var regexp_states   = table['regexp_states'];
var string_states   = table['string_states'];
var stdparam_states = table['stdparam_states'];
var accepting       = table['accepting'];
var default_re      = new RegExp("^[^.\/?]+$");
var start_index     = 0;

highlight_state(0);

tokenize(input, function(token) {
  var end_index = start_index + token.length;

  var new_states = [];
  for(var key in states) {
    var state_parts = states[key];
    var state = state_parts[0];
    var previous_start = state_parts[1];

    if(previous_start == null) {
      if(string_states[state] && string_states[state][token]) {
        var new_state = string_states[state][token];
        highlight_edge(state, new_state);
        highlight_state(new_state);
        new_states.push([new_state, null]);
      }

      if(stdparam_states[state] && default_re.test(token)) {
        for(var key in stdparam_states[state]) {
          var new_state = stdparam_states[state][key];
          highlight_edge(state, new_state);
          highlight_state(new_state);
          new_states.push([new_state, null]);
        }
      }
    }

    if(regexp_states[state]) {
      var slice_start = previous_start != null ? previous_start : start_index;

      for(var key in regexp_states[state]) {
        var re = new RegExp("^" + key + "$");

        var accumulation = input.slice(slice_start, end_index);

        if(re.test(accumulation)) {
          var new_state = regexp_states[state][key];
          highlight_edge(state, new_state);
          highlight_state(new_state);
          new_states.push([new_state, null]);
        }

        // retry the same regexp with the accumulated data either way
        new_states.push([state, slice_start]);
      }
    }
  }

  states = new_states;
  start_index = end_index;
});

for(var key in states) {
  var state_parts = states[key];
  var state = state_parts[0];
  var slice_start = state_parts[1];

  // we must ignore ones that are still accepting more data
  if (slice_start != null) continue;

  if(accepting[state]) {
    highlight_finish(state);
  } else {
    highlight_state(state, "red");
  }
}

return false;

}