Showing Voronoi with TubeMaps
Last active
April 30, 2023 13:50
-
-
Save nicola/10e25b18aca0bc05b192 to your computer and use it in GitHub Desktop.
London Tube Map with Voronoi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>Graph map</title> | |
<meta charset="utf-8" /> | |
<style type="text/css"> | |
svg { | |
font: 10px sans-serif; | |
} | |
.axis path, .axis line { | |
fill: none; | |
stroke: #666; | |
stroke-width: 0.3; | |
} | |
path { | |
stroke: #fff; | |
} | |
circle.station { | |
stroke-width: 0.5; | |
} | |
.voronoi path { | |
stroke: #aaa; | |
} | |
circle { | |
fill: #000; | |
pointer-events: none; | |
} | |
.q0-9 { fill: #d3d3d3;} | |
.q1-9 { fill: #efefef; } | |
.q2-9 { fill: #fefefe; } | |
.q3-9 { fill: #eeeeee; } | |
.q4-9 { fill: #dedede; } | |
.q5-9 { fill: #d0d0d0; } | |
.q6-9 { fill: #eaeaea; } | |
.q7-9 { fill: #e9e9e9; } | |
.q8-9 { fill: #f2f2f2; } | |
</style> | |
</head> | |
<body> | |
<div id="map"></div> | |
<script type="text/javascript" src="tubemaps.js"></script> | |
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.js"></script> | |
<script type="text/javascript"> | |
function polygon(d) { | |
return "M" + d.join("L") + "Z"; | |
} | |
var margin = {top: 20, right: 20, bottom: 30, left: 40}, | |
w = Math.max(760, window.innerWidth) - margin.left - margin.right, | |
h = Math.max(500, window.innerHeight) - margin.top - margin.bottom; | |
var voronoi = d3.geom.voronoi() | |
.clipExtent([[0, 0], [w, h]]); | |
d3.json('london.json', function(data) { | |
data.stations.forEach(function(d) { | |
d.longitude = +d.longitude; | |
d.latitude = +d.latitude; | |
}) | |
var tube = new TubeMaps.TubeMap(data) | |
// Find min and max long and lat | |
var minLat = d3.min(tube.stations, function(d) {return d.latitude}); | |
var minLon = d3.min(tube.stations, function(d) {return d.longitude}); | |
var maxLat = d3.max(tube.stations, function(d) {return d.latitude}); | |
var maxLon = d3.max(tube.stations, function(d) {return d.longitude}); | |
// Set up the scales | |
var x = d3.scale.linear() | |
.domain([minLon, maxLon]) | |
.range([0, w]); | |
var y = d3.scale.linear() | |
.domain([minLat, maxLat]) | |
.range([h, 0]); | |
// Set up the axis | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom") | |
.tickSize(-h); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left") | |
.ticks(5) | |
.tickSize(-w); | |
// Set up what will happen when zooming | |
var zoom = d3.behavior.zoom() | |
.x(x) | |
.y(y) | |
.scaleExtent([1, 10]) | |
.on("zoom", zoomed); | |
/* | |
Drawing from now on | |
*/ | |
// Setting up the canvas | |
var vis = d3.select("#map").append("svg") | |
.attr("width", w + margin.left + margin.right) | |
.attr("height", h + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") | |
// Make sure it is zoomable | |
d3.select("#map svg") | |
.call(zoom); | |
var vornoitube = vis.append("g") | |
.attr('class','voronoi') | |
.selectAll("path") | |
.data(voronoi(tube.stations.map(function(d) { | |
return [x(d.longitude), y(d.latitude)] | |
})), polygon); | |
vornoitube.exit().remove(); | |
vornoitube.enter().append("path") | |
.attr("class", function(d, i) { return "q" + (i % 9) + "-9"; }) | |
.attr("d", polygon); | |
vornoitube.order(); | |
// Drawing lines between stations | |
var route = vis.selectAll("line.route") | |
.data(tube.connections) | |
.enter().append("svg:line") | |
.attr("class", "route") | |
.attr("stroke", function(d) { return '#'+tube.linesById[d.line].colour; }) | |
.attr("stroke-linecap", 'round') | |
.attr("x1", function(d) { return x(d.station1.longitude); }) | |
.attr("y1", function(d) { return y(d.station1.latitude); }) | |
.attr("x2", function(d) { return x(d.station2.longitude); }) | |
.attr("y2", function(d) { return y(d.station2.latitude); }) | |
.style("opacity", 1) | |
// Striped stations (see official map) | |
var stripe = vis.selectAll("line.stripe") | |
.data(tube.connections.filter(function(d) { | |
return tube.linesById[d.line].stripe != "NULL"; | |
})) | |
.enter().append("svg:line") | |
.attr("class", "stripe") | |
.attr("stroke", function(d) { return '#'+tube.linesById[d.line].stripe; }) | |
.attr("stroke-linecap", 'round') | |
.attr("x1", function(d) { return x(d.station1.longitude); }) | |
.attr("y1", function(d) { return y(d.station1.latitude); }) | |
.attr("x2", function(d) { return x(d.station2.longitude); }) | |
.attr("y2", function(d) { return y(d.station2.latitude); }) | |
// Drawing all the stations | |
var station = vis.selectAll("circle.station") | |
.data(tube.stations) | |
.enter().append("svg:circle") | |
.attr("class", "station") | |
.attr("id", function(d) { return 'station'+d.id }) | |
.attr("cx", function(d) { return x(d.longitude); }) | |
.attr("cy", function(d) { return y(d.latitude); }) | |
.attr("data-cx", function(d) { return d.longitude; }) | |
.attr("data-cy", function(d) { return d.latitude; }) | |
.attr("title", function(d) { return d.name }) | |
.style("stroke", 'gray') | |
.style("fill", '#ffffff') | |
.style("opacity", 1) | |
.on('mouseover', function(d,i) { | |
d3.selectAll('#station'+d.id) | |
.transition() | |
.duration(25) | |
.attr("r", 3 / zoom.scale()) | |
.style("stroke", 'black') | |
.style("stroke-width", 1 / zoom.scale()) | |
.style('opacity', 1); | |
}) | |
.on('mouseout', function(d,i) { | |
d3.selectAll('#station'+d.id) | |
.transition() | |
.attr("r", 2.5 / zoom.scale()) | |
.duration(25) | |
.style("stroke-width", 0.5 / zoom.scale()) | |
.style("stroke", 'gray') | |
.style('opacity', 0.3); | |
}) | |
// Adding axis | |
vis.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + h + ")") | |
.call(xAxis); | |
vis.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
zoomed() | |
function zoomed() { | |
// Reset axis | |
vis.select(".x.axis").call(xAxis); | |
vis.select(".y.axis").call(yAxis); | |
vis.selectAll("line.route") | |
.attr("stroke-width", 1 / (zoom.scale())) | |
vis.selectAll("line.stripe") | |
.attr("stroke-width", 0.5 / (zoom.scale())) | |
// Rescale circles | |
vis.selectAll("circle") | |
.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")") | |
.style("stroke-width", 1 / zoom.scale()) | |
.attr("r", 2.5 / zoom.scale()); | |
// Rescale lines | |
vis.selectAll("line.route, line.stripe") | |
.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")") | |
// Voronoi scale | |
vis.selectAll("g.voronoi") | |
.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")") | |
vis.selectAll(".voronoi path") | |
.attr("stroke-width", 0.5 / (zoom.scale())) | |
} | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"connections":[{"station1":"11","station2":"163","line":"1","time":"1"},{"station1":"11","station2":"212","line":"1","time":"2"},{"station1":"49","station2":"87","line":"1","time":"1"},{"station1":"49","station2":"197","line":"1","time":"2"},{"station1":"82","station2":"163","line":"1","time":"2"},{"station1":"82","station2":"193","line":"1","time":"3"},{"station1":"84","station2":"148","line":"1","time":"3"},{"station1":"87","station2":"279","line":"1","time":"2"},{"station1":"113","station2":"246","line":"1","time":"2"},{"station1":"113","station2":"298","line":"1","time":"2"},{"station1":"114","station2":"140","line":"1","time":"2"},{"station1":"137","station2":"206","line":"1","time":"3"},{"station1":"137","station2":"298","line":"1","time":"3"},{"station1":"140","station2":"237","line":"1","time":"2"},{"station1":"143","station2":"159","line":"1","time":"2"},{"station1":"143","station2":"206","line":"1","time":"2"},{"station1":"148","station2":"279","line":"1","time":"1"},{"station1":"159","station2":"278","line":"1","time":"1"},{"station1":"185","station2":"237","line":"1","time":"2"},{"station1":"185","station2":"281","line":"1","time":"2"},{"station1":"192","station2":"197","line":"1","time":"2"},{"station1":"192","station2":"212","line":"1","time":"2"},{"station1":"193","station2":"278","line":"1","time":"2"},{"station1":"246","station2":"281","line":"1","time":"3"},{"station1":"13","station2":"156","line":"2","time":"2"},{"station1":"13","station2":"250","line":"2","time":"2"},{"station1":"16","station2":"91","line":"2","time":"2"},{"station1":"16","station2":"173","line":"2","time":"2"},{"station1":"24","station2":"156","line":"2","time":"3"},{"station1":"24","station2":"164","line":"2","time":"2"},{"station1":"28","station2":"162","line":"2","time":"1"},{"station1":"28","station2":"192","line":"2","time":"1"},{"station1":"37","station2":"158","line":"2","time":"3"},{"station1":"37","station2":"301","line":"2","time":"3"},{"station1":"48","station2":"126","line":"2","time":"1"},{"station1":"48","station2":"250","line":"2","time":"2"},{"station1":"51","station2":"103","line":"2","time":"2"},{"station1":"51","station2":"215","line":"2","time":"2"},{"station1":"68","station2":"158","line":"2","time":"2"},{"station1":"68","station2":"256","line":"2","time":"3"},{"station1":"72","station2":"286","line":"2","time":"3"},{"station1":"76","station2":"181","line":"2","time":"2"},{"station1":"76","station2":"296","line":"2","time":"3"},{"station1":"88","station2":"256","line":"2","time":"2"},{"station1":"91","station2":"109","line":"2","time":"2"},{"station1":"98","station2":"173","line":"2","time":"3"},{"station1":"98","station2":"211","line":"2","time":"2"},{"station1":"103","station2":"109","line":"2","time":"3"},{"station1":"105","station2":"177","line":"2","time":"2"},{"station1":"105","station2":"196","line":"2","time":"2"},{"station1":"112","station2":"181","line":"2","time":"3"},{"station1":"112","station2":"196","line":"2","time":"2"},{"station1":"126","station2":"259","line":"2","time":"2"},{"station1":"127","station2":"186","line":"2","time":"2"},{"station1":"127","station2":"226","line":"2","time":"1"},{"station1":"149","station2":"162","line":"2","time":"3"},{"station1":"149","station2":"208","line":"2","time":"1"},{"station1":"153","station2":"154","line":"2","time":"3"},{"station1":"153","station2":"247","line":"2","time":"2"},{"station1":"154","station2":"230","line":"2","time":"2"},{"station1":"154","station2":"275","line":"2","time":"2"},{"station1":"164","station2":"247","line":"2","time":"4"},{"station1":"177","station2":"239","line":"2","time":"3"},{"station1":"181","station2":"286","line":"2","time":"2"},{"station1":"186","station2":"208","line":"2","time":"2"},{"station1":"192","station2":"259","line":"2","time":"2"},{"station1":"211","station2":"275","line":"2","time":"2"},{"station1":"215","station2":"301","line":"2","time":"3"},{"station1":"221","station2":"239","line":"2","time":"1"},{"station1":"221","station2":"294","line":"2","time":"3"},{"station1":"226","station2":"296","line":"2","time":"3"},{"station1":"230","station2":"241","line":"2","time":"2"},{"station1":"241","station2":"301","line":"2","time":"2"},{"station1":"2","station2":"156","line":"3","time":"2"},{"station1":"2","station2":"263","line":"3","time":"4"},{"station1":"11","station2":"83","line":"3","time":"3"},{"station1":"11","station2":"104","line":"3","time":"3"},{"station1":"14","station2":"92","line":"3","time":"1"},{"station1":"14","station2":"167","line":"3","time":"2"},{"station1":"18","station2":"186","line":"3","time":"2"},{"station1":"18","station2":"193","line":"3","time":"2"},{"station1":"25","station2":"161","line":"3","time":"1"},{"station1":"25","station2":"255","line":"3","time":"2"},{"station1":"44","station2":"161","line":"3","time":"1"},{"station1":"44","station2":"166","line":"3","time":"2"},{"station1":"83","station2":"193","line":"3","time":"3"},{"station1":"87","station2":"255","line":"3","time":"2"},{"station1":"87","station2":"285","line":"3","time":"2"},{"station1":"90","station2":"104","line":"3","time":"2"},{"station1":"90","station2":"145","line":"3","time":"2"},{"station1":"92","station2":"145","line":"3","time":"4"},{"station1":"99","station2":"122","line":"3","time":"4"},{"station1":"99","station2":"236","line":"3","time":"1"},{"station1":"122","station2":"186","line":"3","time":"3"},{"station1":"156","station2":"167","line":"3","time":"2"},{"station1":"166","station2":"263","line":"3","time":"2"},{"station1":"229","station2":"236","line":"3","time":"2"},{"station1":"229","station2":"273","line":"3","time":"2"},{"station1":"248","station2":"273","line":"3","time":"2"},{"station1":"248","station2":"285","line":"3","time":"2"},{"station1":"3","station2":"263","line":"4","time":"2"},{"station1":"3","station2":"295","line":"4","time":"2"},{"station1":"15","station2":"78","line":"4","time":"4"},{"station1":"15","station2":"269","line":"4","time":"2"},{"station1":"17","station2":"110","line":"4","time":"1"},{"station1":"17","station2":"293","line":"4","time":"2"},{"station1":"18","station2":"186","line":"4","time":"2"},{"station1":"18","station2":"193","line":"4","time":"2"},{"station1":"21","station2":"67","line":"4","time":"3"},{"station1":"21","station2":"269","line":"4","time":"2"},{"station1":"25","station2":"161","line":"4","time":"1"},{"station1":"25","station2":"255","line":"4","time":"2"},{"station1":"33","station2":"36","line":"4","time":"2"},{"station1":"33","station2":"164","line":"4","time":"1"},{"station1":"36","station2":"289","line":"4","time":"2"},{"station1":"44","station2":"161","line":"4","time":"1"},{"station1":"44","station2":"166","line":"4","time":"2"},{"station1":"52","station2":"1","line":"4","time":"2"},{"station1":"52","station2":"265","line":"4","time":"2"},{"station1":"66","station2":"67","line":"4","time":"4"},{"station1":"66","station2":"85","line":"4","time":"3"},{"station1":"72","station2":"73","line":"4","time":"4"},{"station1":"73","station2":"1","line":"4","time":"2"},{"station1":"74","station2":"99","line":"4","time":"3"},{"station1":"74","station2":"122","line":"4","time":"3"},{"station1":"74","station2":"138","line":"4","time":"2"},{"station1":"74","station2":"287","line":"4","time":"2"},{"station1":"74","station2":"293","line":"4","time":"1"},{"station1":"78","station2":"270","line":"4","time":"2"},{"station1":"80","station2":"205","line":"4","time":"2"},{"station1":"80","station2":"231","line":"4","time":"2"},{"station1":"83","station2":"193","line":"4","time":"3"},{"station1":"85","station2":"129","line":"4","time":"2"},{"station1":"87","station2":"255","line":"4","time":"2"},{"station1":"87","station2":"285","line":"4","time":"2"},{"station1":"96","station2":"195","line":"4","time":"2"},{"station1":"96","station2":"287","line":"4","time":"1"},{"station1":"99","station2":"236","line":"4","time":"1"},{"station1":"108","station2":"141","line":"4","time":"3"},{"station1":"108","station2":"265","line":"4","time":"3"},{"station1":"110","station2":"209","line":"4","time":"2"},{"station1":"122","station2":"186","line":"4","time":"3"},{"station1":"129","station2":"268","line":"4","time":"2"},{"station1":"141","station2":"213","line":"4","time":"3"},{"station1":"164","station2":"244","line":"4","time":"2"},{"station1":"166","station2":"263","line":"4","time":"2"},{"station1":"195","station2":"205","line":"4","time":"3"},{"station1":"200","station2":"270","line":"4","time":"2"},{"station1":"200","station2":"289","line":"4","time":"2"},{"station1":"209","station2":"242","line":"4","time":"2"},{"station1":"229","station2":"236","line":"4","time":"2"},{"station1":"229","station2":"273","line":"4","time":"2"},{"station1":"231","station2":"300","line":"4","time":"3"},{"station1":"242","station2":"265","line":"4","time":"1"},{"station1":"244","station2":"295","line":"4","time":"3"},{"station1":"248","station2":"273","line":"4","time":"2"},{"station1":"248","station2":"285","line":"4","time":"2"},{"station1":"267","station2":"268","line":"4","time":"3"},{"station1":"299","station2":"300","line":"4","time":"3"},{"station1":"4","station2":"70","line":"13","time":"2"},{"station1":"4","station2":"201","line":"13","time":"2"},{"station1":"13","station2":"225","line":"13","time":"2"},{"station1":"19","station2":"97","line":"13","time":"2"},{"station1":"20","station2":"65","line":"13","time":"2"},{"station1":"20","station2":"217","line":"13","time":"2"},{"station1":"27","station2":"79","line":"13","time":"2"},{"station1":"27","station2":"201","line":"13","time":"2"},{"station1":"32","station2":"70","line":"13","time":"2"},{"station1":"32","station2":"204","line":"13","time":"2"},{"station1":"42","station2":"120","line":"13","time":"2"},{"station1":"42","station2":"292","line":"13","time":"2"},{"station1":"43","station2":"79","line":"13","time":"2"},{"station1":"43","station2":"219","line":"13","time":"2"},{"station1":"61","station2":"171","line":"13","time":"2"},{"station1":"61","station2":"238","line":"13","time":"2"},{"station1":"63","station2":"203","line":"13","time":"2"},{"station1":"63","station2":"219","line":"13","time":"2"},{"station1":"64","station2":"106","line":"13","time":"2"},{"station1":"64","station2":"135","line":"13","time":"2"},{"station1":"65","station2":"97","line":"13","time":"2"},{"station1":"69","station2":"86","line":"13","time":"2"},{"station1":"69","station2":"106","line":"13","time":"2"},{"station1":"86","station2":"152","line":"13","time":"2"},{"station1":"120","station2":"238","line":"13","time":"2"},{"station1":"135","station2":"171","line":"13","time":"2"},{"station1":"155","station2":"225","line":"13","time":"2"},{"station1":"155","station2":"284","line":"13","time":"2"},{"station1":"201","station2":"284","line":"13","time":"2"},{"station1":"201","station2":"292","line":"13","time":"2"},{"station1":"203","station2":"217","line":"13","time":"2"},{"station1":"204","station2":"247","line":"13","time":"2"},{"station1":"225","station2":"262","line":"13","time":"2"},{"station1":"284","station2":"292","line":"13","time":"2"},{"station1":"41","station2":"216","line":"5","time":"1"},{"station1":"41","station2":"253","line":"5","time":"2"},{"station1":"174","station2":"253","line":"5","time":"4"},{"station1":"175","station2":"253","line":"5","time":"4"},{"station1":"216","station2":"276","line":"5","time":"1"},{"station1":"225","station2":"276","line":"5","time":"1"},{"station1":"225","station2":"295","line":"5","time":"2"},{"station1":"228","station2":"295","line":"5","time":"2"},{"station1":"3","station2":"156","line":"6","time":"4"},{"station1":"3","station2":"295","line":"6","time":"2"},{"station1":"11","station2":"83","line":"6","time":"3"},{"station1":"11","station2":"104","line":"6","time":"3"},{"station1":"14","station2":"92","line":"6","time":"1"},{"station1":"14","station2":"167","line":"6","time":"2"},{"station1":"15","station2":"78","line":"6","time":"4"},{"station1":"33","station2":"36","line":"6","time":"2"},{"station1":"33","station2":"164","line":"6","time":"1"},{"station1":"36","station2":"289","line":"6","time":"2"},{"station1":"78","station2":"270","line":"6","time":"2"},{"station1":"83","station2":"193","line":"6","time":"4"},{"station1":"90","station2":"104","line":"6","time":"2"},{"station1":"90","station2":"145","line":"6","time":"2"},{"station1":"92","station2":"145","line":"6","time":"4"},{"station1":"101","station2":"110","line":"6","time":"2"},{"station1":"101","station2":"227","line":"6","time":"1"},{"station1":"147","station2":"150","line":"6","time":"1"},{"station1":"147","station2":"283","line":"6","time":"2"},{"station1":"150","station2":"227","line":"6","time":"2"},{"station1":"156","station2":"167","line":"6","time":"2"},{"station1":"164","station2":"244","line":"6","time":"2"},{"station1":"193","station2":"218","line":"6","time":"1"},{"station1":"200","station2":"270","line":"6","time":"2"},{"station1":"200","station2":"289","line":"6","time":"2"},{"station1":"218","station2":"283","line":"6","time":"2"},{"station1":"244","station2":"295","line":"6","time":"3"},{"station1":"11","station2":"28","line":"7","time":"2"},{"station1":"11","station2":"249","line":"7","time":"4"},{"station1":"23","station2":"41","line":"7","time":"2"},{"station1":"23","station2":"157","line":"7","time":"3"},{"station1":"28","station2":"107","line":"7","time":"2"},{"station1":"41","station2":"42","line":"7","time":"3"},{"station1":"42","station2":"183","line":"7","time":"3"},{"station1":"43","station2":"183","line":"7","time":"3"},{"station1":"43","station2":"289","line":"7","time":"3"},{"station1":"45","station2":"207","line":"7","time":"2"},{"station1":"45","station2":"243","line":"7","time":"2"},{"station1":"71","station2":"172","line":"7","time":"2"},{"station1":"71","station2":"297","line":"7","time":"2"},{"station1":"94","station2":"254","line":"7","time":"2"},{"station1":"94","station2":"290","line":"7","time":"1"},{"station1":"107","station2":"285","line":"7","time":"3"},{"station1":"142","station2":"290","line":"7","time":"2"},{"station1":"142","station2":"297","line":"7","time":"2"},{"station1":"144","station2":"207","line":"7","time":"2"},{"station1":"144","station2":"282","line":"7","time":"4"},{"station1":"157","station2":"233","line":"7","time":"2"},{"station1":"172","station2":"282","line":"7","time":"4"},{"station1":"233","station2":"279","line":"7","time":"1"},{"station1":"247","station2":"289","line":"7","time":"3"},{"station1":"249","station2":"254","line":"7","time":"1"},{"station1":"279","station2":"285","line":"7","time":"2"},{"station1":"2","station2":"156","line":"8","time":"2"},{"station1":"6","station2":"46","line":"8","time":"4"},{"station1":"11","station2":"94","line":"8","time":"6"},{"station1":"11","station2":"104","line":"8","time":"3"},{"station1":"14","station2":"92","line":"8","time":"1"},{"station1":"14","station2":"167","line":"8","time":"2"},{"station1":"46","station2":"50","line":"8","time":"8"},{"station1":"46","station2":"53","line":"8","time":"4"},{"station1":"53","station2":"214","line":"8","time":"4"},{"station1":"62","station2":"168","line":"8","time":"4"},{"station1":"62","station2":"280","line":"8","time":"3"},{"station1":"75","station2":"210","line":"8","time":"2"},{"station1":"75","station2":"222","line":"8","time":"2"},{"station1":"90","station2":"104","line":"8","time":"2"},{"station1":"90","station2":"145","line":"8","time":"2"},{"station1":"92","station2":"145","line":"8","time":"4"},{"station1":"94","station2":"282","line":"8","time":"7"},{"station1":"115","station2":"178","line":"8","time":"2"},{"station1":"115","station2":"184","line":"8","time":"3"},{"station1":"115","station2":"291","line":"8","time":"2"},{"station1":"125","station2":"134","line":"8","time":"2"},{"station1":"125","station2":"271","line":"8","time":"3"},{"station1":"134","station2":"220","line":"8","time":"3"},{"station1":"156","station2":"167","line":"8","time":"2"},{"station1":"168","station2":"179","line":"8","time":"3"},{"station1":"168","station2":"214","line":"8","time":"4"},{"station1":"178","station2":"202","line":"8","time":"2"},{"station1":"179","station2":"180","line":"8","time":"2"},{"station1":"180","station2":"199","line":"8","time":"2"},{"station1":"184","station2":"199","line":"8","time":"3"},{"station1":"202","station2":"282","line":"8","time":"3"},{"station1":"210","station2":"291","line":"8","time":"3"},{"station1":"220","station2":"222","line":"8","time":"2"},{"station1":"7","station2":"145","line":"9","time":"2"},{"station1":"7","station2":"188","line":"9","time":"3"},{"station1":"8","station2":"124","line":"9","time":"3"},{"station1":"8","station2":"264","line":"9","time":"2"},{"station1":"12","station2":"56","line":"9","time":"2"},{"station1":"12","station2":"257","line":"9","time":"1"},{"station1":"13","station2":"157","line":"9","time":"2"},{"station1":"13","station2":"167","line":"9","time":"3"},{"station1":"22","station2":"47","line":"9","time":"2"},{"station1":"22","station2":"111","line":"9","time":"2"},{"station1":"29","station2":"84","line":"9","time":"1"},{"station1":"29","station2":"157","line":"9","time":"2"},{"station1":"34","station2":"100","line":"9","time":"3"},{"station1":"34","station2":"119","line":"9","time":"2"},{"station1":"38","station2":"58","line":"9","time":"2"},{"station1":"38","station2":"81","line":"9","time":"3"},{"station1":"40","station2":"47","line":"9","time":"2"},{"station1":"40","station2":"89","line":"9","time":"3"},{"station1":"40","station2":"139","line":"9","time":"2"},{"station1":"40","station2":"170","line":"9","time":"1"},{"station1":"49","station2":"87","line":"9","time":"1"},{"station1":"49","station2":"151","line":"9","time":"2"},{"station1":"54","station2":"55","line":"9","time":"2"},{"station1":"54","station2":"56","line":"9","time":"2"},{"station1":"55","station2":"245","line":"9","time":"1"},{"station1":"58","station2":"119","line":"9","time":"3"},{"station1":"59","station2":"240","line":"9","time":"2"},{"station1":"59","station2":"258","line":"9","time":"2"},{"station1":"77","station2":"93","line":"9","time":"4"},{"station1":"77","station2":"124","line":"9","time":"2"},{"station1":"84","station2":"136","line":"9","time":"2"},{"station1":"87","station2":"279","line":"9","time":"2"},{"station1":"89","station2":"145","line":"9","time":"2"},{"station1":"89","station2":"170","line":"9","time":"2"},{"station1":"89","station2":"277","line":"9","time":"1"},{"station1":"93","station2":"165","line":"9","time":"3"},{"station1":"93","station2":"288","line":"9","time":"2"},{"station1":"100","station2":"111","line":"9","time":"4"},{"station1":"102","station2":"259","line":"9","time":"1"},{"station1":"102","station2":"277","line":"9","time":"2"},{"station1":"121","station2":"261","line":"9","time":"3"},{"station1":"136","station2":"191","line":"9","time":"2"},{"station1":"136","station2":"279","line":"9","time":"3"},{"station1":"139","station2":"264","line":"9","time":"2"},{"station1":"151","station2":"259","line":"9","time":"1"},{"station1":"167","station2":"188","line":"9","time":"1"},{"station1":"169","station2":"240","line":"9","time":"4"},{"station1":"191","station2":"245","line":"9","time":"3"},{"station1":"257","station2":"258","line":"9","time":"2"},{"station1":"261","station2":"302","line":"9","time":"3"},{"station1":"288","station2":"302","line":"9","time":"1"},{"station1":"1","station2":"73","line":"10","time":"2"},{"station1":"1","station2":"234","line":"10","time":"4"},{"station1":"1","station2":"265","line":"10","time":"3"},{"station1":"5","station2":"194","line":"10","time":"3"},{"station1":"5","station2":"252","line":"10","time":"2"},{"station1":"9","station2":"31","line":"10","time":"3"},{"station1":"9","station2":"232","line":"10","time":"3"},{"station1":"10","station2":"95","line":"10","time":"2"},{"station1":"10","station2":"128","line":"10","time":"1"},{"station1":"17","station2":"74","line":"10","time":"3"},{"station1":"17","station2":"110","line":"10","time":"2"},{"station1":"30","station2":"176","line":"10","time":"2"},{"station1":"30","station2":"190","line":"10","time":"3"},{"station1":"31","station2":"303","line":"10","time":"2"},{"station1":"39","station2":"128","line":"10","time":"2"},{"station1":"39","station2":"145","line":"10","time":"5"},{"station1":"57","station2":"187","line":"10","time":"4"},{"station1":"60","station2":"126","line":"10","time":"1"},{"station1":"60","station2":"151","line":"10","time":"1"},{"station1":"73","station2":"182","line":"10","time":"3"},{"station1":"74","station2":"99","line":"10","time":"2"},{"station1":"75","station2":"210","line":"10","time":"2"},{"station1":"75","station2":"222","line":"10","time":"2"},{"station1":"95","station2":"160","line":"10","time":"2"},{"station1":"99","station2":"236","line":"10","time":"1"},{"station1":"107","station2":"133","line":"10","time":"2"},{"station1":"107","station2":"197","line":"10","time":"1"},{"station1":"110","station2":"265","line":"10","time":"2"},{"station1":"116","station2":"117","line":"10","time":"3"},{"station1":"116","station2":"118","line":"10","time":"3"},{"station1":"116","station2":"132","line":"10","time":"4"},{"station1":"117","station2":"118","line":"10","time":"5"},{"station1":"125","station2":"134","line":"10","time":"2"},{"station1":"125","station2":"271","line":"10","time":"3"},{"station1":"126","station2":"223","line":"10","time":"2"},{"station1":"130","station2":"131","line":"10","time":"2"},{"station1":"130","station2":"132","line":"10","time":"2"},{"station1":"131","station2":"190","line":"10","time":"2"},{"station1":"133","station2":"146","line":"10","time":"2"},{"station1":"134","station2":"220","line":"10","time":"3"},{"station1":"145","station2":"223","line":"10","time":"2"},{"station1":"146","station2":"236","line":"10","time":"3"},{"station1":"151","station2":"197","line":"10","time":"2"},{"station1":"160","station2":"266","line":"10","time":"3"},{"station1":"176","station2":"234","line":"10","time":"1"},{"station1":"182","station2":"194","line":"10","time":"2"},{"station1":"187","station2":"232","line":"10","time":"3"},{"station1":"210","station2":"235","line":"10","time":"3"},{"station1":"220","station2":"222","line":"10","time":"2"},{"station1":"235","station2":"251","line":"10","time":"2"},{"station1":"251","station2":"252","line":"10","time":"3"},{"station1":"266","station2":"303","line":"10","time":"2"},{"station1":"26","station2":"260","line":"11","time":"2"},{"station1":"26","station2":"274","line":"11","time":"2"},{"station1":"35","station2":"245","line":"11","time":"2"},{"station1":"89","station2":"145","line":"11","time":"2"},{"station1":"89","station2":"277","line":"11","time":"1"},{"station1":"95","station2":"123","line":"11","time":"2"},{"station1":"95","station2":"224","line":"11","time":"4"},{"station1":"107","station2":"192","line":"11","time":"2"},{"station1":"107","station2":"273","line":"11","time":"2"},{"station1":"123","station2":"145","line":"11","time":"4"},{"station1":"192","station2":"277","line":"11","time":"2"},{"station1":"198","station2":"272","line":"11","time":"1"},{"station1":"198","station2":"273","line":"11","time":"3"},{"station1":"224","station2":"260","line":"11","time":"3"},{"station1":"245","station2":"272","line":"11","time":"3"},{"station1":"13","station2":"279","line":"12","time":"4"}],"lines":[{"line":"1","name":"Bakerloo Line","colour":"AE6017","stripe":"NULL"},{"line":"3","name":"Circle Line","colour":"FFE02B","stripe":"NULL"},{"line":"6","name":"Hammersmith & City Line","colour":"F491A8","stripe":"NULL"},{"line":"7","name":"Jubilee Line","colour":"949699","stripe":"NULL"},{"line":"11","name":"Victoria Line","colour":"0A9CDA","stripe":"NULL"},{"line":"2","name":"Central Line","colour":"F15B2E","stripe":"NULL"},{"line":"4","name":"District Line","colour":"00A166","stripe":"NULL"},{"line":"5","name":"East London Line","colour":"FBAE34","stripe":"NULL"},{"line":"8","name":"Metropolitan Line","colour":"91005A","stripe":"NULL"},{"line":"9","name":"Northern Line","colour":"000000","stripe":"NULL"},{"line":"10","name":"Piccadilly Line","colour":"094FA3","stripe":"NULL"},{"line":"12","name":"Waterloo & City Line","colour":"88D0C4","stripe":"NULL"},{"line":"13","name":"Docklands Light Railway","colour":"00A77E","stripe":"FFFFFF"}],"stations":[{"id":"1","latitude":"51.5028","longitude":"-0.2801","name":"Acton Town","display_name":"Acton<br />Town","zone":"3","total_lines":"2","rail":"0"},{"id":"2","latitude":"51.5143","longitude":"-0.0755","name":"Aldgate","display_name":"NULL","zone":"1","total_lines":"2","rail":"0"},{"id":"3","latitude":"51.5154","longitude":"-0.0726","name":"Aldgate East","display_name":"Aldgate<br />East","zone":"1","total_lines":"2","rail":"0"},{"id":"4","latitude":"51.5107","longitude":"-0.013","name":"All Saints","display_name":"All<br />Saints","zone":"2","total_lines":"1","rail":"0"},{"id":"5","latitude":"51.5407","longitude":"-0.2997","name":"Alperton","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"7","latitude":"51.5322","longitude":"-0.1058","name":"Angel","display_name":"NULL","zone":"1","total_lines":"1","rail":"0"},{"id":"8","latitude":"51.5653","longitude":"-0.1353","name":"Archway","display_name":"NULL","zone":"2.5","total_lines":"1","rail":"0"},{"id":"9","latitude":"51.6164","longitude":"-0.1331","name":"Arnos Grove","display_name":"Arnos<br />Grove","zone":"4","total_lines":"1","rail":"0"},{"id":"10","latitude":"51.5586","longitude":"-0.1059","name":"Arsenal","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"11","latitude":"51.5226","longitude":"-0.1571","name":"Baker Street","display_name":"Baker<br />Street","zone":"1","total_lines":"5","rail":"0"},{"id":"12","latitude":"51.4431","longitude":"-0.1525","name":"Balham","display_name":"NULL","zone":"3","total_lines":"1","rail":"1"},{"id":"13","latitude":"51.5133","longitude":"-0.0886","name":"Bank","display_name":"NULL","zone":"1","total_lines":"4","rail":"0"},{"id":"14","latitude":"51.5204","longitude":"-0.0979","name":"Barbican","display_name":"NULL","zone":"1","total_lines":"3","rail":"0"},{"id":"15","latitude":"51.5396","longitude":"0.081","name":"Barking","display_name":"NULL","zone":"4","total_lines":"2","rail":"1"},{"id":"16","latitude":"51.5856","longitude":"0.0887","name":"Barkingside","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"17","latitude":"51.4905","longitude":"-0.2139","name":"Barons Court","display_name":"Barons<br />Court","zone":"2","total_lines":"2","rail":"0"},{"id":"18","latitude":"51.5121","longitude":"-0.1879","name":"Bayswater","display_name":"NULL","zone":"1","total_lines":"2","rail":"0"},{"id":"19","latitude":"51.5148","longitude":"0.0613","name":"Beckton","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"20","latitude":"51.5087","longitude":"0.055","name":"Beckton Park","display_name":"Beckton<br />Park","zone":"3","total_lines":"1","rail":"0"},{"id":"21","latitude":"51.5403","longitude":"0.127","name":"Becontree","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"22","latitude":"51.5504","longitude":"-0.1642","name":"Belsize Park","display_name":"Belsize<br />Park","zone":"2","total_lines":"1","rail":"0"},{"id":"24","latitude":"51.527","longitude":"-0.0549","name":"Bethnal Green","display_name":"Bethnal<br />Green","zone":"2","total_lines":"1","rail":"0"},{"id":"25","latitude":"51.512","longitude":"-0.1031","name":"Blackfriars","display_name":"NULL","zone":"1","total_lines":"2","rail":"0"},{"id":"26","latitude":"51.5867","longitude":"-0.0417","name":"Blackhorse Road","display_name":"Blackhorse<br />Road","zone":"3","total_lines":"1","rail":"1"},{"id":"27","latitude":"51.5079","longitude":"-0.0066","name":"Blackwall","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"28","latitude":"51.5142","longitude":"-0.1494","name":"Bond Street","display_name":"Bond<br />Street","zone":"1","total_lines":"2","rail":"0"},{"id":"29","latitude":"51.5011","longitude":"-0.0943","name":"Borough","display_name":"NULL","zone":"1","total_lines":"1","rail":"0"},{"id":"30","latitude":"51.4956","longitude":"-0.325","name":"Boston Manor","display_name":"Boston<br />Manor","zone":"4","total_lines":"1","rail":"0"},{"id":"31","latitude":"51.6071","longitude":"-0.1243","name":"Bounds Green","display_name":"Bounds<br />Green","zone":"3.5","total_lines":"1","rail":"0"},{"id":"32","latitude":"51.5273","longitude":"-0.0208","name":"Bow Church","display_name":"Bow<br />Church","zone":"2","total_lines":"3","rail":"0"},{"id":"33","latitude":"51.5269","longitude":"-0.0247","name":"Bow Road","display_name":"Bow<br />Road","zone":"2","total_lines":"2","rail":"0"},{"id":"34","latitude":"51.5766","longitude":"-0.2136","name":"Brent Cross","display_name":"Brent<br />Cross","zone":"3","total_lines":"1","rail":"0"},{"id":"36","latitude":"51.5248","longitude":"-0.0119","name":"Bromley-By-Bow","display_name":"NULL","zone":"2.5","total_lines":"2","rail":"0"},{"id":"38","latitude":"51.6028","longitude":"-0.2641","name":"Burnt Oak","display_name":"Burnt<br />Oak","zone":"4","total_lines":"1","rail":"0"},{"id":"39","latitude":"51.5481","longitude":"-0.1188","name":"Caledonian Road","display_name":"Caledonian<br />Road","zone":"2","total_lines":"1","rail":"0"},{"id":"40","latitude":"51.5392","longitude":"-0.1426","name":"Camden Town","display_name":"Camden<br />Town","zone":"2","total_lines":"1","rail":"0"},{"id":"42","latitude":"51.5051","longitude":"-0.0209","name":"Canary Wharf","display_name":"Canary<br />Wharf","zone":"2","total_lines":"2","rail":"0"},{"id":"44","latitude":"51.5113","longitude":"-0.0904","name":"Cannon Street","display_name":"Cannon<br />Street","zone":"1","total_lines":"2","rail":"0"},{"id":"45","latitude":"51.6078","longitude":"-0.2947","name":"Canons Park","display_name":"Canons<br />Park","zone":"5","total_lines":"1","rail":"0"},{"id":"47","latitude":"51.5441","longitude":"-0.1538","name":"Chalk Farm","display_name":"Chalk<br />Farm","zone":"2","total_lines":"1","rail":"0"},{"id":"48","latitude":"51.5185","longitude":"-0.1111","name":"Chancery Lane","display_name":"Chancery<br />Lane","zone":"1","total_lines":"1","rail":"0"},{"id":"49","latitude":"51.508","longitude":"-0.1247","name":"Charing Cross","display_name":"Charing<br />Cross","zone":"1","total_lines":"2","rail":"1"},{"id":"51","latitude":"51.6177","longitude":"0.0755","name":"Chigwell","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"52","latitude":"51.4946","longitude":"-0.2678","name":"Chiswick Park","display_name":"Chiswick<br />Park","zone":"3","total_lines":"1","rail":"0"},{"id":"54","latitude":"51.4618","longitude":"-0.1384","name":"Clapham Common","display_name":"Clapham<br />Common","zone":"2","total_lines":"1","rail":"0"},{"id":"55","latitude":"51.4649","longitude":"-0.1299","name":"Clapham North","display_name":"Clapham<br />North","zone":"2","total_lines":"1","rail":"0"},{"id":"56","latitude":"51.4527","longitude":"-0.148","name":"Clapham South","display_name":"Clapham<br />South","zone":"2.5","total_lines":"1","rail":"0"},{"id":"58","latitude":"51.5955","longitude":"-0.2502","name":"Colindale","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"59","latitude":"51.418","longitude":"-0.1778","name":"Colliers Wood","display_name":"Colliers<br />Wood","zone":"3","total_lines":"1","rail":"0"},{"id":"60","latitude":"51.5129","longitude":"-0.1243","name":"Covent Garden","display_name":"Covent<br />Garden","zone":"1","total_lines":"1","rail":"0"},{"id":"61","latitude":"51.4957","longitude":"-0.0144","name":"Crossharbour & London Arena","display_name":"Crossharbour &<br />London Arena","zone":"2","total_lines":"1","rail":"0"},{"id":"63","latitude":"51.5095","longitude":"0.0276","name":"Custom House","display_name":"Custom<br />House","zone":"3","total_lines":"1","rail":"0"},{"id":"65","latitude":"51.5085","longitude":"0.064","name":"Cyprus","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"66","latitude":"51.5443","longitude":"0.1655","name":"Dagenham East","display_name":"Dagenham<br />East","zone":"5","total_lines":"1","rail":"0"},{"id":"67","latitude":"51.5417","longitude":"0.1469","name":"Dagenham Heathway","display_name":"Dagenham<br />Heathway","zone":"5","total_lines":"1","rail":"0"},{"id":"70","latitude":"51.5223","longitude":"-0.0173","name":"Devons Road","display_name":"Devons<br />Road","zone":"2","total_lines":"1","rail":"0"},{"id":"71","latitude":"51.552","longitude":"-0.2387","name":"Dollis Hill","display_name":"Dollis<br />Hill","zone":"3","total_lines":"1","rail":"0"},{"id":"72","latitude":"51.5152","longitude":"-0.3017","name":"Ealing Broadway","display_name":"Ealing<br />Broadway","zone":"3","total_lines":"2","rail":"1"},{"id":"73","latitude":"51.5101","longitude":"-0.2882","name":"Ealing Common","display_name":"Ealing<br />Common","zone":"3","total_lines":"2","rail":"0"},{"id":"74","latitude":"51.492","longitude":"-0.1973","name":"Earl's Court","display_name":"Earl's<br />Court","zone":"1.5","total_lines":"2","rail":"0"},{"id":"75","latitude":"51.5765","longitude":"-0.397","name":"Eastcote","display_name":"NULL","zone":"5","total_lines":"2","rail":"0"},{"id":"76","latitude":"51.5168","longitude":"-0.2474","name":"East Acton","display_name":"East<br />Acton","zone":"2","total_lines":"1","rail":"0"},{"id":"77","latitude":"51.5874","longitude":"-0.165","name":"East Finchley","display_name":"East<br />Finchley","zone":"3","total_lines":"1","rail":"0"},{"id":"78","latitude":"51.5394","longitude":"0.0518","name":"East Ham","display_name":"East<br />Ham","zone":"3.5","total_lines":"2","rail":"0"},{"id":"79","latitude":"51.5093","longitude":"-0.0021","name":"East India","display_name":"East<br />India","zone":"2.5","total_lines":"1","rail":"0"},{"id":"80","latitude":"51.4586","longitude":"-0.2112","name":"East Putney","display_name":"East<br />Putney","zone":"2.5","total_lines":"1","rail":"0"},{"id":"81","latitude":"51.6137","longitude":"-0.275","name":"Edgware","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"82","latitude":"51.5199","longitude":"-0.1679","name":"Edgware Road (B)","display_name":"Edgware<br />Road","zone":"1","total_lines":"1","rail":"0"},{"id":"83","latitude":"51.5203","longitude":"-0.17","name":"Edgware Road (C)","display_name":"Edgware<br />Road","zone":"1","total_lines":"3","rail":"0"},{"id":"84","latitude":"51.4943","longitude":"-0.1001","name":"Elephant & Castle","display_name":"Elephant &<br />Castle","zone":"1.5","total_lines":"2","rail":"1"},{"id":"85","latitude":"51.5496","longitude":"0.1977","name":"Elm Park","display_name":"Elm<br />Park","zone":"6","total_lines":"1","rail":"0"},{"id":"87","latitude":"51.5074","longitude":"-0.1223","name":"Embankment","display_name":"NULL","zone":"1","total_lines":"4","rail":"0"},{"id":"89","latitude":"51.5282","longitude":"-0.1337","name":"Euston","display_name":"NULL","zone":"1","total_lines":"2","rail":"1"},{"id":"90","latitude":"51.526","longitude":"-0.1359","name":"Euston Square","display_name":"Euston<br />Square","zone":"1","total_lines":"3","rail":"0"},{"id":"91","latitude":"51.596","longitude":"0.0912","name":"Fairlop","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"92","latitude":"51.5203","longitude":"-0.1053","name":"Farringdon","display_name":"NULL","zone":"1","total_lines":"3","rail":"1"},{"id":"93","latitude":"51.6012","longitude":"-0.1932","name":"Finchley Central","display_name":"Finchley<br />Central","zone":"4","total_lines":"1","rail":"0"},{"id":"94","latitude":"51.5472","longitude":"-0.1803","name":"Finchley Road","display_name":"Finchley<br />Road","zone":"2","total_lines":"2","rail":"0"},{"id":"95","latitude":"51.5642","longitude":"-0.1065","name":"Finsbury Park","display_name":"Finsbury<br />Park","zone":"2","total_lines":"1","rail":"1"},{"id":"96","latitude":"51.4804","longitude":"-0.195","name":"Fulham Broadway","display_name":"Fulham<br />Broadway","zone":"2","total_lines":"1","rail":"0"},{"id":"97","latitude":"51.5096","longitude":"0.0716","name":"Gallions Reach","display_name":"Gallions<br />Reach","zone":"3","total_lines":"1","rail":"0"},{"id":"98","latitude":"51.5765","longitude":"0.0663","name":"Gants Hill","display_name":"Gants<br />Hill","zone":"4","total_lines":"1","rail":"0"},{"id":"99","latitude":"51.4945","longitude":"-0.1829","name":"Gloucester Road","display_name":"Gloucester<br />Road","zone":"1","total_lines":"3","rail":"0"},{"id":"100","latitude":"51.5724","longitude":"-0.1941","name":"Golders Green","display_name":"Golders<br />Green","zone":"3","total_lines":"1","rail":"0"},{"id":"101","latitude":"51.5018","longitude":"-0.2267","name":"Goldhawk Road","display_name":"Goldhawk<br />Road","zone":"2","total_lines":"1","rail":"0"},{"id":"102","latitude":"51.5205","longitude":"-0.1347","name":"Goodge Street","display_name":"Goodge<br />Street","zone":"1","total_lines":"1","rail":"0"},{"id":"103","latitude":"51.6132","longitude":"0.0923","name":"Grange Hill","display_name":"Grange<br />Hill","zone":"5","total_lines":"1","rail":"0"},{"id":"104","latitude":"51.5238","longitude":"-0.1439","name":"Great Portland Street","display_name":"Great<br />Portland<br />Street","zone":"1","total_lines":"3","rail":"0"},{"id":"105","latitude":"51.5423","longitude":"-0.3456","name":"Greenford","display_name":"NULL","zone":"4","total_lines":"1","rail":"1"},{"id":"107","latitude":"51.5067","longitude":"-0.1428","name":"Green Park","display_name":"Green<br />Park","zone":"1","total_lines":"3","rail":"0"},{"id":"108","latitude":"51.4915","longitude":"-0.2754","name":"Gunnersbury","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"109","latitude":"51.603","longitude":"0.0933","name":"Hainault","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"110","latitude":"51.4936","longitude":"-0.2251","name":"Hammersmith","display_name":"NULL","zone":"2","total_lines":"3","rail":"0"},{"id":"111","latitude":"51.5568","longitude":"-0.178","name":"Hampstead","display_name":"NULL","zone":"2.5","total_lines":"1","rail":"0"},{"id":"112","latitude":"51.5302","longitude":"-0.2933","name":"Hanger Lane","display_name":"Hanger<br />Lane","zone":"3","total_lines":"1","rail":"0"},{"id":"113","latitude":"51.5362","longitude":"-0.2575","name":"Harlesden","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"114","latitude":"51.5925","longitude":"-0.3351","name":"Harrow & Wealdston","display_name":"Harrow &<br />Wealdston","zone":"5","total_lines":"1","rail":"1"},{"id":"115","latitude":"51.5793","longitude":"-0.3366","name":"Harrow-on-the-Hill","display_name":"Harrow-<br />on-the-Hill","zone":"5","total_lines":"1","rail":"1"},{"id":"116","latitude":"51.4669","longitude":"-0.4227","name":"Hatton Cross","display_name":"Hatton<br />Cross","zone":"5.5","total_lines":"1","rail":"0"},{"id":"117","latitude":"51.4713","longitude":"-0.4524","name":"Heathrow Terminals 1, 2 & 3","display_name":"Heathrow<br />Terminals<br />1, 2 & 3","zone":"6","total_lines":"1","rail":"0"},{"id":"118","latitude":"51.4598","longitude":"-0.4476","name":"Heathrow Terminal 4","display_name":"Heathrow<br />Terminal 4","zone":"6","total_lines":"1","rail":"0"},{"id":"119","latitude":"51.5829","longitude":"-0.2259","name":"Hendon Central","display_name":"Hendon<br />Central","zone":"3.5","total_lines":"1","rail":"0"},{"id":"120","latitude":"51.5033","longitude":"-0.0215","name":"Heron Quays","display_name":"Heron<br />Quays","zone":"2","total_lines":"1","rail":"0"},{"id":"122","latitude":"51.5009","longitude":"-0.1925","name":"High Street Kensington","display_name":"High<br />Street<br />Kensington","zone":"1","total_lines":"2","rail":"0"},{"id":"123","latitude":"51.546","longitude":"-0.104","name":"Highbury & Islington","display_name":"Highbury &<br />Islington","zone":"2","total_lines":"1","rail":"1"},{"id":"124","latitude":"51.5777","longitude":"-0.1458","name":"Highgate","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"125","latitude":"51.5538","longitude":"-0.4499","name":"Hillingdon","display_name":"NULL","zone":"6","total_lines":"2","rail":"0"},{"id":"126","latitude":"51.5174","longitude":"-0.12","name":"Holborn","display_name":"NULL","zone":"1","total_lines":"2","rail":"0"},{"id":"127","latitude":"51.5075","longitude":"-0.206","name":"Holland Park","display_name":"Holland<br />Park","zone":"2","total_lines":"1","rail":"0"},{"id":"128","latitude":"51.5526","longitude":"-0.1132","name":"Holloway Road","display_name":"Holloway<br />Road","zone":"2","total_lines":"1","rail":"0"},{"id":"129","latitude":"51.5539","longitude":"0.2184","name":"Hornchurch","display_name":"NULL","zone":"6","total_lines":"1","rail":"0"},{"id":"130","latitude":"51.4713","longitude":"-0.3665","name":"Hounslow Central","display_name":"Hounslow<br />Central","zone":"4","total_lines":"1","rail":"0"},{"id":"131","latitude":"51.4733","longitude":"-0.3564","name":"Hounslow East","display_name":"Hounslow<br />East","zone":"4","total_lines":"1","rail":"0"},{"id":"132","latitude":"51.4734","longitude":"-0.3855","name":"Hounslow West","display_name":"Hounslow<br />West","zone":"5","total_lines":"1","rail":"0"},{"id":"133","latitude":"51.5027","longitude":"-0.1527","name":"Hyde Park Corner","display_name":"Hyde<br />Park<br />Corner","zone":"1","total_lines":"1","rail":"0"},{"id":"134","latitude":"51.5619","longitude":"-0.4421","name":"Ickenham","display_name":"NULL","zone":"6","total_lines":"2","rail":"0"},{"id":"135","latitude":"51.4871","longitude":"-0.0101","name":"Island Gardens","display_name":"Island<br />Gardens","zone":"2","total_lines":"1","rail":"0"},{"id":"136","latitude":"51.4884","longitude":"-0.1053","name":"Kennington","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"137","latitude":"51.5304","longitude":"-0.225","name":"Kensal Green","display_name":"Kensal<br />Green","zone":"2","total_lines":"1","rail":"0"},{"id":"138","latitude":"51.4983","longitude":"-0.2106","name":"Kensington (Olympia)","display_name":"Kensington<br />(Olympia)","zone":"2","total_lines":"1","rail":"1"},{"id":"139","latitude":"51.5507","longitude":"-0.1402","name":"Kentish Town","display_name":"Kentish<br />Town","zone":"2","total_lines":"1","rail":"1"},{"id":"140","latitude":"51.5816","longitude":"-0.3162","name":"Kenton","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"141","latitude":"51.477","longitude":"-0.285","name":"Kew Gardens","display_name":"Kew<br />Gardens","zone":"3.5","total_lines":"1","rail":"0"},{"id":"142","latitude":"51.5471","longitude":"-0.2047","name":"Kilburn","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"143","latitude":"51.5351","longitude":"-0.1939","name":"Kilburn Park","display_name":"Kilburn<br />Park","zone":"2","total_lines":"1","rail":"0"},{"id":"144","latitude":"51.5846","longitude":"-0.2786","name":"Kingsbury","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"145","latitude":"51.5308","longitude":"-0.1238","name":"King's Cross St. Pancras","display_name":"King's Cross<br />St. Pancras","zone":"1","total_lines":"6","rail":"1"},{"id":"146","latitude":"51.5015","longitude":"-0.1607","name":"Knightsbridge","display_name":"NULL","zone":"1","total_lines":"1","rail":"0"},{"id":"147","latitude":"51.5172","longitude":"-0.2107","name":"Ladbroke Grove","display_name":"Ladbroke<br />Grove","zone":"2","total_lines":"1","rail":"0"},{"id":"148","latitude":"51.4991","longitude":"-0.1115","name":"Lambeth North","display_name":"Lambeth<br />North","zone":"1","total_lines":"1","rail":"0"},{"id":"149","latitude":"51.5119","longitude":"-0.1756","name":"Lancaster Gate","display_name":"Lancaster<br />Gate","zone":"1","total_lines":"1","rail":"0"},{"id":"150","latitude":"51.5139","longitude":"-0.2172","name":"Latimer Road","display_name":"Latimer<br />Road","zone":"2","total_lines":"1","rail":"0"},{"id":"151","latitude":"51.5113","longitude":"-0.1281","name":"Leicester Square","display_name":"Leicester<br />Square","zone":"1","total_lines":"2","rail":"0"},{"id":"153","latitude":"51.5566","longitude":"-0.0053","name":"Leyton","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"154","latitude":"51.5683","longitude":"0.0083","name":"Leytonstone","display_name":"NULL","zone":"3.5","total_lines":"1","rail":"0"},{"id":"155","latitude":"51.5123","longitude":"-0.0396","name":"Limehouse","display_name":"NULL","zone":"2","total_lines":"1","rail":"1"},{"id":"156","latitude":"51.5178","longitude":"-0.0823","name":"Liverpool Street","display_name":"Liverpool<br />Street","zone":"1","total_lines":"4","rail":"1"},{"id":"157","latitude":"51.5052","longitude":"-0.0864","name":"London Bridge","display_name":"London<br />Bridge","zone":"1","total_lines":"2","rail":"1"},{"id":"159","latitude":"51.53","longitude":"-0.1854","name":"Maida Vale","display_name":"Maida<br />Vale","zone":"2","total_lines":"1","rail":"0"},{"id":"160","latitude":"51.5712","longitude":"-0.0958","name":"Manor House","display_name":"Manor<br />House","zone":"2.5","total_lines":"1","rail":"0"},{"id":"161","latitude":"51.5122","longitude":"-0.094","name":"Mansion House","display_name":"Mansion<br />House","zone":"1","total_lines":"2","rail":"0"},{"id":"162","latitude":"51.5136","longitude":"-0.1586","name":"Marble Arch","display_name":"Marble<br />Arch","zone":"1","total_lines":"1","rail":"0"},{"id":"163","latitude":"51.5225","longitude":"-0.1631","name":"Marylebone","display_name":"NULL","zone":"1","total_lines":"1","rail":"1"},{"id":"164","latitude":"51.5249","longitude":"-0.0332","name":"Mile End","display_name":"Mile<br />End","zone":"2","total_lines":"3","rail":"0"},{"id":"165","latitude":"51.6082","longitude":"-0.2103","name":"Mill Hill East","display_name":"Mill<br />Hill<br />East","zone":"4","total_lines":"1","rail":"0"},{"id":"166","latitude":"51.5108","longitude":"-0.0863","name":"Monument","display_name":"NULL","zone":"1","total_lines":"2","rail":"0"},{"id":"167","latitude":"51.5186","longitude":"-0.0886","name":"Moorgate","display_name":"NULL","zone":"1","total_lines":"4","rail":"1"},{"id":"168","latitude":"51.6294","longitude":"-0.432","name":"Moor Park","display_name":"Moor<br />Park","zone":"6.5","total_lines":"1","rail":"0"},{"id":"169","latitude":"51.4022","longitude":"-0.1948","name":"Morden","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"170","latitude":"51.5342","longitude":"-0.1387","name":"Mornington Crescent","display_name":"Mornington<br />Crescent","zone":"2","total_lines":"1","rail":"0"},{"id":"171","latitude":"51.4902","longitude":"-0.0145","name":"Mudchute","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"172","latitude":"51.5542","longitude":"-0.2503","name":"Neasden","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"173","latitude":"51.5756","longitude":"0.0899","name":"Newbury Park","display_name":"Newbury<br />Park","zone":"4","total_lines":"1","rail":"0"},{"id":"176","latitude":"51.4995","longitude":"-0.3142","name":"Northfields","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"177","latitude":"51.5483","longitude":"-0.3687","name":"Northolt","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"178","latitude":"51.5784","longitude":"-0.3184","name":"Northwick Park","display_name":"Northwick<br />Park","zone":"4","total_lines":"1","rail":"0"},{"id":"179","latitude":"51.6111","longitude":"-0.424","name":"Northwood","display_name":"NULL","zone":"6","total_lines":"1","rail":"0"},{"id":"180","latitude":"51.6004","longitude":"-0.4092","name":"Northwood Hills","display_name":"Northwood<br />Hills","zone":"6","total_lines":"1","rail":"0"},{"id":"181","latitude":"51.5237","longitude":"-0.2597","name":"North Acton","display_name":"North<br />Acton","zone":"2.5","total_lines":"1","rail":"0"},{"id":"182","latitude":"51.5175","longitude":"-0.2887","name":"North Ealing","display_name":"North<br />Ealing","zone":"3","total_lines":"1","rail":"0"},{"id":"184","latitude":"51.5846","longitude":"-0.3626","name":"North Harrow","display_name":"North<br />Harrow","zone":"5","total_lines":"1","rail":"0"},{"id":"185","latitude":"51.5621","longitude":"-0.3034","name":"North Wembley","display_name":"North<br />Wembley","zone":"4","total_lines":"1","rail":"0"},{"id":"186","latitude":"51.5094","longitude":"-0.1967","name":"Notting Hill Gate","display_name":"Notting<br />Hill Gate","zone":"1.5","total_lines":"3","rail":"0"},{"id":"188","latitude":"51.5263","longitude":"-0.0873","name":"Old Street","display_name":"Old<br />Street","zone":"1","total_lines":"1","rail":"1"},{"id":"190","latitude":"51.4813","longitude":"-0.3522","name":"Osterley","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"191","latitude":"51.4819","longitude":"-0.113","name":"Oval","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"192","latitude":"51.515","longitude":"-0.1415","name":"Oxford Circus","display_name":"Oxford<br />Circus","zone":"1","total_lines":"3","rail":"0"},{"id":"193","latitude":"51.5154","longitude":"-0.1755","name":"Paddington","display_name":"NULL","zone":"1","total_lines":"4","rail":"1"},{"id":"194","latitude":"51.527","longitude":"-0.2841","name":"Park Royal","display_name":"Park<br />Royal","zone":"3","total_lines":"1","rail":"0"},{"id":"195","latitude":"51.4753","longitude":"-0.2011","name":"Parsons Green","display_name":"Parsons<br />Green","zone":"2","total_lines":"1","rail":"0"},{"id":"196","latitude":"51.5366","longitude":"-0.3232","name":"Perivale","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"197","latitude":"51.5098","longitude":"-0.1342","name":"Picadilly Circus","display_name":"Picadilly<br />Circus","zone":"1","total_lines":"2","rail":"0"},{"id":"198","latitude":"51.4893","longitude":"-0.1334","name":"Pimlico","display_name":"NULL","zone":"1","total_lines":"1","rail":"0"},{"id":"199","latitude":"51.5926","longitude":"-0.3805","name":"Pinner","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"200","latitude":"51.5313","longitude":"0.0172","name":"Plaistow","display_name":"NULL","zone":"3","total_lines":"2","rail":"0"},{"id":"201","latitude":"51.5077","longitude":"-0.0173","name":"Poplar","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"202","latitude":"51.572","longitude":"-0.2954","name":"Preston Road","display_name":"Preston<br />Road","zone":"4","total_lines":"1","rail":"0"},{"id":"203","latitude":"51.5093","longitude":"0.0336","name":"Prince Regent","display_name":"Prince<br />Regent","zone":"3","total_lines":"1","rail":"0"},{"id":"205","latitude":"51.4682","longitude":"-0.2089","name":"Putney Bridge","display_name":"Putney<br />Bridge","zone":"2","total_lines":"1","rail":"0"},{"id":"206","latitude":"51.5341","longitude":"-0.2047","name":"Queen's Park","display_name":"Queens<br />Park","zone":"2","total_lines":"1","rail":"1"},{"id":"207","latitude":"51.5942","longitude":"-0.2861","name":"Queensbury","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"208","latitude":"51.5107","longitude":"-0.1877","name":"Queensway","display_name":"NULL","zone":"1","total_lines":"1","rail":"0"},{"id":"209","latitude":"51.4942","longitude":"-0.2359","name":"Ravenscourt Park","display_name":"Ravenscourt<br />Park","zone":"2","total_lines":"1","rail":"0"},{"id":"210","latitude":"51.5753","longitude":"-0.3714","name":"Rayners Lane","display_name":"Rayners<br />Lane","zone":"5","total_lines":"2","rail":"0"},{"id":"211","latitude":"51.5763","longitude":"0.0454","name":"Redbridge","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"212","latitude":"51.5234","longitude":"-0.1466","name":"Regent's Park","display_name":"Regent's<br />Park","zone":"1","total_lines":"1","rail":"0"},{"id":"213","latitude":"51.4633","longitude":"-0.3013","name":"Richmond","display_name":"NULL","zone":"4","total_lines":"1","rail":"1"},{"id":"215","latitude":"51.6171","longitude":"0.0439","name":"Roding Valley","display_name":"Roding<br />Valley","zone":"5","total_lines":"1","rail":"0"},{"id":"216","latitude":"51.501","longitude":"-0.0525","name":"Rotherhithe","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"217","latitude":"51.5084","longitude":"0.0465","name":"Royal Albert","display_name":"Royal<br />Albert","zone":"3","total_lines":"1","rail":"0"},{"id":"218","latitude":"51.519","longitude":"-0.188","name":"Royal Oak","display_name":"Royal<br />Oak","zone":"2","total_lines":"1","rail":"0"},{"id":"219","latitude":"51.5091","longitude":"0.0181","name":"Royal Victoria","display_name":"Royal<br />Victoria","zone":"3","total_lines":"1","rail":"0"},{"id":"220","latitude":"51.5715","longitude":"-0.4213","name":"Ruislip","display_name":"NULL","zone":"6","total_lines":"2","rail":"0"},{"id":"222","latitude":"51.5732","longitude":"-0.4125","name":"Ruislip Manor","display_name":"Ruislip<br />Manor","zone":"6","total_lines":"2","rail":"0"},{"id":"223","latitude":"51.523","longitude":"-0.1244","name":"Russell Square","display_name":"Russell<br />Square","zone":"1","total_lines":"1","rail":"0"},{"id":"224","latitude":"51.5822","longitude":"-0.0749","name":"Seven Sisters","display_name":"Seven<br />Sisters","zone":"3","total_lines":"1","rail":"1"},{"id":"225","latitude":"51.5117","longitude":"-0.056","name":"Shadwell","display_name":"NULL","zone":"2","total_lines":"2","rail":"0"},{"id":"226","latitude":"51.5046","longitude":"-0.2187","name":"Shepherd's Bush (C)","display_name":"Shepherd's<br />Bush","zone":"2","total_lines":"1","rail":"0"},{"id":"227","latitude":"51.5058","longitude":"-0.2265","name":"Shepherd's Bush (H)","display_name":"Shepherd's<br />Bush","zone":"2","total_lines":"1","rail":"0"},{"id":"228","latitude":"51.5227","longitude":"-0.0708","name":"Shoreditch","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"229","latitude":"51.4924","longitude":"-0.1565","name":"Sloane Square","display_name":"Sloane<br />Square","zone":"1","total_lines":"2","rail":"0"},{"id":"230","latitude":"51.5808","longitude":"0.0216","name":"Snaresbrook","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"231","latitude":"51.4454","longitude":"-0.2066","name":"Southfields","display_name":"NULL","zone":"3","total_lines":"1","rail":"0"},{"id":"234","latitude":"51.5011","longitude":"-0.3072","name":"South Ealing","display_name":"South<br />Ealing","zone":"3","total_lines":"1","rail":"0"},{"id":"235","latitude":"51.5646","longitude":"-0.3521","name":"South Harrow","display_name":"South<br />Harrow","zone":"5","total_lines":"1","rail":"0"},{"id":"236","latitude":"51.4941","longitude":"-0.1738","name":"South Kensington","display_name":"South<br />Kensington","zone":"1","total_lines":"3","rail":"0"},{"id":"237","latitude":"51.5701","longitude":"-0.3081","name":"South Kenton","display_name":"South<br />Kenton","zone":"4","total_lines":"1","rail":"0"},{"id":"238","latitude":"51.5007","longitude":"-0.0191","name":"South Quay","display_name":"South<br />Quay","zone":"2","total_lines":"1","rail":"0"},{"id":"239","latitude":"51.5569","longitude":"-0.3988","name":"South Ruislip","display_name":"South<br />Ruislip","zone":"5","total_lines":"1","rail":"1"},{"id":"240","latitude":"51.4154","longitude":"-0.1919","name":"South Wimbledon","display_name":"South<br />Wimbledon","zone":"3.5","total_lines":"1","rail":"0"},{"id":"241","latitude":"51.5917","longitude":"0.0275","name":"South Woodford","display_name":"South<br />Woodford","zone":"4","total_lines":"1","rail":"0"},{"id":"242","latitude":"51.495","longitude":"-0.2459","name":"Stamford Brook","display_name":"Stamford<br />Brook","zone":"2","total_lines":"1","rail":"0"},{"id":"243","latitude":"51.6194","longitude":"-0.3028","name":"Stanmore","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"244","latitude":"51.5221","longitude":"-0.047","name":"Stepney Green","display_name":"Stepney<br />Green","zone":"2","total_lines":"2","rail":"0"},{"id":"245","latitude":"51.4723","longitude":"-0.123","name":"Stockwell","display_name":"NULL","zone":"2","total_lines":"2","rail":"0"},{"id":"246","latitude":"51.5439","longitude":"-0.2759","name":"Stonebridge Park","display_name":"Stonebridge<br />Park","zone":"3","total_lines":"1","rail":"0"},{"id":"247","latitude":"51.5416","longitude":"-0.0042","name":"Stratford","display_name":"NULL","zone":"3","total_lines":"3","rail":"1"},{"id":"248","latitude":"51.4994","longitude":"-0.1335","name":"St. James's Park","display_name":"St. James's<br />Park","zone":"1","total_lines":"2","rail":"0"},{"id":"249","latitude":"51.5347","longitude":"-0.174","name":"St. John's Wood","display_name":"St. John's<br />Wood","zone":"2","total_lines":"1","rail":"0"},{"id":"250","latitude":"51.5146","longitude":"-0.0973","name":"St. Paul's","display_name":"St. Paul's","zone":"1","total_lines":"1","rail":"0"},{"id":"251","latitude":"51.5569","longitude":"-0.3366","name":"Sudbury Hill","display_name":"Sudbury<br />Hill","zone":"4","total_lines":"1","rail":"0"},{"id":"252","latitude":"51.5507","longitude":"-0.3156","name":"Sudbury Town","display_name":"Sudbury<br />Town","zone":"4","total_lines":"1","rail":"0"},{"id":"253","latitude":"51.4933","longitude":"-0.0478","name":"Surrey Quays","display_name":"Surrey<br />Quays","zone":"2","total_lines":"1","rail":"0"},{"id":"254","latitude":"51.5432","longitude":"-0.1738","name":"Swiss Cottage","display_name":"Swiss<br />Cottage","zone":"2","total_lines":"2","rail":"0"},{"id":"255","latitude":"51.5111","longitude":"-0.1141","name":"Temple","display_name":"NULL","zone":"1","total_lines":"2","rail":"0"},{"id":"257","latitude":"51.4361","longitude":"-0.1598","name":"Tooting Bec","display_name":"Tooting<br />Bec","zone":"3","total_lines":"1","rail":"0"},{"id":"258","latitude":"51.4275","longitude":"-0.168","name":"Tooting Broadway","display_name":"Tooting<br />Broadway","zone":"3","total_lines":"1","rail":"0"},{"id":"259","latitude":"51.5165","longitude":"-0.131","name":"Tottenham Court Road","display_name":"Tottenham<br />Court<br />Road","zone":"1","total_lines":"2","rail":"0"},{"id":"260","latitude":"51.5882","longitude":"-0.0594","name":"Tottenham Hale","display_name":"Tottenham<br />Hale","zone":"3","total_lines":"1","rail":"1"},{"id":"262","latitude":"51.5106","longitude":"-0.0743","name":"Tower Gateway","display_name":"Tower<br />Gateway","zone":"1","total_lines":"1","rail":"0"},{"id":"263","latitude":"51.5098","longitude":"-0.0766","name":"Tower Hill","display_name":"Tower<br />Hill","zone":"1","total_lines":"2","rail":"0"},{"id":"264","latitude":"51.5567","longitude":"-0.1374","name":"Tufnell Park","display_name":"Tufnell<br />Park","zone":"2","total_lines":"1","rail":"0"},{"id":"265","latitude":"51.4951","longitude":"-0.2547","name":"Turnham Green","display_name":"Turnham<br />Green","zone":"2.5","total_lines":"2","rail":"0"},{"id":"266","latitude":"51.5904","longitude":"-0.1028","name":"Turnpike Lane","display_name":"Turnpike<br />Lane","zone":"3","total_lines":"1","rail":"0"},{"id":"267","latitude":"51.559","longitude":"0.251","name":"Upminster","display_name":"NULL","zone":"6","total_lines":"1","rail":"1"},{"id":"268","latitude":"51.5582","longitude":"0.2343","name":"Upminster Bridge","display_name":"Upminster<br />Bridge","zone":"6","total_lines":"1","rail":"0"},{"id":"269","latitude":"51.5385","longitude":"0.1014","name":"Upney","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"270","latitude":"51.5352","longitude":"0.0343","name":"Upton Park","display_name":"Upton<br />Park","zone":"3","total_lines":"2","rail":"0"},{"id":"271","latitude":"51.5463","longitude":"-0.4786","name":"Uxbridge","display_name":"NULL","zone":"6","total_lines":"2","rail":"0"},{"id":"272","latitude":"51.4861","longitude":"-0.1253","name":"Vauxhall","display_name":"NULL","zone":"1.5","total_lines":"1","rail":"1"},{"id":"273","latitude":"51.4965","longitude":"-0.1447","name":"Victoria","display_name":"NULL","zone":"1","total_lines":"3","rail":"1"},{"id":"274","latitude":"51.583","longitude":"-0.0195","name":"Walthamstow Central","display_name":"Walthamstow<br />Central","zone":"3","total_lines":"1","rail":"1"},{"id":"275","latitude":"51.5775","longitude":"0.0288","name":"Wanstead","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"276","latitude":"51.5043","longitude":"-0.0558","name":"Wapping","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"277","latitude":"51.5247","longitude":"-0.1384","name":"Warren Street","display_name":"Warren<br />Street","zone":"1","total_lines":"2","rail":"0"},{"id":"278","latitude":"51.5235","longitude":"-0.1835","name":"Warwick Avenue","display_name":"Warwick<br />Avenue","zone":"2","total_lines":"1","rail":"0"},{"id":"279","latitude":"51.5036","longitude":"-0.1143","name":"Waterloo","display_name":"NULL","zone":"1","total_lines":"4","rail":"1"},{"id":"281","latitude":"51.5519","longitude":"-0.2963","name":"Wembley Central","display_name":"Wembley<br />Central","zone":"4","total_lines":"1","rail":"1"},{"id":"282","latitude":"51.5635","longitude":"-0.2795","name":"Wembley Park","display_name":"Wembley<br />Park","zone":"4","total_lines":"2","rail":"0"},{"id":"283","latitude":"51.521","longitude":"-0.2011","name":"Westbourne Park","display_name":"Westbourne<br />Park","zone":"2","total_lines":"1","rail":"0"},{"id":"284","latitude":"51.5097","longitude":"-0.0265","name":"Westferry","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"285","latitude":"51.501","longitude":"-0.1254","name":"Westminster","display_name":"NULL","zone":"1","total_lines":"3","rail":"0"},{"id":"286","latitude":"51.518","longitude":"-0.2809","name":"West Acton","display_name":"West<br />Acton","zone":"3","total_lines":"1","rail":"0"},{"id":"287","latitude":"51.4872","longitude":"-0.1953","name":"West Brompton","display_name":"West<br />Brompton","zone":"2","total_lines":"1","rail":"1"},{"id":"288","latitude":"51.6095","longitude":"-0.1883","name":"West Finchley","display_name":"West<br />Finchley","zone":"4","total_lines":"1","rail":"0"},{"id":"289","latitude":"51.5287","longitude":"0.0056","name":"West Ham","display_name":"West<br />Ham","zone":"3","total_lines":"3","rail":"1"},{"id":"290","latitude":"51.5469","longitude":"-0.1906","name":"West Hampstead","display_name":"West<br />Hampstead","zone":"2","total_lines":"1","rail":"1"},{"id":"291","latitude":"51.5795","longitude":"-0.3533","name":"West Harrow","display_name":"West<br />Harrow","zone":"5","total_lines":"1","rail":"0"},{"id":"292","latitude":"51.507","longitude":"-0.0203","name":"West India Quay","display_name":"West<br />India<br />Quay","zone":"2","total_lines":"1","rail":"0"},{"id":"293","latitude":"51.4907","longitude":"-0.2065","name":"West Kensington","display_name":"West<br />Kensington","zone":"2","total_lines":"1","rail":"0"},{"id":"294","latitude":"51.5696","longitude":"-0.4376","name":"West Ruislip","display_name":"West<br />Ruislip","zone":"6","total_lines":"1","rail":"1"},{"id":"295","latitude":"51.5194","longitude":"-0.0612","name":"Whitechapel","display_name":"NULL","zone":"2","total_lines":"3","rail":"0"},{"id":"296","latitude":"51.512","longitude":"-0.2239","name":"White City","display_name":"White<br />City","zone":"2","total_lines":"1","rail":"0"},{"id":"297","latitude":"51.5492","longitude":"-0.2215","name":"Willesden Green","display_name":"Willesden<br />Green","zone":"2.5","total_lines":"1","rail":"0"},{"id":"298","latitude":"51.5326","longitude":"-0.2478","name":"Willesden Junction","display_name":"Willesden<br />Junction","zone":"3","total_lines":"1","rail":"1"},{"id":"299","latitude":"51.4214","longitude":"-0.2064","name":"Wimbledon","display_name":"NULL","zone":"3","total_lines":"1","rail":"1"},{"id":"300","latitude":"51.4343","longitude":"-0.1992","name":"Wimbledon Park","display_name":"Wimbledon<br />Park","zone":"3","total_lines":"1","rail":"0"},{"id":"301","latitude":"51.607","longitude":"0.0341","name":"Woodford","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"302","latitude":"51.6179","longitude":"-0.1856","name":"Woodside Park","display_name":"Woodside<br />Park","zone":"4","total_lines":"1","rail":"0"},{"id":"303","latitude":"51.5975","longitude":"-0.1097","name":"Wood Green","display_name":"Wood<br />Green","zone":"3","total_lines":"1","rail":"0"},{"id":"35","latitude":"51.4627","longitude":"-0.1145","name":"Brixton","display_name":"NULL","zone":"2","total_lines":"1","rail":"1"},{"id":"6","latitude":"51.6736","longitude":"-0.607","name":"Amersham","display_name":"NULL","zone":"10","total_lines":"1","rail":"1"},{"id":"23","latitude":"51.4979","longitude":"-0.0637","name":"Bermondsey","display_name":"NULL","zone":"2","total_lines":"1","rail":"0"},{"id":"50","latitude":"51.7052","longitude":"-0.611","name":"Chesham","display_name":"NULL","zone":"10","total_lines":"1","rail":"0"},{"id":"46","latitude":"51.6679","longitude":"-0.561","name":"Chalfont & Latimer","display_name":"Chalfont &<br />Latimer","zone":"9","total_lines":"1","rail":"1"},{"id":"53","latitude":"51.6543","longitude":"-0.5183","name":"Chorleywood","display_name":"NULL","zone":"8","total_lines":"1","rail":"0"},{"id":"214","latitude":"51.6404","longitude":"-0.4733","name":"Rickmansworth","display_name":"NULL","zone":"7","total_lines":"1","rail":"0"},{"id":"62","latitude":"51.647","longitude":"-0.4412","name":"Croxley","display_name":"NULL","zone":"7","total_lines":"1","rail":"0"},{"id":"280","latitude":"51.6573","longitude":"-0.4177","name":"Watford","display_name":"NULL","zone":"8","total_lines":"1","rail":"0"},{"id":"221","latitude":"51.5606","longitude":"-0.4103","name":"Ruislip Gardens","display_name":"Ruislip<br />Gardens","zone":"5","total_lines":"1","rail":"0"},{"id":"121","latitude":"51.6503","longitude":"-0.1943","name":"High Barnet","display_name":"High<br />Barnet","zone":"5","total_lines":"1","rail":"0"},{"id":"261","latitude":"51.6302","longitude":"-0.1791","name":"Totteridge & Whetstone","display_name":"Totteridge<br />& Whetstone","zone":"4","total_lines":"1","rail":"0"},{"id":"57","latitude":"51.6517","longitude":"-0.1496","name":"Cockfosters","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"187","latitude":"51.6476","longitude":"-0.1318","name":"Oakwood","display_name":"NULL","zone":"5","total_lines":"1","rail":"0"},{"id":"232","latitude":"51.6322","longitude":"-0.128","name":"Southgate","display_name":"NULL","zone":"4","total_lines":"1","rail":"0"},{"id":"88","latitude":"51.6937","longitude":"0.1139","name":"Epping","display_name":"NULL","zone":"6","total_lines":"1","rail":"0"},{"id":"256","latitude":"51.6717","longitude":"0.1033","name":"Theydon Bois","display_name":"Theydon<br />Bois","zone":"6","total_lines":"1","rail":"0"},{"id":"68","latitude":"51.6455","longitude":"0.0838","name":"Debden","display_name":"NULL","zone":"6","total_lines":"1","rail":"0"},{"id":"158","latitude":"51.6412","longitude":"0.0558","name":"Loughton","display_name":"NULL","zone":"6","total_lines":"1","rail":"0"},{"id":"37","latitude":"51.6266","longitude":"0.0471","name":"Buckhurst Hill","display_name":"Buckhurst<br />Hill","zone":"5","total_lines":"1","rail":"0"},{"id":"204","latitude":"51.5343","longitude":"-0.0139","name":"Pudding Mill Lane","display_name":"Pudding<br />Mill Lane","zone":"2.5","total_lines":"1","rail":"0"},{"id":"233","latitude":"51.501","longitude":"-0.1052","name":"Southwark","display_name":"NULL","zone":"1","total_lines":"1","rail":"0"},{"id":"41","latitude":"51.4982","longitude":"-0.0502","name":"Canada Water","display_name":"Canada<br />Water","zone":"2","total_lines":"2","rail":"0"},{"id":"43","latitude":"51.5147","longitude":"0.0082","name":"Canning Town","display_name":"Canning<br />Town","zone":"3","total_lines":"2","rail":"0"},{"id":"183","latitude":"51.5005","longitude":"0.0039","name":"North Greenwich","display_name":"North<br />Greenwich","zone":"2.5","total_lines":"1","rail":"0"},{"id":"64","latitude":"51.4827","longitude":"-0.0096","name":"Cutty Sark","display_name":"Cutty<br />Sark","zone":"2.5","total_lines":"1","rail":"0"},{"id":"106","latitude":"51.4781","longitude":"-0.0149","name":"Greenwich","display_name":"NULL","zone":"2.5","total_lines":"1","rail":"1"},{"id":"69","latitude":"51.474","longitude":"-0.0216","name":"Deptford Bridge","display_name":"Deptford<br />Bridge","zone":"2.5","total_lines":"1","rail":"0"},{"id":"86","latitude":"51.4693","longitude":"-0.0174","name":"Elverson Road","display_name":"Elverson<br />Road","zone":"2.5","total_lines":"1","rail":"0"},{"id":"152","latitude":"51.4657","longitude":"-0.0142","name":"Lewisham","display_name":"NULL","zone":"2.5","total_lines":"1","rail":"1"},{"id":"174","latitude":"51.4767","longitude":"-0.0327","name":"New Cross","display_name":"New<br />Cross","zone":"2","total_lines":"1","rail":"1"},{"id":"175","latitude":"51.4757","longitude":"-0.0402","name":"New Cross Gate","display_name":"New<br />Cross<br />Gate","zone":"2","total_lines":"1","rail":"1"}]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.TubeMaps=e()}}(function(){var define,module,exports;return (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.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})({1:[function(require,module,exports){ | |
(function (__dirname){ | |
module.exports = { | |
"london": { | |
csv: { | |
connections: __dirname + '/london.connections.csv', | |
lines: __dirname + '/london.lines.csv', | |
stations: __dirname + '/london.stations.csv' | |
}, | |
json: __dirname + '/london.json' | |
} | |
}; | |
}).call(this,"/datasets") | |
},{}],2:[function(require,module,exports){ | |
var async = require('async'); | |
var fs = require('fs'); | |
var path = require('path'); | |
var JSONStream = require('JSONStream'); | |
var __ = require('highland'); | |
var datasets = require('./datasets'); | |
exports.TubeMap = TubeMap; | |
exports.Maps = Maps; | |
function Station(opts) { | |
this.id = opts.id; | |
this.conns = opts.conns || []; | |
this.name = opts.name || "Unknown"; | |
this.display_name = opts.display_name || "Unknown"; | |
this.rail = opts.rail ? parseInt(opts.rail, 10) : null; | |
this.total_lines = opts.total_lines ? parseInt(opts.total_lines, 10) : 1; | |
this.latitude = +parseFloat(opts.latitude); | |
this.longitude = +parseFloat(opts.longitude); | |
} | |
function TubeMap(opts) { | |
opts = opts || {}; | |
this.stationsById = {}; | |
this.stationsByName = {}; | |
this.linesById = {}; | |
this.linesByName = {}; | |
this.stations = opts.stations || []; | |
this.connections = opts.connections || []; | |
this.lines = opts.lines || []; | |
this.make(); | |
} | |
TubeMap.prototype.makeStation = function(s) { | |
s = new Station(s); | |
this.stationsById[s.id] = s; | |
this.stationsByName[s.name] = s; | |
}; | |
TubeMap.prototype.getAdjacent = function (root, line) { | |
var conns = root.conns; | |
if (line) { | |
conns = conns.filter(function (c) { | |
return c.line == line; | |
}); | |
} | |
return conns.map(function(c) { | |
c.station1.line = c.line; | |
c.station2.line = c.line; | |
return c.station1.id !== root.id ? c.station1 : c.station2; | |
}); | |
}; | |
TubeMap.prototype.constructPath = function (dict, path, start, destination){ | |
if (path[path.length-1] && path[path.length-1].station2 == start) { | |
return path; | |
} | |
path.push({station1: destination, station2: dict[destination.id]}); | |
return this.constructPath(dict, path, start, dict[destination.id]); | |
}; | |
TubeMap.prototype.path = function(start, destination, line){ | |
var Q = [destination]; | |
var V = {}; | |
var family = {}; | |
family[destination.id] = null; | |
while (Q.length > 0) { | |
var currentStation = Q.shift(); | |
if (currentStation.id in V) { | |
continue; | |
} | |
var conns = this.getAdjacent(currentStation, line); | |
conns.forEach(function(child) { | |
if (!(child.id in V)){ | |
Q.push(child); | |
} | |
if (!(child.id in family)){ | |
family[child.id] = currentStation; | |
} | |
}); | |
V[currentStation.id] = currentStation; | |
if (currentStation.id === start.id) { | |
var path = this.constructPath(family, [], destination, start); | |
if (line) { | |
path = path.map(function(d) { | |
d.line = line; | |
return d; | |
}); | |
} | |
return path; | |
} | |
} | |
return false; | |
}; | |
TubeMap.prototype.line = function(id) { | |
return this.connections.filter(function(d) { | |
return d.line == id; | |
}); | |
}; | |
TubeMap.prototype.makeConnection = function(c) { | |
c.station1 = this.stationsById[c.station1]; | |
c.station2 = this.stationsById[c.station2]; | |
c.time = parseInt(c.time, 10); | |
c.station1.conns.push(c); | |
c.station2.conns.push(c); | |
}; | |
TubeMap.prototype.makeLine = function(r) { | |
this.linesById[r.line] = r; | |
this.linesByName[r.name] = r; | |
}; | |
TubeMap.prototype.getLine = function(id) { | |
return this.linesById[id]; | |
}; | |
TubeMap.prototype.getLineByName = function(name) { | |
return this.linesByName[name]; | |
} | |
TubeMap.prototype.getStation = function(id) { | |
return this.stationsById[id]; | |
}; | |
TubeMap.prototype.getStationByName = function(display_name) { | |
return this.stationsByName[display_name]; | |
}; | |
TubeMap.prototype.make = function() { | |
this.stations.forEach(this.makeStation.bind(this)); | |
this.connections.forEach(this.makeConnection.bind(this)); | |
this.lines.forEach(this.makeLine.bind(this)); | |
}; | |
function Maps (city, cb) { | |
var dataset = require(datasets[city].json); | |
cb(null, new TubeMap(dataset)); | |
}; | |
},{"./datasets":1,"JSONStream":3,"async":6,"fs":8,"highland":7,"path":15}],3:[function(require,module,exports){ | |
(function (process,Buffer){ | |
var Parser = require('jsonparse') | |
, through = require('through') | |
/* | |
the value of this.stack that creationix's jsonparse has is weird. | |
it makes this code ugly, but his problem is way harder that mine, | |
so i'll forgive him. | |
*/ | |
exports.parse = function (path, map) { | |
var parser = new Parser() | |
var stream = through(function (chunk) { | |
if('string' === typeof chunk) | |
chunk = new Buffer(chunk) | |
parser.write(chunk) | |
}, | |
function (data) { | |
if(data) | |
stream.write(data) | |
stream.queue(null) | |
}) | |
if('string' === typeof path) | |
path = path.split('.').map(function (e) { | |
if (e === '*') | |
return true | |
else if (e === '') // '..'.split('.') returns an empty string | |
return {recurse: true} | |
else | |
return e | |
}) | |
var count = 0, _key | |
if(!path || !path.length) | |
path = null | |
parser.onValue = function (value) { | |
if (!this.root) | |
stream.root = value | |
if(! path) return | |
var i = 0 // iterates on path | |
var j = 0 // iterates on stack | |
while (i < path.length) { | |
var key = path[i] | |
var c | |
j++ | |
if (key && !key.recurse) { | |
c = (j === this.stack.length) ? this : this.stack[j] | |
if (!c) return | |
if (! check(key, c.key)) return | |
i++ | |
} else { | |
i++ | |
var nextKey = path[i] | |
if (! nextKey) return | |
while (true) { | |
c = (j === this.stack.length) ? this : this.stack[j] | |
if (!c) return | |
if (check(nextKey, c.key)) { i++; break} | |
j++ | |
} | |
} | |
} | |
if (j !== this.stack.length) return | |
count ++ | |
var actualPath = this.stack.slice(1).map(function(element) { return element.key }).concat([this.key]) | |
var data = this.value[this.key] | |
if(null != data) | |
if(null != (data = map ? map(data, actualPath) : data)) | |
stream.queue(data) | |
delete this.value[this.key] | |
} | |
parser._onToken = parser.onToken; | |
parser.onToken = function (token, value) { | |
parser._onToken(token, value); | |
if (this.stack.length === 0) { | |
if (stream.root) { | |
if(!path) | |
stream.queue(stream.root) | |
stream.emit('root', stream.root, count) | |
count = 0; | |
stream.root = null; | |
} | |
} | |
} | |
parser.onError = function (err) { | |
stream.emit('error', err) | |
} | |
return stream | |
} | |
function check (x, y) { | |
if ('string' === typeof x) | |
return y == x | |
else if (x && 'function' === typeof x.exec) | |
return x.exec(y) | |
else if ('boolean' === typeof x) | |
return x | |
else if ('function' === typeof x) | |
return x(y) | |
return false | |
} | |
exports.stringify = function (op, sep, cl, indent) { | |
indent = indent || 0 | |
if (op === false){ | |
op = '' | |
sep = '\n' | |
cl = '' | |
} else if (op == null) { | |
op = '[\n' | |
sep = '\n,\n' | |
cl = '\n]\n' | |
} | |
//else, what ever you like | |
var stream | |
, first = true | |
, anyData = false | |
stream = through(function (data) { | |
anyData = true | |
var json = JSON.stringify(data, null, indent) | |
if(first) { first = false ; stream.queue(op + json)} | |
else stream.queue(sep + json) | |
}, | |
function (data) { | |
if(!anyData) | |
stream.queue(op) | |
stream.queue(cl) | |
stream.queue(null) | |
}) | |
return stream | |
} | |
exports.stringifyObject = function (op, sep, cl, indent) { | |
indent = indent || 0 | |
if (op === false){ | |
op = '' | |
sep = '\n' | |
cl = '' | |
} else if (op == null) { | |
op = '{\n' | |
sep = '\n,\n' | |
cl = '\n}\n' | |
} | |
//else, what ever you like | |
var first = true | |
, anyData = false | |
stream = through(function (data) { | |
anyData = true | |
var json = JSON.stringify(data[0]) + ':' + JSON.stringify(data[1], null, indent) | |
if(first) { first = false ; this.queue(op + json)} | |
else this.queue(sep + json) | |
}, | |
function (data) { | |
if(!anyData) this.queue(op) | |
this.queue(cl) | |
this.queue(null) | |
}) | |
return stream | |
} | |
if(!module.parent && process.title !== 'browser') { | |
process.stdin | |
.pipe(exports.parse(process.argv[2])) | |
.pipe(exports.stringify('[', ',\n', ']\n', 2)) | |
.pipe(process.stdout) | |
} | |
}).call(this,require('_process'),require("buffer").Buffer) | |
},{"_process":16,"buffer":9,"jsonparse":4,"through":5}],4:[function(require,module,exports){ | |
(function (Buffer){ | |
/*global Buffer*/ | |
// Named constants with unique integer values | |
var C = {}; | |
// Tokens | |
var LEFT_BRACE = C.LEFT_BRACE = 0x1; | |
var RIGHT_BRACE = C.RIGHT_BRACE = 0x2; | |
var LEFT_BRACKET = C.LEFT_BRACKET = 0x3; | |
var RIGHT_BRACKET = C.RIGHT_BRACKET = 0x4; | |
var COLON = C.COLON = 0x5; | |
var COMMA = C.COMMA = 0x6; | |
var TRUE = C.TRUE = 0x7; | |
var FALSE = C.FALSE = 0x8; | |
var NULL = C.NULL = 0x9; | |
var STRING = C.STRING = 0xa; | |
var NUMBER = C.NUMBER = 0xb; | |
// Tokenizer States | |
var START = C.START = 0x11; | |
var TRUE1 = C.TRUE1 = 0x21; | |
var TRUE2 = C.TRUE2 = 0x22; | |
var TRUE3 = C.TRUE3 = 0x23; | |
var FALSE1 = C.FALSE1 = 0x31; | |
var FALSE2 = C.FALSE2 = 0x32; | |
var FALSE3 = C.FALSE3 = 0x33; | |
var FALSE4 = C.FALSE4 = 0x34; | |
var NULL1 = C.NULL1 = 0x41; | |
var NULL2 = C.NULL3 = 0x42; | |
var NULL3 = C.NULL2 = 0x43; | |
var NUMBER1 = C.NUMBER1 = 0x51; | |
var NUMBER2 = C.NUMBER2 = 0x52; | |
var NUMBER3 = C.NUMBER3 = 0x53; | |
var NUMBER4 = C.NUMBER4 = 0x54; | |
var NUMBER5 = C.NUMBER5 = 0x55; | |
var NUMBER6 = C.NUMBER6 = 0x56; | |
var NUMBER7 = C.NUMBER7 = 0x57; | |
var NUMBER8 = C.NUMBER8 = 0x58; | |
var STRING1 = C.STRING1 = 0x61; | |
var STRING2 = C.STRING2 = 0x62; | |
var STRING3 = C.STRING3 = 0x63; | |
var STRING4 = C.STRING4 = 0x64; | |
var STRING5 = C.STRING5 = 0x65; | |
var STRING6 = C.STRING6 = 0x66; | |
// Parser States | |
var VALUE = C.VALUE = 0x71; | |
var KEY = C.KEY = 0x72; | |
// Parser Modes | |
var OBJECT = C.OBJECT = 0x81; | |
var ARRAY = C.ARRAY = 0x82; | |
// Slow code to string converter (only used when throwing syntax errors) | |
function toknam(code) { | |
var keys = Object.keys(C); | |
for (var i = 0, l = keys.length; i < l; i++) { | |
var key = keys[i]; | |
if (C[key] === code) { return key; } | |
} | |
return code && ("0x" + code.toString(16)); | |
} | |
function Parser() { | |
this.tState = START; | |
this.value = undefined; | |
this.string = undefined; // string data | |
this.unicode = undefined; // unicode escapes | |
// For number parsing | |
this.negative = undefined; | |
this.magnatude = undefined; | |
this.position = undefined; | |
this.exponent = undefined; | |
this.negativeExponent = undefined; | |
this.key = undefined; | |
this.mode = undefined; | |
this.stack = []; | |
this.state = VALUE; | |
this.bytes_remaining = 0; // number of bytes remaining in multi byte utf8 char to read after split boundary | |
this.bytes_in_sequence = 0; // bytes in multi byte utf8 char to read | |
this.temp_buffs = { "2": new Buffer(2), "3": new Buffer(3), "4": new Buffer(4) }; // for rebuilding chars split before boundary is reached | |
} | |
var proto = Parser.prototype; | |
proto.charError = function (buffer, i) { | |
this.onError(new Error("Unexpected " + JSON.stringify(String.fromCharCode(buffer[i])) + " at position " + i + " in state " + toknam(this.tState))); | |
}; | |
proto.onError = function (err) { throw err; }; | |
proto.write = function (buffer) { | |
if (typeof buffer === "string") buffer = new Buffer(buffer); | |
//process.stdout.write("Input: "); | |
//console.dir(buffer.toString()); | |
var n; | |
for (var i = 0, l = buffer.length; i < l; i++) { | |
if (this.tState === START){ | |
n = buffer[i]; | |
if(n === 0x7b){ this.onToken(LEFT_BRACE, "{"); // { | |
}else if(n === 0x7d){ this.onToken(RIGHT_BRACE, "}"); // } | |
}else if(n === 0x5b){ this.onToken(LEFT_BRACKET, "["); // [ | |
}else if(n === 0x5d){ this.onToken(RIGHT_BRACKET, "]"); // ] | |
}else if(n === 0x3a){ this.onToken(COLON, ":"); // : | |
}else if(n === 0x2c){ this.onToken(COMMA, ","); // , | |
}else if(n === 0x74){ this.tState = TRUE1; // t | |
}else if(n === 0x66){ this.tState = FALSE1; // f | |
}else if(n === 0x6e){ this.tState = NULL1; // n | |
}else if(n === 0x22){ this.string = ""; this.tState = STRING1; // " | |
}else if(n === 0x2d){ this.negative = true; this.tState = NUMBER1; // - | |
}else if(n === 0x30){ this.magnatude = 0; this.tState = NUMBER2; // 0 | |
}else{ | |
if (n > 0x30 && n < 0x40) { // 1-9 | |
this.magnatude = n - 0x30; this.tState = NUMBER3; | |
} else if (n === 0x20 || n === 0x09 || n === 0x0a || n === 0x0d) { | |
// whitespace | |
} else { this.charError(buffer, i); } | |
} | |
}else if (this.tState === STRING1){ // After open quote | |
n = buffer[i]; // get current byte from buffer | |
// check for carry over of a multi byte char split between data chunks | |
// & fill temp buffer it with start of this data chunk up to the boundary limit set in the last iteration | |
if (this.bytes_remaining > 0) { | |
for (var j = 0; j < this.bytes_remaining; j++) { | |
this.temp_buffs[this.bytes_in_sequence][this.bytes_in_sequence - this.bytes_remaining + j] = buffer[j]; | |
} | |
this.string += this.temp_buffs[this.bytes_in_sequence].toString(); | |
this.bytes_in_sequence = this.bytes_remaining = 0; | |
i = i + j - 1; | |
} else if (this.bytes_remaining === 0 && n >= 128) { // else if no remainder bytes carried over, parse multi byte (>=128) chars one at a time | |
if ((n >= 194) && (n <= 223)) this.bytes_in_sequence = 2; | |
if ((n >= 224) && (n <= 239)) this.bytes_in_sequence = 3; | |
if ((n >= 240) && (n <= 244)) this.bytes_in_sequence = 4; | |
if ((this.bytes_in_sequence + i) > buffer.length) { // if bytes needed to complete char fall outside buffer length, we have a boundary split | |
for (var k = 0; k <= (buffer.length - 1 - i); k++) { | |
this.temp_buffs[this.bytes_in_sequence][k] = buffer[i + k]; // fill temp buffer of correct size with bytes available in this chunk | |
} | |
this.bytes_remaining = (i + this.bytes_in_sequence) - buffer.length; | |
i = buffer.length - 1; | |
} else { | |
this.string += buffer.slice(i, (i + this.bytes_in_sequence)).toString(); | |
i = i + this.bytes_in_sequence - 1; | |
} | |
} else if (n === 0x22) { this.tState = START; this.onToken(STRING, this.string); this.string = undefined; } | |
else if (n === 0x5c) { this.tState = STRING2; } | |
else if (n >= 0x20) { this.string += String.fromCharCode(n); } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === STRING2){ // After backslash | |
n = buffer[i]; | |
if(n === 0x22){ this.string += "\""; this.tState = STRING1; | |
}else if(n === 0x5c){ this.string += "\\"; this.tState = STRING1; | |
}else if(n === 0x2f){ this.string += "\/"; this.tState = STRING1; | |
}else if(n === 0x62){ this.string += "\b"; this.tState = STRING1; | |
}else if(n === 0x66){ this.string += "\f"; this.tState = STRING1; | |
}else if(n === 0x6e){ this.string += "\n"; this.tState = STRING1; | |
}else if(n === 0x72){ this.string += "\r"; this.tState = STRING1; | |
}else if(n === 0x74){ this.string += "\t"; this.tState = STRING1; | |
}else if(n === 0x75){ this.unicode = ""; this.tState = STRING3; | |
}else{ | |
this.charError(buffer, i); | |
} | |
}else if (this.tState === STRING3 || this.tState === STRING4 || this.tState === STRING5 || this.tState === STRING6){ // unicode hex codes | |
n = buffer[i]; | |
// 0-9 A-F a-f | |
if ((n >= 0x30 && n < 0x40) || (n > 0x40 && n <= 0x46) || (n > 0x60 && n <= 0x66)) { | |
this.unicode += String.fromCharCode(n); | |
if (this.tState++ === STRING6) { | |
this.string += String.fromCharCode(parseInt(this.unicode, 16)); | |
this.unicode = undefined; | |
this.tState = STRING1; | |
} | |
} else { | |
this.charError(buffer, i); | |
} | |
}else if (this.tState === NUMBER1){ // after minus | |
n = buffer[i]; | |
if (n === 0x30) { this.magnatude = 0; this.tState = NUMBER2; } | |
else if (n > 0x30 && n < 0x40) { this.magnatude = n - 0x30; this.tState = NUMBER3; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === NUMBER2){ // * After initial zero | |
n = buffer[i]; | |
if(n === 0x2e){ // . | |
this.position = 0.1; this.tState = NUMBER4; | |
}else if(n === 0x65 || n === 0x45){ // e/E | |
this.exponent = 0; this.tState = NUMBER6; | |
}else{ | |
this.tState = START; | |
this.onToken(NUMBER, 0); | |
this.magnatude = undefined; | |
this.negative = undefined; | |
i--; | |
} | |
}else if (this.tState === NUMBER3){ // * After digit (before period) | |
n = buffer[i]; | |
if(n === 0x2e){ // . | |
this.position = 0.1; this.tState = NUMBER4; | |
}else if(n === 0x65 || n === 0x45){ // e/E | |
this.exponent = 0; this.tState = NUMBER6; | |
}else{ | |
if (n >= 0x30 && n < 0x40) { this.magnatude = this.magnatude * 10 + n - 0x30; } | |
else { | |
this.tState = START; | |
if (this.negative) { | |
this.magnatude = -this.magnatude; | |
this.negative = undefined; | |
} | |
this.onToken(NUMBER, this.magnatude); | |
this.magnatude = undefined; | |
i--; | |
} | |
} | |
}else if (this.tState === NUMBER4){ // After period | |
n = buffer[i]; | |
if (n >= 0x30 && n < 0x40) { // 0-9 | |
this.magnatude += this.position * (n - 0x30); | |
this.position /= 10; | |
this.tState = NUMBER5; | |
} else { this.charError(buffer, i); } | |
}else if (this.tState === NUMBER5){ // * After digit (after period) | |
n = buffer[i]; | |
if (n >= 0x30 && n < 0x40) { // 0-9 | |
this.magnatude += this.position * (n - 0x30); | |
this.position /= 10; | |
} | |
else if (n === 0x65 || n === 0x45) { this.exponent = 0; this.tState = NUMBER6; } // E/e | |
else { | |
this.tState = START; | |
if (this.negative) { | |
this.magnatude = -this.magnatude; | |
this.negative = undefined; | |
} | |
this.onToken(NUMBER, this.negative ? -this.magnatude : this.magnatude); | |
this.magnatude = undefined; | |
this.position = undefined; | |
i--; | |
} | |
}else if (this.tState === NUMBER6){ // After E | |
n = buffer[i]; | |
if (n === 0x2b || n === 0x2d) { // +/- | |
if (n === 0x2d) { this.negativeExponent = true; } | |
this.tState = NUMBER7; | |
} | |
else if (n >= 0x30 && n < 0x40) { | |
this.exponent = this.exponent * 10 + (n - 0x30); | |
this.tState = NUMBER8; | |
} | |
else { this.charError(buffer, i); } | |
}else if (this.tState === NUMBER7){ // After +/- | |
n = buffer[i]; | |
if (n >= 0x30 && n < 0x40) { // 0-9 | |
this.exponent = this.exponent * 10 + (n - 0x30); | |
this.tState = NUMBER8; | |
} | |
else { this.charError(buffer, i); } | |
}else if (this.tState === NUMBER8){ // * After digit (after +/-) | |
n = buffer[i]; | |
if (n >= 0x30 && n < 0x40) { // 0-9 | |
this.exponent = this.exponent * 10 + (n - 0x30); | |
} | |
else { | |
if (this.negativeExponent) { | |
this.exponent = -this.exponent; | |
this.negativeExponent = undefined; | |
} | |
this.magnatude *= Math.pow(10, this.exponent); | |
this.exponent = undefined; | |
if (this.negative) { | |
this.magnatude = -this.magnatude; | |
this.negative = undefined; | |
} | |
this.tState = START; | |
this.onToken(NUMBER, this.magnatude); | |
this.magnatude = undefined; | |
i--; | |
} | |
}else if (this.tState === TRUE1){ // r | |
if (buffer[i] === 0x72) { this.tState = TRUE2; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === TRUE2){ // u | |
if (buffer[i] === 0x75) { this.tState = TRUE3; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === TRUE3){ // e | |
if (buffer[i] === 0x65) { this.tState = START; this.onToken(TRUE, true); } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === FALSE1){ // a | |
if (buffer[i] === 0x61) { this.tState = FALSE2; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === FALSE2){ // l | |
if (buffer[i] === 0x6c) { this.tState = FALSE3; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === FALSE3){ // s | |
if (buffer[i] === 0x73) { this.tState = FALSE4; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === FALSE4){ // e | |
if (buffer[i] === 0x65) { this.tState = START; this.onToken(FALSE, false); } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === NULL1){ // u | |
if (buffer[i] === 0x75) { this.tState = NULL2; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === NULL2){ // l | |
if (buffer[i] === 0x6c) { this.tState = NULL3; } | |
else { this.charError(buffer, i); } | |
}else if (this.tState === NULL3){ // l | |
if (buffer[i] === 0x6c) { this.tState = START; this.onToken(NULL, null); } | |
else { this.charError(buffer, i); } | |
} | |
} | |
}; | |
proto.onToken = function (token, value) { | |
// Override this to get events | |
}; | |
proto.parseError = function (token, value) { | |
this.onError(new Error("Unexpected " + toknam(token) + (value ? ("(" + JSON.stringify(value) + ")") : "") + " in state " + toknam(this.state))); | |
}; | |
proto.onError = function (err) { throw err; }; | |
proto.push = function () { | |
this.stack.push({value: this.value, key: this.key, mode: this.mode}); | |
}; | |
proto.pop = function () { | |
var value = this.value; | |
var parent = this.stack.pop(); | |
this.value = parent.value; | |
this.key = parent.key; | |
this.mode = parent.mode; | |
this.emit(value); | |
if (!this.mode) { this.state = VALUE; } | |
}; | |
proto.emit = function (value) { | |
if (this.mode) { this.state = COMMA; } | |
this.onValue(value); | |
}; | |
proto.onValue = function (value) { | |
// Override me | |
}; | |
proto.onToken = function (token, value) { | |
//console.log("OnToken: state=%s token=%s %s", toknam(this.state), toknam(token), value?JSON.stringify(value):""); | |
if(this.state === VALUE){ | |
if(token === STRING || token === NUMBER || token === TRUE || token === FALSE || token === NULL){ | |
if (this.value) { | |
this.value[this.key] = value; | |
} | |
this.emit(value); | |
}else if(token === LEFT_BRACE){ | |
this.push(); | |
if (this.value) { | |
this.value = this.value[this.key] = {}; | |
} else { | |
this.value = {}; | |
} | |
this.key = undefined; | |
this.state = KEY; | |
this.mode = OBJECT; | |
}else if(token === LEFT_BRACKET){ | |
this.push(); | |
if (this.value) { | |
this.value = this.value[this.key] = []; | |
} else { | |
this.value = []; | |
} | |
this.key = 0; | |
this.mode = ARRAY; | |
this.state = VALUE; | |
}else if(token === RIGHT_BRACE){ | |
if (this.mode === OBJECT) { | |
this.pop(); | |
} else { | |
this.parseError(token, value); | |
} | |
}else if(token === RIGHT_BRACKET){ | |
if (this.mode === ARRAY) { | |
this.pop(); | |
} else { | |
this.parseError(token, value); | |
} | |
}else{ | |
this.parseError(token, value); | |
} | |
}else if(this.state === KEY){ | |
if (token === STRING) { | |
this.key = value; | |
this.state = COLON; | |
} else if (token === RIGHT_BRACE) { | |
this.pop(); | |
} else { | |
this.parseError(token, value); | |
} | |
}else if(this.state === COLON){ | |
if (token === COLON) { this.state = VALUE; } | |
else { this.parseError(token, value); } | |
}else if(this.state === COMMA){ | |
if (token === COMMA) { | |
if (this.mode === ARRAY) { this.key++; this.state = VALUE; } | |
else if (this.mode === OBJECT) { this.state = KEY; } | |
} else if (token === RIGHT_BRACKET && this.mode === ARRAY || token === RIGHT_BRACE && this.mode === OBJECT) { | |
this.pop(); | |
} else { | |
this.parseError(token, value); | |
} | |
}else{ | |
this.parseError(token, value); | |
} | |
}; | |
module.exports = Parser; | |
}).call(this,require("buffer").Buffer) | |
},{"buffer":9}],5:[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('_process')) | |
},{"_process":16,"stream":29}],6:[function(require,module,exports){ | |
(function (process){ | |
/*! | |
* async | |
* https://github.com/caolan/async | |
* | |
* Copyright 2010-2014 Caolan McMahon | |
* Released under the MIT license | |
*/ | |
/*jshint onevar: false, indent:4 */ | |
/*global setImmediate: false, setTimeout: false, console: false */ | |
(function () { | |
var async = {}; | |
// global on the server, window in the browser | |
var root, previous_async; | |
root = this; | |
if (root != null) { | |
previous_async = root.async; | |
} | |
async.noConflict = function () { | |
root.async = previous_async; | |
return async; | |
}; | |
function only_once(fn) { | |
var called = false; | |
return function() { | |
if (called) throw new Error("Callback was already called."); | |
called = true; | |
fn.apply(root, arguments); | |
} | |
} | |
//// cross-browser compatiblity functions //// | |
var _toString = Object.prototype.toString; | |
var _isArray = Array.isArray || function (obj) { | |
return _toString.call(obj) === '[object Array]'; | |
}; | |
var _each = function (arr, iterator) { | |
if (arr.forEach) { | |
return arr.forEach(iterator); | |
} | |
for (var i = 0; i < arr.length; i += 1) { | |
iterator(arr[i], i, arr); | |
} | |
}; | |
var _map = function (arr, iterator) { | |
if (arr.map) { | |
return arr.map(iterator); | |
} | |
var results = []; | |
_each(arr, function (x, i, a) { | |
results.push(iterator(x, i, a)); | |
}); | |
return results; | |
}; | |
var _reduce = function (arr, iterator, memo) { | |
if (arr.reduce) { | |
return arr.reduce(iterator, memo); | |
} | |
_each(arr, function (x, i, a) { | |
memo = iterator(memo, x, i, a); | |
}); | |
return memo; | |
}; | |
var _keys = function (obj) { | |
if (Object.keys) { | |
return Object.keys(obj); | |
} | |
var keys = []; | |
for (var k in obj) { | |
if (obj.hasOwnProperty(k)) { | |
keys.push(k); | |
} | |
} | |
return keys; | |
}; | |
//// exported async module functions //// | |
//// nextTick implementation with browser-compatible fallback //// | |
if (typeof process === 'undefined' || !(process.nextTick)) { | |
if (typeof setImmediate === 'function') { | |
async.nextTick = function (fn) { | |
// not a direct alias for IE10 compatibility | |
setImmediate(fn); | |
}; | |
async.setImmediate = async.nextTick; | |
} | |
else { | |
async.nextTick = function (fn) { | |
setTimeout(fn, 0); | |
}; | |
async.setImmediate = async.nextTick; | |
} | |
} | |
else { | |
async.nextTick = process.nextTick; | |
if (typeof setImmediate !== 'undefined') { | |
async.setImmediate = function (fn) { | |
// not a direct alias for IE10 compatibility | |
setImmediate(fn); | |
}; | |
} | |
else { | |
async.setImmediate = async.nextTick; | |
} | |
} | |
async.each = function (arr, iterator, callback) { | |
callback = callback || function () {}; | |
if (!arr.length) { | |
return callback(); | |
} | |
var completed = 0; | |
_each(arr, function (x) { | |
iterator(x, only_once(done) ); | |
}); | |
function done(err) { | |
if (err) { | |
callback(err); | |
callback = function () {}; | |
} | |
else { | |
completed += 1; | |
if (completed >= arr.length) { | |
callback(); | |
} | |
} | |
} | |
}; | |
async.forEach = async.each; | |
async.eachSeries = function (arr, iterator, callback) { | |
callback = callback || function () {}; | |
if (!arr.length) { | |
return callback(); | |
} | |
var completed = 0; | |
var iterate = function () { | |
iterator(arr[completed], function (err) { | |
if (err) { | |
callback(err); | |
callback = function () {}; | |
} | |
else { | |
completed += 1; | |
if (completed >= arr.length) { | |
callback(); | |
} | |
else { | |
iterate(); | |
} | |
} | |
}); | |
}; | |
iterate(); | |
}; | |
async.forEachSeries = async.eachSeries; | |
async.eachLimit = function (arr, limit, iterator, callback) { | |
var fn = _eachLimit(limit); | |
fn.apply(null, [arr, iterator, callback]); | |
}; | |
async.forEachLimit = async.eachLimit; | |
var _eachLimit = function (limit) { | |
return function (arr, iterator, callback) { | |
callback = callback || function () {}; | |
if (!arr.length || limit <= 0) { | |
return callback(); | |
} | |
var completed = 0; | |
var started = 0; | |
var running = 0; | |
(function replenish () { | |
if (completed >= arr.length) { | |
return callback(); | |
} | |
while (running < limit && started < arr.length) { | |
started += 1; | |
running += 1; | |
iterator(arr[started - 1], function (err) { | |
if (err) { | |
callback(err); | |
callback = function () {}; | |
} | |
else { | |
completed += 1; | |
running -= 1; | |
if (completed >= arr.length) { | |
callback(); | |
} | |
else { | |
replenish(); | |
} | |
} | |
}); | |
} | |
})(); | |
}; | |
}; | |
var doParallel = function (fn) { | |
return function () { | |
var args = Array.prototype.slice.call(arguments); | |
return fn.apply(null, [async.each].concat(args)); | |
}; | |
}; | |
var doParallelLimit = function(limit, fn) { | |
return function () { | |
var args = Array.prototype.slice.call(arguments); | |
return fn.apply(null, [_eachLimit(limit)].concat(args)); | |
}; | |
}; | |
var doSeries = function (fn) { | |
return function () { | |
var args = Array.prototype.slice.call(arguments); | |
return fn.apply(null, [async.eachSeries].concat(args)); | |
}; | |
}; | |
var _asyncMap = function (eachfn, arr, iterator, callback) { | |
arr = _map(arr, function (x, i) { | |
return {index: i, value: x}; | |
}); | |
if (!callback) { | |
eachfn(arr, function (x, callback) { | |
iterator(x.value, function (err) { | |
callback(err); | |
}); | |
}); | |
} else { | |
var results = []; | |
eachfn(arr, function (x, callback) { | |
iterator(x.value, function (err, v) { | |
results[x.index] = v; | |
callback(err); | |
}); | |
}, function (err) { | |
callback(err, results); | |
}); | |
} | |
}; | |
async.map = doParallel(_asyncMap); | |
async.mapSeries = doSeries(_asyncMap); | |
async.mapLimit = function (arr, limit, iterator, callback) { | |
return _mapLimit(limit)(arr, iterator, callback); | |
}; | |
var _mapLimit = function(limit) { | |
return doParallelLimit(limit, _asyncMap); | |
}; | |
// reduce only has a series version, as doing reduce in parallel won't | |
// work in many situations. | |
async.reduce = function (arr, memo, iterator, callback) { | |
async.eachSeries(arr, function (x, callback) { | |
iterator(memo, x, function (err, v) { | |
memo = v; | |
callback(err); | |
}); | |
}, function (err) { | |
callback(err, memo); | |
}); | |
}; | |
// inject alias | |
async.inject = async.reduce; | |
// foldl alias | |
async.foldl = async.reduce; | |
async.reduceRight = function (arr, memo, iterator, callback) { | |
var reversed = _map(arr, function (x) { | |
return x; | |
}).reverse(); | |
async.reduce(reversed, memo, iterator, callback); | |
}; | |
// foldr alias | |
async.foldr = async.reduceRight; | |
var _filter = function (eachfn, arr, iterator, callback) { | |
var results = []; | |
arr = _map(arr, function (x, i) { | |
return {index: i, value: x}; | |
}); | |
eachfn(arr, function (x, callback) { | |
iterator(x.value, function (v) { | |
if (v) { | |
results.push(x); | |
} | |
callback(); | |
}); | |
}, function (err) { | |
callback(_map(results.sort(function (a, b) { | |
return a.index - b.index; | |
}), function (x) { | |
return x.value; | |
})); | |
}); | |
}; | |
async.filter = doParallel(_filter); | |
async.filterSeries = doSeries(_filter); | |
// select alias | |
async.select = async.filter; | |
async.selectSeries = async.filterSeries; | |
var _reject = function (eachfn, arr, iterator, callback) { | |
var results = []; | |
arr = _map(arr, function (x, i) { | |
return {index: i, value: x}; | |
}); | |
eachfn(arr, function (x, callback) { | |
iterator(x.value, function (v) { | |
if (!v) { | |
results.push(x); | |
} | |
callback(); | |
}); | |
}, function (err) { | |
callback(_map(results.sort(function (a, b) { | |
return a.index - b.index; | |
}), function (x) { | |
return x.value; | |
})); | |
}); | |
}; | |
async.reject = doParallel(_reject); | |
async.rejectSeries = doSeries(_reject); | |
var _detect = function (eachfn, arr, iterator, main_callback) { | |
eachfn(arr, function (x, callback) { | |
iterator(x, function (result) { | |
if (result) { | |
main_callback(x); | |
main_callback = function () {}; | |
} | |
else { | |
callback(); | |
} | |
}); | |
}, function (err) { | |
main_callback(); | |
}); | |
}; | |
async.detect = doParallel(_detect); | |
async.detectSeries = doSeries(_detect); | |
async.some = function (arr, iterator, main_callback) { | |
async.each(arr, function (x, callback) { | |
iterator(x, function (v) { | |
if (v) { | |
main_callback(true); | |
main_callback = function () {}; | |
} | |
callback(); | |
}); | |
}, function (err) { | |
main_callback(false); | |
}); | |
}; | |
// any alias | |
async.any = async.some; | |
async.every = function (arr, iterator, main_callback) { | |
async.each(arr, function (x, callback) { | |
iterator(x, function (v) { | |
if (!v) { | |
main_callback(false); | |
main_callback = function () {}; | |
} | |
callback(); | |
}); | |
}, function (err) { | |
main_callback(true); | |
}); | |
}; | |
// all alias | |
async.all = async.every; | |
async.sortBy = function (arr, iterator, callback) { | |
async.map(arr, function (x, callback) { | |
iterator(x, function (err, criteria) { | |
if (err) { | |
callback(err); | |
} | |
else { | |
callback(null, {value: x, criteria: criteria}); | |
} | |
}); | |
}, function (err, results) { | |
if (err) { | |
return callback(err); | |
} | |
else { | |
var fn = function (left, right) { | |
var a = left.criteria, b = right.criteria; | |
return a < b ? -1 : a > b ? 1 : 0; | |
}; | |
callback(null, _map(results.sort(fn), function (x) { | |
return x.value; | |
})); | |
} | |
}); | |
}; | |
async.auto = function (tasks, callback) { | |
callback = callback || function () {}; | |
var keys = _keys(tasks); | |
var remainingTasks = keys.length | |
if (!remainingTasks) { | |
return callback(); | |
} | |
var results = {}; | |
var listeners = []; | |
var addListener = function (fn) { | |
listeners.unshift(fn); | |
}; | |
var removeListener = function (fn) { | |
for (var i = 0; i < listeners.length; i += 1) { | |
if (listeners[i] === fn) { | |
listeners.splice(i, 1); | |
return; | |
} | |
} | |
}; | |
var taskComplete = function () { | |
remainingTasks-- | |
_each(listeners.slice(0), function (fn) { | |
fn(); | |
}); | |
}; | |
addListener(function () { | |
if (!remainingTasks) { | |
var theCallback = callback; | |
// prevent final callback from calling itself if it errors | |
callback = function () {}; | |
theCallback(null, results); | |
} | |
}); | |
_each(keys, function (k) { | |
var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; | |
var taskCallback = function (err) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (args.length <= 1) { | |
args = args[0]; | |
} | |
if (err) { | |
var safeResults = {}; | |
_each(_keys(results), function(rkey) { | |
safeResults[rkey] = results[rkey]; | |
}); | |
safeResults[k] = args; | |
callback(err, safeResults); | |
// stop subsequent errors hitting callback multiple times | |
callback = function () {}; | |
} | |
else { | |
results[k] = args; | |
async.setImmediate(taskComplete); | |
} | |
}; | |
var requires = task.slice(0, Math.abs(task.length - 1)) || []; | |
var ready = function () { | |
return _reduce(requires, function (a, x) { | |
return (a && results.hasOwnProperty(x)); | |
}, true) && !results.hasOwnProperty(k); | |
}; | |
if (ready()) { | |
task[task.length - 1](taskCallback, results); | |
} | |
else { | |
var listener = function () { | |
if (ready()) { | |
removeListener(listener); | |
task[task.length - 1](taskCallback, results); | |
} | |
}; | |
addListener(listener); | |
} | |
}); | |
}; | |
async.retry = function(times, task, callback) { | |
var DEFAULT_TIMES = 5; | |
var attempts = []; | |
// Use defaults if times not passed | |
if (typeof times === 'function') { | |
callback = task; | |
task = times; | |
times = DEFAULT_TIMES; | |
} | |
// Make sure times is a number | |
times = parseInt(times, 10) || DEFAULT_TIMES; | |
var wrappedTask = function(wrappedCallback, wrappedResults) { | |
var retryAttempt = function(task, finalAttempt) { | |
return function(seriesCallback) { | |
task(function(err, result){ | |
seriesCallback(!err || finalAttempt, {err: err, result: result}); | |
}, wrappedResults); | |
}; | |
}; | |
while (times) { | |
attempts.push(retryAttempt(task, !(times-=1))); | |
} | |
async.series(attempts, function(done, data){ | |
data = data[data.length - 1]; | |
(wrappedCallback || callback)(data.err, data.result); | |
}); | |
} | |
// If a callback is passed, run this as a controll flow | |
return callback ? wrappedTask() : wrappedTask | |
}; | |
async.waterfall = function (tasks, callback) { | |
callback = callback || function () {}; | |
if (!_isArray(tasks)) { | |
var err = new Error('First argument to waterfall must be an array of functions'); | |
return callback(err); | |
} | |
if (!tasks.length) { | |
return callback(); | |
} | |
var wrapIterator = function (iterator) { | |
return function (err) { | |
if (err) { | |
callback.apply(null, arguments); | |
callback = function () {}; | |
} | |
else { | |
var args = Array.prototype.slice.call(arguments, 1); | |
var next = iterator.next(); | |
if (next) { | |
args.push(wrapIterator(next)); | |
} | |
else { | |
args.push(callback); | |
} | |
async.setImmediate(function () { | |
iterator.apply(null, args); | |
}); | |
} | |
}; | |
}; | |
wrapIterator(async.iterator(tasks))(); | |
}; | |
var _parallel = function(eachfn, tasks, callback) { | |
callback = callback || function () {}; | |
if (_isArray(tasks)) { | |
eachfn.map(tasks, function (fn, callback) { | |
if (fn) { | |
fn(function (err) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (args.length <= 1) { | |
args = args[0]; | |
} | |
callback.call(null, err, args); | |
}); | |
} | |
}, callback); | |
} | |
else { | |
var results = {}; | |
eachfn.each(_keys(tasks), function (k, callback) { | |
tasks[k](function (err) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (args.length <= 1) { | |
args = args[0]; | |
} | |
results[k] = args; | |
callback(err); | |
}); | |
}, function (err) { | |
callback(err, results); | |
}); | |
} | |
}; | |
async.parallel = function (tasks, callback) { | |
_parallel({ map: async.map, each: async.each }, tasks, callback); | |
}; | |
async.parallelLimit = function(tasks, limit, callback) { | |
_parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); | |
}; | |
async.series = function (tasks, callback) { | |
callback = callback || function () {}; | |
if (_isArray(tasks)) { | |
async.mapSeries(tasks, function (fn, callback) { | |
if (fn) { | |
fn(function (err) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (args.length <= 1) { | |
args = args[0]; | |
} | |
callback.call(null, err, args); | |
}); | |
} | |
}, callback); | |
} | |
else { | |
var results = {}; | |
async.eachSeries(_keys(tasks), function (k, callback) { | |
tasks[k](function (err) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (args.length <= 1) { | |
args = args[0]; | |
} | |
results[k] = args; | |
callback(err); | |
}); | |
}, function (err) { | |
callback(err, results); | |
}); | |
} | |
}; | |
async.iterator = function (tasks) { | |
var makeCallback = function (index) { | |
var fn = function () { | |
if (tasks.length) { | |
tasks[index].apply(null, arguments); | |
} | |
return fn.next(); | |
}; | |
fn.next = function () { | |
return (index < tasks.length - 1) ? makeCallback(index + 1): null; | |
}; | |
return fn; | |
}; | |
return makeCallback(0); | |
}; | |
async.apply = function (fn) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
return function () { | |
return fn.apply( | |
null, args.concat(Array.prototype.slice.call(arguments)) | |
); | |
}; | |
}; | |
var _concat = function (eachfn, arr, fn, callback) { | |
var r = []; | |
eachfn(arr, function (x, cb) { | |
fn(x, function (err, y) { | |
r = r.concat(y || []); | |
cb(err); | |
}); | |
}, function (err) { | |
callback(err, r); | |
}); | |
}; | |
async.concat = doParallel(_concat); | |
async.concatSeries = doSeries(_concat); | |
async.whilst = function (test, iterator, callback) { | |
if (test()) { | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
async.whilst(test, iterator, callback); | |
}); | |
} | |
else { | |
callback(); | |
} | |
}; | |
async.doWhilst = function (iterator, test, callback) { | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (test.apply(null, args)) { | |
async.doWhilst(iterator, test, callback); | |
} | |
else { | |
callback(); | |
} | |
}); | |
}; | |
async.until = function (test, iterator, callback) { | |
if (!test()) { | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
async.until(test, iterator, callback); | |
}); | |
} | |
else { | |
callback(); | |
} | |
}; | |
async.doUntil = function (iterator, test, callback) { | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (!test.apply(null, args)) { | |
async.doUntil(iterator, test, callback); | |
} | |
else { | |
callback(); | |
} | |
}); | |
}; | |
async.queue = function (worker, concurrency) { | |
if (concurrency === undefined) { | |
concurrency = 1; | |
} | |
function _insert(q, data, pos, callback) { | |
if (!q.started){ | |
q.started = true; | |
} | |
if (!_isArray(data)) { | |
data = [data]; | |
} | |
if(data.length == 0) { | |
// call drain immediately if there are no tasks | |
return async.setImmediate(function() { | |
if (q.drain) { | |
q.drain(); | |
} | |
}); | |
} | |
_each(data, function(task) { | |
var item = { | |
data: task, | |
callback: typeof callback === 'function' ? callback : null | |
}; | |
if (pos) { | |
q.tasks.unshift(item); | |
} else { | |
q.tasks.push(item); | |
} | |
if (q.saturated && q.tasks.length === q.concurrency) { | |
q.saturated(); | |
} | |
async.setImmediate(q.process); | |
}); | |
} | |
var workers = 0; | |
var q = { | |
tasks: [], | |
concurrency: concurrency, | |
saturated: null, | |
empty: null, | |
drain: null, | |
started: false, | |
paused: false, | |
push: function (data, callback) { | |
_insert(q, data, false, callback); | |
}, | |
kill: function () { | |
q.drain = null; | |
q.tasks = []; | |
}, | |
unshift: function (data, callback) { | |
_insert(q, data, true, callback); | |
}, | |
process: function () { | |
if (!q.paused && workers < q.concurrency && q.tasks.length) { | |
var task = q.tasks.shift(); | |
if (q.empty && q.tasks.length === 0) { | |
q.empty(); | |
} | |
workers += 1; | |
var next = function () { | |
workers -= 1; | |
if (task.callback) { | |
task.callback.apply(task, arguments); | |
} | |
if (q.drain && q.tasks.length + workers === 0) { | |
q.drain(); | |
} | |
q.process(); | |
}; | |
var cb = only_once(next); | |
worker(task.data, cb); | |
} | |
}, | |
length: function () { | |
return q.tasks.length; | |
}, | |
running: function () { | |
return workers; | |
}, | |
idle: function() { | |
return q.tasks.length + workers === 0; | |
}, | |
pause: function () { | |
if (q.paused === true) { return; } | |
q.paused = true; | |
q.process(); | |
}, | |
resume: function () { | |
if (q.paused === false) { return; } | |
q.paused = false; | |
q.process(); | |
} | |
}; | |
return q; | |
}; | |
async.priorityQueue = function (worker, concurrency) { | |
function _compareTasks(a, b){ | |
return a.priority - b.priority; | |
}; | |
function _binarySearch(sequence, item, compare) { | |
var beg = -1, | |
end = sequence.length - 1; | |
while (beg < end) { | |
var mid = beg + ((end - beg + 1) >>> 1); | |
if (compare(item, sequence[mid]) >= 0) { | |
beg = mid; | |
} else { | |
end = mid - 1; | |
} | |
} | |
return beg; | |
} | |
function _insert(q, data, priority, callback) { | |
if (!q.started){ | |
q.started = true; | |
} | |
if (!_isArray(data)) { | |
data = [data]; | |
} | |
if(data.length == 0) { | |
// call drain immediately if there are no tasks | |
return async.setImmediate(function() { | |
if (q.drain) { | |
q.drain(); | |
} | |
}); | |
} | |
_each(data, function(task) { | |
var item = { | |
data: task, | |
priority: priority, | |
callback: typeof callback === 'function' ? callback : null | |
}; | |
q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item); | |
if (q.saturated && q.tasks.length === q.concurrency) { | |
q.saturated(); | |
} | |
async.setImmediate(q.process); | |
}); | |
} | |
// Start with a normal queue | |
var q = async.queue(worker, concurrency); | |
// Override push to accept second parameter representing priority | |
q.push = function (data, priority, callback) { | |
_insert(q, data, priority, callback); | |
}; | |
// Remove unshift function | |
delete q.unshift; | |
return q; | |
}; | |
async.cargo = function (worker, payload) { | |
var working = false, | |
tasks = []; | |
var cargo = { | |
tasks: tasks, | |
payload: payload, | |
saturated: null, | |
empty: null, | |
drain: null, | |
drained: true, | |
push: function (data, callback) { | |
if (!_isArray(data)) { | |
data = [data]; | |
} | |
_each(data, function(task) { | |
tasks.push({ | |
data: task, | |
callback: typeof callback === 'function' ? callback : null | |
}); | |
cargo.drained = false; | |
if (cargo.saturated && tasks.length === payload) { | |
cargo.saturated(); | |
} | |
}); | |
async.setImmediate(cargo.process); | |
}, | |
process: function process() { | |
if (working) return; | |
if (tasks.length === 0) { | |
if(cargo.drain && !cargo.drained) cargo.drain(); | |
cargo.drained = true; | |
return; | |
} | |
var ts = typeof payload === 'number' | |
? tasks.splice(0, payload) | |
: tasks.splice(0, tasks.length); | |
var ds = _map(ts, function (task) { | |
return task.data; | |
}); | |
if(cargo.empty) cargo.empty(); | |
working = true; | |
worker(ds, function () { | |
working = false; | |
var args = arguments; | |
_each(ts, function (data) { | |
if (data.callback) { | |
data.callback.apply(null, args); | |
} | |
}); | |
process(); | |
}); | |
}, | |
length: function () { | |
return tasks.length; | |
}, | |
running: function () { | |
return working; | |
} | |
}; | |
return cargo; | |
}; | |
var _console_fn = function (name) { | |
return function (fn) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
fn.apply(null, args.concat([function (err) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
if (typeof console !== 'undefined') { | |
if (err) { | |
if (console.error) { | |
console.error(err); | |
} | |
} | |
else if (console[name]) { | |
_each(args, function (x) { | |
console[name](x); | |
}); | |
} | |
} | |
}])); | |
}; | |
}; | |
async.log = _console_fn('log'); | |
async.dir = _console_fn('dir'); | |
/*async.info = _console_fn('info'); | |
async.warn = _console_fn('warn'); | |
async.error = _console_fn('error');*/ | |
async.memoize = function (fn, hasher) { | |
var memo = {}; | |
var queues = {}; | |
hasher = hasher || function (x) { | |
return x; | |
}; | |
var memoized = function () { | |
var args = Array.prototype.slice.call(arguments); | |
var callback = args.pop(); | |
var key = hasher.apply(null, args); | |
if (key in memo) { | |
async.nextTick(function () { | |
callback.apply(null, memo[key]); | |
}); | |
} | |
else if (key in queues) { | |
queues[key].push(callback); | |
} | |
else { | |
queues[key] = [callback]; | |
fn.apply(null, args.concat([function () { | |
memo[key] = arguments; | |
var q = queues[key]; | |
delete queues[key]; | |
for (var i = 0, l = q.length; i < l; i++) { | |
q[i].apply(null, arguments); | |
} | |
}])); | |
} | |
}; | |
memoized.memo = memo; | |
memoized.unmemoized = fn; | |
return memoized; | |
}; | |
async.unmemoize = function (fn) { | |
return function () { | |
return (fn.unmemoized || fn).apply(null, arguments); | |
}; | |
}; | |
async.times = function (count, iterator, callback) { | |
var counter = []; | |
for (var i = 0; i < count; i++) { | |
counter.push(i); | |
} | |
return async.map(counter, iterator, callback); | |
}; | |
async.timesSeries = function (count, iterator, callback) { | |
var counter = []; | |
for (var i = 0; i < count; i++) { | |
counter.push(i); | |
} | |
return async.mapSeries(counter, iterator, callback); | |
}; | |
async.seq = function (/* functions... */) { | |
var fns = arguments; | |
return function () { | |
var that = this; | |
var args = Array.prototype.slice.call(arguments); | |
var callback = args.pop(); | |
async.reduce(fns, args, function (newargs, fn, cb) { | |
fn.apply(that, newargs.concat([function () { | |
var err = arguments[0]; | |
var nextargs = Array.prototype.slice.call(arguments, 1); | |
cb(err, nextargs); | |
}])) | |
}, | |
function (err, results) { | |
callback.apply(that, [err].concat(results)); | |
}); | |
}; | |
}; | |
async.compose = function (/* functions... */) { | |
return async.seq.apply(null, Array.prototype.reverse.call(arguments)); | |
}; | |
var _applyEach = function (eachfn, fns /*args...*/) { | |
var go = function () { | |
var that = this; | |
var args = Array.prototype.slice.call(arguments); | |
var callback = args.pop(); | |
return eachfn(fns, function (fn, cb) { | |
fn.apply(that, args.concat([cb])); | |
}, | |
callback); | |
}; | |
if (arguments.length > 2) { | |
var args = Array.prototype.slice.call(arguments, 2); | |
return go.apply(this, args); | |
} | |
else { | |
return go; | |
} | |
}; | |
async.applyEach = doParallel(_applyEach); | |
async.applyEachSeries = doSeries(_applyEach); | |
async.forever = function (fn, callback) { | |
function next(err) { | |
if (err) { | |
if (callback) { | |
return callback(err); | |
} | |
throw err; | |
} | |
fn(next); | |
} | |
next(); | |
}; | |
// Node.js | |
if (typeof module !== 'undefined' && module.exports) { | |
module.exports = async; | |
} | |
// AMD / RequireJS | |
else if (typeof define !== 'undefined' && define.amd) { | |
define([], function () { | |
return async; | |
}); | |
} | |
// included directly via <script> tag | |
else { | |
root.async = async; | |
} | |
}()); | |
}).call(this,require('_process')) | |
},{"_process":16}],7:[function(require,module,exports){ | |
(function (process,global){ | |
/** | |
* Highland: the high-level streams library | |
* | |
* Highland may be freely distributed under the Apache 2.0 license. | |
* http://github.com/caolan/highland | |
* Copyright (c) Caolan McMahon | |
* | |
*/ | |
var inherits = require('util').inherits; | |
var EventEmitter = require('events').EventEmitter; | |
var Decoder = require('string_decoder').StringDecoder; | |
/** | |
* The Stream constructor, accepts an array of values or a generator function | |
* as an optional argument. This is typically the entry point to the Highland | |
* APIs, providing a convenient way of chaining calls together. | |
* | |
* **Arrays -** Streams created from Arrays will emit each value of the Array | |
* and then emit a [nil](#nil) value to signal the end of the Stream. | |
* | |
* **Generators -** These are functions which provide values for the Stream. | |
* They are lazy and can be infinite, they can also be asynchronous (for | |
* example, making a HTTP request). You emit values on the Stream by calling | |
* `push(err, val)`, much like a standard Node.js callback. Once it has been | |
* called, the generator function will not be called again unless you call | |
* `next()`. This call to `next()` will signal you've finished processing the | |
* current data and allow for the generator function to be called again. If the | |
* Stream is still being consumed the generator function will then be called | |
* again. | |
* | |
* You can also redirect a generator Stream by passing a new source Stream | |
* to read from to next. For example: `next(other_stream)` - then any subsequent | |
* calls will be made to the new source. | |
* | |
* **Node Readable Stream -** Pass in a Node Readable Stream object to wrap | |
* it with the Highland API. Reading from the resulting Highland Stream will | |
* begin piping the data from the Node Stream to the Highland Stream. | |
* | |
* **EventEmitter / jQuery Elements -** Pass in both an event name and an | |
* event emitter as the two arguments to the constructor and the first | |
* argument emitted to the event handler will be written to the new Stream. | |
* | |
* You can also pass as an optional third parameter a function, an array of strings | |
* or a number. In this case the event handler will try to wrap the arguments emitted | |
* to it and write this object to the new stream. | |
* | |
* **Promise -** Accepts an ES6 / jQuery style promise and returns a | |
* Highland Stream which will emit a single value (or an error). | |
* | |
* @id _(source) | |
* @section Stream Objects | |
* @name _(source) | |
* @param {Array | Function | Readable Stream | Promise} source - (optional) source to take values from from | |
* @api public | |
* | |
* // from an Array | |
* _([1, 2, 3, 4]); | |
* | |
* // using a generator function | |
* _(function (push, next) { | |
* push(null, 1); | |
* push(err); | |
* next(); | |
* }); | |
* | |
* // a stream with no source, can pipe node streams through it etc. | |
* var through = _(); | |
* | |
* // wrapping a Node Readable Stream so you can easily manipulate it | |
* _(readable).filter(hasSomething).pipe(writeable); | |
* | |
* // creating a stream from events | |
* _('click', btn).each(handleEvent); | |
* | |
* // creating a stream from events with mapping | |
* _('request', httpServer, ['req', 'res']).each(handleEvent); | |
* | |
* // from a Promise object | |
* var foo = _($.getJSON('/api/foo')); | |
*/ | |
exports = module.exports = function (/*optional*/xs, /*optional*/ee, /*optional*/ mappingHint) { | |
return new Stream(xs, ee, mappingHint); | |
}; | |
var _ = exports; | |
// Save bytes in the minified (but not gzipped) version: | |
var ArrayProto = Array.prototype, | |
ObjProto = Object.prototype; | |
// Create quick reference variables for speed access to core prototypes. | |
var slice = ArrayProto.slice, | |
toString = ObjProto.toString; | |
_.isFunction = function (x) { | |
return typeof x === 'function'; | |
}; | |
_.isObject = function (x) { | |
return typeof x === 'object' && x !== null; | |
}; | |
_.isString = function (x) { | |
return typeof x === 'string'; | |
}; | |
_.isArray = Array.isArray || function (x) { | |
return toString.call(x) === '[object Array]'; | |
}; | |
// setImmediate implementation with browser and older node fallbacks | |
if (typeof setImmediate === 'undefined') { | |
if (typeof process === 'undefined' || !(process.nextTick)) { | |
_.setImmediate = function (fn) { | |
setTimeout(fn, 0); | |
}; | |
} | |
else { | |
// use nextTick on old node versions | |
_.setImmediate = process.nextTick; | |
} | |
} | |
// check no process.stdout to detect browserify | |
else if (typeof process === 'undefined' || !(process.stdout)) { | |
// modern browser - but not a direct alias for IE10 compatibility | |
_.setImmediate = function (fn) { | |
setImmediate(fn); | |
}; | |
} | |
else { | |
_.setImmediate = setImmediate; | |
} | |
/** | |
* The end of stream marker. This is sent along the data channel of a Stream | |
* to tell consumers that the Stream has ended. See the example map code for | |
* an example of detecting the end of a Stream. | |
* | |
* Note: `nil` is setup as a global where possible. This makes it convenient | |
* to access, but more importantly lets Streams from different Highland | |
* instances work together and detect end-of-stream properly. This is mostly | |
* useful for NPM where you may have many different Highland versions installed. | |
* | |
* @id nil | |
* @section Utils | |
* @name _.nil | |
* @api public | |
* | |
* var map = function (iter, source) { | |
* return source.consume(function (err, val, push, next) { | |
* if (err) { | |
* push(err); | |
* next(); | |
* } | |
* else if (val === _.nil) { | |
* push(null, val); | |
* } | |
* else { | |
* push(null, iter(val)); | |
* next(); | |
* } | |
* }); | |
* }; | |
*/ | |
// set up a global nil object in cases where you have multiple Highland | |
// instances installed (often via npm) | |
var _global = this; | |
if (typeof global !== 'undefined') { | |
_global = global; | |
} | |
else if (typeof window !== 'undefined') { | |
_global = window; | |
} | |
if (!_global.nil) { | |
_global.nil = {}; | |
} | |
var nil = _.nil = _global.nil; | |
/** | |
* Transforms a function with specific arity (all arguments must be | |
* defined) in a way that it can be called as a chain of functions until | |
* the arguments list is saturated. | |
* | |
* This function is not itself curryable. | |
* | |
* @id curry | |
* @name curry(fn, [*arguments]) | |
* @section Functions | |
* @param {Function} fn - the function to curry | |
* @param args.. - any number of arguments to pre-apply to the function | |
* @returns Function | |
* @api public | |
* | |
* fn = curry(function (a, b, c) { | |
* return a + b + c; | |
* }); | |
* | |
* fn(1)(2)(3) == fn(1, 2, 3) | |
* fn(1, 2)(3) == fn(1, 2, 3) | |
* fn(1)(2, 3) == fn(1, 2, 3) | |
*/ | |
_.curry = function (fn /* args... */) { | |
var args = slice.call(arguments); | |
return _.ncurry.apply(this, [fn.length].concat(args)); | |
}; | |
/** | |
* Same as `curry` but with a specific number of arguments. This can be | |
* useful when functions do not explicitly define all its parameters. | |
* | |
* This function is not itself curryable. | |
* | |
* @id ncurry | |
* @name ncurry(n, fn, [args...]) | |
* @section Functions | |
* @param {Number} n - the number of arguments to wait for before apply fn | |
* @param {Function} fn - the function to curry | |
* @param args... - any number of arguments to pre-apply to the function | |
* @returns Function | |
* @api public | |
* | |
* fn = ncurry(3, function () { | |
* return Array.prototype.join.call(arguments, '.'); | |
* }); | |
* | |
* fn(1, 2, 3) == '1.2.3'; | |
* fn(1, 2)(3) == '1.2.3'; | |
* fn(1)(2)(3) == '1.2.3'; | |
*/ | |
_.ncurry = function (n, fn /* args... */) { | |
var largs = slice.call(arguments, 2); | |
if (largs.length >= n) { | |
return fn.apply(this, largs.slice(0, n)); | |
} | |
return function () { | |
var args = largs.concat(slice.call(arguments)); | |
if (args.length < n) { | |
return _.ncurry.apply(this, [n, fn].concat(args)); | |
} | |
return fn.apply(this, args.slice(0, n)); | |
}; | |
}; | |
/** | |
* Partially applies the function (regardless of whether it has had curry | |
* called on it). This will always postpone execution until at least the next | |
* call of the partially applied function. | |
* | |
* @id partial | |
* @name partial(fn, args...) | |
* @section Functions | |
* @param {Function} fn - function to partial apply | |
* @param args... - the arguments to apply to the function | |
* @api public | |
* | |
* var addAll = function () { | |
* var args = Array.prototype.slice.call(arguments); | |
* return foldl1(add, args); | |
* }; | |
* var f = partial(addAll, 1, 2); | |
* f(3, 4) == 10 | |
*/ | |
_.partial = function (f /* args... */) { | |
var args = slice.call(arguments, 1); | |
return function () { | |
return f.apply(this, args.concat(slice.call(arguments))); | |
}; | |
}; | |
/** | |
* Evaluates the function `fn` with the argument positions swapped. Only | |
* works with functions that accept two arguments. | |
* | |
* @id flip | |
* @name flip(fn, [x, y]) | |
* @section Functions | |
* @param {Function} f - function to flip argument application for | |
* @param x - parameter to apply to the right hand side of f | |
* @param y - parameter to apply to the left hand side of f | |
* @api public | |
* | |
* div(2, 4) == 0.5 | |
* flip(div, 2, 4) == 2 | |
* flip(div)(2, 4) == 2 | |
*/ | |
_.flip = _.curry(function (fn, x, y) { return fn(y, x); }); | |
/** | |
* Creates a composite function, which is the application of function1 to | |
* the results of function2. You can pass an arbitrary number of arguments | |
* and have them composed. This means you can't partially apply the compose | |
* function itself. | |
* | |
* @id compose | |
* @name compose(fn1, fn2, ...) | |
* @section Functions | |
* @api public | |
* | |
* var add1 = add(1); | |
* var mul3 = mul(3); | |
* | |
* var add1mul3 = compose(mul3, add1); | |
* add1mul3(2) == 9 | |
*/ | |
_.compose = function (/*functions...*/) { | |
var fns = slice.call(arguments).reverse(); | |
return _.seq.apply(null, fns); | |
}; | |
/** | |
* The reversed version of compose. Where arguments are in the order of | |
* application. | |
* | |
* @id seq | |
* @name seq(fn1, fn2, ...) | |
* @section Functions | |
* @api public | |
* | |
* var add1 = add(1); | |
* var mul3 = mul(3); | |
* | |
* var add1mul3 = seq(add1, mul3); | |
* add1mul3(2) == 9 | |
*/ | |
_.seq = function () { | |
var fns = slice.call(arguments); | |
return function () { | |
if (!fns.length) { | |
return; | |
} | |
var r = fns[0].apply(this, arguments); | |
for (var i = 1; i < fns.length; i++) { | |
r = fns[i].call(this, r); | |
} | |
return r; | |
}; | |
}; | |
/** | |
* Actual Stream constructor wrapped the the main exported function | |
*/ | |
function Stream(/*optional*/xs, /*optional*/ee, /*optional*/mappingHint) { | |
if (xs && _.isStream(xs)) { | |
// already a Stream | |
return xs; | |
} | |
EventEmitter.call(this); | |
var self = this; | |
// used to detect Highland Streams using isStream(x), this | |
// will work even in cases where npm has installed multiple | |
// versions, unlike an instanceof check | |
self.__HighlandStream__ = true; | |
self.id = ('' + Math.random()).substr(2, 6); | |
this.paused = true; | |
this._incoming = []; | |
this._outgoing = []; | |
this._consumers = []; | |
this._observers = []; | |
this._destructors = []; | |
this._send_events = false; | |
this._delegate = null; | |
this.source = null; | |
// Old-style node Stream.pipe() checks for this | |
this.writable = true; | |
self.on('newListener', function (ev) { | |
if (ev === 'data') { | |
self._send_events = true; | |
_.setImmediate(self.resume.bind(self)); | |
} | |
else if (ev === 'end') { | |
// this property avoids us checking the length of the | |
// listners subscribed to each event on each _send() call | |
self._send_events = true; | |
} | |
}); | |
// TODO: write test to cover this removeListener code | |
self.on('removeListener', function (ev) { | |
if (ev === 'end' || ev === 'data') { | |
var end_listeners = self.listeners('end').length; | |
var data_listeners = self.listeners('data').length; | |
if (end_listeners + data_listeners === 0) { | |
// stop emitting events | |
self._send_events = false; | |
} | |
} | |
}); | |
if (xs === undefined) { | |
// nothing else to do | |
} | |
else if (_.isArray(xs)) { | |
self._incoming = xs.concat([nil]); | |
} | |
else if (typeof xs === 'function') { | |
this._generator = xs; | |
this._generator_push = function (err, x) { | |
self.write(err ? new StreamError(err): x); | |
}; | |
this._generator_next = function (s) { | |
if (s) { | |
// we MUST pause to get the redirect object into the _incoming | |
// buffer otherwise it would be passed directly to _send(), | |
// which does not handle StreamRedirect objects! | |
var _paused = self.paused; | |
if (!_paused) { | |
self.pause(); | |
} | |
self.write(new StreamRedirect(s)); | |
if (!_paused) { | |
self.resume(); | |
} | |
} | |
else { | |
self._generator_running = false; | |
} | |
if (!self.paused) { | |
self.resume(); | |
} | |
}; | |
} | |
else if (_.isObject(xs)) { | |
if (_.isFunction(xs.then)) { | |
// probably a promise | |
return _(function (push) { | |
xs.then(function (value) { | |
push(null, value); | |
return push(null, nil); | |
}, | |
function (err) { | |
push(err); | |
return push(null, nil); | |
}); | |
}); | |
} | |
else { | |
// write any errors into the stream | |
xs.on('error', function (err) { | |
self.write(new StreamError(err)); | |
}); | |
// assume it's a pipeable stream as a source | |
xs.pipe(self); | |
} | |
} | |
else if (typeof xs === 'string') { | |
var mappingHintType = (typeof mappingHint); | |
var mapper; | |
if (mappingHintType === 'function') { | |
mapper = mappingHint; | |
} else if (mappingHintType === 'number') { | |
mapper = function () { | |
return slice.call(arguments, 0, mappingHint); | |
}; | |
} else if (_.isArray(mappingHint)) { | |
mapper = function () { | |
var args = arguments; | |
return mappingHint.reduce(function (ctx, hint, idx) { | |
ctx[hint] = args[idx]; | |
return ctx; | |
}, {}); | |
}; | |
} else { | |
mapper = function (x) { return x; }; | |
} | |
ee.on(xs, function () { | |
var ctx = mapper.apply(this, arguments); | |
self.write(ctx); | |
}); | |
} | |
else { | |
throw new Error( | |
'Unexpected argument type to Stream(): ' + (typeof xs) | |
); | |
} | |
} | |
inherits(Stream, EventEmitter); | |
/** | |
* adds a top-level _.foo(mystream) style export for Stream methods | |
*/ | |
function exposeMethod(name) { | |
var f = Stream.prototype[name]; | |
var n = f.length; | |
_[name] = _.ncurry(n + 1, function () { | |
var args = Array.prototype.slice.call(arguments); | |
var s = _(args.pop()); | |
return f.apply(s, args); | |
}); | |
} | |
/** | |
* Used as an Error marker when writing to a Stream's incoming buffer | |
*/ | |
function StreamError(err) { | |
this.__HighlandStreamError__ = true; | |
this.error = err; | |
} | |
/** | |
* Used as a Redirect marker when writing to a Stream's incoming buffer | |
*/ | |
function StreamRedirect(to) { | |
this.__HighlandStreamRedirect__ = true; | |
this.to = to; | |
} | |
/** | |
* Returns true if `x` is a Highland Stream. | |
* | |
* @id isStream | |
* @section Utils | |
* @name _.isStream(x) | |
* @param x - the object to test | |
* @api public | |
* | |
* _.isStream('foo') // => false | |
* _.isStream(_([1,2,3])) // => true | |
*/ | |
_.isStream = function (x) { | |
return _.isObject(x) && x.__HighlandStream__; | |
}; | |
_._isStreamError = function (x) { | |
return _.isObject(x) && x.__HighlandStreamError__; | |
}; | |
_._isStreamRedirect = function (x) { | |
return _.isObject(x) && x.__HighlandStreamRedirect__; | |
}; | |
/** | |
* Sends errors / data to consumers, observers and event handlers | |
*/ | |
Stream.prototype._send = function (err, x) { | |
//console.log(['_send', this.id, err, x]); | |
if (x === nil) { | |
this.ended = true; | |
} | |
if (this._consumers.length) { | |
for (var i = 0, len = this._consumers.length; i < len; i++) { | |
var c = this._consumers[i]; | |
if (err) { | |
c.write(new StreamError(err)); | |
} | |
else { | |
c.write(x); | |
} | |
} | |
} | |
if (this._observers.length) { | |
for (var j = 0, len2 = this._observers.length; j < len2; j++) { | |
this._observers[j].write(x); | |
} | |
} | |
if (this._send_events) { | |
if (x === nil) { | |
this.emit('end'); | |
} | |
else { | |
this.emit('data', x); | |
} | |
} | |
}; | |
/** | |
* Pauses the stream. All Highland Streams start in the paused state. | |
* | |
* @id pause | |
* @section Stream Objects | |
* @name Stream.pause() | |
* @api public | |
* | |
* var xs = _(generator); | |
* xs.pause(); | |
*/ | |
Stream.prototype.pause = function () { | |
//console.log(['pause', this.id]); | |
this.paused = true; | |
if (this.source) { | |
this.source._checkBackPressure(); | |
} | |
}; | |
/** | |
* When there is a change in downstream consumers, it will often ask | |
* the parent Stream to re-check it's state and pause/resume accordingly. | |
*/ | |
Stream.prototype._checkBackPressure = function () { | |
if (!this._consumers.length) { | |
return this.pause(); | |
} | |
for (var i = 0, len = this._consumers.length; i < len; i++) { | |
if (this._consumers[i].paused) { | |
return this.pause(); | |
} | |
} | |
return this.resume(); | |
}; | |
/** | |
* Starts pull values out of the incoming buffer and sending them downstream, | |
* this will exit early if this causes a downstream consumer to pause. | |
*/ | |
Stream.prototype._readFromBuffer = function () { | |
//console.log(['_readFromBuffer', this.id, this.paused, this._incoming]); | |
var len = this._incoming.length; | |
var i = 0; | |
while (i < len && !this.paused) { | |
var x = this._incoming[i]; | |
if (_._isStreamError(x)) { | |
this._send(x.error); | |
} | |
else if (_._isStreamRedirect(x)) { | |
this._redirect(x.to); | |
} | |
else { | |
this._send(null, x); | |
} | |
i++; | |
} | |
// remove processed data from _incoming buffer | |
this._incoming.splice(0, i); | |
}; | |
/** | |
* Starts pull values out of the incoming buffer and sending them downstream, | |
* this will exit early if this causes a downstream consumer to pause. | |
*/ | |
Stream.prototype._sendOutgoing = function () { | |
//console.log(['_sendOutgoing', this.id, this.paused, this._outgoing]); | |
var len = this._outgoing.length; | |
var i = 0; | |
while (i < len && !this.paused) { | |
var x = this._outgoing[i]; | |
if (_._isStreamError(x)) { | |
Stream.prototype._send.call(this, x.error); | |
} | |
else if (_._isStreamRedirect(x)) { | |
this._redirect(x.to); | |
} | |
else { | |
Stream.prototype._send.call(this, null, x); | |
} | |
i++; | |
} | |
// remove processed data from _outgoing buffer | |
this._outgoing.splice(0, i); | |
}; | |
/** | |
* Resumes a paused Stream. This will either read from the Stream's incoming | |
* buffer or request more data from an upstream source. | |
* | |
* @id resume | |
* @section Stream Objects | |
* @name Stream.resume() | |
* @api public | |
* | |
* var xs = _(generator); | |
* xs.resume(); | |
*/ | |
Stream.prototype.resume = function () { | |
//console.log(['resume', this.id]); | |
if (this._resume_running) { | |
//console.log(['resume already processing _incoming buffer, ignore resume call']); | |
// already processing _incoming buffer, ignore resume call | |
this._repeat_resume = true; | |
return; | |
} | |
this._resume_running = true; | |
do { | |
// use a repeat flag to avoid recursing resume() calls | |
this._repeat_resume = false; | |
this.paused = false; | |
// send values from outgoing buffer first | |
this._sendOutgoing(); | |
// send values from incoming buffer before reading from source | |
this._readFromBuffer(); | |
// we may have paused while reading from buffer | |
if (!this.paused) { | |
// ask parent for more data | |
if (this.source) { | |
//console.log(['ask parent for more data']); | |
this.source._checkBackPressure(); | |
} | |
// run _generator to fill up _incoming buffer | |
else if (this._generator) { | |
//console.log(['run generator to fill up _incoming buffer']); | |
this._runGenerator(); | |
} | |
else { | |
// perhaps a node stream is being piped in | |
this.emit('drain'); | |
} | |