Showing Voronoi with TubeMaps
Last active
August 29, 2015 14:10
-
-
Save nicola/19280aa61bef13cc4c64 to your computer and use it in GitHub Desktop.
London TubeMap with Voronoi showing closures and Cycle Hire bikes
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
[ | |
{ | |
"@ID": "1", | |
"name": "River Street , Clerkenwell", | |
"latitude": "51.52916347", | |
"longitude": "-0.109970527", | |
"bikesAvailable": "14", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "2", | |
"name": "Phillimore Gardens, Kensington", | |
"latitude": "51.49960695", | |
"longitude": "-0.197574246", | |
"bikesAvailable": "6", | |
"emptySlots": "31", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "3", | |
"name": "Christopher Street, Liverpool Street", | |
"latitude": "51.52128377", | |
"longitude": "-0.084605692", | |
"bikesAvailable": "21", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "4", | |
"name": "St. Chad's Street, King's Cross", | |
"latitude": "51.53005939", | |
"longitude": "-0.120973687", | |
"bikesAvailable": "13", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "5", | |
"name": "Sedding Street, Sloane Square", | |
"latitude": "51.49313", | |
"longitude": "-0.156876", | |
"bikesAvailable": "24", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "6", | |
"name": "Broadcasting House, Marylebone", | |
"latitude": "51.51811784", | |
"longitude": "-0.144228881", | |
"bikesAvailable": "17", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "7", | |
"name": "Charlbert Street, St. John's Wood", | |
"latitude": "51.53430039", | |
"longitude": "-0.1680743", | |
"bikesAvailable": "2", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "8", | |
"name": "Lodge Road, St. John's Wood", | |
"latitude": "51.52834133", | |
"longitude": "-0.170134484", | |
"bikesAvailable": "10", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "9", | |
"name": "New Globe Walk, Bankside", | |
"latitude": "51.5073853", | |
"longitude": "-0.096440751", | |
"bikesAvailable": "16", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "10", | |
"name": "Park Street, Bankside", | |
"latitude": "51.50597426", | |
"longitude": "-0.092754157", | |
"bikesAvailable": "16", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "11", | |
"name": "Brunswick Square, Bloomsbury", | |
"latitude": "51.52395143", | |
"longitude": "-0.122502346", | |
"bikesAvailable": "18", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "12", | |
"name": "Malet Street, Bloomsbury", | |
"latitude": "51.52168078", | |
"longitude": "-0.130431727", | |
"bikesAvailable": "43", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "13", | |
"name": "Scala Street, Fitzrovia", | |
"latitude": "51.51991453", | |
"longitude": "-0.136039674", | |
"bikesAvailable": "18", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "14", | |
"name": "Belgrove Street , King's Cross", | |
"latitude": "51.52994371", | |
"longitude": "-0.123616824", | |
"bikesAvailable": "9", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "15", | |
"name": "Great Russell Street, Bloomsbury", | |
"latitude": "51.51772703", | |
"longitude": "-0.127854211", | |
"bikesAvailable": "12", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "17", | |
"name": "Hatton Wall, Holborn", | |
"latitude": "51.5216612", | |
"longitude": "-0.109006325", | |
"bikesAvailable": "19", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "16", | |
"name": "Cartwright Gardens , Bloomsbury", | |
"latitude": "51.52635795", | |
"longitude": "-0.125979294", | |
"bikesAvailable": "11", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "19", | |
"name": "Taviton Street, Bloomsbury", | |
"latitude": "51.52505093", | |
"longitude": "-0.131161087", | |
"bikesAvailable": "9", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "18", | |
"name": "Drury Lane, Covent Garden", | |
"latitude": "51.51477076", | |
"longitude": "-0.12221963", | |
"bikesAvailable": "13", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "21", | |
"name": "Hampstead Road (Cartmel), Euston", | |
"latitude": "51.53007835", | |
"longitude": "-0.13884627", | |
"bikesAvailable": "0", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "20", | |
"name": "Drummond Street , Euston", | |
"latitude": "51.52773634", | |
"longitude": "-0.135273468", | |
"bikesAvailable": "4", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "23", | |
"name": "Red Lion Square, Holborn", | |
"latitude": "51.51943538", | |
"longitude": "-0.119123345", | |
"bikesAvailable": "9", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "22", | |
"name": "Northington Street , Holborn", | |
"latitude": "51.5222641", | |
"longitude": "-0.114079481", | |
"bikesAvailable": "15", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "25", | |
"name": "Doric Way , Somers Town", | |
"latitude": "51.5288338", | |
"longitude": "-0.132250369", | |
"bikesAvailable": "4", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "24", | |
"name": "British Museum, Bloomsbury", | |
"latitude": "51.51908011", | |
"longitude": "-0.124678402", | |
"bikesAvailable": "30", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "27", | |
"name": "Bouverie Street, Temple", | |
"latitude": "51.51382102", | |
"longitude": "-0.107927706", | |
"bikesAvailable": "27", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "26", | |
"name": "Ampton Street , Clerkenwell", | |
"latitude": "51.52728093", | |
"longitude": "-0.11829517", | |
"bikesAvailable": "13", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "29", | |
"name": "Hereford Road, Bayswater", | |
"latitude": "51.513735", | |
"longitude": "-0.193487", | |
"bikesAvailable": "0", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "28", | |
"name": "Bolsover Street, Fitzrovia", | |
"latitude": "51.52351808", | |
"longitude": "-0.143613641", | |
"bikesAvailable": "16", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "31", | |
"name": "Fanshaw Street, Hoxton", | |
"latitude": "51.52953709", | |
"longitude": "-0.083353323", | |
"bikesAvailable": "15", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "30", | |
"name": "Windsor Terrace, Hoxton", | |
"latitude": "51.52915444", | |
"longitude": "-0.093421615", | |
"bikesAvailable": "20", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "34", | |
"name": "Pancras Road, King's Cross", | |
"latitude": "51.5341235", | |
"longitude": "-0.129386874", | |
"bikesAvailable": "4", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "32", | |
"name": "Leonard Circus , Shoreditch", | |
"latitude": "51.52469624", | |
"longitude": "-0.084439283", | |
"bikesAvailable": "37", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "33", | |
"name": "Altab Ali Park, Whitechapel", | |
"latitude": "51.51681603", | |
"longitude": "-0.067827542", | |
"bikesAvailable": "6", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "38", | |
"name": "Abingdon Villas, Kensington", | |
"latitude": "51.4973875", | |
"longitude": "-0.197245586", | |
"bikesAvailable": "0", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "39", | |
"name": "Shoreditch High Street, Shoreditch", | |
"latitude": "51.5263778", | |
"longitude": "-0.078130921", | |
"bikesAvailable": "31", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "36", | |
"name": "De Vere Gardens, Kensington", | |
"latitude": "51.50173726", | |
"longitude": "-0.184980612", | |
"bikesAvailable": "3", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "37", | |
"name": "Penywern Road, Earl's Court", | |
"latitude": "51.49159394", | |
"longitude": "-0.192369256", | |
"bikesAvailable": "9", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "42", | |
"name": "Wenlock Road , Hoxton", | |
"latitude": "51.53099181", | |
"longitude": "-0.093903825", | |
"bikesAvailable": "23", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "43", | |
"name": "Crawford Street, Marylebone", | |
"latitude": "51.52026", | |
"longitude": "-0.157183945", | |
"bikesAvailable": "16", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "40", | |
"name": "Commercial Street, Shoreditch", | |
"latitude": "51.52127071", | |
"longitude": "-0.0755789", | |
"bikesAvailable": "13", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "41", | |
"name": "Sun Street, Liverpool Street", | |
"latitude": "51.52001715", | |
"longitude": "-0.083911168", | |
"bikesAvailable": "23", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "46", | |
"name": "Nesham Street, Wapping", | |
"latitude": "51.507131", | |
"longitude": "-0.06691", | |
"bikesAvailable": "7", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "47", | |
"name": "Warwick Avenue Station, Maida Vale", | |
"latitude": "51.52334476", | |
"longitude": "-0.183846408", | |
"bikesAvailable": "4", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "51", | |
"name": "Finsbury Library , Finsbury", | |
"latitude": "51.52671796", | |
"longitude": "-0.104298194", | |
"bikesAvailable": "18", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "50", | |
"name": "East Road, Hoxton", | |
"latitude": "51.52867339", | |
"longitude": "-0.087459376", | |
"bikesAvailable": "12", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "49", | |
"name": "Curzon Street, Mayfair", | |
"latitude": "51.50706909", | |
"longitude": "-0.145904427", | |
"bikesAvailable": "6", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "48", | |
"name": "Godliman Street, St. Paul's", | |
"latitude": "51.51248445", | |
"longitude": "-0.099141408", | |
"bikesAvailable": "19", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "55", | |
"name": "Finsbury Circus, Liverpool Street", | |
"latitude": "51.51707521", | |
"longitude": "-0.086685542", | |
"bikesAvailable": "24", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "54", | |
"name": "Golden Lane, Barbican", | |
"latitude": "51.52174785", | |
"longitude": "-0.094475072", | |
"bikesAvailable": "21", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "53", | |
"name": "Grafton Street, Mayfair", | |
"latitude": "51.5099923", | |
"longitude": "-0.143495266", | |
"bikesAvailable": "17", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "52", | |
"name": "Roscoe Street, St. Luke's", | |
"latitude": "51.52295439", | |
"longitude": "-0.094934859", | |
"bikesAvailable": "18", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "59", | |
"name": "Rodney Street, Angel", | |
"latitude": "51.53176825", | |
"longitude": "-0.114329032", | |
"bikesAvailable": "13", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "58", | |
"name": "New Inn Yard, Shoreditch", | |
"latitude": "51.52452699", | |
"longitude": "-0.079248081", | |
"bikesAvailable": "17", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "57", | |
"name": "Guilford Street , Bloomsbury", | |
"latitude": "51.52334672", | |
"longitude": "-0.120202614", | |
"bikesAvailable": "27", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "56", | |
"name": "Paddington Street, Marylebone", | |
"latitude": "51.52058381", | |
"longitude": "-0.154701411", | |
"bikesAvailable": "7", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "63", | |
"name": "Murray Grove , Hoxton", | |
"latitude": "51.53089041", | |
"longitude": "-0.089782579", | |
"bikesAvailable": "16", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "62", | |
"name": "Cotton Garden Estate, Kennington", | |
"latitude": "51.4907579", | |
"longitude": "-0.106323685", | |
"bikesAvailable": "6", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "61", | |
"name": "Great Dover Street, Borough", | |
"latitude": "51.49738251", | |
"longitude": "-0.089446947", | |
"bikesAvailable": "19", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "60", | |
"name": "Lisson Grove, St. John's Wood", | |
"latitude": "51.52644828", | |
"longitude": "-0.172190727", | |
"bikesAvailable": "8", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "68", | |
"name": "Theobalds Road , Holborn", | |
"latitude": "51.52059681", | |
"longitude": "-0.116688468", | |
"bikesAvailable": "19", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "69", | |
"name": "Euston Road, Euston", | |
"latitude": "51.5262363", | |
"longitude": "-0.134407652", | |
"bikesAvailable": "11", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "70", | |
"name": "Calshot Street , King's Cross", | |
"latitude": "51.53136059", | |
"longitude": "-0.117069978", | |
"bikesAvailable": "18", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "71", | |
"name": "Newgate Street , St. Paul's", | |
"latitude": "51.5154186", | |
"longitude": "-0.098850915", | |
"bikesAvailable": "16", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "64", | |
"name": "William IV Street, Strand", | |
"latitude": "51.50946212", | |
"longitude": "-0.124749274", | |
"bikesAvailable": "15", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "65", | |
"name": "Gower Place , Euston", | |
"latitude": "51.52522753", | |
"longitude": "-0.13518856", | |
"bikesAvailable": "2", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "66", | |
"name": "Holborn Circus, Holborn", | |
"latitude": "51.51795029", | |
"longitude": "-0.108657431", | |
"bikesAvailable": "19", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "67", | |
"name": "Hatton Garden, Holborn", | |
"latitude": "51.51882555", | |
"longitude": "-0.108028472", | |
"bikesAvailable": "23", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "76", | |
"name": "Longford Street, Regent's Park", | |
"latitude": "51.52559505", | |
"longitude": "-0.144083893", | |
"bikesAvailable": "8", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "77", | |
"name": "Russell Square Station, Bloomsbury", | |
"latitude": "51.52341837", | |
"longitude": "-0.124121774", | |
"bikesAvailable": "21", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "78", | |
"name": "Sadlers Sports Centre, Finsbury", | |
"latitude": "51.52486887", | |
"longitude": "-0.099489485", | |
"bikesAvailable": "11", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "72", | |
"name": "Farringdon Lane, Clerkenwell", | |
"latitude": "51.52352001", | |
"longitude": "-0.108340165", | |
"bikesAvailable": "13", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "73", | |
"name": "Old Street Station, St. Luke's", | |
"latitude": "51.52572618", | |
"longitude": "-0.088486188", | |
"bikesAvailable": "30", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "74", | |
"name": "Vauxhall Cross, Vauxhall", | |
"latitude": "51.48591714", | |
"longitude": "-0.124469948", | |
"bikesAvailable": "4", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "75", | |
"name": "Torrens Street, Angel", | |
"latitude": "51.53219984", | |
"longitude": "-0.105480698", | |
"bikesAvailable": "10", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "85", | |
"name": "Tanner Street, Bermondsey", | |
"latitude": "51.50064702", | |
"longitude": "-0.078600401", | |
"bikesAvailable": "3", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "84", | |
"name": "Breams Buildings, Holborn", | |
"latitude": "51.51593725", | |
"longitude": "-0.111778348", | |
"bikesAvailable": "23", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "87", | |
"name": "Devonshire Square, Liverpool Street", | |
"latitude": "51.51646835", | |
"longitude": "-0.079684557", | |
"bikesAvailable": "14", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "86", | |
"name": "Sancroft Street, Vauxhall", | |
"latitude": "51.48947903", | |
"longitude": "-0.115156562", | |
"bikesAvailable": "6", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "81", | |
"name": "Great Titchfield Street, Fitzrovia", | |
"latitude": "51.52025302", | |
"longitude": "-0.141327271", | |
"bikesAvailable": "19", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "80", | |
"name": "Webber Street , Southwark", | |
"latitude": "51.50069361", | |
"longitude": "-0.102091246", | |
"bikesAvailable": "3", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "83", | |
"name": "Panton Street, West End", | |
"latitude": "51.50963938", | |
"longitude": "-0.131510949", | |
"bikesAvailable": "19", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "82", | |
"name": "Chancery Lane, Holborn", | |
"latitude": "51.514274", | |
"longitude": "-0.111257", | |
"bikesAvailable": "13", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "93", | |
"name": "Cloudesley Road, Angel", | |
"latitude": "51.53440868", | |
"longitude": "-0.109025404", | |
"bikesAvailable": "19", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "92", | |
"name": "Borough Road, Elephant & Castle", | |
"latitude": "51.49889832", | |
"longitude": "-0.100440521", | |
"bikesAvailable": "19", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "95", | |
"name": "Aldersgate Street, Barbican", | |
"latitude": "51.5208417", | |
"longitude": "-0.097340162", | |
"bikesAvailable": "12", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "94", | |
"name": "Bricklayers Arms, Borough", | |
"latitude": "51.49506109", | |
"longitude": "-0.085814489", | |
"bikesAvailable": "2", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "89", | |
"name": "Tavistock Place, Bloomsbury", | |
"latitude": "51.5262503", | |
"longitude": "-0.123509611", | |
"bikesAvailable": "1", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "88", | |
"name": "Bayley Street , Bloomsbury", | |
"latitude": "51.51858757", | |
"longitude": "-0.132053392", | |
"bikesAvailable": "13", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "91", | |
"name": "Walnut Tree Walk, Vauxhall", | |
"latitude": "51.49368637", | |
"longitude": "-0.111014912", | |
"bikesAvailable": "9", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "90", | |
"name": "Harrington Square, Camden Town", | |
"latitude": "51.53301907", | |
"longitude": "-0.139174593", | |
"bikesAvailable": "17", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "102", | |
"name": "Jewry Street, Aldgate", | |
"latitude": "51.51340693", | |
"longitude": "-0.076793375", | |
"bikesAvailable": "10", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "103", | |
"name": "Vicarage Gate, Kensington", | |
"latitude": "51.50472376", | |
"longitude": "-0.192538767", | |
"bikesAvailable": "12", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "100", | |
"name": "Albert Embankment, Vauxhall", | |
"latitude": "51.49043573", | |
"longitude": "-0.122806861", | |
"bikesAvailable": "14", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "101", | |
"name": "Queen Street, Bank", | |
"latitude": "51.51155322", | |
"longitude": "-0.0929401", | |
"bikesAvailable": "17", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "98", | |
"name": "Hampstead Road, Euston", | |
"latitude": "51.52554222", | |
"longitude": "-0.138231303", | |
"bikesAvailable": "18", | |
"emptySlots": "35", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "99", | |
"name": "Old Quebec Street, Marylebone", | |
"latitude": "51.51457763", | |
"longitude": "-0.158264483", | |
"bikesAvailable": "13", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "96", | |
"name": "Falkirk Street, Hoxton", | |
"latitude": "51.53095071", | |
"longitude": "-0.078505384", | |
"bikesAvailable": "14", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "97", | |
"name": "Gloucester Road (North), Kensington", | |
"latitude": "51.49792478", | |
"longitude": "-0.183834706", | |
"bikesAvailable": "15", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "110", | |
"name": "Wellington Road, St. John's Wood", | |
"latitude": "51.53304322", | |
"longitude": "-0.172528678", | |
"bikesAvailable": "12", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "111", | |
"name": "Park Lane , Hyde Park", | |
"latitude": "51.5100172", | |
"longitude": "-0.157275636", | |
"bikesAvailable": "22", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "108", | |
"name": "Abbey Orchard Street, Westminster", | |
"latitude": "51.49812559", | |
"longitude": "-0.132102166", | |
"bikesAvailable": "18", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "109", | |
"name": "Soho Square , Soho", | |
"latitude": "51.51563144", | |
"longitude": "-0.132328837", | |
"bikesAvailable": "28", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "106", | |
"name": "Woodstock Street, Mayfair", | |
"latitude": "51.51410514", | |
"longitude": "-0.147301667", | |
"bikesAvailable": "18", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "107", | |
"name": "Finsbury Leisure Centre, St. Luke's", | |
"latitude": "51.52600832", | |
"longitude": "-0.096317627", | |
"bikesAvailable": "9", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "104", | |
"name": "Crosswall, Tower", | |
"latitude": "51.51159481", | |
"longitude": "-0.077121322", | |
"bikesAvailable": "23", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "105", | |
"name": "Westbourne Grove, Bayswater", | |
"latitude": "51.51552971", | |
"longitude": "-0.190240716", | |
"bikesAvailable": "2", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "119", | |
"name": "Bath Street, St. Luke's", | |
"latitude": "51.52589324", | |
"longitude": "-0.090847761", | |
"bikesAvailable": "7", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "118", | |
"name": "Rochester Row, Westminster", | |
"latitude": "51.49582705", | |
"longitude": "-0.13547809", | |
"bikesAvailable": "3", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "117", | |
"name": "Lollard Street, Vauxhall", | |
"latitude": "51.49288067", | |
"longitude": "-0.114934001", | |
"bikesAvailable": "9", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "116", | |
"name": "Little Argyll Street, West End", | |
"latitude": "51.51449962", | |
"longitude": "-0.141423695", | |
"bikesAvailable": "21", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "115", | |
"name": "Braham Street, Aldgate", | |
"latitude": "51.51423368", | |
"longitude": "-0.073537654", | |
"bikesAvailable": "26", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "114", | |
"name": "\n Park Road (Baker Street), Regent's Park\n ", | |
"latitude": "51.52451738", | |
"longitude": "-0.158963647", | |
"bikesAvailable": "10", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "113", | |
"name": "\n Gloucester Road (Central), South Kensington\n ", | |
"latitude": "51.49646288", | |
"longitude": "-0.183289032", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "112", | |
"name": "Stonecutter Street, Holborn", | |
"latitude": "51.51580998", | |
"longitude": "-0.105270275", | |
"bikesAvailable": "38", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "127", | |
"name": "Wood Street, Guildhall", | |
"latitude": "51.51700801", | |
"longitude": "-0.09388536", | |
"bikesAvailable": "9", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "126", | |
"name": "Museum of London, Barbican", | |
"latitude": "51.51782144", | |
"longitude": "-0.096496865", | |
"bikesAvailable": "43", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "125", | |
"name": "Borough High Street, The Borough", | |
"latitude": "51.50069491", | |
"longitude": "-0.094524319", | |
"bikesAvailable": "20", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "124", | |
"name": "Eaton Square, Belgravia", | |
"latitude": "51.49654462", | |
"longitude": "-0.150905245", | |
"bikesAvailable": "16", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "123", | |
"name": "St. John Street, Finsbury", | |
"latitude": "51.52836014", | |
"longitude": "-0.104724625", | |
"bikesAvailable": "17", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "122", | |
"name": "Norton Folgate, Liverpool Street", | |
"latitude": "51.52111369", | |
"longitude": "-0.078869751", | |
"bikesAvailable": "22", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "121", | |
"name": "Baker Street, Marylebone", | |
"latitude": "51.51891348", | |
"longitude": "-0.156166631", | |
"bikesAvailable": "6", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "120", | |
"name": "The Guildhall, Guildhall", | |
"latitude": "51.51573534", | |
"longitude": "-0.093080779", | |
"bikesAvailable": "16", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "137", | |
"name": "Bourne Street, Belgravia", | |
"latitude": "51.49087475", | |
"longitude": "-0.153319609", | |
"bikesAvailable": "12", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "136", | |
"name": "Queen Victoria Street, St. Paul's", | |
"latitude": "51.51196176", | |
"longitude": "-0.097441687", | |
"bikesAvailable": "28", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "139", | |
"name": "Lambeth Road, Vauxhall", | |
"latitude": "51.49488108", | |
"longitude": "-0.117974901", | |
"bikesAvailable": "12", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "138", | |
"name": "Green Street, Mayfair", | |
"latitude": "51.51227622", | |
"longitude": "-0.157436972", | |
"bikesAvailable": "11", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "141", | |
"name": "Chapel Place, Marylebone", | |
"latitude": "51.51530805", | |
"longitude": "-0.147203711", | |
"bikesAvailable": "26", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "140", | |
"name": "Finsbury Square , Moorgate", | |
"latitude": "51.52096262", | |
"longitude": "-0.085634242", | |
"bikesAvailable": "26", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "143", | |
"name": "Pont Street, Knightsbridge", | |
"latitude": "51.4968865", | |
"longitude": "-0.161203828", | |
"bikesAvailable": "14", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "142", | |
"name": "West Cromwell Road, Earl's Court", | |
"latitude": "51.49372451", | |
"longitude": "-0.198286569", | |
"bikesAvailable": "6", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "129", | |
"name": "Golden Square, Soho", | |
"latitude": "51.5118973", | |
"longitude": "-0.137043852", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "128", | |
"name": "Emperor's Gate, South Kensington", | |
"latitude": "51.49536226", | |
"longitude": "-0.185296516", | |
"bikesAvailable": "10", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "131", | |
"name": "Eversholt Street , Camden Town", | |
"latitude": "51.53300545", | |
"longitude": "-0.136792671", | |
"bikesAvailable": "12", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "130", | |
"name": "Tower Gardens , Tower", | |
"latitude": "51.50950627", | |
"longitude": "-0.075459482", | |
"bikesAvailable": "19", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "133", | |
"name": "Derry Street, Kensington", | |
"latitude": "51.50136494", | |
"longitude": "-0.191462381", | |
"bikesAvailable": "12", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "132", | |
"name": "Bethnal Green Road, Shoreditch", | |
"latitude": "51.52364804", | |
"longitude": "-0.074754872", | |
"bikesAvailable": "31", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "135", | |
"name": "Clerkenwell Green, Clerkenwell", | |
"latitude": "51.52326004", | |
"longitude": "-0.104708922", | |
"bikesAvailable": "9", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "134", | |
"name": "Wapping High Street, Wapping", | |
"latitude": "51.504904", | |
"longitude": "-0.06797", | |
"bikesAvailable": "10", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "152", | |
"name": "Hampton Street, Walworth", | |
"latitude": "51.49217002", | |
"longitude": "-0.101536865", | |
"bikesAvailable": "9", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "153", | |
"name": "Bayswater Road, Hyde Park", | |
"latitude": "51.51183419", | |
"longitude": "-0.174292825", | |
"bikesAvailable": "8", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "154", | |
"name": "Waterloo Station 3, Waterloo", | |
"latitude": "51.50379168", | |
"longitude": "-0.11282408", | |
"bikesAvailable": "11", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "155", | |
"name": "Lexham Gardens, Kensington", | |
"latitude": "51.49586666", | |
"longitude": "-0.191933711", | |
"bikesAvailable": "4", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "156", | |
"name": "New Kent Road, Borough", | |
"latitude": "51.49443626", | |
"longitude": "-0.092921165", | |
"bikesAvailable": "16", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "157", | |
"name": "Wright's Lane, Kensington", | |
"latitude": "51.50039792", | |
"longitude": "-0.193068385", | |
"bikesAvailable": "31", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "158", | |
"name": "Trebovir Road, Earl's Court", | |
"latitude": "51.49085368", | |
"longitude": "-0.196170309", | |
"bikesAvailable": "1", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "159", | |
"name": "Great Marlborough Street, Soho", | |
"latitude": "51.51461995", | |
"longitude": "-0.137841333", | |
"bikesAvailable": "35", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "144", | |
"name": "Kennington Cross, Kennington", | |
"latitude": "51.48894022", | |
"longitude": "-0.111435796", | |
"bikesAvailable": "4", | |
"emptySlots": "40", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "145", | |
"name": "Ilchester Place, Kensington", | |
"latitude": "51.50074359", | |
"longitude": "-0.202759212", | |
"bikesAvailable": "5", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "146", | |
"name": "Vauxhall Bridge , Pimlico", | |
"latitude": "51.48836528", | |
"longitude": "-0.129361842", | |
"bikesAvailable": "11", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "147", | |
"name": "Portugal Street, Holborn", | |
"latitude": "51.51494305", | |
"longitude": "-0.11614642", | |
"bikesAvailable": "8", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "148", | |
"name": "Tachbrook Street, Victoria", | |
"latitude": "51.49211134", | |
"longitude": "-0.138364847", | |
"bikesAvailable": "13", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "149", | |
"name": "Kennington Road Post Office, Oval", | |
"latitude": "51.48478899", | |
"longitude": "-0.110683213", | |
"bikesAvailable": "2", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "150", | |
"name": "Holy Trinity Brompton, Knightsbridge", | |
"latitude": "51.49705603", | |
"longitude": "-0.168917077", | |
"bikesAvailable": "10", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "151", | |
"name": "Chepstow Villas, Notting Hill", | |
"latitude": "51.51213691", | |
"longitude": "-0.201554966", | |
"bikesAvailable": "2", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "171", | |
"name": "Collingham Gardens, Earl's Court", | |
"latitude": "51.4916156", | |
"longitude": "-0.186753859", | |
"bikesAvailable": "4", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "170", | |
"name": "Hardwick Street, Clerkenwell", | |
"latitude": "51.52784273", | |
"longitude": "-0.108068155", | |
"bikesAvailable": "14", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "169", | |
"name": "Porchester Place, Paddington", | |
"latitude": "51.51474612", | |
"longitude": "-0.165164288", | |
"bikesAvailable": "3", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "168", | |
"name": "Argyll Road, Kensington", | |
"latitude": "51.50040123", | |
"longitude": "-0.195455928", | |
"bikesAvailable": "15", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "175", | |
"name": "Appold Street, Liverpool Street", | |
"latitude": "51.52174384", | |
"longitude": "-0.0811189", | |
"bikesAvailable": "22", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "174", | |
"name": "Strand, Strand", | |
"latitude": "51.512529", | |
"longitude": "-0.115163", | |
"bikesAvailable": "34", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "173", | |
"name": "Waterloo Roundabout, Waterloo ", | |
"latitude": "51.50486", | |
"longitude": "-0.113001", | |
"bikesAvailable": "2", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "172", | |
"name": "Sumner Place, South Kensington", | |
"latitude": "51.49121192", | |
"longitude": "-0.173715911", | |
"bikesAvailable": "16", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "163", | |
"name": "Sloane Avenue, Knightsbridge", | |
"latitude": "51.4931848", | |
"longitude": "-0.167894973", | |
"bikesAvailable": "11", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "162", | |
"name": "Southampton Place, Holborn", | |
"latitude": "51.51760685", | |
"longitude": "-0.121328408", | |
"bikesAvailable": "19", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "161", | |
"name": "Guildhouse Street, Victoria", | |
"latitude": "51.49234577", | |
"longitude": "-0.141334487", | |
"bikesAvailable": "4", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "160", | |
"name": "Waterloo Place, St. James's", | |
"latitude": "51.50663341", | |
"longitude": "-0.131773845", | |
"bikesAvailable": "13", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "167", | |
"name": "Eccleston Place, Victoria", | |
"latitude": "51.49395092", | |
"longitude": "-0.147624377", | |
"bikesAvailable": "9", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "166", | |
"name": "Seville Street, Knightsbridge", | |
"latitude": "51.50185512", | |
"longitude": "-0.159237081", | |
"bikesAvailable": "23", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "165", | |
"name": "Orsett Terrace, Bayswater", | |
"latitude": "51.517932", | |
"longitude": "-0.183716959", | |
"bikesAvailable": "0", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "164", | |
"name": "Cleveland Gardens, Bayswater", | |
"latitude": "51.515607", | |
"longitude": "-0.183118788", | |
"bikesAvailable": "0", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "186", | |
"name": "South Wharf Road, Paddington", | |
"latitude": "51.51733558", | |
"longitude": "-0.175810943", | |
"bikesAvailable": "4", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "187", | |
"name": "\n Queen's Gate (South), South Kensington\n ", | |
"latitude": "51.49247977", | |
"longitude": "-0.178433004", | |
"bikesAvailable": "6", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "184", | |
"name": "Portland Place, Marylebone", | |
"latitude": "51.52071513", | |
"longitude": "-0.14521173", | |
"bikesAvailable": "32", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "185", | |
"name": "Alderney Street, Pimlico", | |
"latitude": "51.48805753", | |
"longitude": "-0.140741432", | |
"bikesAvailable": "5", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "190", | |
"name": "Rampayne Street, Pimlico", | |
"latitude": "51.48997562", | |
"longitude": "-0.132845681", | |
"bikesAvailable": "14", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "191", | |
"name": "Hyde Park Corner, Hyde Park", | |
"latitude": "51.50311799", | |
"longitude": "-0.153520935", | |
"bikesAvailable": "10", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "188", | |
"name": "Nutford Place, Marylebone", | |
"latitude": "51.5165179", | |
"longitude": "-0.164393768", | |
"bikesAvailable": "12", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "189", | |
"name": "Claremont Square, Angel", | |
"latitude": "51.53166681", | |
"longitude": "-0.109914711", | |
"bikesAvailable": "8", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "178", | |
"name": "Warwick Square, Pimlico", | |
"latitude": "51.48985626", | |
"longitude": "-0.141923621", | |
"bikesAvailable": "11", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "176", | |
"name": "Gloucester Terrace, Bayswater", | |
"latitude": "51.51791921", | |
"longitude": "-0.188098863", | |
"bikesAvailable": "1", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "177", | |
"name": "Ashley Place, Victoria", | |
"latitude": "51.49616092", | |
"longitude": "-0.140947636", | |
"bikesAvailable": "11", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "182", | |
"name": "Bell Street , Marylebone", | |
"latitude": "51.52202903", | |
"longitude": "-0.165842551", | |
"bikesAvailable": "2", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "180", | |
"name": "North Audley Street, Mayfair", | |
"latitude": "51.5129118", | |
"longitude": "-0.153645496", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "181", | |
"name": "Belgrave Square, Belgravia", | |
"latitude": "51.49941247", | |
"longitude": "-0.152317537", | |
"bikesAvailable": "20", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "205", | |
"name": "New Road 2, Whitechapel", | |
"latitude": "51.518144", | |
"longitude": "-0.06275", | |
"bikesAvailable": "17", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "204", | |
"name": "Margery Street, Clerkenwell", | |
"latitude": "51.52659961", | |
"longitude": "-0.112432615", | |
"bikesAvailable": "13", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "207", | |
"name": "Grosvenor Crescent, Belgravia", | |
"latitude": "51.50135267", | |
"longitude": "-0.153194766", | |
"bikesAvailable": "18", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "206", | |
"name": "New Road 1 , Whitechapel", | |
"latitude": "51.518154", | |
"longitude": "-0.062697", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "201", | |
"name": "Dorset Square, Marylebone", | |
"latitude": "51.5225965", | |
"longitude": "-0.161113413", | |
"bikesAvailable": "4", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "200", | |
"name": "LMU Commercial Road, Whitechapel", | |
"latitude": "51.51492456", | |
"longitude": "-0.066078037", | |
"bikesAvailable": "6", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "203", | |
"name": "West Smithfield Rotunda, Farringdon", | |
"latitude": "51.51821864", | |
"longitude": "-0.100791005", | |
"bikesAvailable": "7", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "202", | |
"name": "Leman Street, Aldgate", | |
"latitude": "51.51236389", | |
"longitude": "-0.06954201", | |
"bikesAvailable": "13", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "197", | |
"name": "Stamford Street, South Bank", | |
"latitude": "51.50556905", | |
"longitude": "-0.111606696", | |
"bikesAvailable": "11", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "196", | |
"name": "Union Street, The Borough", | |
"latitude": "51.50368837", | |
"longitude": "-0.098497684", | |
"bikesAvailable": "16", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "199", | |
"name": "Great Tower Street, Monument", | |
"latitude": "51.51048489", | |
"longitude": "-0.082989638", | |
"bikesAvailable": "21", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "193", | |
"name": "Bankside Mix, Bankside", | |
"latitude": "51.50581776", | |
"longitude": "-0.100186337", | |
"bikesAvailable": "57", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "192", | |
"name": "Wardour Street, Soho", | |
"latitude": "51.51251523", | |
"longitude": "-0.133201961", | |
"bikesAvailable": "7", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "195", | |
"name": "Milroy Walk, South Bank", | |
"latitude": "51.50724437", | |
"longitude": "-0.106237501", | |
"bikesAvailable": "18", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "194", | |
"name": "Hop Exchange, The Borough", | |
"latitude": "51.50462759", | |
"longitude": "-0.091773776", | |
"bikesAvailable": "18", | |
"emptySlots": "33", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "220", | |
"name": "Chelsea Green, Chelsea", | |
"latitude": "51.49066456", | |
"longitude": "-0.166485083", | |
"bikesAvailable": "21", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "221", | |
"name": "Horseferry Road, Westminster", | |
"latitude": "51.49481649", | |
"longitude": "-0.13045856", | |
"bikesAvailable": "12", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "222", | |
"name": "Knightsbridge, Hyde Park", | |
"latitude": "51.50275704", | |
"longitude": "-0.155349725", | |
"bikesAvailable": "31", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "223", | |
"name": "Rodney Road , Walworth", | |
"latitude": "51.49148474", | |
"longitude": "-0.090220911", | |
"bikesAvailable": "5", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "216", | |
"name": "Old Brompton Road, South Kensington", | |
"latitude": "51.49094565", | |
"longitude": "-0.181190899", | |
"bikesAvailable": "11", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "217", | |
"name": "Wormwood Street, Liverpool Street", | |
"latitude": "51.51615461", | |
"longitude": "-0.082422399", | |
"bikesAvailable": "4", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "218", | |
"name": "St. Luke's Church, Chelsea", | |
"latitude": "51.48971651", | |
"longitude": "-0.170194408", | |
"bikesAvailable": "18", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "219", | |
"name": "Bramham Gardens, Earl's Court", | |
"latitude": "51.49016361", | |
"longitude": "-0.19039362", | |
"bikesAvailable": "7", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "212", | |
"name": "Campden Hill Road, Notting Hill", | |
"latitude": "51.50658458", | |
"longitude": "-0.199004026", | |
"bikesAvailable": "0", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "213", | |
"name": "Wellington Arch, Hyde Park", | |
"latitude": "51.50274025", | |
"longitude": "-0.149569201", | |
"bikesAvailable": "15", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "214", | |
"name": "Endsleigh Gardens, Euston", | |
"latitude": "51.52683806", | |
"longitude": "-0.130504336", | |
"bikesAvailable": "15", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "215", | |
"name": "Moorfields, Moorgate", | |
"latitude": "51.51906932", | |
"longitude": "-0.088285377", | |
"bikesAvailable": "47", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "208", | |
"name": "Mallory Street, Marylebone", | |
"latitude": "51.52505151", | |
"longitude": "-0.166304359", | |
"bikesAvailable": "0", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "209", | |
"name": "Denyer Street, Knightsbridge", | |
"latitude": "51.49358391", | |
"longitude": "-0.165101392", | |
"bikesAvailable": "14", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "210", | |
"name": "Hinde Street, Marylebone", | |
"latitude": "51.51681444", | |
"longitude": "-0.151926305", | |
"bikesAvailable": "20", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "211", | |
"name": "Cadogan Place, Knightsbridge", | |
"latitude": "51.49464523", | |
"longitude": "-0.158105512", | |
"bikesAvailable": "32", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "239", | |
"name": "Warren Street Station, Euston", | |
"latitude": "51.52443845", | |
"longitude": "-0.138019439", | |
"bikesAvailable": "12", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "238", | |
"name": "Frampton Street, Paddington", | |
"latitude": "51.5233534", | |
"longitude": "-0.175116099", | |
"bikesAvailable": "0", | |
"emptySlots": "32", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "237", | |
"name": "Vaughan Way, Wapping", | |
"latitude": "51.5084448", | |
"longitude": "-0.067176443", | |
"bikesAvailable": "29", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "236", | |
"name": "Fashion Street, Whitechapel", | |
"latitude": "51.51838043", | |
"longitude": "-0.073438925", | |
"bikesAvailable": "29", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "235", | |
"name": "Kennington Road , Vauxhall", | |
"latitude": "51.49571828", | |
"longitude": "-0.110889274", | |
"bikesAvailable": "10", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "234", | |
"name": "Liverpool Road (N1 Centre), Angel", | |
"latitude": "51.53450449", | |
"longitude": "-0.106992706", | |
"bikesAvailable": "5", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "233", | |
"name": "Pall Mall East, West End", | |
"latitude": "51.50777049", | |
"longitude": "-0.130699733", | |
"bikesAvailable": "16", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "232", | |
"name": "Carey Street, Holborn", | |
"latitude": "51.51501025", | |
"longitude": "-0.112753217", | |
"bikesAvailable": "10", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "231", | |
"name": "\n Queen's Gate (Central), South Kensington\n ", | |
"latitude": "51.49396755", | |
"longitude": "-0.17873226", | |
"bikesAvailable": "9", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "230", | |
"name": "Poured Lines, Bankside", | |
"latitude": "51.50669284", | |
"longitude": "-0.103137426", | |
"bikesAvailable": "31", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "229", | |
"name": "Whitehall Place, Strand", | |
"latitude": "51.50654321", | |
"longitude": "-0.123179697", | |
"bikesAvailable": "19", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "228", | |
"name": "St. James's Square, St. James's", | |
"latitude": "51.50742485", | |
"longitude": "-0.134621209", | |
"bikesAvailable": "38", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "227", | |
"name": "Great Percy Street, Clerkenwell", | |
"latitude": "51.52891573", | |
"longitude": "-0.115480888", | |
"bikesAvailable": "19", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "226", | |
"name": "Charles II Street, West End", | |
"latitude": "51.50844614", | |
"longitude": "-0.131961389", | |
"bikesAvailable": "24", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "225", | |
"name": "Notting Hill Gate Station, Notting Hill", | |
"latitude": "51.50935342", | |
"longitude": "-0.196422", | |
"bikesAvailable": "1", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "224", | |
"name": "Whiteley's, Bayswater", | |
"latitude": "51.51476963", | |
"longitude": "-0.188129731", | |
"bikesAvailable": "0", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "254", | |
"name": "Chadwell Street, Angel", | |
"latitude": "51.53051587", | |
"longitude": "-0.106408455", | |
"bikesAvailable": "11", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "255", | |
"name": "Clifton Road, Maida Vale", | |
"latitude": "51.52557531", | |
"longitude": "-0.179592915", | |
"bikesAvailable": "6", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "252", | |
"name": "Jubilee Gardens, South Bank", | |
"latitude": "51.5046364", | |
"longitude": "-0.116542278", | |
"bikesAvailable": "7", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "253", | |
"name": "Shoreditch Park, Hoxton", | |
"latitude": "51.53404294", | |
"longitude": "-0.086379717", | |
"bikesAvailable": "18", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "250", | |
"name": "Royal Avenue 1, Chelsea", | |
"latitude": "51.489932", | |
"longitude": "-0.162727", | |
"bikesAvailable": "3", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "251", | |
"name": "Brushfield Street, Liverpool Street", | |
"latitude": "51.518908", | |
"longitude": "-0.079249", | |
"bikesAvailable": "30", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "248", | |
"name": "Triangle Car Park, Hyde Park", | |
"latitude": "51.50645179", | |
"longitude": "-0.170279555", | |
"bikesAvailable": "8", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "249", | |
"name": "Harper Road, Borough", | |
"latitude": "51.49859784", | |
"longitude": "-0.096191134", | |
"bikesAvailable": "15", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "246", | |
"name": "Berry Street, Clerkenwell", | |
"latitude": "51.52285301", | |
"longitude": "-0.099994052", | |
"bikesAvailable": "32", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "247", | |
"name": "\n St. John's Wood Church, Regent's Park\n ", | |
"latitude": "51.530052", | |
"longitude": "-0.168314", | |
"bikesAvailable": "26", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "244", | |
"name": "Earnshaw Street , Covent Garden", | |
"latitude": "51.51611887", | |
"longitude": "-0.128585022", | |
"bikesAvailable": "6", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "245", | |
"name": "Grosvenor Road, Pimlico", | |
"latitude": "51.4853572", | |
"longitude": "-0.142207481", | |
"bikesAvailable": "3", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "242", | |
"name": "Beaumont Street, Marylebone", | |
"latitude": "51.52200801", | |
"longitude": "-0.151359288", | |
"bikesAvailable": "13", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "243", | |
"name": "Gloucester Street, Pimlico", | |
"latitude": "51.49096258", | |
"longitude": "-0.139625122", | |
"bikesAvailable": "7", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "240", | |
"name": "Colombo Street, Southwark", | |
"latitude": "51.50545935", | |
"longitude": "-0.10569204", | |
"bikesAvailable": "13", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "275", | |
"name": "Barbican Centre, Barbican", | |
"latitude": "51.52004497", | |
"longitude": "-0.092176447", | |
"bikesAvailable": "13", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "274", | |
"name": "Warwick Road, Olympia", | |
"latitude": "51.49671237", | |
"longitude": "-0.20528437", | |
"bikesAvailable": "9", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "273", | |
"name": "Belvedere Road, South Bank", | |
"latitude": "51.50613324", | |
"longitude": "-0.114686385", | |
"bikesAvailable": "31", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "272", | |
"name": "Baylis Road, Waterloo", | |
"latitude": "51.50144456", | |
"longitude": "-0.110699309", | |
"bikesAvailable": "8", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "279", | |
"name": "North Wharf Road, Paddington", | |
"latitude": "51.51862243", | |
"longitude": "-0.176645823", | |
"bikesAvailable": "17", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "278", | |
"name": "Tooley Street, Bermondsey", | |
"latitude": "51.5034938", | |
"longitude": "-0.07962099", | |
"bikesAvailable": "4", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "277", | |
"name": "Kensington Church Street, Kensington", | |
"latitude": "51.50315739", | |
"longitude": "-0.191496313", | |
"bikesAvailable": "2", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "276", | |
"name": "Lower Thames Street, Monument", | |
"latitude": "51.50930161", | |
"longitude": "-0.084985356", | |
"bikesAvailable": "20", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "283", | |
"name": "Kingsway, Covent Garden", | |
"latitude": "51.51440928", | |
"longitude": "-0.118478757", | |
"bikesAvailable": "11", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "282", | |
"name": "Royal London Hospital, Whitechapel", | |
"latitude": "51.51906446", | |
"longitude": "-0.059642081", | |
"bikesAvailable": "22", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "281", | |
"name": "Smith Square, Westminster", | |
"latitude": "51.49580589", | |
"longitude": "-0.127575233", | |
"bikesAvailable": "8", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "280", | |
"name": "Royal Avenue 2, Chelsea", | |
"latitude": "51.490083", | |
"longitude": "-0.162418", | |
"bikesAvailable": "8", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "287", | |
"name": "Bedford Way, Bloomsbury", | |
"latitude": "51.52367314", | |
"longitude": "-0.128377673", | |
"bikesAvailable": "9", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "286", | |
"name": "\n St. John's Wood Road, St. John's Wood\n ", | |
"latitude": "51.5272947", | |
"longitude": "-0.174653609", | |
"bikesAvailable": "0", | |
"emptySlots": "34", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "284", | |
"name": "Lambeth North Station, Waterloo", | |
"latitude": "51.49914063", | |
"longitude": "-0.112031483", | |
"bikesAvailable": "3", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "258", | |
"name": "Kensington Gore, Knightsbridge", | |
"latitude": "51.50143293", | |
"longitude": "-0.178656971", | |
"bikesAvailable": "20", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "259", | |
"name": "Embankment (Horse Guards), Westminster", | |
"latitude": "51.50494561", | |
"longitude": "-0.123247648", | |
"bikesAvailable": "16", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "256", | |
"name": "Houghton Street, Strand", | |
"latitude": "51.51362054", | |
"longitude": "-0.116764211", | |
"bikesAvailable": "12", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "257", | |
"name": "Westminster University, Marylebone", | |
"latitude": "51.52248185", | |
"longitude": "-0.154907218", | |
"bikesAvailable": "2", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "262", | |
"name": "\n LSBU (Borough Road), Elephant & Castle\n ", | |
"latitude": "51.49874469", | |
"longitude": "-0.103132904", | |
"bikesAvailable": "18", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "263", | |
"name": "St. Mary Axe, Aldgate", | |
"latitude": "51.51422502", | |
"longitude": "-0.080660083", | |
"bikesAvailable": "9", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "260", | |
"name": "Broadwick Street, Soho", | |
"latitude": "51.5136846", | |
"longitude": "-0.135580879", | |
"bikesAvailable": "17", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "261", | |
"name": "Princes Square, Bayswater", | |
"latitude": "51.5134891", | |
"longitude": "-0.191351186", | |
"bikesAvailable": "1", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "266", | |
"name": "Queen's Gate (North), Kensington", | |
"latitude": "51.50102668", | |
"longitude": "-0.180246101", | |
"bikesAvailable": "15", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "267", | |
"name": "Regency Street, Westminster", | |
"latitude": "51.49206037", | |
"longitude": "-0.132224622", | |
"bikesAvailable": "11", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "264", | |
"name": "Tysoe Street, Clerkenwell", | |
"latitude": "51.52644342", | |
"longitude": "-0.109256828", | |
"bikesAvailable": "9", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "265", | |
"name": "Southwick Street, Paddington", | |
"latitude": "51.51595344", | |
"longitude": "-0.169249375", | |
"bikesAvailable": "12", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "270", | |
"name": "Kennington Lane Rail Bridge, Vauxhall", | |
"latitude": "51.4863434", | |
"longitude": "-0.122492418", | |
"bikesAvailable": "22", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "271", | |
"name": "London Zoo, Regents Park", | |
"latitude": "51.53583617", | |
"longitude": "-0.156285395", | |
"bikesAvailable": "14", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "268", | |
"name": "Belgrave Road, Victoria", | |
"latitude": "51.49320445", | |
"longitude": "-0.144132875", | |
"bikesAvailable": "9", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "269", | |
"name": "Empire Square, The Borough", | |
"latitude": "51.50082346", | |
"longitude": "-0.089740764", | |
"bikesAvailable": "19", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "305", | |
"name": "Kennington Lane Tesco, Vauxhall", | |
"latitude": "51.48677988", | |
"longitude": "-0.115853961", | |
"bikesAvailable": "13", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "304", | |
"name": "Cumberland Gate, Hyde Park", | |
"latitude": "51.51211869", | |
"longitude": "-0.16209757", | |
"bikesAvailable": "12", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "307", | |
"name": "Black Lion Gate, Kensington Gardens", | |
"latitude": "51.50990837", | |
"longitude": "-0.187842717", | |
"bikesAvailable": "12", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "306", | |
"name": "Rathbone Street, Fitzrovia ", | |
"latitude": "51.51816295", | |
"longitude": "-0.135025698", | |
"bikesAvailable": "7", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "309", | |
"name": "Embankment (Savoy), Strand", | |
"latitude": "51.50963123", | |
"longitude": "-0.119047563", | |
"bikesAvailable": "34", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "308", | |
"name": "Long Lane , Bermondsey", | |
"latitude": "51.49907558", | |
"longitude": "-0.085666316", | |
"bikesAvailable": "7", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "311", | |
"name": "Foley Street, Fitzrovia", | |
"latitude": "51.51918144", | |
"longitude": "-0.140485596", | |
"bikesAvailable": "23", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "310", | |
"name": "Black Prince Road, Vauxhall", | |
"latitude": "51.4908679", | |
"longitude": "-0.116911864", | |
"bikesAvailable": "11", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "313", | |
"name": "Wells Street, Fitzrovia ", | |
"latitude": "51.51734403", | |
"longitude": "-0.138072691", | |
"bikesAvailable": "33", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "312", | |
"name": "Grove End Road, St. John's Wood", | |
"latitude": "51.53088935", | |
"longitude": "-0.176770502", | |
"bikesAvailable": "1", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "315", | |
"name": "The Tennis Courts, Regent's Park", | |
"latitude": "51.52536703", | |
"longitude": "-0.153463612", | |
"bikesAvailable": "13", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "314", | |
"name": "Tyers Gate, Bermondsey", | |
"latitude": "51.50088934", | |
"longitude": "-0.083159352", | |
"bikesAvailable": "24", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "317", | |
"name": "Dickens Square, Borough", | |
"latitude": "51.49679128", | |
"longitude": "-0.093913472", | |
"bikesAvailable": "16", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "316", | |
"name": "Cardinal Place, Victoria", | |
"latitude": "51.49768448", | |
"longitude": "-0.141943703", | |
"bikesAvailable": "8", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "319", | |
"name": "Baldwin Street, St. Luke's", | |
"latitude": "51.52702563", | |
"longitude": "-0.088542771", | |
"bikesAvailable": "17", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "318", | |
"name": "Sackville Street, Mayfair", | |
"latitude": "51.51004801", | |
"longitude": "-0.138846453", | |
"bikesAvailable": "11", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "288", | |
"name": "Elizabeth Bridge, Victoria", | |
"latitude": "51.49236962", | |
"longitude": "-0.147478734", | |
"bikesAvailable": "19", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "289", | |
"name": "South Audley Street, Mayfair", | |
"latitude": "51.50923022", | |
"longitude": "-0.151296092", | |
"bikesAvailable": "11", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "290", | |
"name": "Winsland Street, Paddington", | |
"latitude": "51.51678023", | |
"longitude": "-0.175488803", | |
"bikesAvailable": "5", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "291", | |
"name": "Claverton Street, Pimlico", | |
"latitude": "51.48483991", | |
"longitude": "-0.138089062", | |
"bikesAvailable": "5", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "292", | |
"name": "Montpelier Street, Knightsbridge", | |
"latitude": "51.49888404", | |
"longitude": "-0.165471605", | |
"bikesAvailable": "6", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "293", | |
"name": "Kensington Olympia Station, Olympia", | |
"latitude": "51.49815779", | |
"longitude": "-0.209494128", | |
"bikesAvailable": "0", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "294", | |
"name": "St. George's Square, Pimlico", | |
"latitude": "51.488226", | |
"longitude": "-0.135635511", | |
"bikesAvailable": "7", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "295", | |
"name": "Swan Street, The Borough", | |
"latitude": "51.50029631", | |
"longitude": "-0.092762704", | |
"bikesAvailable": "19", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "296", | |
"name": "Knaresborough Place, Earl's Court", | |
"latitude": "51.49363156", | |
"longitude": "-0.190603326", | |
"bikesAvailable": "6", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "297", | |
"name": "Geraldine Street, Elephant & Castle", | |
"latitude": "51.49612799", | |
"longitude": "-0.106000855", | |
"bikesAvailable": "1", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "298", | |
"name": "Curlew Street, Shad Thames", | |
"latitude": "51.50227992", | |
"longitude": "-0.074189225", | |
"bikesAvailable": "3", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "299", | |
"name": "Vincent Square, Westminster", | |
"latitude": "51.49398524", | |
"longitude": "-0.136928582", | |
"bikesAvailable": "17", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "300", | |
"name": "Serpentine Car Park, Hyde Park", | |
"latitude": "51.50501351", | |
"longitude": "-0.172729559", | |
"bikesAvailable": "8", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "301", | |
"name": "Marylebone Lane, Marylebone", | |
"latitude": "51.51475963", | |
"longitude": "-0.148105415", | |
"bikesAvailable": "22", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "303", | |
"name": "Albert Gate, Hyde Park", | |
"latitude": "51.50295379", | |
"longitude": "-0.158456089", | |
"bikesAvailable": "16", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "343", | |
"name": "London Zoo Car Park, Regent's Park", | |
"latitude": "51.53692216", | |
"longitude": "-0.150181444", | |
"bikesAvailable": "7", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "341", | |
"name": "Craven Street, Strand", | |
"latitude": "51.50810309", | |
"longitude": "-0.12602103", | |
"bikesAvailable": "3", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "340", | |
"name": "Bank of England Museum, Bank", | |
"latitude": "51.51444134", | |
"longitude": "-0.087587447", | |
"bikesAvailable": "8", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "339", | |
"name": "Risinghill Street, Angel", | |
"latitude": "51.5333196", | |
"longitude": "-0.111781191", | |
"bikesAvailable": "8", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "338", | |
"name": "Wellington Street , Strand", | |
"latitude": "51.51175646", | |
"longitude": "-0.119643424", | |
"bikesAvailable": "13", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "337", | |
"name": "Pembridge Villas, Notting Hill", | |
"latitude": "51.51108452", | |
"longitude": "-0.197524944", | |
"bikesAvailable": "3", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "336", | |
"name": "Concert Hall Approach 2, South Bank", | |
"latitude": "51.504942", | |
"longitude": "-0.115533", | |
"bikesAvailable": "6", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "351", | |
"name": "Macclesfield Rd, St Lukes", | |
"latitude": "51.529423", | |
"longitude": "-0.097122", | |
"bikesAvailable": "22", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "350", | |
"name": "Queen's Gate, Kensington Gardens", | |
"latitude": "51.5017154373867", | |
"longitude": "-0.1798541843891", | |
"bikesAvailable": "10", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "349", | |
"name": "St. George Street, Mayfair", | |
"latitude": "51.51196", | |
"longitude": "-0.142783033", | |
"bikesAvailable": "17", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "348", | |
"name": "Grosvenor Square, Mayfair", | |
"latitude": "51.51217033", | |
"longitude": "-0.150481272", | |
"bikesAvailable": "11", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "347", | |
"name": "Lower Marsh, Waterloo", | |
"latitude": "51.50013942", | |
"longitude": "-0.113936001", | |
"bikesAvailable": "9", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "345", | |
"name": "Flood Street, Chelsea", | |
"latitude": "51.48802358", | |
"longitude": "-0.166878535", | |
"bikesAvailable": "18", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "344", | |
"name": "Goswell Road (City Uni), Finsbury", | |
"latitude": "51.528246", | |
"longitude": "-0.10102611", | |
"bikesAvailable": "11", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "326", | |
"name": "Graham Street, Angel", | |
"latitude": "51.53266186", | |
"longitude": "-0.099981142", | |
"bikesAvailable": "17", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "327", | |
"name": "New North Road 1, Hoxton", | |
"latitude": "51.53114", | |
"longitude": "-0.086016", | |
"bikesAvailable": "13", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "324", | |
"name": "Ontario Street, Elephant & Castle", | |
"latitude": "51.49652013", | |
"longitude": "-0.101384068", | |
"bikesAvailable": "4", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "325", | |
"name": "St. Martin's Street, West End", | |
"latitude": "51.50908747", | |
"longitude": "-0.129697889", | |
"bikesAvailable": "15", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "322", | |
"name": "Palissy Street, Shoreditch", | |
"latitude": "51.526293", | |
"longitude": "-0.073955", | |
"bikesAvailable": "11", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "323", | |
"name": "Clifton Street, Shoreditch", | |
"latitude": "51.523196", | |
"longitude": "-0.083067", | |
"bikesAvailable": "20", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "320", | |
"name": "Queen Mother Sports Centre, Victoria", | |
"latitude": "51.49357351", | |
"longitude": "-0.139956043", | |
"bikesAvailable": "25", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "321", | |
"name": "Bermondsey Street, Bermondsey", | |
"latitude": "51.49785559", | |
"longitude": "-0.081608045", | |
"bikesAvailable": "3", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "334", | |
"name": "Concert Hall Approach 1, South Bank", | |
"latitude": "51.505044", | |
"longitude": "-0.115851", | |
"bikesAvailable": "3", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "335", | |
"name": "Tavistock Street, Covent Garden", | |
"latitude": "51.51196803", | |
"longitude": "-0.120718759", | |
"bikesAvailable": "25", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "332", | |
"name": "Nevern Place, Earl's Court", | |
"latitude": "51.49334336", | |
"longitude": "-0.194757949", | |
"bikesAvailable": "3", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "333", | |
"name": "Palace Gardens Terrace, Notting Hill", | |
"latitude": "51.50860544", | |
"longitude": "-0.193764092", | |
"bikesAvailable": "1", | |
"emptySlots": "36", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "330", | |
"name": "Eastbourne Mews, Paddington", | |
"latitude": "51.51641749", | |
"longitude": "-0.179135079", | |
"bikesAvailable": "0", | |
"emptySlots": "34", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "331", | |
"name": "Bunhill Row, Moorgate", | |
"latitude": "51.52085887", | |
"longitude": "-0.089887855", | |
"bikesAvailable": "22", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "328", | |
"name": "New North Road 2, Hoxton", | |
"latitude": "51.53095", | |
"longitude": "-0.085603", | |
"bikesAvailable": "17", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "329", | |
"name": "Prince Albert Road, Regent's Park", | |
"latitude": "51.53589283", | |
"longitude": "-0.160854428", | |
"bikesAvailable": "17", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "373", | |
"name": "Prince Consort Road, Knightsbridge", | |
"latitude": "51.49980661", | |
"longitude": "-0.176415994", | |
"bikesAvailable": "5", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "372", | |
"name": "Sardinia Street, Holborn", | |
"latitude": "51.515208", | |
"longitude": "-0.117863", | |
"bikesAvailable": "17", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "375", | |
"name": "Kensington Town Hall, Kensington", | |
"latitude": "51.50194596", | |
"longitude": "-0.194392952", | |
"bikesAvailable": "6", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "374", | |
"name": "Waterloo Station 1, Waterloo", | |
"latitude": "51.50402793", | |
"longitude": "-0.11386435", | |
"bikesAvailable": "15", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "368", | |
"name": "Harriet Street, Knightsbridge", | |
"latitude": "51.50024195", | |
"longitude": "-0.15934065", | |
"bikesAvailable": "24", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "371", | |
"name": "King Edward Walk, Waterloo", | |
"latitude": "51.49775", | |
"longitude": "-0.10988", | |
"bikesAvailable": "6", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "370", | |
"name": "Paddington Green, Paddington", | |
"latitude": "51.520205", | |
"longitude": "-0.174593", | |
"bikesAvailable": "7", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "381", | |
"name": "Charlotte Street, Fitzrovia", | |
"latitude": "51.51953043", | |
"longitude": "-0.13577731", | |
"bikesAvailable": "4", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "380", | |
"name": "Stanhope Gate, Mayfair", | |
"latitude": "51.50686435", | |
"longitude": "-0.150666888", | |
"bikesAvailable": "11", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "383", | |
"name": "Frith Street, Soho", | |
"latitude": "51.51310333", | |
"longitude": "-0.13121385", | |
"bikesAvailable": "16", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "382", | |
"name": "Farm Street, Mayfair", | |
"latitude": "51.50935171", | |
"longitude": "-0.14744969", | |
"bikesAvailable": "7", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "377", | |
"name": "Waterloo Bridge, South Bank", | |
"latitude": "51.50535447", | |
"longitude": "-0.113656543", | |
"bikesAvailable": "3", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "376", | |
"name": "Millbank House, Pimlico", | |
"latitude": "51.49188409", | |
"longitude": "-0.125674815", | |
"bikesAvailable": "18", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "379", | |
"name": "Turquoise Island, Notting Hill", | |
"latitude": "51.51431171", | |
"longitude": "-0.200838199", | |
"bikesAvailable": "2", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "378", | |
"name": "Natural History Museum, South Kensington", | |
"latitude": "51.49559291", | |
"longitude": "-0.179077626", | |
"bikesAvailable": "39", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "356", | |
"name": "\n South Kensington Station, South Kensington\n ", | |
"latitude": "51.494412", | |
"longitude": "-0.173881", | |
"bikesAvailable": "8", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "357", | |
"name": "Howland Street, Fitzrovia", | |
"latitude": "51.520994", | |
"longitude": "-0.139016", | |
"bikesAvailable": "26", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "358", | |
"name": "High Holborn , Covent Garden", | |
"latitude": "51.51643491", | |
"longitude": "-0.124332175", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "359", | |
"name": "Butler Place, Westminster", | |
"latitude": "51.49782999", | |
"longitude": "-0.135440826", | |
"bikesAvailable": "12", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "352", | |
"name": "Vauxhall Street, Vauxhall", | |
"latitude": "51.486965", | |
"longitude": "-0.116625", | |
"bikesAvailable": "4", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "353", | |
"name": "Greycoat Street , Westminster", | |
"latitude": "51.49459148", | |
"longitude": "-0.134234258", | |
"bikesAvailable": "14", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "354", | |
"name": "Northumberland Avenue, Strand", | |
"latitude": "51.506767", | |
"longitude": "-0.123702", | |
"bikesAvailable": "16", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "355", | |
"name": "Oval Way, Lambeth", | |
"latitude": "51.486575", | |
"longitude": "-0.117286", | |
"bikesAvailable": "6", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "364", | |
"name": "Alfred Place, Bloomsbury", | |
"latitude": "51.519656", | |
"longitude": "-0.132339", | |
"bikesAvailable": "24", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "365", | |
"name": "City Road, Angel", | |
"latitude": "51.530344", | |
"longitude": "-0.100168", | |
"bikesAvailable": "41", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "366", | |
"name": "Millennium Hotel, Mayfair", | |
"latitude": "51.5109192966489", | |
"longitude": "-0.1511263847351", | |
"bikesAvailable": "25", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "367", | |
"name": "Harrowby Street, Marylebone", | |
"latitude": "51.5173721", | |
"longitude": "-0.1642075", | |
"bikesAvailable": "2", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "360", | |
"name": "Howick Place, Westminster", | |
"latitude": "51.49675303", | |
"longitude": "-0.138733562", | |
"bikesAvailable": "24", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "361", | |
"name": "Waterloo Station 2, Waterloo", | |
"latitude": "51.50391972", | |
"longitude": "-0.11342628", | |
"bikesAvailable": "0", | |
"emptySlots": "55", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "362", | |
"name": "Royal College Street, Camden Town", | |
"latitude": "51.536264", | |
"longitude": "-0.133952", | |
"bikesAvailable": "28", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "363", | |
"name": "Lord's, St. John's Wood", | |
"latitude": "51.5291212008901", | |
"longitude": "-0.171185284853", | |
"bikesAvailable": "9", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "410", | |
"name": "Edgware Road Station, Paddington", | |
"latitude": "51.519968", | |
"longitude": "-0.169774", | |
"bikesAvailable": "30", | |
"emptySlots": "34", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "411", | |
"name": "Walworth Road, Southwark", | |
"latitude": "51.49337264", | |
"longitude": "-0.09968067", | |
"bikesAvailable": "4", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "408", | |
"name": "\n Paddington Green Police Station, Paddington\n ", | |
"latitude": "51.51994326", | |
"longitude": "-0.170704337", | |
"bikesAvailable": "2", | |
"emptySlots": "38", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "409", | |
"name": "Strata, Southwark", | |
"latitude": "51.493146", | |
"longitude": "-0.099828", | |
"bikesAvailable": "9", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "412", | |
"name": "Cleaver Street, Kennington", | |
"latitude": "51.488105", | |
"longitude": "-0.110121", | |
"bikesAvailable": "6", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "402", | |
"name": "Penfold Street, Marylebone", | |
"latitude": "51.52289229", | |
"longitude": "-0.171681991", | |
"bikesAvailable": "11", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "403", | |
"name": "George Place Mews, Marylebone", | |
"latitude": "51.51689296", | |
"longitude": "-0.158249929", | |
"bikesAvailable": "15", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "400", | |
"name": "George Street, Marylebone", | |
"latitude": "51.517703", | |
"longitude": "-0.154106", | |
"bikesAvailable": "25", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "401", | |
"name": "Columbia Road, Weavers", | |
"latitude": "51.528187", | |
"longitude": "-0.075375", | |
"bikesAvailable": "6", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "406", | |
"name": "Speakers' Corner 2, Hyde Park", | |
"latitude": "51.512303", | |
"longitude": "-0.159988", | |
"bikesAvailable": "9", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "407", | |
"name": "Speakers' Corner 1, Hyde Park", | |
"latitude": "51.51222", | |
"longitude": "-0.160785", | |
"bikesAvailable": "17", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "404", | |
"name": "Palace Gate, Kensington Gardens", | |
"latitude": "51.50204238", | |
"longitude": "-0.184400221", | |
"bikesAvailable": "4", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "405", | |
"name": "\n Gloucester Road Station, South Kensington\n ", | |
"latitude": "51.49418566", | |
"longitude": "-0.18267094", | |
"bikesAvailable": "6", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "395", | |
"name": "Cadogan Gardens, Sloane Square", | |
"latitude": "51.492462", | |
"longitude": "-0.159919", | |
"bikesAvailable": "14", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "394", | |
"name": "Aberdeen Place, St. John's Wood", | |
"latitude": "51.524826", | |
"longitude": "-0.176268", | |
"bikesAvailable": "9", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "393", | |
"name": "Snow Hill, Farringdon", | |
"latitude": "51.51733427", | |
"longitude": "-0.103604248", | |
"bikesAvailable": "11", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "392", | |
"name": "Imperial College, Knightsbridge", | |
"latitude": "51.49942855", | |
"longitude": "-0.179702476", | |
"bikesAvailable": "9", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "399", | |
"name": "Brick Lane Market, Shoreditch", | |
"latitude": "51.52261762", | |
"longitude": "-0.071653961", | |
"bikesAvailable": "14", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "398", | |
"name": "Holland Park, Kensington", | |
"latitude": "51.502319", | |
"longitude": "-0.200742", | |
"bikesAvailable": "1", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "397", | |
"name": "Devonshire Terrace, Bayswater", | |
"latitude": "51.51348", | |
"longitude": "-0.17977", | |
"bikesAvailable": "1", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "396", | |
"name": "Shouldham Street, Marylebone", | |
"latitude": "51.51809", | |
"longitude": "-0.163609", | |
"bikesAvailable": "4", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "387", | |
"name": "Fire Brigade Pier, Vauxhall", | |
"latitude": "51.49369988", | |
"longitude": "-0.121394101", | |
"bikesAvailable": "23", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "386", | |
"name": "Moor Street, Soho", | |
"latitude": "51.51352755", | |
"longitude": "-0.130110822", | |
"bikesAvailable": "13", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "384", | |
"name": "Marloes Road, Kensington", | |
"latitude": "51.496481", | |
"longitude": "-0.192404", | |
"bikesAvailable": "5", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "391", | |
"name": "Clifford Street, Mayfair", | |
"latitude": "51.51066202", | |
"longitude": "-0.142345694", | |
"bikesAvailable": "25", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "390", | |
"name": "Buxton Street 1, Shoreditch", | |
"latitude": "51.521776", | |
"longitude": "-0.068856", | |
"bikesAvailable": "36", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "389", | |
"name": "Upper Grosvenor Street, Mayfair", | |
"latitude": "51.51013066", | |
"longitude": "-0.155757901", | |
"bikesAvailable": "6", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "388", | |
"name": "Southampton Street, Strand", | |
"latitude": "51.51070161", | |
"longitude": "-0.121723604", | |
"bikesAvailable": "1", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "440", | |
"name": "Kennington Oval, Oval", | |
"latitude": "51.483145", | |
"longitude": "-0.113134", | |
"bikesAvailable": "23", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "441", | |
"name": "Sail Street, Vauxhall", | |
"latitude": "51.495656", | |
"longitude": "-0.114605", | |
"bikesAvailable": "9", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "442", | |
"name": "Walmer Road, Notting Hill", | |
"latitude": "51.510101", | |
"longitude": "-0.211358", | |
"bikesAvailable": "11", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "443", | |
"name": "Philpot Street, Whitechapel", | |
"latitude": "51.515256", | |
"longitude": "-0.058641", | |
"bikesAvailable": "2", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "444", | |
"name": "Bethnal Green Gardens, Bethnal Green", | |
"latitude": "51.52568", | |
"longitude": "-0.055312", | |
"bikesAvailable": "22", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "445", | |
"name": "Cheshire Street, Bethnal Green", | |
"latitude": "51.52388", | |
"longitude": "-0.065076", | |
"bikesAvailable": "12", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "446", | |
"name": "York Hall, Bethnal Green", | |
"latitude": "51.528936", | |
"longitude": "-0.055894", | |
"bikesAvailable": "24", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "447", | |
"name": "Jubilee Crescent, Cubitt Town", | |
"latitude": "51.493381", | |
"longitude": "-0.007542", | |
"bikesAvailable": "34", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "432", | |
"name": "Exhibition Road Museums, Knightsbridge", | |
"latitude": "51.496957", | |
"longitude": "-0.173894", | |
"bikesAvailable": "18", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "433", | |
"name": "Wren Street, Holborn", | |
"latitude": "51.524564", | |
"longitude": "-0.116279", | |
"bikesAvailable": "24", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "435", | |
"name": "Kennington Station, Kennington", | |
"latitude": "51.488852", | |
"longitude": "-0.105593", | |
"bikesAvailable": "13", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "436", | |
"name": "Red Lion Street, Holborn", | |
"latitude": "51.51824", | |
"longitude": "-0.11655", | |
"bikesAvailable": "29", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "437", | |
"name": "Vauxhall Walk, Vauxhall", | |
"latitude": "51.488124", | |
"longitude": "-0.120903", | |
"bikesAvailable": "15", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "439", | |
"name": "Killick Street, Kings Cross", | |
"latitude": "51.5338", | |
"longitude": "-0.118677", | |
"bikesAvailable": "15", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "425", | |
"name": "Harrington Square 2, Camden Town", | |
"latitude": "51.533379", | |
"longitude": "-0.139159", | |
"bikesAvailable": "25", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "424", | |
"name": "Ebury Bridge, Pimlico", | |
"latitude": "51.490491", | |
"longitude": "-0.149186", | |
"bikesAvailable": "20", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "427", | |
"name": "Cheapside, Bank", | |
"latitude": "51.51397065", | |
"longitude": "-0.09294031", | |
"bikesAvailable": "41", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "426", | |
"name": "Vincent Street, Pimlico", | |
"latitude": "51.493072", | |
"longitude": "-0.129925", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "428", | |
"name": "Exhibition Road, South Kensington", | |
"latitude": "51.499917", | |
"longitude": "-0.174554", | |
"bikesAvailable": "10", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "431", | |
"name": "Crinan Street, King's Cross", | |
"latitude": "51.534474", | |
"longitude": "-0.122203", | |
"bikesAvailable": "8", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "430", | |
"name": "South Parade, Chelsea", | |
"latitude": "51.48902", | |
"longitude": "-0.17524", | |
"bikesAvailable": "14", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "421", | |
"name": "Southwark Station 2, Southwark", | |
"latitude": "51.504044", | |
"longitude": "-0.104778", | |
"bikesAvailable": "18", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "420", | |
"name": "Southwark Station 1, Southwark", | |
"latitude": "51.504043", | |
"longitude": "-0.105312", | |
"bikesAvailable": "33", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "423", | |
"name": "Eaton Square (South), Belgravia", | |
"latitude": "51.49456127", | |
"longitude": "-0.15393398", | |
"bikesAvailable": "31", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "478", | |
"name": "Stepney Green Station, Stepney", | |
"latitude": "51.521889", | |
"longitude": "-0.04667", | |
"bikesAvailable": "4", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "479", | |
"name": "Pott Street, Bethnal Green", | |
"latitude": "51.527152", | |
"longitude": "-0.058005", | |
"bikesAvailable": "13", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "476", | |
"name": "Stebondale Street, Cubitt Town", | |
"latitude": "51.489096", | |
"longitude": "-0.009205", | |
"bikesAvailable": "28", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "477", | |
"name": "Spindrift Avenue, Millwall", | |
"latitude": "51.49109", | |
"longitude": "-0.018716", | |
"bikesAvailable": "19", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "474", | |
"name": "Castalia Square, Cubitt Town", | |
"latitude": "51.498125", | |
"longitude": "-0.011457", | |
"bikesAvailable": "15", | |
"emptySlots": "23", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "475", | |
"name": "Lightermans Road, Millwall", | |
"latitude": "51.499041", | |
"longitude": "-0.020157", | |
"bikesAvailable": "47", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "472", | |
"name": "Malmesbury Road, Bow", | |
"latitude": "51.529452", | |
"longitude": "-0.027616", | |
"bikesAvailable": "11", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "473", | |
"name": "Millharbour, Millwall", | |
"latitude": "51.496137", | |
"longitude": "-0.019355", | |
"bikesAvailable": "14", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "470", | |
"name": "Mostyn Grove, Bow", | |
"latitude": "51.530535", | |
"longitude": "-0.025492", | |
"bikesAvailable": "15", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "471", | |
"name": "Hewison Street, Old Ford", | |
"latitude": "51.533283", | |
"longitude": "-0.028155", | |
"bikesAvailable": "1", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "468", | |
"name": "Cantrell Road, Bow", | |
"latitude": "51.521564", | |
"longitude": "-0.022694", | |
"bikesAvailable": "21", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "469", | |
"name": "Lindfield Street, Poplar", | |
"latitude": "51.513757", | |
"longitude": "-0.020467", | |
"bikesAvailable": "22", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "466", | |
"name": "Whiston Road, Haggerston", | |
"latitude": "51.534464", | |
"longitude": "-0.076341", | |
"bikesAvailable": "6", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "467", | |
"name": "Southern Grove, Bow", | |
"latitude": "51.523538", | |
"longitude": "-0.030556", | |
"bikesAvailable": "3", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "464", | |
"name": "St. Mary and St. Michael Church, Stepney", | |
"latitude": "51.51417", | |
"longitude": "-0.052099", | |
"bikesAvailable": "18", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "465", | |
"name": "Pitfield Street (North),Hoxton", | |
"latitude": "51.53558", | |
"longitude": "-0.08249", | |
"bikesAvailable": "6", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "463", | |
"name": "Thurtle Road, Haggerston", | |
"latitude": "51.534776", | |
"longitude": "-0.071881", | |
"bikesAvailable": "3", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "462", | |
"name": "Bonny Street, Camden Town", | |
"latitude": "51.541603", | |
"longitude": "-0.138853", | |
"bikesAvailable": "2", | |
"emptySlots": "43", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "461", | |
"name": "Aston Street, Stepney", | |
"latitude": "51.516", | |
"longitude": "-0.038775", | |
"bikesAvailable": "6", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "460", | |
"name": "Burdett Road, Mile End", | |
"latitude": "51.516196", | |
"longitude": "-0.029138", | |
"bikesAvailable": "18", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "459", | |
"name": "Gun Makers Lane, Old Ford", | |
"latitude": "51.535179", | |
"longitude": "-0.03338", | |
"bikesAvailable": "2", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "458", | |
"name": "Wapping Lane, Wapping", | |
"latitude": "51.504749", | |
"longitude": "-0.057544", | |
"bikesAvailable": "2", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "457", | |
"name": "Castlehaven Road, Camden Town", | |
"latitude": "51.542138", | |
"longitude": "-0.145393", | |
"bikesAvailable": "18", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "456", | |
"name": "Parkway, Camden Town", | |
"latitude": "51.538071", | |
"longitude": "-0.144664", | |
"bikesAvailable": "21", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "455", | |
"name": "East Ferry Road, Cubitt Town", | |
"latitude": "51.49447", | |
"longitude": "-0.014409", | |
"bikesAvailable": "15", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "454", | |
"name": "Napier Avenue, Millwall", | |
"latitude": "51.487679", | |
"longitude": "-0.021582", | |
"bikesAvailable": "12", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "453", | |
"name": "Garnet Street, Shadwell", | |
"latitude": "51.508447", | |
"longitude": "-0.055167", | |
"bikesAvailable": "5", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "452", | |
"name": "St Katharines Way, Tower", | |
"latitude": "51.505697", | |
"longitude": "-0.070542", | |
"bikesAvailable": "18", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "451", | |
"name": "Hermitage Court, Wapping", | |
"latitude": "51.504719", | |
"longitude": "-0.063531", | |
"bikesAvailable": "2", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "450", | |
"name": "Jubilee Street, Stepney", | |
"latitude": "51.515975", | |
"longitude": "-0.053177", | |
"bikesAvailable": "14", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "449", | |
"name": "Shadwell Station, Shadwell", | |
"latitude": "51.511088", | |
"longitude": "-0.057159", | |
"bikesAvailable": "0", | |
"emptySlots": "34", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "448", | |
"name": "Fishermans Walk West, Canary Wharf", | |
"latitude": "51.50623", | |
"longitude": "-0.02296", | |
"bikesAvailable": "18", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "508", | |
"name": "Fournier Street, Whitechapel", | |
"latitude": "51.519362", | |
"longitude": "-0.074431", | |
"bikesAvailable": "16", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "509", | |
"name": "Fore Street, Guildhall", | |
"latitude": "51.517842", | |
"longitude": "-0.090075", | |
"bikesAvailable": "10", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "510", | |
"name": "Westferry DLR, Limehouse", | |
"latitude": "51.509303", | |
"longitude": "-0.025996", | |
"bikesAvailable": "39", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "511", | |
"name": "Sutton Street, Shadwell", | |
"latitude": "51.511066", | |
"longitude": "-0.053558", | |
"bikesAvailable": "0", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "504", | |
"name": "St John's Park, Cubitt Town", | |
"latitude": "51.496454", | |
"longitude": "-0.009506", | |
"bikesAvailable": "20", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "505", | |
"name": "Ackroyd Drive, Bow", | |
"latitude": "51.520398", | |
"longitude": "-0.026768", | |
"bikesAvailable": "19", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "506", | |
"name": "Bell Lane, Liverpool Street", | |
"latitude": "51.517475", | |
"longitude": "-0.075855", | |
"bikesAvailable": "17", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "507", | |
"name": "Clarkson Street, Bethnal Green", | |
"latitude": "51.528692", | |
"longitude": "-0.059091", | |
"bikesAvailable": "5", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "500", | |
"name": "Sidney Street, Stepney", | |
"latitude": "51.519806", | |
"longitude": "-0.055731", | |
"bikesAvailable": "7", | |
"emptySlots": "36", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "501", | |
"name": "Cephas Street, Bethnal Green", | |
"latitude": "51.522561", | |
"longitude": "-0.054883", | |
"bikesAvailable": "9", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "502", | |
"name": "South Quay West, Canary Wharf", | |
"latitude": "51.502635", | |
"longitude": "-0.022702", | |
"bikesAvailable": "18", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "503", | |
"name": "Cleveland Way, Bethnal Green", | |
"latitude": "51.520893", | |
"longitude": "-0.051394", | |
"bikesAvailable": "14", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "496", | |
"name": "Devons Road, Bow", | |
"latitude": "51.52512", | |
"longitude": "-0.015578", | |
"bikesAvailable": "15", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "497", | |
"name": "Coborn Street, Mile End", | |
"latitude": "51.526535", | |
"longitude": "-0.028619", | |
"bikesAvailable": "22", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "498", | |
"name": "Bow Road Station, Bow", | |
"latitude": "51.527058", | |
"longitude": "-0.025296", | |
"bikesAvailable": "37", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "499", | |
"name": "Furze Green, Bow", | |
"latitude": "51.519265", | |
"longitude": "-0.021345", | |
"bikesAvailable": "6", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "492", | |
"name": "The Green Bridge, Mile End", | |
"latitude": "51.524677", | |
"longitude": "-0.03562", | |
"bikesAvailable": "28", | |
"emptySlots": "35", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "495", | |
"name": "Bow Church Station, Bow", | |
"latitude": "51.528169", | |
"longitude": "-0.018703", | |
"bikesAvailable": "18", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "494", | |
"name": "South Quay East, Canary Wharf", | |
"latitude": "51.50196", | |
"longitude": "-0.016251", | |
"bikesAvailable": "25", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "489", | |
"name": "Christian Street, Whitechapel", | |
"latitude": "51.513074", | |
"longitude": "-0.064094", | |
"bikesAvailable": "9", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "488", | |
"name": "Reardon Street, Wapping", | |
"latitude": "51.506946", | |
"longitude": "-0.058681", | |
"bikesAvailable": "0", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "491", | |
"name": "Queen Marys, Mile End", | |
"latitude": "51.522507", | |
"longitude": "-0.041378", | |
"bikesAvailable": "28", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "490", | |
"name": "Pennington Street, Wapping", | |
"latitude": "51.508622", | |
"longitude": "-0.065006", | |
"bikesAvailable": "28", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "485", | |
"name": "Old Ford Road, Bethnal Green", | |
"latitude": "51.531127", | |
"longitude": "-0.048017", | |
"bikesAvailable": "3", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "484", | |
"name": "Bromley High Street, Bromley", | |
"latitude": "51.528828", | |
"longitude": "-0.013258", | |
"bikesAvailable": "8", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "487", | |
"name": "Canton Street, Poplar", | |
"latitude": "51.511811", | |
"longitude": "-0.025626", | |
"bikesAvailable": "10", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "486", | |
"name": "Granby Street, Shoreditch", | |
"latitude": "51.525645", | |
"longitude": "-0.069543", | |
"bikesAvailable": "7", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "481", | |
"name": "Saunders Ness Road, Cubitt Town", | |
"latitude": "51.487129", | |
"longitude": "-0.009001", | |
"bikesAvailable": "30", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "480", | |
"name": "Flamborough Street, Limehouse", | |
"latitude": "51.5128711", | |
"longitude": "-0.0389866", | |
"bikesAvailable": "1", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "483", | |
"name": "Albert Gardens, Stepney", | |
"latitude": "51.51328", | |
"longitude": "-0.047784", | |
"bikesAvailable": "11", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "482", | |
"name": "Thornfield House, Poplar", | |
"latitude": "51.509843", | |
"longitude": "-0.02377", | |
"bikesAvailable": "15", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "550", | |
"name": "Harford Street, Mile End", | |
"latitude": "51.521564", | |
"longitude": "-0.039264", | |
"bikesAvailable": "22", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "551", | |
"name": "Montgomery Square, Canary Wharf", | |
"latitude": "51.503447", | |
"longitude": "-0.016233", | |
"bikesAvailable": "34", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "548", | |
"name": "\n Westminster Bridge Road, Elephant & Castle\n ", | |
"latitude": "51.498386", | |
"longitude": "-0.107913", | |
"bikesAvailable": "13", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "549", | |
"name": "Gaywood Street, Elephant & Castle", | |
"latitude": "51.49605", | |
"longitude": "-0.104193", | |
"bikesAvailable": "13", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "546", | |
"name": "New Fetter Lane, Holborn", | |
"latitude": "51.517428", | |
"longitude": "-0.107987", | |
"bikesAvailable": "10", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "547", | |
"name": "East India DLR, Blackwall", | |
"latitude": "51.509474", | |
"longitude": "-0.002275", | |
"bikesAvailable": "35", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "544", | |
"name": "Percival Street, Finsbury", | |
"latitude": "51.526153", | |
"longitude": "-0.102208", | |
"bikesAvailable": "14", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "545", | |
"name": "Arlington Road, Camden Town", | |
"latitude": "51.539957", | |
"longitude": "-0.145246", | |
"bikesAvailable": "18", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "558", | |
"name": "Page Street, Westminster", | |
"latitude": "51.493978", | |
"longitude": "-0.127554", | |
"bikesAvailable": "20", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "559", | |
"name": "Abbotsbury Road, Holland Park", | |
"latitude": "51.501391", | |
"longitude": "-0.205991", | |
"bikesAvailable": "3", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "556", | |
"name": "Heron Quays DLR, Canary Wharf", | |
"latitude": "51.502661", | |
"longitude": "-0.021596", | |
"bikesAvailable": "21", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "557", | |
"name": "King Edward Street, St Pauls", | |
"latitude": "51.51601", | |
"longitude": "-0.0985", | |
"bikesAvailable": "17", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "554", | |
"name": "Aberfeldy Street, Poplar", | |
"latitude": "51.513548", | |
"longitude": "-0.005659", | |
"bikesAvailable": "8", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "555", | |
"name": "\n Westfield Eastern Access Road, Shepherd's Bush\n ", | |
"latitude": "51.509424", | |
"longitude": "-0.219712", | |
"bikesAvailable": "18", | |
"emptySlots": "37", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "552", | |
"name": "Watney Street, Shadwell", | |
"latitude": "51.511542", | |
"longitude": "-0.056667", | |
"bikesAvailable": "0", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "553", | |
"name": "Regent's Row , Haggerston ", | |
"latitude": "51.535678", | |
"longitude": "-0.062546", | |
"bikesAvailable": "5", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "566", | |
"name": "Westfield Ariel Way, White City", | |
"latitude": "51.509158", | |
"longitude": "-0.224103", | |
"bikesAvailable": "24", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "565", | |
"name": "Selby Street, Whitechapel", | |
"latitude": "51.521905", | |
"longitude": "-0.063386", | |
"bikesAvailable": "9", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "564", | |
"name": "Somerset House, Strand", | |
"latitude": "51.509943", | |
"longitude": "-0.117619", | |
"bikesAvailable": "34", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "563", | |
"name": "Preston's Road, Cubitt Town", | |
"latitude": "51.499286", | |
"longitude": "-0.009152", | |
"bikesAvailable": "25", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "562", | |
"name": "Bury Place, Holborn", | |
"latitude": "51.51746", | |
"longitude": "-0.12335", | |
"bikesAvailable": "17", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "561", | |
"name": "Rectory Square, Stepney", | |
"latitude": "51.518369", | |
"longitude": "-0.043371", | |
"bikesAvailable": "2", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "560", | |
"name": "Ladbroke Grove Central, Notting Hill", | |
"latitude": "51.511624", | |
"longitude": "-0.205921", | |
"bikesAvailable": "11", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "574", | |
"name": "Eagle Wharf Road, Hoxton", | |
"latitude": "51.53356", | |
"longitude": "-0.09315", | |
"bikesAvailable": "35", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "573", | |
"name": "Limerston Street, Chelsea", | |
"latitude": "51.485587", | |
"longitude": "-0.18119", | |
"bikesAvailable": "9", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "572", | |
"name": "Greenland Road, Camden Town", | |
"latitude": "51.539099", | |
"longitude": "-0.141728", | |
"bikesAvailable": "18", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "571", | |
"name": "\n Westfield Southern Terrace ,Shepherd's Bush\n ", | |
"latitude": "51.506256", | |
"longitude": "-0.218337", | |
"bikesAvailable": "16", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "570", | |
"name": "Upper Bank Street, Canary Wharf", | |
"latitude": "51.503083", | |
"longitude": "-0.017676", | |
"bikesAvailable": "34", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "569", | |
"name": "Pitfield Street Central, Hoxton", | |
"latitude": "51.53213", | |
"longitude": "-0.08299", | |
"bikesAvailable": "10", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "568", | |
"name": "Bishop's Bridge Road West, Bayswater", | |
"latitude": "51.51616", | |
"longitude": "-0.18697", | |
"bikesAvailable": "0", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "516", | |
"name": "Chrisp Street Market, Poplar", | |
"latitude": "51.5112", | |
"longitude": "-0.014438", | |
"bikesAvailable": "16", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "517", | |
"name": "Ford Road, Old Ford", | |
"latitude": "51.532513", | |
"longitude": "-0.033085", | |
"bikesAvailable": "0", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "518", | |
"name": "Antill Road, Mile End", | |
"latitude": "51.528224", | |
"longitude": "-0.037471", | |
"bikesAvailable": "6", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "519", | |
"name": "Teviot Street, Poplar", | |
"latitude": "51.518811", | |
"longitude": "-0.011662", | |
"bikesAvailable": "26", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "512", | |
"name": "Pritchard's Road, Bethnal Green", | |
"latitude": "51.532091", | |
"longitude": "-0.06142", | |
"bikesAvailable": "4", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "513", | |
"name": "Watney Market, Stepney", | |
"latitude": "51.5142228", | |
"longitude": "-0.055656", | |
"bikesAvailable": "9", | |
"emptySlots": "42", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "514", | |
"name": "Portman Square, Marylebone", | |
"latitude": "51.516204", | |
"longitude": "-0.155525", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "515", | |
"name": "Russell Gardens, Holland Park", | |
"latitude": "51.500088", | |
"longitude": "-0.211316", | |
"bikesAvailable": "4", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "524", | |
"name": "Lancaster Gate , Bayswater", | |
"latitude": "51.511654", | |
"longitude": "-0.179668", | |
"bikesAvailable": "8", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "525", | |
"name": "Churchill Place, Canary Wharf", | |
"latitude": "51.504714", | |
"longitude": "-0.014293", | |
"bikesAvailable": "8", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "526", | |
"name": "Lancaster Drive, Blackwall", | |
"latitude": "51.503143", | |
"longitude": "-0.008428", | |
"bikesAvailable": "23", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "527", | |
"name": "Hansard Mews, Shepherds Bush", | |
"latitude": "51.503802", | |
"longitude": "-0.215808", | |
"bikesAvailable": "19", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "520", | |
"name": "Bancroft Road, Bethnal Green", | |
"latitude": "51.526041", | |
"longitude": "-0.047218", | |
"bikesAvailable": "9", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "521", | |
"name": "Driffield Road, Old Ford", | |
"latitude": "51.534137", | |
"longitude": "-0.037366", | |
"bikesAvailable": "1", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "522", | |
"name": "Clinton Road, Mile End", | |
"latitude": "51.525941", | |
"longitude": "-0.036017", | |
"bikesAvailable": "23", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "523", | |
"name": "Langdon Park, Poplar", | |
"latitude": "51.51549", | |
"longitude": "-0.013475", | |
"bikesAvailable": "15", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "533", | |
"name": "Wellington Row, Bethnal Green", | |
"latitude": "51.528222", | |
"longitude": "-0.069743", | |
"bikesAvailable": "5", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "532", | |
"name": "Jubilee Plaza, Canary Wharf", | |
"latitude": "51.50357", | |
"longitude": "-0.020068", | |
"bikesAvailable": "56", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "535", | |
"name": "Gloucester Avenue, Camden Town", | |
"latitude": "51.537349", | |
"longitude": "-0.147154", | |
"bikesAvailable": "7", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "534", | |
"name": "Goldsmiths Row, Haggerston", | |
"latitude": "51.531864", | |
"longitude": "-0.066035", | |
"bikesAvailable": "15", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "529", | |
"name": "Manresa Road, Chelsea", | |
"latitude": "51.486892", | |
"longitude": "-0.170983", | |
"bikesAvailable": "16", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "528", | |
"name": "Clarges Street, West End", | |
"latitude": "51.507326", | |
"longitude": "-0.145827", | |
"bikesAvailable": "12", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "531", | |
"name": "Twig Folly Bridge, Mile End", | |
"latitude": "51.530326", | |
"longitude": "-0.042744", | |
"bikesAvailable": "3", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "530", | |
"name": "Newby Place, Poplar", | |
"latitude": "51.508896", | |
"longitude": "-0.012413", | |
"bikesAvailable": "16", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "541", | |
"name": "Green Park Station, West End", | |
"latitude": "51.506613", | |
"longitude": "-0.142844", | |
"bikesAvailable": "16", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "540", | |
"name": "Albany Street, Regent's Park", | |
"latitude": "51.528302", | |
"longitude": "-0.144466", | |
"bikesAvailable": "11", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "543", | |
"name": "Lansdowne Walk, Notting Hill", | |
"latitude": "51.509591", | |
"longitude": "-0.204666", | |
"bikesAvailable": "8", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "542", | |
"name": "Salmon Lane, Limehouse", | |
"latitude": "51.514115", | |
"longitude": "-0.033828", | |
"bikesAvailable": "11", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "537", | |
"name": "Old Montague Street, Whitechapel", | |
"latitude": "51.51793", | |
"longitude": "-0.067937", | |
"bikesAvailable": "8", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "536", | |
"name": "Queensbridge Road, Haggerston", | |
"latitude": "51.53385", | |
"longitude": "-0.06992", | |
"bikesAvailable": "24", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "539", | |
"name": "Hoxton Station, Haggerston", | |
"latitude": "51.531091", | |
"longitude": "-0.075901", | |
"bikesAvailable": "17", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "538", | |
"name": "Naval Row, Blackwall", | |
"latitude": "51.508981", | |
"longitude": "-0.00699", | |
"bikesAvailable": "20", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "610", | |
"name": "Danvers Street, West Chelsea", | |
"latitude": "51.48256792", | |
"longitude": "-0.172078187", | |
"bikesAvailable": "5", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "611", | |
"name": "Princedale Road , Holland Park", | |
"latitude": "51.50646524", | |
"longitude": "-0.208486599", | |
"bikesAvailable": "13", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "608", | |
"name": "Colet Gardens, Hammersmith", | |
"latitude": "51.491093", | |
"longitude": "-0.216493", | |
"bikesAvailable": "19", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "609", | |
"name": "Sugden Road, Battersea", | |
"latitude": "51.46512358", | |
"longitude": "-0.157788279", | |
"bikesAvailable": "0", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "614", | |
"name": "Bradmead, Nine Elms", | |
"latitude": "51.47817208", | |
"longitude": "-0.144690541", | |
"bikesAvailable": "26", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "615", | |
"name": "Finlay Street, Fulham", | |
"latitude": "51.4768851", | |
"longitude": "-0.215895601", | |
"bikesAvailable": "2", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "612", | |
"name": "\n Wandsworth Rd, Isley Court, Wandsworth Road\n ", | |
"latitude": "51.46925984", | |
"longitude": "-0.141812513", | |
"bikesAvailable": "15", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "613", | |
"name": "Woodstock Grove, Shepherd's Bush", | |
"latitude": "51.50403821", | |
"longitude": "-0.217400093", | |
"bikesAvailable": "6", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "618", | |
"name": "Eel Brook Common, Walham Green", | |
"latitude": "51.47625965", | |
"longitude": "-0.193254007", | |
"bikesAvailable": "1", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "619", | |
"name": "Irene Road, Parsons Green", | |
"latitude": "51.4737636", | |
"longitude": "-0.197010096", | |
"bikesAvailable": "0", | |
"emptySlots": "43", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "616", | |
"name": "Aintree Street, Fulham", | |
"latitude": "51.48102131", | |
"longitude": "-0.209973497", | |
"bikesAvailable": "2", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "617", | |
"name": "Elysium Place, Fulham", | |
"latitude": "51.47107905", | |
"longitude": "-0.207842908", | |
"bikesAvailable": "7", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "622", | |
"name": "Lansdowne Road, Ladbroke Grove", | |
"latitude": "51.50748124", | |
"longitude": "-0.205535908", | |
"bikesAvailable": "25", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "623", | |
"name": "Nantes Close, Wandsworth", | |
"latitude": "51.46193072", | |
"longitude": "-0.180791784", | |
"bikesAvailable": "1", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "620", | |
"name": "Surrey Lane, Battersea", | |
"latitude": "51.47518024", | |
"longitude": "-0.167160736", | |
"bikesAvailable": "0", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "621", | |
"name": "Wandsworth Town Station, Wandsworth", | |
"latitude": "51.46086446", | |
"longitude": "-0.187427294", | |
"bikesAvailable": "23", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "627", | |
"name": "Holden Street, Battersea", | |
"latitude": "51.4687905", | |
"longitude": "-0.158230901", | |
"bikesAvailable": "0", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "626", | |
"name": "Normand Park, Fulham", | |
"latitude": "51.48438657", | |
"longitude": "-0.20481514", | |
"bikesAvailable": "1", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "625", | |
"name": "Queen's Circus, Battersea Park", | |
"latitude": "51.47761941", | |
"longitude": "-0.149551631", | |
"bikesAvailable": "1", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "624", | |
"name": "Courland Grove, Wandsworth Road", | |
"latitude": "51.4729184", | |
"longitude": "-0.132102704", | |
"bikesAvailable": "24", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "631", | |
"name": "Battersea Park Road, Nine Elms", | |
"latitude": "51.4795017", | |
"longitude": "-0.141770709", | |
"bikesAvailable": "13", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "630", | |
"name": "Clarence Walk, Stockwell", | |
"latitude": "51.47073264", | |
"longitude": "-0.126994068", | |
"bikesAvailable": "1", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "629", | |
"name": "Morie Street, Wandsworth", | |
"latitude": "51.45995384", | |
"longitude": "-0.190184054", | |
"bikesAvailable": "14", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "628", | |
"name": "William Morris Way, Sands End", | |
"latitude": "51.46881971", | |
"longitude": "-0.184318843", | |
"bikesAvailable": "2", | |
"emptySlots": "23", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "635", | |
"name": "Greyhound Road, Hammersmith", | |
"latitude": "51.48606206", | |
"longitude": "-0.214428378", | |
"bikesAvailable": "1", | |
"emptySlots": "36", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "634", | |
"name": "Brook Green South, Brook Green", | |
"latitude": "51.49434708", | |
"longitude": "-0.215804559", | |
"bikesAvailable": "26", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "633", | |
"name": "Vereker Road, West Kensington", | |
"latitude": "51.48959104", | |
"longitude": "-0.209378594", | |
"bikesAvailable": "1", | |
"emptySlots": "31", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "632", | |
"name": "Sheepcote Lane, Battersea", | |
"latitude": "51.47053858", | |
"longitude": "-0.163041605", | |
"bikesAvailable": "12", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "639", | |
"name": "Coomer Place, West Kensington", | |
"latitude": "51.48357068", | |
"longitude": "-0.202038682", | |
"bikesAvailable": "9", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "638", | |
"name": "Falcon Road, Clapham Junction", | |
"latitude": "51.46663393", | |
"longitude": "-0.169821175", | |
"bikesAvailable": "7", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "637", | |
"name": "Spencer Park, Wandsworth Common", | |
"latitude": "51.45787019", | |
"longitude": "-0.174691623", | |
"bikesAvailable": "5", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "636", | |
"name": "South Park, Sands End", | |
"latitude": "51.46706414", | |
"longitude": "-0.193502076", | |
"bikesAvailable": "4", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "576", | |
"name": "Alpha Grove, Millwall", | |
"latitude": "51.497304", | |
"longitude": "-0.022793", | |
"bikesAvailable": "22", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "577", | |
"name": "Globe Town Market, Bethnal Green", | |
"latitude": "51.528869", | |
"longitude": "-0.047548", | |
"bikesAvailable": "3", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "578", | |
"name": "Hollybush Gardens, Bethnal Green", | |
"latitude": "51.527607", | |
"longitude": "-0.057133", | |
"bikesAvailable": "23", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "579", | |
"name": "Queen Street 2, Bank", | |
"latitude": "51.511246", | |
"longitude": "-0.093051", | |
"bikesAvailable": "29", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "580", | |
"name": "Doddington Grove, Kennington", | |
"latitude": "51.48692917", | |
"longitude": "-0.102996299", | |
"bikesAvailable": "5", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "581", | |
"name": "New Cavendish Street, Marylebone", | |
"latitude": "51.519167", | |
"longitude": "-0.147983", | |
"bikesAvailable": "15", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "583", | |
"name": "Abingdon Green, Great College Street", | |
"latitude": "51.497622", | |
"longitude": "-0.125978", | |
"bikesAvailable": "20", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "584", | |
"name": "Ilchester Gardens, Bayswater", | |
"latitude": "51.51244", | |
"longitude": "-0.19096", | |
"bikesAvailable": "1", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "586", | |
"name": "Mudchute DLR, Cubitt Town", | |
"latitude": "51.490645", | |
"longitude": "-0.014582", | |
"bikesAvailable": "23", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "587", | |
"name": "Monument Street, Monument", | |
"latitude": "51.50964", | |
"longitude": "-0.08497", | |
"bikesAvailable": "18", | |
"emptySlots": "1", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "588", | |
"name": "Hoxton Street, Hoxton", | |
"latitude": "51.52959", | |
"longitude": "-0.0801", | |
"bikesAvailable": "17", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "589", | |
"name": "Drayton Gardens, Chelsea", | |
"latitude": "51.487196", | |
"longitude": "-0.179369", | |
"bikesAvailable": "3", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "590", | |
"name": "Greenberry Street, St.John's Wood", | |
"latitude": "51.53256", | |
"longitude": "-0.16862", | |
"bikesAvailable": "17", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "591", | |
"name": "\n Westfield Library Corner, Shepherd's Bush\n ", | |
"latitude": "51.506093", | |
"longitude": "-0.2242237", | |
"bikesAvailable": "21", | |
"emptySlots": "6", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "593", | |
"name": "Northdown Street, King's Cross", | |
"latitude": "51.531066", | |
"longitude": "-0.11934", | |
"bikesAvailable": "15", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "592", | |
"name": "Bishop's Bridge Road East, Bayswater", | |
"latitude": "51.5171", | |
"longitude": "-0.18377", | |
"bikesAvailable": "0", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "595", | |
"name": "Hammersmith Road, Hammersmith", | |
"latitude": "51.493267", | |
"longitude": "-0.21985", | |
"bikesAvailable": "12", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "594", | |
"name": "Kingsway Southbound, Strand", | |
"latitude": "51.513875", | |
"longitude": "-0.117774", | |
"bikesAvailable": "33", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "597", | |
"name": "Fulham Park Road, Fulham", | |
"latitude": "51.473471", | |
"longitude": "-0.20782", | |
"bikesAvailable": "1", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "596", | |
"name": "Parson's Green , Parson's Green", | |
"latitude": "51.472817", | |
"longitude": "-0.199783", | |
"bikesAvailable": "2", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "599", | |
"name": "Manbre Road, Hammersmith", | |
"latitude": "51.485743", | |
"longitude": "-0.223616", | |
"bikesAvailable": "7", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "598", | |
"name": "Southerton Road, Hammersmith", | |
"latitude": "51.494499", | |
"longitude": "-0.228188", | |
"bikesAvailable": "4", | |
"emptySlots": "34", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "601", | |
"name": "BBC White City, White City", | |
"latitude": "51.514767", | |
"longitude": "-0.225787", | |
"bikesAvailable": "8", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "600", | |
"name": "South Lambeth Road, Vauxhall", | |
"latitude": "51.481747", | |
"longitude": "-0.124642", | |
"bikesAvailable": "1", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "603", | |
"name": "Caldwell Street, Stockwell", | |
"latitude": "51.477839", | |
"longitude": "-0.116493", | |
"bikesAvailable": "5", | |
"emptySlots": "38", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "602", | |
"name": "Union Grove, Wandsworth Road", | |
"latitude": "51.472993", | |
"longitude": "-0.133972", | |
"bikesAvailable": "10", | |
"emptySlots": "35", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "605", | |
"name": "Seymour Place, Marylebone", | |
"latitude": "51.520331", | |
"longitude": "-0.163667", | |
"bikesAvailable": "14", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "604", | |
"name": "St Martin's Close, Camden Town", | |
"latitude": "51.538792", | |
"longitude": "-0.138535", | |
"bikesAvailable": "11", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "607", | |
"name": "Putney Bridge Station, Fulham", | |
"latitude": "51.468814", | |
"longitude": "-0.210279", | |
"bikesAvailable": "9", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "606", | |
"name": "Addison Road, Holland Park", | |
"latitude": "51.504199", | |
"longitude": "-0.210941", | |
"bikesAvailable": "16", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "687", | |
"name": "Maclise Road, Olympia", | |
"latitude": "51.49760804", | |
"longitude": "-0.21145598", | |
"bikesAvailable": "6", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "686", | |
"name": "Beryl Road, Hammersmith", | |
"latitude": "51.48814438", | |
"longitude": "-0.222456468", | |
"bikesAvailable": "2", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "685", | |
"name": "Osiers Road, Wandsworth", | |
"latitude": "51.46067005", | |
"longitude": "-0.198735357", | |
"bikesAvailable": "0", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "684", | |
"name": "Neville Gill Close, Wandsworth", | |
"latitude": "51.45475251", | |
"longitude": "-0.195197203", | |
"bikesAvailable": "18", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "683", | |
"name": "Dorothy Road, Clapham Junction", | |
"latitude": "51.46506424", | |
"longitude": "-0.163750945", | |
"bikesAvailable": "13", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "682", | |
"name": "Crisp Road, Hammersmith", | |
"latitude": "51.48810829", | |
"longitude": "-0.22660621", | |
"bikesAvailable": "11", | |
"emptySlots": "35", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "681", | |
"name": "Bishop's Avenue, Fulham", | |
"latitude": "51.47303687", | |
"longitude": "-0.214749808", | |
"bikesAvailable": "2", | |
"emptySlots": "42", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "680", | |
"name": "Westbridge Road, Battersea", | |
"latitude": "51.47768469", | |
"longitude": "-0.170329317", | |
"bikesAvailable": "12", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "679", | |
"name": "Orbel Street, Battersea", | |
"latitude": "51.47453545", | |
"longitude": "-0.170210533", | |
"bikesAvailable": "1", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "678", | |
"name": "Esmond Street, Putney", | |
"latitude": "51.46231278", | |
"longitude": "-0.211468596", | |
"bikesAvailable": "8", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "677", | |
"name": "Heath Road, Battersea", | |
"latitude": "51.46866929", | |
"longitude": "-0.146544642", | |
"bikesAvailable": "9", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "676", | |
"name": "Hartington Road, Stockwell", | |
"latitude": "51.47787084", | |
"longitude": "-0.126874471", | |
"bikesAvailable": "9", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "675", | |
"name": "Usk Road, Battersea", | |
"latitude": "51.46348914", | |
"longitude": "-0.182126248", | |
"bikesAvailable": "1", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "674", | |
"name": "Carnegie Street, King's Cross", | |
"latitude": "51.53546778", | |
"longitude": "-0.11619105", | |
"bikesAvailable": "28", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "673", | |
"name": "Hibbert Street, Battersea", | |
"latitude": "51.46517078", | |
"longitude": "-0.18038939", | |
"bikesAvailable": "18", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "672", | |
"name": "Grant Road Central, Clapham Junction", | |
"latitude": "51.4646884", | |
"longitude": "-0.173656546", | |
"bikesAvailable": "23", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "702", | |
"name": "Durant Street, Bethnal Green", | |
"latitude": "51.52868155", | |
"longitude": "-0.065550321", | |
"bikesAvailable": "9", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "703", | |
"name": "St. Bride Street, Holborn", | |
"latitude": "51.51505991", | |
"longitude": "-0.10534448", | |
"bikesAvailable": "22", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "700", | |
"name": "Battersea Church Road, Battersea", | |
"latitude": "51.47696496", | |
"longitude": "-0.174347066", | |
"bikesAvailable": "7", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "701", | |
"name": "Vicarage Crescent, Battersea", | |
"latitude": "51.47287627", | |
"longitude": "-0.176267008", | |
"bikesAvailable": "0", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "698", | |
"name": "Shoreditch Court, Haggerston", | |
"latitude": "51.53908372", | |
"longitude": "-0.070329419", | |
"bikesAvailable": "12", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "699", | |
"name": "Belford House, Haggerston", | |
"latitude": "51.5366541", | |
"longitude": "-0.07023031", | |
"bikesAvailable": "13", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "696", | |
"name": "Charing Cross Hospital, Hammersmith", | |
"latitude": "51.48728535", | |
"longitude": "-0.217995921", | |
"bikesAvailable": "9", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "697", | |
"name": "Charlotte Terrace, Angel", | |
"latitude": "51.53639219", | |
"longitude": "-0.112721065", | |
"bikesAvailable": "18", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "694", | |
"name": "Putney Rail Station, Putney", | |
"latitude": "51.46071722", | |
"longitude": "-0.216526155", | |
"bikesAvailable": "3", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "695", | |
"name": "Islington Green, Angel", | |
"latitude": "51.53638435", | |
"longitude": "-0.102757578", | |
"bikesAvailable": "8", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "692", | |
"name": "Cadogan Close, Victoria Park", | |
"latitude": "51.54211855", | |
"longitude": "-0.028941601", | |
"bikesAvailable": "5", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "693", | |
"name": "Felsham Road, Putney", | |
"latitude": "51.464786", | |
"longitude": "-0.215618902", | |
"bikesAvailable": "0", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "690", | |
"name": "Stanley Grove, Battersea", | |
"latitude": "51.47047503", | |
"longitude": "-0.152130083", | |
"bikesAvailable": "3", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "691", | |
"name": "Erin Close, Walham Green", | |
"latitude": "51.47946386", | |
"longitude": "-0.195777222", | |
"bikesAvailable": "10", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "688", | |
"name": "Northfields, Wandsworth", | |
"latitude": "51.46095151", | |
"longitude": "-0.20066766", | |
"bikesAvailable": "1", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "689", | |
"name": "Spanish Road, Wandsworth", | |
"latitude": "51.45922541", | |
"longitude": "-0.180884959", | |
"bikesAvailable": "0", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "653", | |
"name": "Simpson Street, Battersea", | |
"latitude": "51.47084722", | |
"longitude": "-0.17070367", | |
"bikesAvailable": "5", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "652", | |
"name": "Evesham Street, Avondale", | |
"latitude": "51.51148696", | |
"longitude": "-0.218190203", | |
"bikesAvailable": "16", | |
"emptySlots": "13", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "655", | |
"name": "Crabtree Lane, Fulham", | |
"latitude": "51.48294452", | |
"longitude": "-0.219346128", | |
"bikesAvailable": "4", | |
"emptySlots": "23", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "654", | |
"name": "Ashmole Estate, Oval", | |
"latitude": "51.48267821", | |
"longitude": "-0.117661574", | |
"bikesAvailable": "9", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "649", | |
"name": "World's End Place, West Chelsea", | |
"latitude": "51.48180515", | |
"longitude": "-0.18027465", | |
"bikesAvailable": "1", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "648", | |
"name": "Peterborough Road, Sands End", | |
"latitude": "51.46904022", | |
"longitude": "-0.19627483", | |
"bikesAvailable": "0", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "651", | |
"name": "Thorndike Close, West Chelsea", | |
"latitude": "51.48089844", | |
"longitude": "-0.183853573", | |
"bikesAvailable": "1", | |
"emptySlots": "23", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "650", | |
"name": "St. Mark's Road, North Kensington", | |
"latitude": "51.51687069", | |
"longitude": "-0.213872396", | |
"bikesAvailable": "2", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "645", | |
"name": "Great Suffolk Street, The Borough", | |
"latitude": "51.50173215", | |
"longitude": "-0.100292412", | |
"bikesAvailable": "5", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "644", | |
"name": "Rainville Road, Hammersmith", | |
"latitude": "51.48373225", | |
"longitude": "-0.223852256", | |
"bikesAvailable": "11", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "647", | |
"name": "Richmond Way, Shepherd's Bush", | |
"latitude": "51.50035306", | |
"longitude": "-0.217515071", | |
"bikesAvailable": "1", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "646", | |
"name": "Buckingham Gate, Westminster", | |
"latitude": "51.49886563", | |
"longitude": "-0.137424571", | |
"bikesAvailable": "5", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "641", | |
"name": "Archbishop's Park, Waterloo", | |
"latitude": "51.49824168", | |
"longitude": "-0.117495865", | |
"bikesAvailable": "21", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "640", | |
"name": "Silverthorne Road, Battersea", | |
"latitude": "51.47286577", | |
"longitude": "-0.148059277", | |
"bikesAvailable": "2", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "643", | |
"name": "All Saints' Road, Portobello", | |
"latitude": "51.5190427", | |
"longitude": "-0.204764421", | |
"bikesAvailable": "1", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "642", | |
"name": "Fawcett Close, Battersea", | |
"latitude": "51.46916161", | |
"longitude": "-0.174485792", | |
"bikesAvailable": "27", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "668", | |
"name": "Ravenscourt Park Station, Hammersmith", | |
"latitude": "51.49422354", | |
"longitude": "-0.236769936", | |
"bikesAvailable": "8", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "669", | |
"name": "Teversham Lane, Stockwell", | |
"latitude": "51.47614939", | |
"longitude": "-0.1232585", | |
"bikesAvailable": "24", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "670", | |
"name": "Ashley Crescent, Battersea", | |
"latitude": "51.46718562", | |
"longitude": "-0.152248582", | |
"bikesAvailable": "0", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "671", | |
"name": "Parsons Green Station, Parsons Green", | |
"latitude": "51.475089", | |
"longitude": "-0.201968", | |
"bikesAvailable": "11", | |
"emptySlots": "44", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "664", | |
"name": "Austin Road, Battersea", | |
"latitude": "51.474376", | |
"longitude": "-0.155442787", | |
"bikesAvailable": "8", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "665", | |
"name": "Smugglers Way, Wandsworth", | |
"latitude": "51.46108367", | |
"longitude": "-0.191722864", | |
"bikesAvailable": "0", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "666", | |
"name": "Olympia Way, Olympia", | |
"latitude": "51.49610093", | |
"longitude": "-0.208158259", | |
"bikesAvailable": "5", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "667", | |
"name": "\n Shepherd's Bush Road North, Shepherd's Bush\n ", | |
"latitude": "51.5015946", | |
"longitude": "-0.222293381", | |
"bikesAvailable": "21", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "660", | |
"name": "West Kensington Station, West Kensington", | |
"latitude": "51.49087074", | |
"longitude": "-0.206029743", | |
"bikesAvailable": "2", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "661", | |
"name": "All Saints Church, Portobello", | |
"latitude": "51.51632095", | |
"longitude": "-0.202608612", | |
"bikesAvailable": "2", | |
"emptySlots": "35", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "662", | |
"name": "Phene Street, Chelsea", | |
"latitude": "51.48498496", | |
"longitude": "-0.167919869", | |
"bikesAvailable": "20", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "663", | |
"name": "Clarendon Road, Avondale", | |
"latitude": "51.51323001", | |
"longitude": "-0.211593602", | |
"bikesAvailable": "12", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "656", | |
"name": "Broomhouse Lane, Parsons Green", | |
"latitude": "51.46841875", | |
"longitude": "-0.199135704", | |
"bikesAvailable": "2", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "657", | |
"name": "Blythe Road West, Shepherd's Bush", | |
"latitude": "51.4996806", | |
"longitude": "-0.221791552", | |
"bikesAvailable": "2", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "658", | |
"name": "Ethelburga Estate, Battersea Park", | |
"latitude": "51.47729232", | |
"longitude": "-0.16478637", | |
"bikesAvailable": "1", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "659", | |
"name": "Grant Road West, Clapham Junction", | |
"latitude": "51.46437067", | |
"longitude": "-0.174619404", | |
"bikesAvailable": "23", | |
"emptySlots": "33", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "747", | |
"name": "Ormonde Gate, Chelsea", | |
"latitude": "51.48796408", | |
"longitude": "-0.161765173", | |
"bikesAvailable": "17", | |
"emptySlots": "7", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "746", | |
"name": "Lots Road, West Chelsea", | |
"latitude": "51.4795738", | |
"longitude": "-0.17903854", | |
"bikesAvailable": "9", | |
"emptySlots": "15", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "745", | |
"name": "Upcerne Road, West Chelsea", | |
"latitude": "51.47816972", | |
"longitude": "-0.18243547", | |
"bikesAvailable": "8", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "744", | |
"name": "Ingrave Street, Battersea", | |
"latitude": "51.46745485", | |
"longitude": "-0.172293499", | |
"bikesAvailable": "0", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "751", | |
"name": "Newton Street, Covent Garden", | |
"latitude": "51.51612862", | |
"longitude": "-0.120909408", | |
"bikesAvailable": "20", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "750", | |
"name": "Culvert Road, Battersea", | |
"latitude": "51.4710956", | |
"longitude": "-0.157850096", | |
"bikesAvailable": "2", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "749", | |
"name": "Haggerston Road, Haggerston ", | |
"latitude": "51.53932857", | |
"longitude": "-0.074284675", | |
"bikesAvailable": "16", | |
"emptySlots": "19", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "748", | |
"name": "Hertford Road, De Beauvoir Town", | |
"latitude": "51.53727795", | |
"longitude": "-0.079201849", | |
"bikesAvailable": "17", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "739", | |
"name": "Hortensia Road, West Brompton", | |
"latitude": "51.48176572", | |
"longitude": "-0.185273723", | |
"bikesAvailable": "4", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "738", | |
"name": "Imperial Road, Sands End", | |
"latitude": "51.47514228", | |
"longitude": "-0.187278987", | |
"bikesAvailable": "5", | |
"emptySlots": "32", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "737", | |
"name": "Fulham Broadway, Walham Green", | |
"latitude": "51.47993289", | |
"longitude": "-0.19411695", | |
"bikesAvailable": "6", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "736", | |
"name": "Queensdale Road, Shepherd's Bush", | |
"latitude": "51.50659035", | |
"longitude": "-0.216104479", | |
"bikesAvailable": "3", | |
"emptySlots": "27", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "743", | |
"name": "Oxford Road, Putney", | |
"latitude": "51.46079243", | |
"longitude": "-0.212607684", | |
"bikesAvailable": "9", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "742", | |
"name": "Blenheim Crescent, Ladbroke Grove", | |
"latitude": "51.51510818", | |
"longitude": "-0.208565479", | |
"bikesAvailable": "5", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "741", | |
"name": "Freston Road, Avondale", | |
"latitude": "51.5129814", | |
"longitude": "-0.219486603", | |
"bikesAvailable": "9", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "740", | |
"name": "Sirdar Road, Avondale", | |
"latitude": "51.51092871", | |
"longitude": "-0.214594781", | |
"bikesAvailable": "18", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "762", | |
"name": "Storey's Gate, Westminster", | |
"latitude": "51.50070305", | |
"longitude": "-0.129698963", | |
"bikesAvailable": "15", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "763", | |
"name": "Mile End Park Leisure Centre, Mile End", | |
"latitude": "51.52059714", | |
"longitude": "-0.032566533", | |
"bikesAvailable": "16", | |
"emptySlots": "31", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "760", | |
"name": "Rossmore Road, Marylebone", | |
"latitude": "51.52526975", | |
"longitude": "-0.163795009", | |
"bikesAvailable": "8", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "761", | |
"name": "Humbolt Road, Fulham", | |
"latitude": "51.48321729", | |
"longitude": "-0.211860644", | |
"bikesAvailable": "7", | |
"emptySlots": "20", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "766", | |
"name": "Ram Street, Wandsworth", | |
"latitude": "51.45752945", | |
"longitude": "-0.192165613", | |
"bikesAvailable": "17", | |
"emptySlots": "8", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "767", | |
"name": "Santos Road, Wandsworth", | |
"latitude": "51.45705988", | |
"longitude": "-0.200806304", | |
"bikesAvailable": "0", | |
"emptySlots": "30", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "764", | |
"name": "St. John's Road, Clapham Junction", | |
"latitude": "51.46239255", | |
"longitude": "-0.16829214", | |
"bikesAvailable": "16", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "765", | |
"name": "Ranelagh Gardens, Fulham", | |
"latitude": "51.46760141", | |
"longitude": "-0.20682737", | |
"bikesAvailable": "4", | |
"emptySlots": "33", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "754", | |
"name": "Grenfell Road, Avondale", | |
"latitude": "51.5129006", | |
"longitude": "-0.214762686", | |
"bikesAvailable": "7", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "755", | |
"name": "The Vale, Chelsea", | |
"latitude": "51.48512191", | |
"longitude": "-0.174971902", | |
"bikesAvailable": "12", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "752", | |
"name": "Manfred Road, East Putney", | |
"latitude": "51.45816465", | |
"longitude": "-0.20600248", | |
"bikesAvailable": "3", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "753", | |
"name": "Hammersmith Town Hall, Hammersmith", | |
"latitude": "51.49263658", | |
"longitude": "-0.234094148", | |
"bikesAvailable": "3", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "758", | |
"name": "Westbourne Park Road, Portobello", | |
"latitude": "51.51787005", | |
"longitude": "-0.201005397", | |
"bikesAvailable": "1", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "759", | |
"name": "Broadley Terrace, Marylebone", | |
"latitude": "51.52456169", | |
"longitude": "-0.165668686", | |
"bikesAvailable": "4", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "756", | |
"name": "Prince of Wales Drive, Battersea Park", | |
"latitude": "51.47515398", | |
"longitude": "-0.159169801", | |
"bikesAvailable": "0", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "757", | |
"name": "Harcourt Terrace, West Brompton", | |
"latitude": "51.48795853", | |
"longitude": "-0.187404506", | |
"bikesAvailable": "2", | |
"emptySlots": "22", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "713", | |
"name": "Hawley Crescent, Camden Town", | |
"latitude": "51.54100708", | |
"longitude": "-0.14326094", | |
"bikesAvailable": "22", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "712", | |
"name": "Mile End Stadium, Mile End", | |
"latitude": "51.51854104", | |
"longitude": "-0.034903714", | |
"bikesAvailable": "10", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "715", | |
"name": "Aylward Street, Stepney", | |
"latitude": "51.51563007", | |
"longitude": "-0.049067243", | |
"bikesAvailable": "6", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "714", | |
"name": "Stewart's Road, Nine Elms", | |
"latitude": "51.47311696", | |
"longitude": "-0.137235175", | |
"bikesAvailable": "17", | |
"emptySlots": "11", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "717", | |
"name": "Dunston Road , Haggerston", | |
"latitude": "51.53658514", | |
"longitude": "-0.075885686", | |
"bikesAvailable": "19", | |
"emptySlots": "23", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "716", | |
"name": "Stainsby Road , Poplar", | |
"latitude": "51.51542791", | |
"longitude": "-0.02356501", | |
"bikesAvailable": "3", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "719", | |
"name": "Victoria Park Road, Hackney Central", | |
"latitude": "51.53642464", | |
"longitude": "-0.054162264", | |
"bikesAvailable": "13", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "718", | |
"name": "Ada Street, Hackney Central", | |
"latitude": "51.53571683", | |
"longitude": "-0.060291813", | |
"bikesAvailable": "9", | |
"emptySlots": "35", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "705", | |
"name": "Upper Richmond Road, East Putney", | |
"latitude": "51.45971528", | |
"longitude": "-0.212145939", | |
"bikesAvailable": "4", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "704", | |
"name": "Mexfield Road, East Putney", | |
"latitude": "51.45682071", | |
"longitude": "-0.202802098", | |
"bikesAvailable": "1", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "707", | |
"name": "Barons Court Station, West Kensington", | |
"latitude": "51.49021762", | |
"longitude": "-0.215087092", | |
"bikesAvailable": "27", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "706", | |
"name": "Snowsfields, London Bridge", | |
"latitude": "51.50215353", | |
"longitude": "-0.083632928", | |
"bikesAvailable": "14", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "709", | |
"name": "Montserrat Road , Putney", | |
"latitude": "51.46321128", | |
"longitude": "-0.215550761", | |
"bikesAvailable": "14", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "708", | |
"name": "Disraeli Road, Putney", | |
"latitude": "51.46161068", | |
"longitude": "-0.21614583", | |
"bikesAvailable": "6", | |
"emptySlots": "25", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "711", | |
"name": "Everington Street, Fulham", | |
"latitude": "51.48335692", | |
"longitude": "-0.216305546", | |
"bikesAvailable": "8", | |
"emptySlots": "23", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "710", | |
"name": "Albert Bridge Road, Battersea Park", | |
"latitude": "51.47439218", | |
"longitude": "-0.163347594", | |
"bikesAvailable": "3", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "728", | |
"name": "Putney Bridge Road, East Putney", | |
"latitude": "51.46199911", | |
"longitude": "-0.206240805", | |
"bikesAvailable": "24", | |
"emptySlots": "5", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "729", | |
"name": "St. Peter's Terrace, Fulham", | |
"latitude": "51.47893931", | |
"longitude": "-0.208485293", | |
"bikesAvailable": "2", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "730", | |
"name": "Bridge Avenue, Hammersmith", | |
"latitude": "51.49208492", | |
"longitude": "-0.229116862", | |
"bikesAvailable": "14", | |
"emptySlots": "3", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "731", | |
"name": "Michael Road, Walham Green", | |
"latitude": "51.47727637", | |
"longitude": "-0.189210466", | |
"bikesAvailable": "0", | |
"emptySlots": "36", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "732", | |
"name": "Duke Street Hill, London Bridge", | |
"latitude": "51.50630441", | |
"longitude": "-0.087262995", | |
"bikesAvailable": "14", | |
"emptySlots": "2", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "733", | |
"name": "Park Lane, Mayfair", | |
"latitude": "51.50542628", | |
"longitude": "-0.150817316", | |
"bikesAvailable": "2", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "734", | |
"name": "Plough Terrace, Clapham Junction", | |
"latitude": "51.46230566", | |
"longitude": "-0.175407201", | |
"bikesAvailable": "2", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "735", | |
"name": "Grant Road East, Clapham Junction", | |
"latitude": "51.46489445", | |
"longitude": "-0.17302926", | |
"bikesAvailable": "24", | |
"emptySlots": "9", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "720", | |
"name": "Star Road, West Kensington", | |
"latitude": "51.48724429", | |
"longitude": "-0.205279052", | |
"bikesAvailable": "0", | |
"emptySlots": "24", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "721", | |
"name": "Wendon Street, Old Ford", | |
"latitude": "51.53603947", | |
"longitude": "-0.026262677", | |
"bikesAvailable": "4", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "722", | |
"name": "Finnis Street, Bethnal Green", | |
"latitude": "51.52458353", | |
"longitude": "-0.058631453", | |
"bikesAvailable": "5", | |
"emptySlots": "14", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "723", | |
"name": "Stephendale Road, Sands End", | |
"latitude": "51.46822047", | |
"longitude": "-0.190346493", | |
"bikesAvailable": "5", | |
"emptySlots": "43", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "724", | |
"name": "Alma Road, Wandsworth", | |
"latitude": "51.45799126", | |
"longitude": "-0.184806157", | |
"bikesAvailable": "1", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "725", | |
"name": "Thessaly Road North, Nine Elms", | |
"latitude": "51.47732253", | |
"longitude": "-0.138748723", | |
"bikesAvailable": "7", | |
"emptySlots": "21", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "726", | |
"name": "Alfreda Street, Battersea Park", | |
"latitude": "51.47505096", | |
"longitude": "-0.150908371", | |
"bikesAvailable": "3", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "727", | |
"name": "Chesilton Road, Fulham", | |
"latitude": "51.47569809", | |
"longitude": "-0.20587627", | |
"bikesAvailable": "4", | |
"emptySlots": "17", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "774", | |
"name": "Hurlingham Park, Parsons Green", | |
"latitude": "51.470131", | |
"longitude": "-0.20464", | |
"bikesAvailable": "10", | |
"emptySlots": "10", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "775", | |
"name": "Little Brook Green, Brook Green", | |
"latitude": "51.496664", | |
"longitude": "-0.223868", | |
"bikesAvailable": "3", | |
"emptySlots": "29", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "772", | |
"name": "Binfield Road, Stockwell", | |
"latitude": "51.47250956", | |
"longitude": "-0.122831913", | |
"bikesAvailable": "8", | |
"emptySlots": "26", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "773", | |
"name": "Tallis Street, Temple", | |
"latitude": "51.511891", | |
"longitude": "-0.107349", | |
"bikesAvailable": "22", | |
"emptySlots": "0", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "770", | |
"name": "Gwendwr Road, West Kensington", | |
"latitude": "51.491026", | |
"longitude": "-0.209121", | |
"bikesAvailable": "2", | |
"emptySlots": "28", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "771", | |
"name": "Rifle Place, Avondale", | |
"latitude": "51.509224", | |
"longitude": "-0.216016", | |
"bikesAvailable": "9", | |
"emptySlots": "12", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "768", | |
"name": "Clapham Common Northside, Clapham Common", | |
"latitude": "51.46134382", | |
"longitude": "-0.159322467", | |
"bikesAvailable": "2", | |
"emptySlots": "34", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "769", | |
"name": "Sandilands Road, Walham Green", | |
"latitude": "51.473611", | |
"longitude": "-0.191803", | |
"bikesAvailable": "5", | |
"emptySlots": "16", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "776", | |
"name": "Abyssinia Close, Clapham Junction", | |
"latitude": "51.460333", | |
"longitude": "-0.167029", | |
"bikesAvailable": "2", | |
"emptySlots": "18", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
}, | |
{ | |
"@ID": "777", | |
"name": "Limburg Road, Clapham Common", | |
"latitude": "51.4619230679", | |
"longitude": "-0.165297856693", | |
"bikesAvailable": "16", | |
"emptySlots": "4", | |
"installed": "true", | |
"locked": "false", | |
"temporary": "false" | |
} | |
] |
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) { | |
d3.json('bikes.json', function(bikes) { | |
bikes.forEach(function(b) { | |
b.latitude = +b.latitude; | |
b.longitude = +b.longitude; | |
}) | |
data.stations.forEach(function(d) { | |
d.longitude = +d.longitude; | |
d.latitude = +d.latitude; | |
}) | |
var tube = new TubeMaps.TubeMap(data) | |
var closures = []; | |
closures = closures.concat(tube.path(tube.getStationByName("Euston"), tube.getStationByName("Victoria"))); | |
closures = closures.concat(tube.path(tube.getStationByName("West Ruislip"), tube.getStationByName("North Acton"))); | |
closures = closures.concat(tube.path(tube.getStationByName("Tottenham Hale"), tube.getStationByName("Finsbury Park"))); | |
var closuresById = {}; | |
closures.forEach(function(d) { | |
if (!(d.station1.id in closuresById)) { | |
closuresById[d.station1.id] = d.station1 | |
} | |
if (!(d.station2.id in closuresById)) { | |
closuresById[d.station2.id] = d.station2 | |
} | |
}) | |
// Find min and max long and lat | |
// var minLat = d3.min(bikes, function(d) {return d.latitude}); | |
// var minLon = d3.min(bikes, function(d) {return d.longitude}); | |
// var maxLat = d3.max(bikes, function(d) {return d.latitude}); | |
// var maxLon = d3.max(bikes, function(d) {return d.longitude}); | |
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 voronoiMap = vis.append("g").attr('class','voronoi').selectAll("path"); | |
var stations = tube.stations.map(function(d) { | |
return [x(d.longitude), y(d.latitude)] | |
}) | |
var voronoiData = voronoi(stations); | |
var mapped = [] | |
var voronoiColors = {}; | |
tube.stations.forEach(function(station, i) { | |
if (station.id in closuresById) { | |
// console.log(closuresById[station.id]) | |
voronoiColors[i] = closuresById[station.id].line; | |
console.log(voronoiColors[i]) | |
mapped.push(voronoiData[i]) | |
} | |
}) | |
voronoiMap = voronoiMap | |
.data(voronoiData, polygon); | |
voronoiMap.exit().remove(); | |
voronoiMap.enter().append("path") | |
.attr("class", function(d, i) { | |
return "q" + (i % 9) + "-9"; | |
}) | |
.style('fill', function(d, i) { | |
if (i in voronoiColors) { | |
return '#'+tube.linesById[voronoiColors[i]].colour; | |
} | |
}) | |
.style('opacity', function(d, i) { | |
return 0.3; | |
}) | |
.attr("d", polygon); | |
voronoiMap.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) | |
// Drawing routes | |
var closure = vis.selectAll("line.closure") | |
.data(closures) | |
.enter().append("svg:line") | |
.attr("class", "route closure") | |
.attr("stroke", function(d) { return '#'+tube.linesById[d.line || d.station1.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); }) | |
var bike = vis.selectAll("circle.bikes") | |
.data(bikes) | |
.enter().append("svg:circle") | |
.attr("class", "bikes") | |
.attr("cx", function(d) { return x(d.longitude); }) | |
.attr("cy", function(d) { return y(d.latitude); }) | |
.style("fill", 'blue') | |
.style("stroke", 'black') | |
.style("opacity", 0.4) | |
// 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); | |
}) | |
// .on('click', selectStation); | |
// 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); | |
// 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() + ")") | |
vis.selectAll("line.route") | |
.attr("stroke-width", 1 / (zoom.scale())) | |
vis.selectAll("line.stripe") | |
.attr("stroke-width", 0.8 / (zoom.scale())) | |
vis.selectAll("line.closure") | |
.attr("stroke-width", 4 / (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'); | |
} | |
} | |
} while (this._repeat_resume); | |
this._resume_running = false; | |
}; | |
/** | |
* Ends a Stream. This is the same as sending a [nil](#nil) value as data. | |
* You shouldn't need to call this directly, rather it will be called by | |
* any [Node Readable Streams](http://nodejs.org/api/stream.html#stream_class_stream_readable) | |
* you pipe in. | |
* | |
* @id end | |
* @section Stream Objects | |
* @name Stream.end() | |
* @api public | |
* | |
* mystream.end(); | |
*/ | |
Stream.prototype.end = function () { | |
this.write(nil); | |
}; | |
/** | |
* Pipes a Highland Stream to a [Node Writable Stream](http://nodejs.org/api/stream.html#stream_class_stream_writable) | |
* (Highland Streams are also Node Writable Streams). This will pull all the | |
* data from the source Highland Stream and write it to the destination, | |
* automatically managing flow so that the destination is not overwhelmed | |
* by a fast source. | |
* | |
* This function returns the destination so you can chain together pipe calls. | |
* | |
* @id pipe | |
* @section Consumption | |
* @name Stream.pipe(dest) | |
* @param {Writable Stream} dest - the destination to write all data to | |
* @api public | |
* | |
* var source = _(generator); | |
* var dest = fs.createWriteStream('myfile.txt') | |
* source.pipe(dest); | |
* | |
* // chained call | |
* source.pipe(through).pipe(dest); | |
*/ | |
Stream.prototype.pipe = function (dest) { | |
var self = this; | |
// stdout and stderr are special case writables that cannot be closed | |
var canClose = dest !== process.stdout && dest !== process.stderr; | |
var s = self.consume(function (err, x, push, next) { | |
if (err) { | |
self.emit('error', err); | |
return; | |
} | |
if (x === nil) { | |
if (canClose) { | |
dest.end(); | |
} | |
} | |
else if (dest.write(x) !== false) { | |
next(); | |
} | |
}); | |
dest.on('drain', onConsumerDrain); | |
// Since we don't keep a reference to piped-to streams, | |
// save a callback that will unbind the event handler. | |
this._destructors.push(function () { | |
dest.removeListener('drain', onConsumerDrain); | |
}); | |
s.resume(); | |
return dest; | |
function onConsumerDrain() { | |
s.resume(); | |
} | |
}; | |
/** | |
* Destroys a stream by unlinking it from any consumers and sources. This will | |
* stop all consumers from receiving events from this stream and removes this | |
* stream as a consumer of any source stream. | |
* | |
* This function calls end() on the stream and unlinks it from any piped-to streams. | |
* | |
* @id destroy | |
* @section Stream Objects | |
* @name Stream.destroy() | |
* @api public | |
*/ | |
Stream.prototype.destroy = function () { | |
var self = this; | |
this.end(); | |
_(this._consumers).each(function (consumer) { | |
self._removeConsumer(consumer); | |
}); | |
if (this.source) { | |
this.source._removeConsumer(this); | |
} | |
_(this._destructors).each(function (destructor) { | |
destructor(); | |
}); | |
}; | |
/** | |
* Runs the generator function for this Stream. If the generator is already | |
* running (it has been called and not called next() yet) then this function | |
* will do nothing. | |
*/ | |
Stream.prototype._runGenerator = function () { | |
//console.log(['_runGenerator', this.id]); | |
// if _generator already running, exit | |
if (this._generator_running) { | |
return; | |
} | |
this._generator_running = true; | |
this._generator(this._generator_push, this._generator_next); | |
}; | |
/** | |
* Performs the redirect from one Stream to another. In order for the | |
* redirect to happen at the appropriate time, it is put on the incoming | |
* buffer as a StreamRedirect object, and this function is called | |
* once it is read from the buffer. | |
*/ | |
Stream.prototype._redirect = function (to) { | |
//console.log(['_redirect', this.id, '=>', to.id]); | |
// coerce to Stream | |
to = _(to); | |
while (to._delegate) { | |
to = to._delegate; | |
} | |
to._consumers = this._consumers.map(function (c) { | |
c.source = to; | |
return c; | |
}); | |
// TODO: copy _observers | |
this._consumers = []; | |
//[this.consume = function () { | |
// return to.consume.apply(to, arguments); | |
//}; | |
//this._removeConsumer = function () { | |
// return to._removeConsumer.apply(to, arguments); | |
//}; | |
// this will cause a memory leak as long as the root object is around | |
to._delegate_source = this._delegate_source || this; | |
to._delegate_source._delegate = to; | |
if (this.paused) { | |
to.pause(); | |
} | |
else { | |
this.pause(); | |
to._checkBackPressure(); | |
} | |
}; | |
/** | |
* Adds a new consumer Stream, which will accept data and provide backpressure | |
* to this Stream. Adding more than one consumer will cause an exception to be | |
* thrown as the backpressure strategy must be explicitly chosen by the | |
* developer (through calling fork or observe). | |
*/ | |
Stream.prototype._addConsumer = function (s) { | |
if (this._consumers.length) { | |
throw new Error( | |
'Stream already being consumed, you must either fork() or observe()' | |
); | |
} | |
s.source = this; | |
this._consumers.push(s); | |
this._checkBackPressure(); | |
}; | |
/** | |
* Removes a consumer from this Stream. | |
*/ | |
Stream.prototype._removeConsumer = function (s) { | |
var src = this; | |
while (src._delegate) { | |
src = src._delegate; | |
} | |
src._consumers = src._consumers.filter(function (c) { | |
return c !== s; | |
}); | |
if (s.source === src) { | |
s.source = null; | |
} | |
src._checkBackPressure(); | |
}; | |
/** | |
* Consumes values from a Stream (once resumed) and returns a new Stream for | |
* you to optionally push values onto using the provided push / next functions. | |
* | |
* This function forms the basis of many higher-level Stream operations. | |
* It will not cause a paused stream to immediately resume, but behaves more | |
* like a 'through' stream, handling values as they are read. | |
* | |
* @id consume | |
* @section Transforms | |
* @name Stream.consume(f) | |
* @param {Function} f - the function to handle errors and values | |
* @api public | |
* | |
* var filter = function (f, source) { | |
* return source.consume(function (err, x, push, next) { | |
* if (err) { | |
* // pass errors along the stream and consume next value | |
* push(err); | |
* next(); | |
* } | |
* else if (x === _.nil) { | |
* // pass nil (end event) along the stream | |
* push(null, x); | |
* } | |
* else { | |
* // pass on the value only if the value passes the predicate | |
* if (f(x)) { | |
* push(null, x); | |
* } | |
* next(); | |
* } | |
* }); | |
* }; | |
*/ | |
Stream.prototype.consume = function (f) { | |
var self = this; | |
while (self._delegate) { | |
self = self._delegate; | |
} | |
var s = new Stream(); | |
var _send = s._send; | |
var push = function (err, x) { | |
//console.log(['push', err, x, s.paused]); | |
if (x === nil) { | |
// ended, remove consumer from source | |
self._removeConsumer(s); | |
} | |
if (s.paused) { | |
if (err) { | |
s._outgoing.push(new StreamError(err)); | |
} | |
else { | |
s._outgoing.push(x); | |
} | |
} | |
else { | |
_send.call(s, err, x); | |
} | |
}; | |
var async; | |
var next_called; | |
var next = function (s2) { | |
//console.log(['next', async]); | |
if (s2) { | |
// 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 = s.paused; | |
if (!_paused) { | |
s.pause(); | |
} | |
s.write(new StreamRedirect(s2)); | |
if (!_paused) { | |
s.resume(); | |
} | |
} | |
else if (async) { | |
s.resume(); | |
} | |
else { | |
next_called = true; | |
} | |
}; | |
s._send = function (err, x) { | |
async = false; | |
next_called = false; | |
f(err, x, push, next); | |
async = true; | |
if (!next_called) { | |
s.pause(); | |
} | |
}; | |
self._addConsumer(s); | |
return s; | |
}; | |
exposeMethod('consume'); | |
/** | |
* Consumes a single item from the Stream. Unlike consume, this function will | |
* not provide a new stream for you to push values onto, and it will unsubscribe | |
* as soon as it has a single error, value or nil from the source. | |
* | |
* You probably won't need to use this directly, but it is used internally by | |
* some functions in the Highland library. | |
* | |
* @id pull | |
* @section Consumption | |
* @name Stream.pull(f) | |
* @param {Function} f - the function to handle data | |
* @api public | |
* | |
* xs.pull(function (err, x) { | |
* // do something | |
* }); | |
*/ | |
Stream.prototype.pull = function (f) { | |
var s = this.consume(function (err, x) { | |
s.source._removeConsumer(s); | |
f(err, x); | |
}); | |
s.id = 'pull:' + s.id; | |
s.resume(); | |
}; | |
/** | |
* Writes a value to the Stream. If the Stream is paused it will go into the | |
* Stream's incoming buffer, otherwise it will be immediately processed and | |
* sent to the Stream's consumers (if any). Returns false if the Stream is | |
* paused, true otherwise. This lets Node's pipe method handle back-pressure. | |
* | |
* You shouldn't need to call this yourself, but it may be called by Node | |
* functions which treat Highland Streams as a [Node Writable Stream](http://nodejs.org/api/stream.html#stream_class_stream_writable). | |
* | |
* @id write | |
* @section Stream Objects | |
* @name Stream.write(x) | |
* @param x - the value to write to the Stream | |
* @api public | |
* | |
* var xs = _(); | |
* xs.write(1); | |
* xs.write(2); | |
* xs.end(); | |
* | |
* xs.toArray(function (ys) { | |
* // ys will be [1, 2] | |
* }); | |
*/ | |
Stream.prototype.write = function (x) { | |
if (this.paused) { | |
this._incoming.push(x); | |
} | |
else { | |
if (_._isStreamError(x)) { | |
this._send(x.error); | |
} | |
else { | |
this._send(null, x); | |
} | |
} | |
return !this.paused; | |
}; | |
/** | |
* Forks a stream, allowing you to add additional consumers with shared | |
* back-pressure. A stream forked to multiple consumers will only pull values | |
* from it's source as fast as the slowest consumer can handle them. | |
* | |
* @id fork | |
* @section Higher-order Streams | |
* @name Stream.fork() | |
* @api public | |
* | |
* var xs = _([1, 2, 3, 4]); | |
* var ys = xs.fork(); | |
* var zs = xs.fork(); | |
* | |
* // no values will be pulled from xs until zs also resume | |
* ys.resume(); | |
* | |
* // now both ys and zs will get values from xs | |
* zs.resume(); | |
*/ | |
Stream.prototype.fork = function () { | |
var s = new Stream(); | |
s.id = 'fork:' + s.id; | |
s.source = this; | |
this._consumers.push(s); | |
this._checkBackPressure(); | |
return s; | |
}; | |
/** | |
* Observes a stream, allowing you to handle values as they are emitted, without | |
* adding back-pressure or causing data to be pulled from the source. This can | |
* be useful when you are performing two related queries on a stream where one | |
* would block the other. Just be aware that a slow observer could fill up it's | |
* buffer and cause memory issues. Where possible, you should use [fork](#fork). | |
* | |
* @id observe | |
* @section Higher-order Streams | |
* @name Stream.observe() | |
* @api public | |
* | |
* var xs = _([1, 2, 3, 4]); | |
* var ys = xs.fork(); | |
* var zs = xs.observe(); | |
* | |
* // now both zs and ys will receive data as fast as ys can handle it | |
* ys.resume(); | |
*/ | |
Stream.prototype.observe = function () { | |
var s = new Stream(); | |
s.id = 'observe:' + s.id; | |
s.source = this; | |
this._observers.push(s); | |
return s; | |
}; | |
/** | |
* Extracts errors from a Stream and applies them to an error handler | |
* function. Returns a new Stream with the errors removed (unless the error | |
* handler chooses to rethrow them using `push`). Errors can also be | |
* transformed and put back onto the Stream as values. | |
* | |
* @id errors | |
* @section Transforms | |
* @name Stream.errors(f) | |
* @param {Function} f - the function to pass all errors to | |
* @api public | |
* | |
* getDocument.errors(function (err, push) { | |
* if (err.statusCode === 404) { | |
* // not found, return empty doc | |
* push(null, {}); | |
* } | |
* else { | |
* // otherwise, re-throw the error | |
* push(err); | |
* } | |
* }); | |
*/ | |
Stream.prototype.errors = function (f) { | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
f(err, push); | |
next(); | |
} | |
else if (x === nil) { | |
push(null, nil); | |
} | |
else { | |
push(null, x); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('errors'); | |
/** | |
* Like the [errors](#errors) method, but emits a Stream end marker after | |
* an Error is encountered. | |
* | |
* @id stopOnError | |
* @section Transforms | |
* @name Stream.stopOnError(f) | |
* @param {Function} f - the function to handle an error | |
* @api public | |
* | |
* brokenStream.stopOnError(function (err) { | |
* //console.error('Something broke: ' + err); | |
* }); | |
*/ | |
Stream.prototype.stopOnError = function (f) { | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
f(err, push); | |
push(null, nil); | |
} | |
else if (x === nil) { | |
push(null, nil); | |
} | |
else { | |
push(null, x); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('stopOnError'); | |
/** | |
* Iterates over every value from the Stream, calling the iterator function | |
* on each of them. This function causes a **thunk**. | |
* | |
* If an error from the Stream reaches the `each` call, it will emit an | |
* error event (which will cause it to throw if unhandled). | |
* | |
* @id each | |
* @section Consumption | |
* @name Stream.each(f) | |
* @param {Function} f - the iterator function | |
* @api public | |
* | |
* _([1, 2, 3, 4]).each(function (x) { | |
* // will be called 4 times with x being 1, 2, 3 and 4 | |
* }); | |
*/ | |
Stream.prototype.each = function (f) { | |
var self = this; | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
self.emit('error', err); | |
} | |
else if (x !== nil) { | |
f(x); | |
next(); | |
} | |
}).resume(); | |
}; | |
exposeMethod('each'); | |
/** | |
* Applies results from a Stream as arguments to a function | |
* | |
* @id apply | |
* @section Consumption | |
* @name Stream.apply(f) | |
* @param {Function} f - the function to apply arguments to | |
* @api public | |
* | |
* _([1, 2, 3]).apply(function (a, b, c) { | |
* // a === 1 | |
* // b === 2 | |
* // c === 3 | |
* }); | |
*/ | |
Stream.prototype.apply = function (f) { | |
return this.toArray(function (args) { | |
f.apply(null, args); | |
}); | |
}; | |
exposeMethod('apply'); | |
/** | |
* Collects all values from a Stream into an Array and calls a function with | |
* once with the result. This function causes a **thunk**. | |
* | |
* If an error from the Stream reaches the `toArray` call, it will emit an | |
* error event (which will cause it to throw if unhandled). | |
* | |
* @id toArray | |
* @section Consumption | |
* @name Stream.toArray(f) | |
* @param {Function} f - the callback to provide the completed Array to | |
* @api public | |
* | |
* _([1, 2, 3, 4]).toArray(function (x) { | |
* // parameter x will be [1,2,3,4] | |
* }); | |
*/ | |
Stream.prototype.toArray = function (f) { | |
var self = this; | |
return this.collect().pull(function (err, x) { | |
if (err) { | |
self.emit('error', err); | |
} | |
else { | |
f(x); | |
} | |
}); | |
}; | |
/** | |
* Creates a new Stream of transformed values by applying a function to each | |
* value from the source. The transformation function can be replaced with | |
* a non-function value for convenience, and it will emit that value | |
* for every data event on the source Stream. | |
* | |
* @id map | |
* @section Transforms | |
* @name Stream.map(f) | |
* @param f - the transformation function or value to map to | |
* @api public | |
* | |
* var doubled = _([1, 2, 3, 4]).map(function (x) { | |
* return x * 2; | |
* }); | |
* | |
* _([1, 2, 3]).map('hi') // => 'hi', 'hi', 'hi' | |
*/ | |
Stream.prototype.map = function (f) { | |
if (!_.isFunction(f)) { | |
var val = f; | |
f = function () { | |
return val; | |
}; | |
} | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} | |
else if (x === nil) { | |
push(err, x); | |
} | |
else { | |
var fnVal, fnErr; | |
try { | |
fnVal = f(x); | |
} catch (e) { | |
fnErr = e; | |
} | |
push(fnErr, fnVal); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('map'); | |
/** | |
* Creates a new Stream which applies a function to each value from the source | |
* and re-emits the source value. Useful when you want to mutate the value or | |
* perform side effects | |
* | |
* @id doto | |
* @section Transforms | |
* @name Stream.doto(f) | |
* @param f - the function to apply | |
* @api public | |
* | |
* var appended = _([[1], [2], [3], [4]]).doto(function (x) { | |
* x.push(1); | |
* }); | |
* | |
* _([1, 2, 3]).doto(console.log) | |
* // 1 | |
* // 2 | |
* // 3 | |
* // => 1, 2, 3 | |
*/ | |
Stream.prototype.doto = function (f) { | |
return this.map(function (x) { | |
f(x); | |
return x; | |
}); | |
}; | |
exposeMethod('doto'); | |
/** | |
* Limits number of values through the stream to a maximum of number of values | |
* per window. Errors are not limited but allowed to pass through as soon as | |
* they are read from the source. | |
* | |
* @id ratelimit | |
* @section Transforms | |
* @name Stream.ratelimit(num, ms) | |
* @param {Number} num - the number of operations to perform per window | |
* @param {Number} ms - the window of time to limit the operations in (in ms) | |
* @api public | |
* | |
* _([1, 2, 3, 4, 5]).ratelimit(2, 100); | |
* | |
* // after 0ms => 1, 2 | |
* // after 100ms => 1, 2, 3, 4 | |
* // after 200ms => 1, 2, 3, 4, 5 | |
*/ | |
Stream.prototype.ratelimit = function (num, ms) { | |
if (num < 1) { | |
throw new Error('Invalid number of operations per ms: ' + num); | |
} | |
var sent = 0; | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} | |
else if (x === nil) { | |
push(null, nil); | |
} | |
else { | |
if (sent < num) { | |
sent++; | |
push(null, x); | |
next(); | |
} | |
else { | |
setTimeout(function () { | |
sent = 1; | |
push(null, x); | |
next(); | |
}, ms); | |
} | |
} | |
}); | |
}; | |
exposeMethod('ratelimit'); | |
/** | |
* Creates a new Stream of values by applying each item in a Stream to an | |
* iterator function which must return a (possibly empty) Stream. Each item on | |
* these result Streams are then emitted on a single output Stream. | |
* | |
* @id flatMap | |
* @section Higher-order Streams | |
* @name Stream.flatMap(f) | |
* @param {Function} f - the iterator function | |
* @api public | |
* | |
* filenames.flatMap(readFile) | |
*/ | |
Stream.prototype.flatMap = function (f) { | |
return this.map(f).sequence(); | |
}; | |
exposeMethod('flatMap'); | |
/** | |
* Retrieves values associated with a given property from all elements in | |
* the collection. | |
* | |
* @id pluck | |
* @section Transforms | |
* @name Stream.pluck(property) | |
* @param {String} prop - the property to which values should be associated | |
* @api public | |
* | |
* var docs = [ | |
* {type: 'blogpost', title: 'foo'}, | |
* {type: 'blogpost', title: 'bar'}, | |
* {type: 'comment', title: 'baz'} | |
* ]; | |
* | |
* _(docs).pluck('title').toArray(function (xs) { | |
* // xs is now ['foo', 'bar', 'baz'] | |
* }); | |
*/ | |
Stream.prototype.pluck = function (prop) { | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} | |
else if (x === nil) { | |
push(err, x); | |
} | |
else if (_.isObject(x)) { | |
push(null, x[prop]); | |
next(); | |
} | |
else { | |
push(new Error( | |
'Expected Object, got ' + (typeof x) | |
)); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('pluck'); | |
/** | |
* Creates a new Stream including only the values which pass a truth test. | |
* | |
* @id filter | |
* @section Transforms | |
* @name Stream.filter(f) | |
* @param f - the truth test function | |
* @api public | |
* | |
* var evens = _([1, 2, 3, 4]).filter(function (x) { | |
* return x % 2 === 0; | |
* }); | |
*/ | |
Stream.prototype.filter = function (f) { | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} | |
else if (x === nil) { | |
push(err, x); | |
} | |
else { | |
var fnVal, fnErr; | |
try { | |
fnVal = f(x); | |
} catch (e) { | |
fnErr = e; | |
} | |
if (fnErr) { | |
push(fnErr); | |
} else if (fnVal) { | |
push(null, x); | |
} | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('filter'); | |
/** | |
* Filters using a predicate which returns a Stream. If you need to check | |
* against an asynchronous data source when filtering a Stream, this can | |
* be convenient. The Stream returned from the filter function should have | |
* a Boolean as it's first value (all other values on the Stream will be | |
* disregarded). | |
* | |
* @id flatFilter | |
* @section Higher-order Streams | |
* @name Stream.flatFilter(f) | |
* @param {Function} f - the truth test function which returns a Stream | |
* @api public | |
* | |
* var checkExists = _.wrapCallback(fs.exists); | |
* filenames.flatFilter(checkExists) | |
*/ | |
Stream.prototype.flatFilter = function (f) { | |
return this.flatMap(function (x) { | |
return f(x).take(1).otherwise(errorStream()) | |
.flatMap(function (bool) { | |
return _(bool ? [x] : []); | |
}); | |
}); | |
function errorStream() { | |
return _(function (push) { | |
push(new Error('Stream returned by function was empty.')); | |
push(null, _.nil); | |
}); | |
} | |
}; | |
exposeMethod('flatFilter'); | |
/** | |
* The inverse of [filter](#filter). | |
* | |
* @id reject | |
* @section Transforms | |
* @name Stream.reject(f) | |
* @param {Function} f - the truth test function | |
* @api public | |
* | |
* var odds = _([1, 2, 3, 4]).reject(function (x) { | |
* return x % 2 === 0; | |
* }); | |
*/ | |
Stream.prototype.reject = function (f) { | |
return this.filter(_.compose(_.not, f)); | |
}; | |
exposeMethod('reject'); | |
/** | |
* A convenient form of filter, which returns the first object from a | |
* Stream that passes the provided truth test | |
* | |
* @id find | |
* @section Transforms | |
* @name Stream.find(f) | |
* @param {Function} f - the truth test function which returns a Stream | |
* @api public | |
* | |
* var docs = [ | |
* {type: 'blogpost', title: 'foo'}, | |
* {type: 'blogpost', title: 'bar'}, | |
* {type: 'comment', title: 'foo'} | |
* ]; | |
* | |
* var f = function (x) { | |
* return x.type == 'blogpost'; | |
* }; | |
* | |
* _(docs).find(f); | |
* // => {type: 'blogpost', title: 'foo'} | |
* | |
* // example with partial application | |
* var firstBlogpost = _.find(f); | |
* | |
* firstBlogpost(docs) | |
* // => {type: 'blogpost', title: 'foo'} | |
*/ | |
Stream.prototype.find = function (f) { | |
return this.filter(f).take(1); | |
}; | |
exposeMethod('find'); | |
/** | |
* A convenient form of where, which returns the first object from a | |
* Stream that matches a set of property values. findWhere is to where as find is to filter. | |
* | |
* @id findWhere | |
* @section Transforms | |
* @name Stream.findWhere(props) | |
* @param {Object} props - the properties to match against | |
* @api public | |
* | |
* var docs = [ | |
* {type: 'blogpost', title: 'foo'}, | |
* {type: 'blogpost', title: 'bar'}, | |
* {type: 'comment', title: 'foo'} | |
* ]; | |
* | |
* _(docs).findWhere({type: 'blogpost'}) | |
* // => {type: 'blogpost', title: 'foo'} | |
* | |
* // example with partial application | |
* var firstBlogpost = _.findWhere({type: 'blogpost'}); | |
* | |
* firstBlogpost(docs) | |
* // => {type: 'blogpost', title: 'foo'} | |
*/ | |
Stream.prototype.findWhere = function (props) { | |
return this.where(props).take(1); | |
}; | |
exposeMethod('findWhere'); | |
/** | |
* A convenient form of reduce, which groups items based on a function or property name | |
* | |
* @id group | |
* @section Transforms | |
* @name Stream.group(f) | |
* @param {Function|String} f - the function or property name on which to group, | |
* toString() is called on the result of a function. | |
* @api public | |
* | |
* var docs = [ | |
* {type: 'blogpost', title: 'foo'}, | |
* {type: 'blogpost', title: 'bar'}, | |
* {type: 'comment', title: 'foo'} | |
* ]; | |
* | |
* var f = function (x) { | |
* return x.type; | |
* }; | |
* | |
* _(docs).group(f); OR _(docs).group('type'); | |
* // => { | |
* // => 'blogpost': [{type: 'blogpost', title: 'foo'}, {type: 'blogpost', title: 'bar'}] | |
* // => 'comment': [{type: 'comment', title: 'foo'}] | |
* // => } | |
* | |
*/ | |
Stream.prototype.group = function (f) { | |
var lambda = _.isString(f) ? _.get(f) : f; | |
return this.reduce({}, function (m, o) { | |
var key = lambda(o); | |
if (!m.hasOwnProperty(key)) { m[key] = []; } | |
m[key].push(o); | |
return m; | |
}.bind(this)); | |
}; | |
exposeMethod('group'); | |
/** | |
* Filters a Stream to drop all non-truthy values. | |
* | |
* @id compact | |
* @section Transforms | |
* @name Stream.compact() | |
* @api public | |
* | |
* var compacted = _([0, 1, false, 3, null, undefined, 6]).compact(); | |
* // => 1, 3, 6 | |
*/ | |
Stream.prototype.compact = function () { | |
return this.filter(function (x) { | |
return x; | |
}); | |
}; | |
exposeMethod('compact'); | |
/** | |
* A convenient form of filter, which returns all objects from a Stream | |
* which match a set of property values. | |
* | |
* @id where | |
* @section Transforms | |
* @name Stream.where(props) | |
* @param {Object} props - the properties to match against | |
* @api public | |
* | |
* var docs = [ | |
* {type: 'blogpost', title: 'foo'}, | |
* {type: 'blogpost', title: 'bar'}, | |
* {type: 'comment', title: 'foo'} | |
* ]; | |
* | |
* _(docs).where({title: 'foo'}) | |
* // => {type: 'blogpost', title: 'foo'} | |
* // => {type: 'comment', title: 'foo'} | |
* | |
* // example with partial application | |
* var getBlogposts = _.where({type: 'blogpost'}); | |
* | |
* getBlogposts(docs) | |
* // => {type: 'blogpost', title: 'foo'} | |
* // => {type: 'blogpost', title: 'bar'} | |
*/ | |
Stream.prototype.where = function (props) { | |
return this.filter(function (x) { | |
for (var k in props) { | |
if (x[k] !== props[k]) { | |
return false; | |
} | |
} | |
return true; | |
}); | |
}; | |
exposeMethod('where'); | |
/** | |
* Takes two Streams and returns a Stream of corresponding pairs. | |
* | |
* @id zip | |
* @section Higher-order Streams | |
* @name Stream.zip(ys) | |
* @param {Array | Stream} ys - the other stream to combine values with | |
* @api public | |
* | |
* _(['a', 'b', 'c']).zip([1, 2, 3]) // => ['a', 1], ['b', 2], ['c', 3] | |
*/ | |
Stream.prototype.zip = function (ys) { | |
ys = _(ys); | |
var xs = this; | |
var returned = 0; | |
var z = []; | |
function nextValue(index, max, src, push, next) { | |
src.pull(function (err, x) { | |
if (err) { | |
push(err); | |
nextValue(index, max, src, push, next); | |
} | |
else if (x === _.nil) { | |
push(null, nil); | |
} | |
else { | |
returned++; | |
z[index] = x; | |
if (returned === max) { | |
push(null, z); | |
next(); | |
} | |
} | |
}); | |
} | |
return _(function (push, next) { | |
returned = 0; | |
z = []; | |
nextValue(0, 2, xs, push, next); | |
nextValue(1, 2, ys, push, next); | |
}); | |
}; | |
exposeMethod('zip'); | |
/** | |
* Takes one Stream and batches incoming data into arrays of given length | |
* | |
* @id batch | |
* @section Transforms | |
* @name Stream.batch(n) | |
* @param {Number} n - length of the array to batch | |
* @api public | |
* | |
* _([1, 2, 3, 4, 5]).batch(2) // => [1, 2], [3, 4], [5] | |
*/ | |
Stream.prototype.batch = function (n) { | |
var batched = []; | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} else if (x === nil) { | |
if (batched.length > 0) { | |
push(null, batched); | |
} | |
push(null, nil); | |
} else { | |
batched.push(x); | |
if (batched.length === n) { | |
push(null, batched); | |
batched = []; | |
} | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('batch'); | |
/** | |
* Creates a new Stream with the separator interspersed between the elements of the source. | |
* | |
* intersperse is effectively the inverse of [splitBy](#splitBy). | |
* | |
* @id intersperse | |
* @section Transforms | |
* @name Stream.intersperse(sep) | |
* @param sep - the value to intersperse between the source elements | |
* @api public | |
* | |
* _(['ba', 'a', 'a']).intersperse('n') // => ba, n, a, n, a | |
* _(['mississippi']).splitBy('ss').intersperse('ss') // => mi, ss, i, ss, ippi | |
* _(['foo']).intersperse('bar') // => foo | |
*/ | |
Stream.prototype.intersperse = function (separator) { | |
var started = false; | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} else if (x === nil) { | |
push(null, nil); | |
} else { | |
if (started) { | |
push(null, separator); | |
} else { | |
started = true; | |
} | |
push(null, x); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('intersperse'); | |
/** | |
* Splits the source Stream by a separator and emits the pieces in between, much like splitting a string. | |
* | |
* splitBy is effectively the inverse of [intersperse](#intersperse). | |
* | |
* @id splitBy | |
* @section Transforms | |
* @name Stream.splitBy(sep) | |
* @param sep - the separator to split on | |
* @api public | |
* | |
* _(['mis', 'si', 's', 'sippi']).splitBy('ss') // => mi, i, ippi | |
* _(['ba', 'a', 'a']).intersperse('n').splitBy('n') // => ba, a, a | |
* _(['foo']).splitBy('bar') // => foo | |
*/ | |
Stream.prototype.splitBy = function (sep) { | |
var decoder = new Decoder(); | |
var buffer = ''; | |
function drain(x, push) { | |
buffer = buffer + decoder.write(x); | |
var pieces = buffer.split(sep); | |
buffer = pieces.pop(); | |
pieces.forEach(function (piece) { | |
push(null, piece); | |
}); | |
} | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} else if (x === nil) { | |
drain(decoder.end(), push); | |
if (buffer) push(null, buffer); | |
push(null, nil); | |
} else { | |
drain(x, push); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('splitBy'); | |
/** | |
* [splitBy](#splitBy) over newlines. | |
* | |
* @id split | |
* @section Transforms | |
* @name Stream.split() | |
* @api public | |
* | |
* _(['a\n', 'b\nc\n', 'd', '\ne']).split() // => a, b, c, d, e | |
* _(['a\r\nb\nc']]).split() // => a, b, c | |
*/ | |
Stream.prototype.split = function () { | |
return this.splitBy(/\r?\n/); | |
}; | |
exposeMethod('split'); | |
/** | |
* Creates a new Stream with the first `n` values from the source. | |
* | |
* @id take | |
* @section Transforms | |
* @name Stream.take(n) | |
* @param {Number} n - integer representing number of values to read from source | |
* @api public | |
* | |
* _([1, 2, 3, 4]).take(2) // => 1, 2 | |
*/ | |
Stream.prototype.take = function (n) { | |
if (n === 0) { | |
return _([]); | |
} | |
var s = this.consume(function (err, x, push, next) { | |
//console.log(['take', err, x, n]); | |
if (err) { | |
push(err); | |
if (n > 0) { | |
next(); | |
} | |
else { | |
push(null, nil); | |
} | |
} | |
else if (x === nil) { | |
push(null, nil); | |
} | |
else { | |
n--; | |
push(null, x); | |
if (n > 0) { | |
next(); | |
} | |
else { | |
push(null, nil); | |
} | |
} | |
}); | |
s.id = 'take:' + s.id; | |
return s; | |
}; | |
exposeMethod('take'); | |
/** | |
* Creates a new Stream with only the first value from the source. | |
* | |
* @id head | |
* @section Transforms | |
* @name Stream.head() | |
* @api public | |
* | |
* _([1, 2, 3, 4]).head() // => 1 | |
*/ | |
Stream.prototype.head = function () { | |
return this.take(1); | |
}; | |
exposeMethod('head'); | |
/** | |
* Drops all values from the Stream apart from the last one (if any). | |
* | |
* @id last | |
* @section Transforms | |
* @name Stream.last() | |
* @api public | |
* | |
* _([1, 2, 3, 4]).last() // => 4 | |
*/ | |
Stream.prototype.last = function () { | |
var nothing = {}; | |
var prev = nothing; | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} | |
else if (x === nil) { | |
if (prev !== nothing) { | |
push(null, prev); | |
} | |
push(null, nil); | |
} | |
else { | |
prev = x; | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('last'); | |
/** | |
* Passes the current Stream to a function, returning the result. Can also | |
* be used to pipe the current Stream through another Stream. It will always | |
* return a Highland Stream (instead of the piped to target directly as in | |
* Node.js). | |
* | |
* @id through | |
* @section Higher-order Streams | |
* @name Stream.through(target) | |
* @api public | |
* | |
* function oddDoubler(s) { | |
* return s.filter(function (x) { | |
* return x % 2; // odd numbers only | |
* }) | |
* .map(function (x) { | |
* return x * 2; | |
* }); | |
* } | |
* | |
* _([1, 2, 3, 4]).through(oddDoubler).toArray(function (xs) { | |
* // xs will be [2, 6] | |
* }); | |
* | |
* // Can also be used with Node Through Streams | |
* _(filenames).through(jsonParser).map(function (obj) { | |
* // ... | |
* }); | |
*/ | |
Stream.prototype.through = function (target) { | |
if (_.isFunction(target)) { | |
return target(this); | |
} | |
else { | |
var output = _(); | |
target.pause(); | |
this.pipe(target).pipe(output); | |
return output; | |
} | |
}; | |
exposeMethod('through'); | |
/** | |
* Creates a 'Through Stream', which passes data through a pipeline | |
* of functions or other through Streams. This is particularly useful | |
* when combined with partial application of Highland functions to expose a | |
* Node-compatible Through Stream. | |
* | |
* This is not a method on a Stream, and it only exposed at the top-level | |
* as `_.pipeline`. It takes an arbitrary number of arguments. | |
* | |
* @id pipeline | |
* @section Higher-order Streams | |
* @name _.pipeline(...) | |
* @api public | |
* | |
* var through = _.pipeline( | |
* _.map(parseJSON), | |
* _.filter(isBlogpost), | |
* _.reduce(collectCategories) | |
* _.through(otherPipeline) | |
* ); | |
* | |
* readStream.pipe(through).pipe(outStream); | |
* | |
* // Alternatively, you can use pipeline to manipulate a stream in | |
* // the chained method call style: | |
* | |
* var through2 = _.pipeline(function (s) { | |
* return s.map(parseJSON).filter(isBlogpost); // etc. | |
* }); | |
*/ | |
_.pipeline = function (/*through...*/) { | |
if (!arguments.length) { | |
return _(); | |
} | |
var start = arguments[0], rest; | |
if (!_.isStream(start) && !_.isFunction(start.resume)) { | |
// not a Highland stream or Node stream, start with empty stream | |
start = _(); | |
rest = slice.call(arguments); | |
} | |
else { | |
// got a stream as first argument, co-erce to Highland stream | |
start = _(start); | |
rest = slice.call(arguments, 1); | |
} | |
var end = rest.reduce(function (src, dest) { | |
return src.through(dest); | |
}, start); | |
var wrapper = _(function (push, next) { | |
end.pull(function (err, x) { | |
if (err) { | |
wrapper._send(err); | |
next(); | |
} | |
else if (x === nil) { | |
wrapper._send(null, nil); | |
} | |
else { | |
wrapper._send(null, x); | |
next(); | |
} | |
}); | |
}); | |
wrapper.write = function (x) { | |
start.write(x); | |
}; | |
return wrapper; | |
}; | |
/** | |
* Reads values from a Stream of Streams, emitting them on a Single output | |
* Stream. This can be thought of as a flatten, just one level deep. Often | |
* used for resolving asynchronous actions such as a HTTP request or reading | |
* a file. | |
* | |
* @id sequence | |
* @section Higher-order Streams | |
* @name Stream.sequence() | |
* @api public | |
* | |
* var nums = _([ | |
* _([1, 2, 3]), | |
* _([4, 5, 6]) | |
* ]); | |
* | |
* nums.sequence() // => 1, 2, 3, 4, 5, 6 | |
* | |
* // using sequence to read from files in series | |
* filenames.map(readFile).sequence() | |
*/ | |
Stream.prototype.sequence = function () { | |
var original = this; | |
var curr = this; | |
return _(function (push, next) { | |
curr.pull(function (err, x) { | |
if (err) { | |
push(err); | |
return next(); | |
} | |
else if (_.isArray(x)) { | |
if (onOriginalStream()) { | |
// just send all values from array directly | |
x.forEach(function (y) { | |
push(null, y); | |
}); | |
} else { | |
push(null, x); | |
} | |
return next(); | |
} | |
else if (_.isStream(x)) { | |
if (onOriginalStream()) { | |
// switch to reading new stream | |
curr = x; | |
return next(); | |
} | |
else { | |
// sequence only goes 1 level deep | |
push(null, x); | |
return next(); | |
} | |
} | |
else if (x === nil) { | |
if (onOriginalStream()) { | |
push(null, nil); | |
} | |
else { | |
// resume reading from original | |
curr = original; | |
return next(); | |
} | |
} | |
else { | |
if (onOriginalStream()) { | |
// we shouldn't be getting non-stream (or array) | |
// values from the top-level stream | |
push(new Error( | |
'Expected Stream, got ' + (typeof x) | |
)); | |
return next(); | |
} | |
else { | |
push(null, x); | |
return next(); | |
} | |
} | |
}); | |
}); | |
function onOriginalStream() { | |
return curr === original; | |
} | |
}; | |
exposeMethod('sequence'); | |
/** | |
* An alias for the [sequence](#sequence) method. | |
* | |
* @id series | |
* @section Higher-order Streams | |
* @name Stream.series() | |
* @api public | |
* | |
* filenames.map(readFile).series() | |
*/ | |
Stream.prototype.series = Stream.prototype.sequence; | |
_.series = _.sequence; | |
/** | |
* Recursively reads values from a Stream which may contain nested Streams | |
* or Arrays. As values or errors are encountered, they are emitted on a | |
* single output Stream. | |
* | |
* @id flatten | |
* @section Higher-order Streams | |
* @name Stream.flatten() | |
* @api public | |
* | |
* _([1, [2, 3], [[4]]]).flatten(); // => 1, 2, 3, 4 | |
* | |
* var nums = _( | |
* _([1, 2, 3]), | |
* _([4, _([5, 6]) ]) | |
* ); | |
* | |
* nums.flatten(); // => 1, 2, 3, 4, 5, 6 | |
*/ | |
Stream.prototype.flatten = function () { | |
var curr = this; | |
var stack = []; | |
return _(function (push, next) { | |
curr.pull(function (err, x) { | |
if (err) { | |
push(err); | |
return next(); | |
} | |
if (_.isArray(x)) { | |
x = _(x); | |
} | |
if (_.isStream(x)) { | |
stack.push(curr); | |
curr = x; | |
next(); | |
} | |
else if (x === nil) { | |
if (stack.length) { | |
curr = stack.pop(); | |
next(); | |
} | |
else { | |
push(null, nil); | |
} | |
} | |
else { | |
push(null, x); | |
next(); | |
} | |
}); | |
}); | |
}; | |
exposeMethod('flatten'); | |
/** | |
* Takes a Stream of Streams and reads from them in parallel, buffering | |
* the results until they can be returned to the consumer in their original | |
* order. | |
* | |
* @id parallel | |
* @section Higher-order Streams | |
* @name Stream.parallel(n) | |
* @param {Number} n - the maximum number of concurrent reads/buffers | |
* @api public | |
* | |
* var readFile = _.wrapCallback(fs.readFile); | |
* var filenames = _(['foo.txt', 'bar.txt', 'baz.txt']); | |
* | |
* // read from up to 10 files at once | |
* filenames.map(readFile).parallel(10); | |
*/ | |
Stream.prototype.parallel = function (n) { | |
var source = this; | |
var running = []; | |
var ended = false; | |
var reading_source = false; | |
return _(function (push, next) { | |
if (running.length && running[0].buffer.length) { | |
// send buffered data | |
var buf = running[0].buffer; | |
for (var i = 0; i < buf.length; i++) { | |
if (buf[i][1] === nil) { | |
// this stream has ended | |
running.shift(); | |
return next(); | |
} | |
else { | |
// send the buffered output | |
push.apply(null, buf[i]); | |
} | |
} | |
// still waiting for more data before we can shift | |
// the running array... | |
} | |
else if (running.length < n && !ended && !reading_source) { | |
// get another stream if not already waiting for one | |
reading_source = true; | |
source.pull(function (err, x) { | |
reading_source = false; | |
if (err) { | |
push(err); | |
} | |
else if (x === nil) { | |
ended = true; | |
} | |
else { | |
// got a new source, add it to the running array | |
var run = {stream: x, buffer: []}; | |
running.push(run); | |
x.consume(function (err, y, _push, _next) { | |
if (running[0] === run) { | |
// current output stream | |
if (y === nil) { | |
// remove self from running and check | |
// to see if we need to read from source again | |
running.shift(); | |
next(); | |
} | |
else { | |
// push directly onto parallel output stream | |
push(err, y); | |
} | |
} | |
else { | |
// we're reading ahead, buffer the output | |
run.buffer.push([err, y]); | |
} | |
if (y !== nil) { | |
// keep reading until we hit nil | |
_next(); | |
} | |
}).resume(); | |
} | |
// check if we need to get any more streams | |
return next(); | |
}); | |
} | |
else if (!running.length && ended) { | |
// nothing more to do | |
push(null, nil); | |
} | |
else { | |
// wait for more data to arrive from running streams | |
} | |
}); | |
}; | |
exposeMethod('parallel'); | |
/** | |
* Switches source to an alternate Stream if the current Stream is empty. | |
* | |
* @id otherwise | |
* @section Higher-order Streams | |
* @name Stream.otherwise(ys) | |
* @param {Stream} ys - alternate stream to use if this stream is empty | |
* @api public | |
* | |
* _([1,2,3]).otherwise(['foo']) // => 1, 2, 3 | |
* _([]).otherwise(['foo']) // => 'foo' | |
* | |
* _.otherwise(_(['foo']), _([1,2,3])) // => 1, 2, 3 | |
* _.otherwise(_(['foo']), _([])) // => 'foo' | |
*/ | |
Stream.prototype.otherwise = function (ys) { | |
var xs = this; | |
return xs.consume(function (err, x, push, next) { | |
if (err) { | |
// got an error, just keep going | |
push(err); | |
next(); | |
} else if (x === nil) { | |
// hit the end without redirecting to xs, use alternative | |
next(ys); | |
} | |
else { | |
// got a value, push it, then redirect to xs | |
push(null, x); | |
next(xs); | |
} | |
}); | |
}; | |
exposeMethod('otherwise'); | |
/** | |
* Adds a value to the end of a Stream. | |
* | |
* @id append | |
* @section Transforms | |
* @name Stream.append(y) | |
* @param y - the value to append to the Stream | |
* @api public | |
* | |
* _([1, 2, 3]).append(4) // => 1, 2, 3, 4 | |
*/ | |
Stream.prototype.append = function (y) { | |
return this.consume(function (err, x, push, next) { | |
if (x === nil) { | |
push(null, y); | |
push(null, _.nil); | |
} | |
else { | |
push(err, x); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('append'); | |
/** | |
* Boils down a Stream to a single value. The memo is the initial state | |
* of the reduction, and each successive step of it should be returned by | |
* the iterator function. The iterator is passed two arguments: | |
* the memo and the next value. | |
* | |
* If the iterator throws an error, the reduction stops and the resulting | |
* stream will emit that error instead of a value. | |
* | |
* @id reduce | |
* @section Transforms | |
* @name Stream.reduce(memo, iterator) | |
* @param memo - the initial state of the reduction | |
* @param {Function} iterator - the function which reduces the values | |
* @api public | |
* | |
* var add = function (a, b) { | |
* return a + b; | |
* }; | |
* | |
* _([1, 2, 3, 4]).reduce(0, add) // => 10 | |
*/ | |
Stream.prototype.reduce = function (z, f) { | |
// This can't be implemented with scan(), because we don't know if the | |
// errors that we see from the scan were thrown by the iterator or just | |
// passed through from the source stream. | |
return this.consume(function (err, x, push, next) { | |
if (x === nil) { | |
push(null, z); | |
push(null, _.nil); | |
} | |
else if (err) { | |
push(err); | |
next(); | |
} | |
else { | |
try { | |
z = f(z, x); | |
} catch (e) { | |
push(e); | |
push(null, _.nil); | |
return; | |
} | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('reduce'); | |
/** | |
* Same as [reduce](#reduce), but uses the first element as the initial | |
* state instead of passing in a `memo` value. | |
* | |
* @id reduce1 | |
* @section Transforms | |
* @name Stream.reduce1(iterator) | |
* @param {Function} iterator - the function which reduces the values | |
* @api public | |
* | |
* _([1, 2, 3, 4]).reduce1(add) // => 10 | |
*/ | |
Stream.prototype.reduce1 = function (f) { | |
var self = this; | |
return _(function (push, next) { | |
self.pull(function (err, x) { | |
if (err) { | |
push(err); | |
next(); | |
} else if (x === nil) { | |
push(null, nil); | |
} | |
else { | |
next(self.reduce(x, f)); | |
} | |
}); | |
}); | |
}; | |
exposeMethod('reduce1'); | |
/** | |
* Groups all values into an Array and passes down the stream as a single | |
* data event. This is a bit like doing [toArray](#toArray), but instead | |
* of accepting a callback and causing a *thunk*, it passes the value on. | |
* | |
* @id collect | |
* @section Transforms | |
* @name Stream.collect() | |
* @api public | |
* | |
* _(['foo', 'bar']).collect().toArray(function (xs) { | |
* // xs will be [['foo', 'bar']] | |
* }); | |
*/ | |
Stream.prototype.collect = function () { | |
var xs = []; | |
return this.consume(function (err, x, push, next) { | |
if (err) { | |
push(err); | |
next(); | |
} | |
else if (x === nil) { | |
push(null, xs); | |
push(null, nil); | |
} | |
else { | |
xs.push(x); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('collect'); | |
/** | |
* Like [reduce](#reduce), but emits each intermediate value of the | |
* reduction as it is calculated. | |
* | |
* If the iterator throws an error, the scan will stop and the stream will | |
* emit that error. Any intermediate values that were produced before the | |
* error will still be emitted. | |
* | |
* @id scan | |
* @section Transforms | |
* @name Stream.scan(memo, iterator) | |
* @param memo - the initial state of the reduction | |
* @param {Function} iterator - the function which reduces the values | |
* @api public | |
* | |
* _([1, 2, 3, 4]).scan(0, add) // => 0, 1, 3, 6, 10 | |
*/ | |
Stream.prototype.scan = function (z, f) { | |
var self = this; | |
return _([z]).concat( | |
self.consume(function (err, x, push, next) { | |
if (x === nil) { | |
push(null, _.nil); | |
} | |
else if (err) { | |
push(err); | |
next(); | |
} | |
else { | |
try { | |
z = f(z, x); | |
} catch (e) { | |
push(e); | |
push(null, _.nil); | |
return; | |
} | |
push(null, z); | |
next(); | |
} | |
}) | |
); | |
}; | |
exposeMethod('scan'); | |
/** | |
* Same as [scan](#scan), but uses the first element as the initial | |
* state instead of passing in a `memo` value. | |
* | |
* @id scan1 | |
* @section Transforms | |
* @name Stream.scan1(iterator) | |
* @param {Function} iterator - the function which reduces the values | |
* @api public | |
* | |
* _([1, 2, 3, 4]).scan1(add) // => 1, 3, 6, 10 | |
*/ | |
Stream.prototype.scan1 = function (f) { | |
var self = this; | |
return _(function (push, next) { | |
self.pull(function (err, x) { | |
if (err) { | |
push(err); | |
next(); | |
} else if (x === nil) { | |
push(null, nil); | |
} | |
else { | |
next(self.scan(x, f)); | |
} | |
}); | |
}); | |
}; | |
exposeMethod('scan1'); | |
/** | |
* Concatenates a Stream to the end of this Stream. | |
* | |
* Be aware that in the top-level export, the args may be in the reverse | |
* order to what you'd expect `_([a], [b]) => b, a`, as this follows the | |
* convention of other top-level exported functions which do `x` to `y`. | |
* | |
* @id concat | |
* @section Higher-order Streams | |
* @name Stream.concat(ys) | |
* @params {Stream | Array} ys - the values to concatenate onto this Stream | |
* @api public | |
* | |
* _([1, 2]).concat([3, 4]) // => 1, 2, 3, 4 | |
* _.concat([3, 4], [1, 2]) // => 1, 2, 3, 4 | |
*/ | |
Stream.prototype.concat = function (ys) { | |
ys = _(ys); | |
return this.consume(function (err, x, push, next) { | |
if (x === nil) { | |
next(ys); | |
} | |
else { | |
push(err, x); | |
next(); | |
} | |
}); | |
}; | |
exposeMethod('concat'); | |
/** | |
* Takes a Stream of Streams and merges their values and errors into a | |
* single new Stream. The merged stream ends when all source streams have | |
* ended. | |
* | |
* Note that no guarantee is made with respect to the order in which | |
* values for each stream end up in the merged stream. Values in the | |
* merged stream will, however, respect the order they were emitted from | |
* their respective streams. | |
* | |
* @id merge | |
* @section Higher-order Streams | |
* @name Stream.merge() | |
* @api public | |
* | |
* var txt = _(['foo.txt', 'bar.txt']).map(readFile) | |
* var md = _(['baz.md']).map(readFile) | |
* | |
* _([txt, md]).merge(); | |
* // => contents of foo.txt, bar.txt and baz.txt in the order they were read | |
*/ | |
Stream.prototype.merge = function () { | |
var self = this; | |
var resuming = false; | |
var go_next = false; | |
var srcs; | |
return _(function (push, next) { | |
var safeNext = function () { | |
if (!resuming) { | |
next(); | |
} | |
else { | |
go_next = true; | |
} | |
}; | |
if (!srcs) { | |
self.errors(push).toArray(function (xs) { | |
srcs = xs; | |
srcs.forEach(function (src) { | |
src.on('end', function () { | |
srcs = srcs.filter(function (s) { | |
return s !== src; | |
}); | |
safeNext(); | |
}); | |
src.on('data', function (x) { | |
src.pause(); | |
push(null, x); | |
safeNext(); | |
}); | |
src.on('error', function (err) { | |
push(err); | |
safeNext(); | |
}); | |
}); | |
next(); | |
}); | |
} | |
else if (srcs.length === 0) { | |
push(null, nil); | |
} | |
else { | |
go_next = false; | |
resuming = true; | |
srcs.forEach(function (src) { | |
src.resume(); | |
}); | |
resuming = false; | |
if (go_next) { | |
next(); | |
} | |
} | |
}); | |
}; | |
exposeMethod('merge'); | |
/** | |
* Calls a named method on each object from the Stream - returning | |
* a new stream with the result of those calls. | |
* | |
* @id invoke | |
* @section Transforms | |
* @name Stream.invoke(method, args) | |
* @param {String} method - the method name to call | |
* @param {Array} args - the arguments to call the method with | |
* @api public | |
* | |
* _(['foo', 'bar']).invoke('toUpperCase', []) // => FOO, BAR | |
* | |
* filenames.map(readFile).sequence().invoke('toString', ['utf8']); | |
*/ | |
Stream.prototype.invoke = function (method, args) { | |
return this.map(function (x) { | |
return x[method].apply(x, args); | |
}); | |
}; | |
exposeMethod('invoke'); | |
/** | |
* Ensures that only one data event is push downstream (or into the buffer) | |
* every `ms` milliseconds, any other values are dropped. | |
* | |
* @id throttle | |
* @section Transforms | |
* @name Stream.throttle(ms) | |
* @param {Number} ms - the minimum milliseconds between each value | |
* @api public | |
* | |
* _('mousemove', document).throttle(1000); | |
*/ | |
Stream.prototype.throttle = function (ms) { | |
var s = new Stream(); | |
var last = 0 - ms; | |
var _write = s.write; | |
s.write = function (x) { | |
var now = new Date().getTime(); | |
if (_._isStreamError(x) || x === nil) { | |
return _write.apply(this, arguments); | |
} | |
else if (now - ms >= last) { | |
last = now; | |
return _write.apply(this, arguments); | |
} | |
}; | |
this._addConsumer(s); | |
return s; | |
}; | |
exposeMethod('throttle'); | |
/** | |
* Holds off pushing data events downstream until there has been no more | |
* data for `ms` milliseconds. Sends the last value that occurred before | |
* the delay, discarding all other values. | |
* | |
* @id debounce | |
* @section Transforms | |
* @name Stream.debounce(ms) | |
* @param {Number} ms - the milliseconds to wait before sending data | |
* @api public | |
* | |
* // sends last keyup event after user has stopped typing for 1 second | |
* $('keyup', textbox).debounce(1000); | |
*/ | |
Stream.prototype.debounce = function (ms) { | |
var s = new Stream(); | |
var t = null; | |
var nothing = {}; | |
var last = nothing; | |
var _write = s.write; | |
s.write = function (x) { | |
if (_._isStreamError(x)) { | |
// let errors through regardless | |
return _write.apply(this, arguments); | |
} | |
else if (x === nil) { | |
if (t) { | |
clearTimeout(t); | |
} | |
if (last !== nothing) { | |
_write.call(s, last); | |
} | |
return _write.apply(this, arguments); | |
} | |
else { | |
last = x; | |
if (t) { | |
clearTimeout(t); | |
} | |
t = setTimeout(function () { | |
_write.call(s, last); | |
}, ms); | |
return !this.paused; | |
} | |
}; | |
this._addConsumer(s); | |
return s; | |
}; | |
exposeMethod('debounce'); | |
/** | |
* Creates a new Stream, which when read from, only returns the last | |
* seen value from the source. The source stream does not experience | |
* back-pressure. Useful if you're using a Stream to model a changing | |
* property which you need to query periodically. | |
* | |
* @id latest | |
* @section Transforms | |
* @name Stream.latest() | |
* @api public | |
* | |
* // slowThing will always get the last known mouse position | |
* // when it asks for more data from the mousePosition stream | |
* mousePosition.latest().map(slowThing) | |
*/ | |
Stream.prototype.latest = function () { | |
var s = new Stream(); | |
var _write = s.write; | |
s.pause = function () { | |
this.paused = true; | |
// do not force parent to checkBackpressure | |
}; | |
s.write = function (x) { | |
if (_._isStreamError(x)) { | |
// pass errors straight through | |
_write.call(this, x); | |
} | |
else if (x === nil) { | |
_write.call(this, x); | |
} | |
else { | |
if (this.paused) { | |
this._incoming = this._incoming.filter(function (x) { | |
// remove any existing values from buffer | |
return _._isStreamError(x) || x === nil; | |
}); | |
this._incoming.push(x); | |
} | |
else { | |
_write.call(this, x); | |
} | |
} | |
// never push back | |
return true; | |
}; | |
this._addConsumer(s); | |
s.resume(); | |
return s; | |
}; | |
exposeMethod('latest'); | |
/** | |
* Returns values from an Object as a Stream. Reads properties | |
* lazily, so if you don't read from all keys on an object, not | |
* all properties will be read from (may have an effect where getters | |
* are used). | |
* | |
* @id values | |
* @section Objects | |
* @name _.values(obj) | |
* @param {Object} obj - the object to return values from | |
* @api public | |
* | |
* _.values({foo: 1, bar: 2, baz: 3}) // => 1, 2, 3 | |
*/ | |
_.values = function (obj) { | |
return _.keys(obj).map(function (k) { | |
return obj[k]; | |
}); | |
}; | |
/** | |
* Returns keys from an Object as a Stream. | |
* | |
* @id keys | |
* @section Objects | |
* @name _.keys(obj) | |
* @param {Object} obj - the object to return keys from | |
* @api public | |
* | |
* _.keys({foo: 1, bar: 2, baz: 3}) // => 'foo', 'bar', 'baz' | |
*/ | |
_.keys = function (obj) { | |
var keys = []; | |
for (var k in obj) { | |
if (obj.hasOwnProperty(k)) { | |
keys.push(k); | |
} | |
} | |
return _(keys); | |
}; | |
/** | |
* Returns key/value pairs for an Object as a Stream. Reads properties | |
* lazily, so if you don't read from all keys on an object, not | |
* all properties will be read from (may have an effect where getters | |
* are used). | |
* | |
* @id pairs | |
* @section Objects | |
* @name _.pairs(obj) | |
* @param {Object} obj - the object to return key/value pairs from | |
* @api public | |
* | |
* _.pairs({foo: 1, bar: 2}) // => ['foo', 1], ['bar', 2] | |
*/ | |
_.pairs = function (obj) { | |
return _.keys(obj).map(function (k) { | |
return [k, obj[k]]; | |
}); | |
}; | |
/** | |
* Extends one object with the properties of another. **Note:** The | |
* arguments are in the reverse order of other libraries such as | |
* underscore. This is so it follows the convention of other functions in | |
* this library and so you can more meaningfully partially apply it. | |
* | |
* @id extend | |
* @section Objects | |
* @name _.extend(a, b) | |
* @param {Object} a - the properties to extend b with | |
* @param {Object} b - the original object to extend | |
* @api public | |
* | |
* _.extend({name: 'bar'}, {name: 'foo', price: 20}) | |
* // => {name: 'bar', price: 20} | |
* | |
* // example of partial application | |
* var publish = _.extend({published: true}); | |
* | |
* publish({title: 'test post'}) | |
* // => {title: 'test post', published: true} | |
*/ | |
_.extend = _.curry(function (extensions, target) { | |
for (var k in extensions) { | |
if (extensions.hasOwnProperty(k)) { | |
target[k] = extensions[k]; | |
} | |
} | |
return target; | |
}); | |
/** | |
* Returns a property from an object. | |
* | |
* @id get | |
* @section Objects | |
* @name _.get(prop, obj) | |
* @param {String} prop - the property to return | |
* @param {Object} obj - the object to read properties from | |
* @api public | |
* | |
* var obj = {foo: 'bar', baz: 123}; | |
* _.get('foo', obj) // => 'bar' | |
* | |
* // making use of partial application | |
* var posts = [ | |
* {title: 'one'}, | |
* {title: 'two'}, | |
* {title: 'three'} | |
* ]; | |
* | |
* _(posts).map(_.get('title')) // => 'one', 'two', 'three' | |
*/ | |
_.get = _.curry(function (prop, obj) { | |
return obj[prop]; | |
}); | |
/** | |
* Updates a property on an object, returning the updated object. | |
* | |
* @id set | |
* @section Objects | |
* @name _.set(prop, value, obj) | |
* @param {String} prop - the property to return | |
* @param value - the value to set the property to | |
* @param {Object} obj - the object to set properties on | |
* @api public | |
* | |
* var obj = {foo: 'bar', baz: 123}; | |
* _.set('foo', 'wheeee', obj) // => {foo: 'wheeee', baz: 123} | |
* | |
* // making use of partial application | |
* var publish = _.set('published', true); | |
* | |
* publish({title: 'example'}) // => {title: 'example', published: true} | |
*/ | |
_.set = _.curry(function (prop, val, obj) { | |
obj[prop] = val; | |
return obj; | |
}); | |
/** | |
* Logs values to the console, a simple wrapper around `console.log` that | |
* it suitable for passing to other functions by reference without having to | |
* call `bind`. | |
* | |
* @id log | |
* @section Utils | |
* @name _.log(args..) | |
* @api public | |
* | |
* _.log('Hello, world!'); | |
* | |
* _([1, 2, 3, 4]).each(_.log); | |
*/ | |
_.log = function () { | |
console.log.apply(console, arguments); | |
}; | |
/** | |
* Wraps a node-style async function which accepts a callback, transforming | |
* it to a function which accepts the same arguments minus the callback and | |
* returns a Highland Stream instead. Only the first argument to the | |
* callback (or an error) will be pushed onto the Stream. | |
* | |
* @id wrapCallback | |
* @section Utils | |
* @name _.wrapCallback(f) | |
* @param {Function} f - the node-style function to wrap | |
* @api public | |
* | |
* var fs = require('fs'); | |
* | |
* var readFile = _.wrapCallback(fs.readFile); | |
* | |
* readFile('example.txt').apply(function (data) { | |
* // data is now the contents of example.txt | |
* }); | |
*/ | |
_.wrapCallback = function (f) { | |
return function () { | |
var args = slice.call(arguments); | |
return _(function (push) { | |
var cb = function (err, x) { | |
if (err) { | |
push(err); | |
} | |
else { | |
push(null, x); | |
} | |
push(null, nil); | |
}; | |
f.apply(null, args.concat([cb])); | |
}); | |
}; | |
}; | |
/** | |
* Add two values. Can be partially applied. | |
* | |
* @id add | |
* @section Operators | |
* @name _.add(a, b) | |
* @api public | |
* | |
* add(1, 2) === 3 | |
* add(1)(5) === 6 | |
*/ | |
_.add = _.curry(function (a, b) { | |
return a + b; | |
}); | |
/** | |
* Perform logical negation on a value. If `x` is truthy then returns false, | |
* otherwise returns true. | |
* | |
* @id not | |
* @section Operators | |
* @name _.not(x) | |
* @param x - the value to negate | |
* @api public | |
* | |
* _.not(true) // => false | |
* _.not(false) // => true | |
*/ | |
_.not = function (x) { | |
return !x; | |
}; | |
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"_process":16,"events":12,"string_decoder":30,"util":32}],8:[function(require,module,exports){ | |
},{}],9:[function(require,module,exports){ | |
/*! | |
* The buffer module from node.js, for the browser. | |
* | |
* @author Feross Aboukhadijeh <[email protected]> <http://feross.org> | |
* @license MIT | |
*/ | |
var base64 = require('base64-js') | |
var ieee754 = require('ieee754') | |
exports.Buffer = Buffer | |
exports.SlowBuffer = Buffer | |
exports.INSPECT_MAX_BYTES = 50 | |
Buffer.poolSize = 8192 | |
/** | |
* If `TYPED_ARRAY_SUPPORT`: | |
* === true Use Uint8Array implementation (fastest) | |
* === false Use Object implementation (most compatible, even IE6) | |
* | |
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, | |
* Opera 11.6+, iOS 4.2+. | |
* | |
* Note: | |
* | |
* - Implementation must support adding new properties to `Uint8Array` instances. | |
* Firefox 4-29 lacked support, fixed in Firefox 30+. | |
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. | |
* | |
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. | |
* | |
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of | |
* incorrect length in some situations. | |
* | |
* We detect these buggy browsers and set `TYPED_ARRAY_SUPPORT` to `false` so they will | |
* get the Object implementation, which is slower but will work correctly. | |
*/ | |
var TYPED_ARRAY_SUPPORT = (function () { | |
try { | |
var buf = new ArrayBuffer(0) | |
var arr = new Uint8Array(buf) | |
arr.foo = function () { return 42 } | |
return 42 === arr.foo() && // typed array instances can be augmented | |
typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` | |
new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` | |
} catch (e) { | |
return false | |
} | |
})() | |
/** | |
* Class: Buffer | |
* ============= | |
* | |
* The Buffer constructor returns instances of `Uint8Array` that are augmented | |
* with function properties for all the node `Buffer` API functions. We use | |
* `Uint8Array` so that square bracket notation works as expected -- it returns | |
* a single octet. | |
* | |
* By augmenting the instances, we can avoid modifying the `Uint8Array` | |
* prototype. | |
*/ | |
function Buffer (subject, encoding, noZero) { | |
if (!(this instanceof Buffer)) | |
return new Buffer(subject, encoding, noZero) | |
var type = typeof subject | |
// Find the length | |
var length | |
if (type === 'number') | |
length = subject > 0 ? subject >>> 0 : 0 | |
else if (type === 'string') { | |
if (encoding === 'base64') | |
subject = base64clean(subject) | |
length = Buffer.byteLength(subject, encoding) | |
} else if (type === 'object' && subject !== null) { // assume object is array-like | |
if (subject.type === 'Buffer' && isArray(subject.data)) | |
subject = subject.data | |
length = +subject.length > 0 ? Math.floor(+subject.length) : 0 | |
} else | |
throw new Error('First argument needs to be a number, array or string.') | |
var buf | |
if (TYPED_ARRAY_SUPPORT) { | |
// Preferred: Return an augmented `Uint8Array` instance for best performance | |
buf = Buffer._augment(new Uint8Array(length)) | |
} else { | |
// Fallback: Return THIS instance of Buffer (created by `new`) | |
buf = this | |
buf.length = length | |
buf._isBuffer = true | |
} | |
var i | |
if (TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') { | |
// Speed optimization -- use set if we're copying from a typed array | |
buf._set(subject) | |
} else if (isArrayish(subject)) { | |
// Treat array-ish objects as a byte array | |
if (Buffer.isBuffer(subject)) { | |
for (i = 0; i < length; i++) | |
buf[i] = subject.readUInt8(i) | |
} else { | |
for (i = 0; i < length; i++) | |
buf[i] = ((subject[i] % 256) + 256) % 256 | |
} | |
} else if (type === 'string') { | |
buf.write(subject, 0, encoding) | |
} else if (type === 'number' && !TYPED_ARRAY_SUPPORT && !noZero) { | |
for (i = 0; i < length; i++) { | |
buf[i] = 0 | |
} | |
} | |
return buf | |
} | |
// STATIC METHODS | |
// ============== | |
Buffer.isEncoding = function (encoding) { | |
switch (String(encoding).toLowerCase()) { | |
case 'hex': | |
case 'utf8': | |
case 'utf-8': | |
case 'ascii': | |
case 'binary': | |
case 'base64': | |
case 'raw': | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
return true | |
default: | |
return false | |
} | |
} | |
Buffer.isBuffer = function (b) { | |
return !!(b != null && b._isBuffer) | |
} | |
Buffer.byteLength = function (str, encoding) { | |
var ret | |
str = str.toString() | |
switch (encoding || 'utf8') { | |
case 'hex': | |
ret = str.length / 2 | |
break | |
case 'utf8': | |
case 'utf-8': | |
ret = utf8ToBytes(str).length | |
break | |
case 'ascii': | |
case 'binary': | |
case 'raw': | |
ret = str.length | |
break | |
case 'base64': | |
ret = base64ToBytes(str).length | |
break | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
ret = str.length * 2 | |
break | |
default: | |
throw new Error('Unknown encoding') | |
} | |
return ret | |
} | |
Buffer.concat = function (list, totalLength) { | |
assert(isArray(list), 'Usage: Buffer.concat(list[, length])') | |
if (list.length === 0) { | |
return new Buffer(0) | |
} else if (list.length === 1) { | |
return list[0] | |
} | |
var i | |
if (totalLength === undefined) { | |
totalLength = 0 | |
for (i = 0; i < list.length; i++) { | |
totalLength += list[i].length | |
} | |
} | |
var buf = new Buffer(totalLength) | |
var pos = 0 | |
for (i = 0; i < list.length; i++) { | |
var item = list[i] | |
item.copy(buf, pos) | |
pos += item.length | |
} | |
return buf | |
} | |
Buffer.compare = function (a, b) { | |
assert(Buffer.isBuffer(a) && Buffer.isBuffer(b), 'Arguments must be Buffers') | |
var x = a.length | |
var y = b.length | |
for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {} | |
if (i !== len) { | |
x = a[i] | |
y = b[i] | |
} | |
if (x < y) { | |
return -1 | |
} | |
if (y < x) { | |
return 1 | |
} | |
return 0 | |
} | |
// BUFFER INSTANCE METHODS | |
// ======================= | |
function hexWrite (buf, string, offset, length) { | |
offset = Number(offset) || 0 | |
var remaining = buf.length - offset | |
if (!length) { | |
length = remaining | |
} else { | |
length = Number(length) | |
if (length > remaining) { | |
length = remaining | |
} | |
} | |
// must be an even number of digits | |
var strLen = string.length | |
assert(strLen % 2 === 0, 'Invalid hex string') | |
if (length > strLen / 2) { | |
length = strLen / 2 | |
} | |
for (var i = 0; i < length; i++) { | |
var byte = parseInt(string.substr(i * 2, 2), 16) | |
assert(!isNaN(byte), 'Invalid hex string') | |
buf[offset + i] = byte | |
} | |
return i | |
} | |
function utf8Write (buf, string, offset, length) { | |
var charsWritten = blitBuffer(utf8ToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
function asciiWrite (buf, string, offset, length) { | |
var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
function binaryWrite (buf, string, offset, length) { | |
return asciiWrite(buf, string, offset, length) | |
} | |
function base64Write (buf, string, offset, length) { | |
var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
function utf16leWrite (buf, string, offset, length) { | |
var charsWritten = blitBuffer(utf16leToBytes(string), buf, offset, length) | |
return charsWritten | |
} | |
Buffer.prototype.write = function (string, offset, length, encoding) { | |
// Support both (string, offset, length, encoding) | |
// and the legacy (string, encoding, offset, length) | |
if (isFinite(offset)) { | |
if (!isFinite(length)) { | |
encoding = length | |
length = undefined | |
} | |
} else { // legacy | |
var swap = encoding | |
encoding = offset | |
offset = length | |
length = swap | |
} | |
offset = Number(offset) || 0 | |
var remaining = this.length - offset | |
if (!length) { | |
length = remaining | |
} else { | |
length = Number(length) | |
if (length > remaining) { | |
length = remaining | |
} | |
} | |
encoding = String(encoding || 'utf8').toLowerCase() | |
var ret | |
switch (encoding) { | |
case 'hex': | |
ret = hexWrite(this, string, offset, length) | |
break | |
case 'utf8': | |
case 'utf-8': | |
ret = utf8Write(this, string, offset, length) | |
break | |
case 'ascii': | |
ret = asciiWrite(this, string, offset, length) | |
break | |
case 'binary': | |
ret = binaryWrite(this, string, offset, length) | |
break | |
case 'base64': | |
ret = base64Write(this, string, offset, length) | |
break | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
ret = utf16leWrite(this, string, offset, length) | |
break | |
default: | |
throw new Error('Unknown encoding') | |
} | |
return ret | |
} | |
Buffer.prototype.toString = function (encoding, start, end) { | |
var self = this | |
encoding = String(encoding || 'utf8').toLowerCase() | |
start = Number(start) || 0 | |
end = (end === undefined) ? self.length : Number(end) | |
// Fastpath empty strings | |
if (end === start) | |
return '' | |
var ret | |
switch (encoding) { | |
case 'hex': | |
ret = hexSlice(self, start, end) | |
break | |
case 'utf8': | |
case 'utf-8': | |
ret = utf8Slice(self, start, end) | |
break | |
case 'ascii': | |
ret = asciiSlice(self, start, end) | |
break | |
case 'binary': | |
ret = binarySlice(self, start, end) | |
break | |
case 'base64': | |
ret = base64Slice(self, start, end) | |
break | |
case 'ucs2': | |
case 'ucs-2': | |
case 'utf16le': | |
case 'utf-16le': | |
ret = utf16leSlice(self, start, end) | |
break | |
default: | |
throw new Error('Unknown encoding') | |
} | |
return ret | |
} | |
Buffer.prototype.toJSON = function () { | |
return { | |
type: 'Buffer', | |
data: Array.prototype.slice.call(this._arr || this, 0) | |
} | |
} | |
Buffer.prototype.equals = function (b) { | |
assert(Buffer.isBuffer(b), 'Argument must be a Buffer') | |
return Buffer.compare(this, b) === 0 | |
} | |
Buffer.prototype.compare = function (b) { | |
assert(Buffer.isBuffer(b), 'Argument must be a Buffer') | |
return Buffer.compare(this, b) | |
} | |
// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) | |
Buffer.prototype.copy = function (target, target_start, start, end) { | |
var source = this | |
if (!start) start = 0 | |
if (!end && end !== 0) end = this.length | |
if (!target_start) target_start = 0 | |
// Copy 0 bytes; we're done | |
if (end === start) return | |
if (target.length === 0 || source.length === 0) return | |
// Fatal error conditions | |
assert(end >= start, 'sourceEnd < sourceStart') | |
assert(target_start >= 0 && target_start < target.length, | |
'targetStart out of bounds') | |
assert(start >= 0 && start < source.length, 'sourceStart out of bounds') | |
assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds') | |
// Are we oob? | |
if (end > this.length) | |
end = this.length | |
if (target.length - target_start < end - start) | |
end = target.length - target_start + start | |
var len = end - start | |
if (len < 100 || !TYPED_ARRAY_SUPPORT) { | |
for (var i = 0; i < len; i++) { | |
target[i + target_start] = this[i + start] | |
} | |
} else { | |
target._set(this.subarray(start, start + len), target_start) | |
} | |
} | |
function base64Slice (buf, start, end) { | |
if (start === 0 && end === buf.length) { | |
return base64.fromByteArray(buf) | |
} else { | |
return base64.fromByteArray(buf.slice(start, end)) | |
} | |
} | |
function utf8Slice (buf, start, end) { | |
var res = '' | |
var tmp = '' | |
end = Math.min(buf.length, end) | |
for (var i = start; i < end; i++) { | |
if (buf[i] <= 0x7F) { | |
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) | |
tmp = '' | |
} else { | |
tmp += '%' + buf[i].toString(16) | |
} | |
} | |
return res + decodeUtf8Char(tmp) | |
} | |
function asciiSlice (buf, start, end) { | |
var ret = '' | |
end = Math.min(buf.length, end) | |
for (var i = start; i < end; i++) { | |
ret += String.fromCharCode(buf[i]) | |
} | |
return ret | |
} | |
function binarySlice (buf, start, end) { | |
return asciiSlice(buf, start, end) | |
} | |
function hexSlice (buf, start, end) { | |
var len = buf.length | |
if (!start || start < 0) start = 0 | |
if (!end || end < 0 || end > len) end = len | |
var out = '' | |
for (var i = start; i < end; i++) { | |
out += toHex(buf[i]) | |
} | |
return out | |
} | |
function utf16leSlice (buf, start, end) { | |
var bytes = buf.slice(start, end) | |
var res = '' | |
for (var i = 0; i < bytes.length; i += 2) { | |
res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) | |
} | |
return res | |
} | |
Buffer.prototype.slice = function (start, end) { | |
var len = this.length | |
start = ~~start | |
end = end === undefined ? len : ~~end | |
if (start < 0) { | |
start += len; | |
if (start < 0) | |
start = 0 | |
} else if (start > len) { | |
start = len | |
} | |
if (end < 0) { | |
end += len | |
if (end < 0) | |
end = 0 | |
} else if (end > len) { | |
end = len | |
} | |
if (end < start) | |
end = start | |
if (TYPED_ARRAY_SUPPORT) { | |
return Buffer._augment(this.subarray(start, end)) | |
} else { | |
var sliceLen = end - start | |
var newBuf = new Buffer(sliceLen, undefined, true) | |
for (var i = 0; i < sliceLen; i++) { | |
newBuf[i] = this[i + start] | |
} | |
return newBuf | |
} | |
} | |
// `get` will be removed in Node 0.13+ | |
Buffer.prototype.get = function (offset) { | |
console.log('.get() is deprecated. Access using array indexes instead.') | |
return this.readUInt8(offset) | |
} | |
// `set` will be removed in Node 0.13+ | |
Buffer.prototype.set = function (v, offset) { | |
console.log('.set() is deprecated. Access using array indexes instead.') | |
return this.writeUInt8(v, offset) | |
} | |
Buffer.prototype.readUInt8 = function (offset, noAssert) { | |
if (!noAssert) { | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset < this.length, 'Trying to read beyond buffer length') | |
} | |
if (offset >= this.length) | |
return | |
return this[offset] | |
} | |
function readUInt16 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val | |
if (littleEndian) { | |
val = buf[offset] | |
if (offset + 1 < len) | |
val |= buf[offset + 1] << 8 | |
} else { | |
val = buf[offset] << 8 | |
if (offset + 1 < len) | |
val |= buf[offset + 1] | |
} | |
return val | |
} | |
Buffer.prototype.readUInt16LE = function (offset, noAssert) { | |
return readUInt16(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readUInt16BE = function (offset, noAssert) { | |
return readUInt16(this, offset, false, noAssert) | |
} | |
function readUInt32 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val | |
if (littleEndian) { | |
if (offset + 2 < len) | |
val = buf[offset + 2] << 16 | |
if (offset + 1 < len) | |
val |= buf[offset + 1] << 8 | |
val |= buf[offset] | |
if (offset + 3 < len) | |
val = val + (buf[offset + 3] << 24 >>> 0) | |
} else { | |
if (offset + 1 < len) | |
val = buf[offset + 1] << 16 | |
if (offset + 2 < len) | |
val |= buf[offset + 2] << 8 | |
if (offset + 3 < len) | |
val |= buf[offset + 3] | |
val = val + (buf[offset] << 24 >>> 0) | |
} | |
return val | |
} | |
Buffer.prototype.readUInt32LE = function (offset, noAssert) { | |
return readUInt32(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readUInt32BE = function (offset, noAssert) { | |
return readUInt32(this, offset, false, noAssert) | |
} | |
Buffer.prototype.readInt8 = function (offset, noAssert) { | |
if (!noAssert) { | |
assert(offset !== undefined && offset !== null, | |
'missing offset') | |
assert(offset < this.length, 'Trying to read beyond buffer length') | |
} | |
if (offset >= this.length) | |
return | |
var neg = this[offset] & 0x80 | |
if (neg) | |
return (0xff - this[offset] + 1) * -1 | |
else | |
return this[offset] | |
} | |
function readInt16 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val = readUInt16(buf, offset, littleEndian, true) | |
var neg = val & 0x8000 | |
if (neg) | |
return (0xffff - val + 1) * -1 | |
else | |
return val | |
} | |
Buffer.prototype.readInt16LE = function (offset, noAssert) { | |
return readInt16(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readInt16BE = function (offset, noAssert) { | |
return readInt16(this, offset, false, noAssert) | |
} | |
function readInt32 (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
var val = readUInt32(buf, offset, littleEndian, true) | |
var neg = val & 0x80000000 | |
if (neg) | |
return (0xffffffff - val + 1) * -1 | |
else | |
return val | |
} | |
Buffer.prototype.readInt32LE = function (offset, noAssert) { | |
return readInt32(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readInt32BE = function (offset, noAssert) { | |
return readInt32(this, offset, false, noAssert) | |
} | |
function readFloat (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') | |
} | |
return ieee754.read(buf, offset, littleEndian, 23, 4) | |
} | |
Buffer.prototype.readFloatLE = function (offset, noAssert) { | |
return readFloat(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readFloatBE = function (offset, noAssert) { | |
return readFloat(this, offset, false, noAssert) | |
} | |
function readDouble (buf, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset + 7 < buf.length, 'Trying to read beyond buffer length') | |
} | |
return ieee754.read(buf, offset, littleEndian, 52, 8) | |
} | |
Buffer.prototype.readDoubleLE = function (offset, noAssert) { | |
return readDouble(this, offset, true, noAssert) | |
} | |
Buffer.prototype.readDoubleBE = function (offset, noAssert) { | |
return readDouble(this, offset, false, noAssert) | |
} | |
Buffer.prototype.writeUInt8 = function (value, offset, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset < this.length, 'trying to write beyond buffer length') | |
verifuint(value, 0xff) | |
} | |
if (offset >= this.length) return | |
this[offset] = value | |
return offset + 1 | |
} | |
function writeUInt16 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'trying to write beyond buffer length') | |
verifuint(value, 0xffff) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) { | |
buf[offset + i] = | |
(value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> | |
(littleEndian ? i : 1 - i) * 8 | |
} | |
return offset + 2 | |
} | |
Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) { | |
return writeUInt16(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) { | |
return writeUInt16(this, value, offset, false, noAssert) | |
} | |
function writeUInt32 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'trying to write beyond buffer length') | |
verifuint(value, 0xffffffff) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) { | |
buf[offset + i] = | |
(value >>> (littleEndian ? i : 3 - i) * 8) & 0xff | |
} | |
return offset + 4 | |
} | |
Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) { | |
return writeUInt32(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) { | |
return writeUInt32(this, value, offset, false, noAssert) | |
} | |
Buffer.prototype.writeInt8 = function (value, offset, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset < this.length, 'Trying to write beyond buffer length') | |
verifsint(value, 0x7f, -0x80) | |
} | |
if (offset >= this.length) | |
return | |
if (value >= 0) | |
this.writeUInt8(value, offset, noAssert) | |
else | |
this.writeUInt8(0xff + value + 1, offset, noAssert) | |
return offset + 1 | |
} | |
function writeInt16 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 1 < buf.length, 'Trying to write beyond buffer length') | |
verifsint(value, 0x7fff, -0x8000) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
if (value >= 0) | |
writeUInt16(buf, value, offset, littleEndian, noAssert) | |
else | |
writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert) | |
return offset + 2 | |
} | |
Buffer.prototype.writeInt16LE = function (value, offset, noAssert) { | |
return writeInt16(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeInt16BE = function (value, offset, noAssert) { | |
return writeInt16(this, value, offset, false, noAssert) | |
} | |
function writeInt32 (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') | |
verifsint(value, 0x7fffffff, -0x80000000) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
if (value >= 0) | |
writeUInt32(buf, value, offset, littleEndian, noAssert) | |
else | |
writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert) | |
return offset + 4 | |
} | |
Buffer.prototype.writeInt32LE = function (value, offset, noAssert) { | |
return writeInt32(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeInt32BE = function (value, offset, noAssert) { | |
return writeInt32(this, value, offset, false, noAssert) | |
} | |
function writeFloat (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') | |
verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
ieee754.write(buf, value, offset, littleEndian, 23, 4) | |
return offset + 4 | |
} | |
Buffer.prototype.writeFloatLE = function (value, offset, noAssert) { | |
return writeFloat(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeFloatBE = function (value, offset, noAssert) { | |
return writeFloat(this, value, offset, false, noAssert) | |
} | |
function writeDouble (buf, value, offset, littleEndian, noAssert) { | |
if (!noAssert) { | |
assert(value !== undefined && value !== null, 'missing value') | |
assert(typeof littleEndian === 'boolean', 'missing or invalid endian') | |
assert(offset !== undefined && offset !== null, 'missing offset') | |
assert(offset + 7 < buf.length, | |
'Trying to write beyond buffer length') | |
verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308) | |
} | |
var len = buf.length | |
if (offset >= len) | |
return | |
ieee754.write(buf, value, offset, littleEndian, 52, 8) | |
return offset + 8 | |
} | |
Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) { | |
return writeDouble(this, value, offset, true, noAssert) | |
} | |
Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) { | |
return writeDouble(this, value, offset, false, noAssert) | |
} | |
// fill(value, start=0, end=buffer.length) | |
Buffer.prototype.fill = function (value, start, end) { | |
if (!value) value = 0 | |
if (!start) start = 0 | |
if (!end) end = this.length | |
assert(end >= start, 'end < start') | |
// Fill 0 bytes; we're done | |
if (end === start) return | |
if (this.length === 0) return | |
assert(start >= 0 && start < this.length, 'start out of bounds') | |
assert(end >= 0 && end <= this.length, 'end out of bounds') | |
var i | |
if (typeof value === 'number') { | |
for (i = start; i < end; i++) { | |
this[i] = value | |
} | |
} else { | |
var bytes = utf8ToBytes(value.toString()) | |
var len = bytes.length | |
for (i = start; i < end; i++) { | |
this[i] = bytes[i % len] | |
} | |
} | |
return this | |
} | |
Buffer.prototype.inspect = function () { | |
var out = [] | |
var len = this.length | |
for (var i = 0; i < len; i++) { | |
out[i] = toHex(this[i]) | |
if (i === exports.INSPECT_MAX_BYTES) { | |
out[i + 1] = '...' | |
break | |
} | |
} | |
return '<Buffer ' + out.join(' ') + '>' | |
} | |
/** | |
* Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. | |
* Added in Node 0.12. Only available in browsers that support ArrayBuffer. | |
*/ | |
Buffer.prototype.toArrayBuffer = function () { | |
if (typeof Uint8Array !== 'undefined') { | |
if (TYPED_ARRAY_SUPPORT) { | |
return (new Buffer(this)).buffer | |
} else { | |
var buf = new Uint8Array(this.length) | |
for (var i = 0, len = buf.length; i < len; i += 1) { | |
buf[i] = this[i] | |
} | |
return buf.buffer | |
} | |
} else { | |
throw new Error('Buffer.toArrayBuffer not supported in this browser') | |
} | |
} | |
// HELPER FUNCTIONS | |
// ================ | |
var BP = Buffer.prototype | |
/** | |
* Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods | |
*/ | |
Buffer._augment = function (arr) { | |
arr._isBuffer = true | |
// save reference to original Uint8Array get/set methods before overwriting | |
arr._get = arr.get | |
arr._set = arr.set | |
// deprecated, will be removed in node 0.13+ | |
arr.get = BP.get | |
arr.set = BP.set | |
arr.write = BP.write | |
arr.toString = BP.toString | |
arr.toLocaleString = BP.toString | |
arr.toJSON = BP.toJSON | |
arr.equals = BP.equals | |
arr.compare = BP.compare | |
arr.copy = BP.copy | |
arr.slice = BP.slice | |
arr.readUInt8 = BP.readUInt8 | |
arr.readUInt16LE = BP.readUInt16LE | |
arr.readUInt16BE = BP.readUInt16BE | |
arr.readUInt32LE = BP.readUInt32LE | |
arr.readUInt32BE = BP.readUInt32BE | |
arr.readInt8 = BP.readInt8 | |
arr.readInt16LE = BP.readInt16LE | |
arr.readInt16BE = BP.readInt16BE | |
arr.readInt32LE = BP.readInt32LE | |
arr.readInt32BE = BP.readInt32BE | |
arr.readFloatLE = BP.readFloatLE | |
arr.readFloatBE = BP.readFloatBE | |
arr.readDoubleLE = BP.readDoubleLE | |
arr.readDoubleBE = BP.readDoubleBE | |
arr.writeUInt8 = BP.writeUInt8 | |
arr.writeUInt16LE = BP.writeUInt16LE | |
arr.writeUInt16BE = BP.writeUInt16BE | |
arr.writeUInt32LE = BP.writeUInt32LE | |
arr.writeUInt32BE = BP.writeUInt32BE | |
arr.writeInt8 = BP.writeInt8 | |
arr.writeInt16LE = BP.writeInt16LE | |
arr.writeInt16BE = BP.writeInt16BE | |
arr.writeInt32LE = BP.writeInt32LE | |
arr.writeInt32BE = BP.writeInt32BE | |
arr.writeFloatLE = BP.writeFloatLE | |
arr.writeFloatBE = BP.writeFloatBE | |
arr.writeDoubleLE = BP.writeDoubleLE | |
arr.writeDoubleBE = BP.writeDoubleBE | |
arr.fill = BP.fill | |
arr.inspect = BP.inspect | |
arr.toArrayBuffer = BP.toArrayBuffer | |
return arr | |
} | |
var INVALID_BASE64_RE = /[^+\/0-9A-z]/g | |
function base64clean (str) { | |
// Node strips out invalid characters like \n and \t from the string, base64-js does not | |
str = stringtrim(str).replace(INVALID_BASE64_RE, '') | |
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not | |
while (str.length % 4 !== 0) { | |
str = str + '=' | |
} | |
return str | |
} | |
function stringtrim (str) { | |
if (str.trim) return str.trim() | |
return str.replace(/^\s+|\s+$/g, '') | |
} | |
function isArray (subject) { | |
return (Array.isArray || function (subject) { | |
return Object.prototype.toString.call(subject) === '[object Array]' | |
})(subject) | |
} | |
function isArrayish (subject) { | |
return isArray(subject) || Buffer.isBuffer(subject) || | |
subject && typeof subject === 'object' && | |
typeof subject.length === 'number' | |
} | |
function toHex (n) { | |
if (n < 16) return '0' + n.toString(16) | |
return n.toString(16) | |
} | |
function utf8ToBytes (str) { | |
var byteArray = [] | |
for (var i = 0; i < str.length; i++) { | |
var b = str.charCodeAt(i) | |
if (b <= 0x7F) { | |
byteArray.push(b) | |
} else { | |
var start = i | |
if (b >= 0xD800 && b <= 0xDFFF) i++ | |
var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%') | |
for (var j = 0; j < h.length; j++) { | |
byteArray.push(parseInt(h[j], 16)) | |
} | |
} | |
} | |
return byteArray | |
} | |
function asciiToBytes (str) { | |
var byteArray = [] | |
for (var i = 0; i < str.length; i++) { | |
// Node's code seems to be doing this and not & 0x7F.. | |
byteArray.push(str.charCodeAt(i) & 0xFF) | |
} | |
return byteArray | |
} | |
function utf16leToBytes (str) { | |
var c, hi, lo | |
var byteArray = [] | |
for (var i = 0; i < str.length; i++) { | |
c = str.charCodeAt(i) | |
hi = c >> 8 | |
lo = c % 256 | |
byteArray.push(lo) | |
byteArray.push(hi) | |
} | |
return byteArray | |
} | |
function base64ToBytes (str) { | |
return base64.toByteArray(str) | |
} | |
function blitBuffer (src, dst, offset, length) { | |
for (var i = 0; i < length; i++) { | |
if ((i + offset >= dst.length) || (i >= src.length)) | |
break | |
dst[i + offset] = src[i] | |
} | |
return i | |
} | |
function decodeUtf8Char (str) { | |
try { | |
return decodeURIComponent(str) | |
} catch (err) { | |
return String.fromCharCode(0xFFFD) // UTF 8 invalid char | |
} | |
} | |
/* | |
* We have to make sure that the value is a valid integer. This means that it | |
* is non-negative. It has no fractional component and that it does not | |
* exceed the maximum allowed value. | |
*/ | |
function verifuint (value, max) { | |
assert(typeof value === 'number', 'cannot write a non-number as a number') | |
assert(value >= 0, 'specified a negative value for writing an unsigned value') | |
assert(value <= max, 'value is larger than maximum value for type') | |
assert(Math.floor(value) === value, 'value has a fractional component') | |
} | |
function verifsint (value, max, min) { | |
assert(typeof value === 'number', 'cannot write a non-number as a number') | |
assert(value <= max, 'value larger than maximum allowed value') | |
assert(value >= min, 'value smaller than minimum allowed value') | |
assert(Math.floor(value) === value, 'value has a fractional component') | |
} | |
function verifIEEE754 (value, max, min) { | |
assert(typeof value === 'number', 'cannot write a non-number as a number') | |
assert(value <= max, 'value larger than maximum allowed value') | |
assert(value >= min, 'value smaller than minimum allowed value') | |
} | |
function assert (test, message) { | |
if (!test) throw new Error(message || 'Failed assertion') | |
} | |
},{"base64-js":10,"ieee754":11}],10:[function(require,module,exports){ | |
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | |
;(function (exports) { | |
'use strict'; | |
var Arr = (typeof Uint8Array !== 'undefined') | |
? Uint8Array | |
: Array | |
var PLUS = '+'.charCodeAt(0) | |
var SLASH = '/'.charCodeAt(0) | |
var NUMBER = '0'.charCodeAt(0) | |
var LOWER = 'a'.charCodeAt(0) | |
var UPPER = 'A'.charCodeAt(0) | |
function decode (elt) { | |
var code = elt.charCodeAt(0) | |
if (code === PLUS) | |
return 62 // '+' | |
if (code === SLASH) | |
return 63 // '/' | |
if (code < NUMBER) | |
return -1 //no match | |
if (code < NUMBER + 10) | |
return code - NUMBER + 26 + 26 | |
if (code < UPPER + 26) | |
return code - UPPER | |
if (code < LOWER + 26) | |
return code - LOWER + 26 | |
} | |
function b64ToByteArray (b64) { | |
var i, j, l, tmp, placeHolders, arr | |
if (b64.length % 4 > 0) { | |
throw new Error('Invalid string. Length must be a multiple of 4') | |
} | |
// the number of equal signs (place holders) | |
// if there are two placeholders, than the two characters before it | |
// represent one byte | |
// if there is only one, then the three characters before it represent 2 bytes | |
// this is just a cheap hack to not do indexOf twice | |
var len = b64.length | |
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 | |
// base64 is 4/3 + up to two characters of the original data | |
arr = new Arr(b64.length * 3 / 4 - placeHolders) | |
// if there are placeholders, only get up to the last complete 4 chars | |
l = placeHolders > 0 ? b64.length - 4 : b64.length | |
var L = 0 | |
function push (v) { | |
arr[L++] = v | |
} | |
for (i = 0, j = 0; i < l; i += 4, j += 3) { | |
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) | |
push((tmp & 0xFF0000) >> 16) | |
push((tmp & 0xFF00) >> 8) | |
push(tmp & 0xFF) | |
} | |
if (placeHolders === 2) { | |
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) | |
push(tmp & 0xFF) | |
} else if (placeHolders === 1) { | |
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) | |
push((tmp >> 8) & 0xFF) | |
push(tmp & 0xFF) | |
} | |
return arr | |
} | |
function uint8ToBase64 (uint8) { | |
var i, | |
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes | |
output = "", | |
temp, length | |
function encode (num) { | |
return lookup.charAt(num) | |
} | |
function tripletToBase64 (num) { | |
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) | |
} | |
// go through the array every three bytes, we'll deal with trailing stuff later | |
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { | |
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) | |
output += tripletToBase64(temp) | |
} | |
// pad the end with zeros, but make sure to not forget the extra bytes | |
switch (extraBytes) { | |
case 1: | |
temp = uint8[uint8.length - 1] | |
output += encode(temp >> 2) | |
output += encode((temp << 4) & 0x3F) | |
output += '==' | |
break | |
case 2: | |
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) | |
output += encode(temp >> 10) | |
output += encode((temp >> 4) & 0x3F) | |
output += encode((temp << 2) & 0x3F) | |
output += '=' | |
break | |
} | |
return output | |
} | |
exports.toByteArray = b64ToByteArray | |
exports.fromByteArray = uint8ToBase64 | |
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) | |
},{}],11:[function(require,module,exports){ | |
exports.read = function(buffer, offset, isLE, mLen, nBytes) { | |
var e, m, | |
eLen = nBytes * 8 - mLen - 1, | |
eMax = (1 << eLen) - 1, | |
eBias = eMax >> 1, | |
nBits = -7, | |
i = isLE ? (nBytes - 1) : 0, | |
d = isLE ? -1 : 1, | |
s = buffer[offset + i]; | |
i += d; | |
e = s & ((1 << (-nBits)) - 1); | |
s >>= (-nBits); | |
nBits += eLen; | |
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); | |
m = e & ((1 << (-nBits)) - 1); | |
e >>= (-nBits); | |
nBits += mLen; | |
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); | |
if (e === 0) { | |
e = 1 - eBias; | |
} else if (e === eMax) { | |
return m ? NaN : ((s ? -1 : 1) * Infinity); | |
} else { | |
m = m + Math.pow(2, mLen); | |
e = e - eBias; | |
} | |
return (s ? -1 : 1) * m * Math.pow(2, e - mLen); | |
}; | |
exports.write = function(buffer, value, offset, isLE, mLen, nBytes) { | |
var e, m, c, | |
eLen = nBytes * 8 - mLen - 1, | |
eMax = (1 << eLen) - 1, | |
eBias = eMax >> 1, | |
rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), | |
i = isLE ? 0 : (nBytes - 1), | |
d = isLE ? 1 : -1, | |
s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; | |
value = Math.abs(value); | |
if (isNaN(value) || value === Infinity) { | |
m = isNaN(value) ? 1 : 0; | |
e = eMax; | |
} else { | |
e = Math.floor(Math.log(value) / Math.LN2); | |
if (value * (c = Math.pow(2, -e)) < 1) { | |
e--; | |
c *= 2; | |
} | |
if (e + eBias >= 1) { | |
value += rt / c; | |
} else { | |
value += rt * Math.pow(2, 1 - eBias); | |
} | |
if (value * c >= 2) { | |
e++; | |
c /= 2; | |
} | |
if (e + eBias >= eMax) { | |
m = 0; | |
e = eMax; | |
} else if (e + eBias >= 1) { | |
m = (value * c - 1) * Math.pow(2, mLen); | |
e = e + eBias; | |
} else { | |
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); | |
e = 0; | |
} | |
} | |
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); | |
e = (e << mLen) | m; | |
eLen += mLen; | |
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); | |
buffer[offset + i - d] |= s * 128; | |
}; | |
},{}],12:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
function EventEmitter() { | |
this._events = this._events || {}; | |
this._maxListeners = this._maxListeners || undefined; | |
} | |
module.exports = EventEmitter; | |
// Backwards-compat with node 0.10.x | |
EventEmitter.EventEmitter = EventEmitter; | |
EventEmitter.prototype._events = undefined; | |
EventEmitter.prototype._maxListeners = undefined; | |
// By default EventEmitters will print a warning if more than 10 listeners are | |
// added to it. This is a useful default which helps finding memory leaks. | |
EventEmitter.defaultMaxListeners = 10; | |
// Obviously not all Emitters should be limited to 10. This function allows | |
// that to be increased. Set to zero for unlimited. | |
EventEmitter.prototype.setMaxListeners = function(n) { | |
if (!isNumber(n) || n < 0 || isNaN(n)) | |
throw TypeError('n must be a positive number'); | |
this._maxListeners = n; | |
return this; | |
}; | |
EventEmitter.prototype.emit = function(type) { | |
var er, handler, len, args, i, listeners; | |
if (!this._events) | |
this._events = {}; | |
// If there is no 'error' event listener then throw. | |
if (type === 'error') { | |
if (!this._events.error || | |
(isObject(this._events.error) && !this._events.error.length)) { | |
er = arguments[1]; | |
if (er instanceof Error) { | |
throw er; // Unhandled 'error' event | |
} else { | |
throw TypeError('Uncaught, unspecified "error" event.'); | |
} | |
return false; | |
} | |
} | |
handler = this._events[type]; | |
if (isUndefined(handler)) | |
return false; | |
if (isFunction(handler)) { | |
switch (arguments.length) { | |
// fast cases | |
case 1: | |
handler.call(this); | |
break; | |
case 2: | |
handler.call(this, arguments[1]); | |
break; | |
case 3: | |
handler.call(this, arguments[1], arguments[2]); | |
break; | |
// slower | |
default: | |
len = arguments.length; | |
args = new Array(len - 1); | |
for (i = 1; i < len; i++) | |
args[i - 1] = arguments[i]; | |
handler.apply(this, args); | |
} | |
} else if (isObject(handler)) { | |
len = arguments.length; | |
args = new Array(len - 1); | |
for (i = 1; i < len; i++) | |
args[i - 1] = arguments[i]; | |
listeners = handler.slice(); | |
len = listeners.length; | |
for (i = 0; i < len; i++) | |
listeners[i].apply(this, args); | |
} | |
return true; | |
}; | |
EventEmitter.prototype.addListener = function(type, listener) { | |
var m; | |
if (!isFunction(listener)) | |
throw TypeError('listener must be a function'); | |
if (!this._events) | |
this._events = {}; | |
// To avoid recursion in the case that type === "newListener"! Before | |
// adding it to the listeners, first emit "newListener". | |
if (this._events.newListener) | |
this.emit('newListener', type, | |
isFunction(listener.listener) ? | |
listener.listener : listener); | |
if (!this._events[type]) | |
// Optimize the case of one listener. Don't need the extra array object. | |
this._events[type] = listener; | |
else if (isObject(this._events[type])) | |
// If we've already got an array, just append. | |
this._events[type].push(listener); | |
else | |
// Adding the second element, need to change to array. | |
this._events[type] = [this._events[type], listener]; | |
// Check for listener leak | |
if (isObject(this._events[type]) && !this._events[type].warned) { | |
var m; | |
if (!isUndefined(this._maxListeners)) { | |
m = this._maxListeners; | |
} else { | |
m = EventEmitter.defaultMaxListeners; | |
} | |
if (m && m > 0 && this._events[type].length > m) { | |
this._events[type].warned = true; | |
console.error('(node) warning: possible EventEmitter memory ' + | |
'leak detected. %d listeners added. ' + | |
'Use emitter.setMaxListeners() to increase limit.', | |
this._events[type].length); | |
if (typeof console.trace === 'function') { | |
// not supported in IE 10 | |
console.trace(); | |
} | |
} | |
} | |
return this; | |
}; | |
EventEmitter.prototype.on = EventEmitter.prototype.addListener; | |
EventEmitter.prototype.once = function(type, listener) { | |
if (!isFunction(listener)) | |
throw TypeError('listener must be a function'); | |
var fired = false; | |
function g() { | |
this.removeListener(type, g); | |
if (!fired) { | |
fired = true; | |
listener.apply(this, arguments); | |
} | |
} | |
g.listener = listener; | |
this.on(type, g); | |
return this; | |
}; | |
// emits a 'removeListener' event iff the listener was removed | |
EventEmitter.prototype.removeListener = function(type, listener) { | |
var list, position, length, i; | |
if (!isFunction(listener)) | |
throw TypeError('listener must be a function'); | |
if (!this._events || !this._events[type]) | |
return this; | |
list = this._events[type]; | |
length = list.length; | |
position = -1; | |
if (list === listener || | |
(isFunction(list.listener) && list.listener === listener)) { | |
delete this._events[type]; | |
if (this._events.removeListener) | |
this.emit('removeListener', type, listener); | |
} else if (isObject(list)) { | |
for (i = length; i-- > 0;) { | |
if (list[i] === listener || | |
(list[i].listener && list[i].listener === listener)) { | |
position = i; | |
break; | |
} | |
} | |
if (position < 0) | |
return this; | |
if (list.length === 1) { | |
list.length = 0; | |
delete this._events[type]; | |
} else { | |
list.splice(position, 1); | |
} | |
if (this._events.removeListener) | |
this.emit('removeListener', type, listener); | |
} | |
return this; | |
}; | |
EventEmitter.prototype.removeAllListeners = function(type) { | |
var key, listeners; | |
if (!this._events) | |
return this; | |
// not listening for removeListener, no need to emit | |
if (!this._events.removeListener) { | |
if (arguments.length === 0) | |
this._events = {}; | |
else if (this._events[type]) | |
delete this._events[type]; | |
return this; | |
} | |
// emit removeListener for all listeners on all events | |
if (arguments.length === 0) { | |
for (key in this._events) { | |
if (key === 'removeListener') continue; | |
this.removeAllListeners(key); | |
} | |
this.removeAllListeners('removeListener'); | |
this._events = {}; | |
return this; | |
} | |
listeners = this._events[type]; | |
if (isFunction(listeners)) { | |
this.removeListener(type, listeners); | |
} else { | |
// LIFO order | |
while (listeners.length) | |
this.removeListener(type, listeners[listeners.length - 1]); | |
} | |
delete this._events[type]; | |
return this; | |
}; | |
EventEmitter.prototype.listeners = function(type) { | |
var ret; | |
if (!this._events || !this._events[type]) | |
ret = []; | |
else if (isFunction(this._events[type])) | |
ret = [this._events[type]]; | |
else | |
ret = this._events[type].slice(); | |
return ret; | |
}; | |
EventEmitter.listenerCount = function(emitter, type) { | |
var ret; | |
if (!emitter._events || !emitter._events[type]) | |
ret = 0; | |
else if (isFunction(emitter._events[type])) | |
ret = 1; | |
else | |
ret = emitter._events[type].length; | |
return ret; | |
}; | |
function isFunction(arg) { | |
return typeof arg === 'function'; | |
} | |
function isNumber(arg) { | |
return typeof arg === 'number'; | |
} | |
function isObject(arg) { | |
return typeof arg === 'object' && arg !== null; | |
} | |
function isUndefined(arg) { | |
return arg === void 0; | |
} | |
},{}],13:[function(require,module,exports){ | |
if (typeof Object.create === 'function') { | |
// implementation from standard node.js 'util' module | |
module.exports = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
ctor.prototype = Object.create(superCtor.prototype, { | |
constructor: { | |
value: ctor, | |
enumerable: false, | |
writable: true, | |
configurable: true | |
} | |
}); | |
}; | |
} else { | |
// old school shim for old browsers | |
module.exports = function inherits(ctor, superCtor) { | |
ctor.super_ = superCtor | |
var TempCtor = function () {} | |
TempCtor.prototype = superCtor.prototype | |
ctor.prototype = new TempCtor() | |
ctor.prototype.constructor = ctor | |
} | |
} | |
},{}],14:[function(require,module,exports){ | |
module.exports = Array.isArray || function (arr) { | |
return Object.prototype.toString.call(arr) == '[object Array]'; | |
}; | |
},{}],15:[function(require,module,exports){ | |
(function (process){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
// resolves . and .. elements in a path array with directory names there | |
// must be no slashes, empty elements, or device names (c:\) in the array | |
// (so also no leading and trailing slashes - it does not distinguish | |
// relative and absolute paths) | |
function normalizeArray(parts, allowAboveRoot) { | |
// if the path tries to go above the root, `up` ends up > 0 | |
var up = 0; | |
for (var i = parts.length - 1; i >= 0; i--) { | |
var last = parts[i]; | |
if (last === '.') { | |
parts.splice(i, 1); | |
} else if (last === '..') { | |
parts.splice(i, 1); | |
up++; | |
} else if (up) { | |
parts.splice(i, 1); | |
up--; | |
} | |
} | |
// if the path is allowed to go above the root, restore leading ..s | |
if (allowAboveRoot) { | |
for (; up--; up) { | |
parts.unshift('..'); | |
} | |
} | |
return parts; | |
} | |
// Split a filename into [root, dir, basename, ext], unix version | |
// 'root' is just a slash, or nothing. | |
var splitPathRe = | |
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; | |
var splitPath = function(filename) { | |
return splitPathRe.exec(filename).slice(1); | |
}; | |
// path.resolve([from ...], to) | |
// posix version | |
exports.resolve = function() { | |
var resolvedPath = '', | |
resolvedAbsolute = false; | |
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { | |
var path = (i >= 0) ? arguments[i] : process.cwd(); | |
// Skip empty and invalid entries | |
if (typeof path !== 'string') { | |
throw new TypeError('Arguments to path.resolve must be strings'); | |
} else if (!path) { | |
continue; | |
} | |
resolvedPath = path + '/' + resolvedPath; | |
resolvedAbsolute = path.charAt(0) === '/'; | |
} | |
// At this point the path should be resolved to a full absolute path, but | |
// handle relative paths to be safe (might happen when process.cwd() fails) | |
// Normalize the path | |
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { | |
return !!p; | |
}), !resolvedAbsolute).join('/'); | |
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; | |
}; | |
// path.normalize(path) | |
// posix version | |
exports.normalize = function(path) { | |
var isAbsolute = exports.isAbsolute(path), | |
trailingSlash = substr(path, -1) === '/'; | |
// Normalize the path | |
path = normalizeArray(filter(path.split('/'), function(p) { | |
return !!p; | |
}), !isAbsolute).join('/'); | |
if (!path && !isAbsolute) { | |
path = '.'; | |
} | |
if (path && trailingSlash) { | |
path += '/'; | |
} | |
return (isAbsolute ? '/' : '') + path; | |
}; | |
// posix version | |
exports.isAbsolute = function(path) { | |
return path.charAt(0) === '/'; | |
}; | |
// posix version | |
exports.join = function() { | |
var paths = Array.prototype.slice.call(arguments, 0); | |
return exports.normalize(filter(paths, function(p, index) { | |
if (typeof p !== 'string') { | |
throw new TypeError('Arguments to path.join must be strings'); | |
} | |
return p; | |
}).join('/')); | |
}; | |
// path.relative(from, to) | |
// posix version | |
exports.relative = function(from, to) { | |
from = exports.resolve(from).substr(1); | |
to = exports.resolve(to).substr(1); | |
function trim(arr) { | |
var start = 0; | |
for (; start < arr.length; start++) { | |
if (arr[start] !== '') break; | |
} | |
var end = arr.length - 1; | |
for (; end >= 0; end--) { | |
if (arr[end] !== '') break; | |
} | |
if (start > end) return []; | |
return arr.slice(start, end - start + 1); | |
} | |
var fromParts = trim(from.split('/')); | |
var toParts = trim(to.split('/')); | |
var length = Math.min(fromParts.length, toParts.length); | |
var samePartsLength = length; | |
for (var i = 0; i < length; i++) { | |
if (fromParts[i] !== toParts[i]) { | |
samePartsLength = i; | |
break; | |
} | |
} | |
var outputParts = []; | |
for (var i = samePartsLength; i < fromParts.length; i++) { | |
outputParts.push('..'); | |
} | |
outputParts = outputParts.concat(toParts.slice(samePartsLength)); | |
return outputParts.join('/'); | |
}; | |
exports.sep = '/'; | |
exports.delimiter = ':'; | |
exports.dirname = function(path) { | |
var result = splitPath(path), | |
root = result[0], | |
dir = result[1]; | |
if (!root && !dir) { | |
// No dirname whatsoever | |
return '.'; | |
} | |
if (dir) { | |
// It has a dirname, strip trailing slash | |
dir = dir.substr(0, dir.length - 1); | |
} | |
return root + dir; | |
}; | |
exports.basename = function(path, ext) { | |
var f = splitPath(path)[2]; | |
// TODO: make this comparison case-insensitive on windows? | |
if (ext && f.substr(-1 * ext.length) === ext) { | |
f = f.substr(0, f.length - ext.length); | |
} | |
return f; | |
}; | |
exports.extname = function(path) { | |
return splitPath(path)[3]; | |
}; | |
function filter (xs, f) { | |
if (xs.filter) return xs.filter(f); | |
var res = []; | |
for (var i = 0; i < xs.length; i++) { | |
if (f(xs[i], i, xs)) res.push(xs[i]); | |
} | |
return res; | |
} | |
// String.prototype.substr - negative index don't work in IE8 | |
var substr = 'ab'.substr(-1) === 'b' | |
? function (str, start, len) { return str.substr(start, len) } | |
: function (str, start, len) { | |
if (start < 0) start = str.length + start; | |
return str.substr(start, len); | |
} | |
; | |
}).call(this,require('_process')) | |
},{"_process":16}],16:[function(require,module,exports){ | |
// shim for using process in browser | |
var process = module.exports = {}; | |
process.nextTick = (function () { | |
var canSetImmediate = typeof window !== 'undefined' | |
&& window.setImmediate; | |
var canPost = typeof window !== 'undefined' | |
&& window.postMessage && window.addEventListener | |
; | |
if (canSetImmediate) { | |
return function (f) { return window.setImmediate(f) }; | |
} | |
if (canPost) { | |
var queue = []; | |
window.addEventListener('message', function (ev) { | |
var source = ev.source; | |
if ((source === window || source === null) && ev.data === 'process-tick') { | |
ev.stopPropagation(); | |
if (queue.length > 0) { | |
var fn = queue.shift(); | |
fn(); | |
} | |
} | |
}, true); | |
return function nextTick(fn) { | |
queue.push(fn); | |
window.postMessage('process-tick', '*'); | |
}; | |
} | |
return function nextTick(fn) { | |
setTimeout(fn, 0); | |
}; | |
})(); | |
process.title = 'browser'; | |
process.browser = true; | |
process.env = {}; | |
process.argv = []; | |
function noop() {} | |
process.on = noop; | |
process.addListener = noop; | |
process.once = noop; | |
process.off = noop; | |
process.removeListener = noop; | |
process.removeAllListeners = noop; | |
process.emit = noop; | |
process.binding = function (name) { | |
throw new Error('process.binding is not supported'); | |
} | |
// TODO(shtylman) | |
process.cwd = function () { return '/' }; | |
process.chdir = function (dir) { | |
throw new Error('process.chdir is not supported'); | |
}; | |
},{}],17:[function(require,module,exports){ | |
module.exports = require("./lib/_stream_duplex.js") | |
},{"./lib/_stream_duplex.js":18}],18:[function(require,module,exports){ | |
(function (process){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
// a duplex stream is just a stream that is both readable and writable. | |
// Since JS doesn't have multiple prototypal inheritance, this class | |
// prototypally inherits from Readable, and then parasitically from | |
// Writable. | |
module.exports = Duplex; | |
/*<replacement>*/ | |
var objectKeys = Object.keys || function (obj) { | |
var keys = []; | |
for (var key in obj) keys.push(key); | |
return keys; | |
} | |
/*</replacement>*/ | |
/*<replacement>*/ | |
var util = require('core-util-is'); | |
util.inherits = require('inherits'); | |
/*</replacement>*/ | |
var Readable = require('./_stream_readable'); | |
var Writable = require('./_stream_writable'); | |
util.inherits(Duplex, Readable); | |
forEach(objectKeys(Writable.prototype), function(method) { | |
if (!Duplex.prototype[method]) | |
Duplex.prototype[method] = Writable.prototype[method]; | |
}); | |
function Duplex(options) { | |
if (!(this instanceof Duplex)) | |
return new Duplex(options); | |
Readable.call(this, options); | |
Writable.call(this, options); | |
if (options && options.readable === false) | |
this.readable = false; | |
if (options && options.writable === false) | |
this.writable = false; | |
this.allowHalfOpen = true; | |
if (options && options.allowHalfOpen === false) | |
this.allowHalfOpen = false; | |
this.once('end', onend); | |
} | |
// the no-half-open enforcer | |
function onend() { | |
// if we allow half-open state, or if the writable side ended, | |
// then we're ok. | |
if (this.allowHalfOpen || this._writableState.ended) | |
return; | |
// no more data can be written. | |
// But allow more writes to happen in this tick. | |
process.nextTick(this.end.bind(this)); | |
} | |
function forEach (xs, f) { | |
for (var i = 0, l = xs.length; i < l; i++) { | |
f(xs[i], i); | |
} | |
} | |
}).call(this,require('_process')) | |
},{"./_stream_readable":20,"./_stream_writable":22,"_process":16,"core-util-is":23,"inherits":13}],19:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
// a passthrough stream. | |
// basically just the most minimal sort of Transform stream. | |
// Every written chunk gets output as-is. | |
module.exports = PassThrough; | |
var Transform = require('./_stream_transform'); | |
/*<replacement>*/ | |
var util = require('core-util-is'); | |
util.inherits = require('inherits'); | |
/*</replacement>*/ | |
util.inherits(PassThrough, Transform); | |
function PassThrough(options) { | |
if (!(this instanceof PassThrough)) | |
return new PassThrough(options); | |
Transform.call(this, options); | |
} | |
PassThrough.prototype._transform = function(chunk, encoding, cb) { | |
cb(null, chunk); | |
}; | |
},{"./_stream_transform":21,"core-util-is":23,"inherits":13}],20:[function(require,module,exports){ | |
(function (process){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
module.exports = Readable; | |
/*<replacement>*/ | |
var isArray = require('isarray'); | |
/*</replacement>*/ | |
/*<replacement>*/ | |
var Buffer = require('buffer').Buffer; | |
/*</replacement>*/ | |
Readable.ReadableState = ReadableState; | |
var EE = require('events').EventEmitter; | |
/*<replacement>*/ | |
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) { | |
return emitter.listeners(type).length; | |
}; | |
/*</replacement>*/ | |
var Stream = require('stream'); | |
/*<replacement>*/ | |
var util = require('core-util-is'); | |
util.inherits = require('inherits'); | |
/*</replacement>*/ | |
var StringDecoder; | |
util.inherits(Readable, Stream); | |
function ReadableState(options, stream) { | |
options = options || {}; | |
// the point at which it stops calling _read() to fill the buffer | |
// Note: 0 is a valid value, means "don't call _read preemptively ever" | |
var hwm = options.highWaterMark; | |
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024; | |
// cast to ints. | |
this.highWaterMark = ~~this.highWaterMark; | |
this.buffer = []; | |
this.length = 0; | |
this.pipes = null; | |
this.pipesCount = 0; | |
this.flowing = false; | |
this.ended = false; | |
this.endEmitted = false; | |
this.reading = false; | |
// In streams that never have any data, and do push(null) right away, | |
// the consumer can miss the 'end' event if they do some I/O before | |
// consuming the stream. So, we don't emit('end') until some reading | |
// happens. | |
this.calledRead = false; | |
// a flag to be able to tell if the onwrite cb is called immediately, | |
// or on a later tick. We set this to true at first, becuase any | |
// actions that shouldn't happen until "later" should generally also | |
// not happen before the first write call. | |
this.sync = true; | |
// whenever we return null, then we set a flag to say | |
// that we're awaiting a 'readable' event emission. | |
this.needReadable = false; | |
this.emittedReadable = false; | |
this.readableListening = false; | |
// object stream flag. Used to make read(n) ignore n and to | |
// make all the buffer merging and length checks go away | |
this.objectMode = !!options.objectMode; | |
// Crypto is kind of old and crusty. Historically, its default string | |
// encoding is 'binary' so we have to make this configurable. | |
// Everything else in the universe uses 'utf8', though. | |
this.defaultEncoding = options.defaultEncoding || 'utf8'; | |
// when piping, we only care about 'readable' events that happen | |
// after read()ing all the bytes and not getting any pushback. | |
this.ranOut = false; | |
// the number of writers that are awaiting a drain event in .pipe()s | |
this.awaitDrain = 0; | |
// if true, a maybeReadMore has been scheduled | |
this.readingMore = false; | |
this.decoder = null; | |
this.encoding = null; | |
if (options.encoding) { | |
if (!StringDecoder) | |
StringDecoder = require('string_decoder/').StringDecoder; | |
this.decoder = new StringDecoder(options.encoding); | |
this.encoding = options.encoding; | |
} | |
} | |
function Readable(options) { | |
if (!(this instanceof Readable)) | |
return new Readable(options); | |
this._readableState = new ReadableState(options, this); | |
// legacy | |
this.readable = true; | |
Stream.call(this); | |
} | |
// Manually shove something into the read() buffer. | |
// This returns true if the highWaterMark has not been hit yet, | |
// similar to how Writable.write() returns true if you should | |
// write() some more. | |
Readable.prototype.push = function(chunk, encoding) { | |
var state = this._readableState; | |
if (typeof chunk === 'string' && !state.objectMode) { | |
encoding = encoding || state.defaultEncoding; | |
if (encoding !== state.encoding) { | |
chunk = new Buffer(chunk, encoding); | |
encoding = ''; | |
} | |
} | |
return readableAddChunk(this, state, chunk, encoding, false); | |
}; | |
// Unshift should *always* be something directly out of read() | |
Readable.prototype.unshift = function(chunk) { | |
var state = this._readableState; | |
return readableAddChunk(this, state, chunk, '', true); | |
}; | |
function readableAddChunk(stream, state, chunk, encoding, addToFront) { | |
var er = chunkInvalid(state, chunk); | |
if (er) { | |
stream.emit('error', er); | |
} else if (chunk === null || chunk === undefined) { | |
state.reading = false; | |
if (!state.ended) | |
onEofChunk(stream, state); | |
} else if (state.objectMode || chunk && chunk.length > 0) { | |
if (state.ended && !addToFront) { | |
var e = new Error('stream.push() after EOF'); | |
stream.emit('error', e); | |
} else if (state.endEmitted && addToFront) { | |
var e = new Error('stream.unshift() after end event'); | |
stream.emit('error', e); | |
} else { | |
if (state.decoder && !addToFront && !encoding) | |
chunk = state.decoder.write(chunk); | |
// update the buffer info. | |
state.length += state.objectMode ? 1 : chunk.length; | |
if (addToFront) { | |
state.buffer.unshift(chunk); | |
} else { | |
state.reading = false; | |
state.buffer.push(chunk); | |
} | |
if (state.needReadable) | |
emitReadable(stream); | |
maybeReadMore(stream, state); | |
} | |
} else if (!addToFront) { | |
state.reading = false; | |
} | |
return needMoreData(state); | |
} | |
// if it's past the high water mark, we can push in some more. | |
// Also, if we have no data yet, we can stand some | |
// more bytes. This is to work around cases where hwm=0, | |
// such as the repl. Also, if the push() triggered a | |
// readable event, and the user called read(largeNumber) such that | |
// needReadable was set, then we ought to push more, so that another | |
// 'readable' event will be triggered. | |
function needMoreData(state) { | |
return !state.ended && | |
(state.needReadable || | |
state.length < state.highWaterMark || | |
state.length === 0); | |
} | |
// backwards compatibility. | |
Readable.prototype.setEncoding = function(enc) { | |
if (!StringDecoder) | |
StringDecoder = require('string_decoder/').StringDecoder; | |
this._readableState.decoder = new StringDecoder(enc); | |
this._readableState.encoding = enc; | |
}; | |
// Don't raise the hwm > 128MB | |
var MAX_HWM = 0x800000; | |
function roundUpToNextPowerOf2(n) { | |
if (n >= MAX_HWM) { | |
n = MAX_HWM; | |
} else { | |
// Get the next highest power of 2 | |
n--; | |
for (var p = 1; p < 32; p <<= 1) n |= n >> p; | |
n++; | |
} | |
return n; | |
} | |
function howMuchToRead(n, state) { | |
if (state.length === 0 && state.ended) | |
return 0; | |
if (state.objectMode) | |
return n === 0 ? 0 : 1; | |
if (isNaN(n) || n === null) { | |
// only flow one buffer at a time | |
if (state.flowing && state.buffer.length) | |
return state.buffer[0].length; | |
else | |
return state.length; | |
} | |
if (n <= 0) | |
return 0; | |
// If we're asking for more than the target buffer level, | |
// then raise the water mark. Bump up to the next highest | |
// power of 2, to prevent increasing it excessively in tiny | |
// amounts. | |
if (n > state.highWaterMark) | |
state.highWaterMark = roundUpToNextPowerOf2(n); | |
// don't have that much. return null, unless we've ended. | |
if (n > state.length) { | |
if (!state.ended) { | |
state.needReadable = true; | |
return 0; | |
} else | |
return state.length; | |
} | |
return n; | |
} | |
// you can override either this method, or the async _read(n) below. | |
Readable.prototype.read = function(n) { | |
var state = this._readableState; | |
state.calledRead = true; | |
var nOrig = n; | |
if (typeof n !== 'number' || n > 0) | |
state.emittedReadable = false; | |
// if we're doing read(0) to trigger a readable event, but we | |
// already have a bunch of data in the buffer, then just trigger | |
// the 'readable' event and move on. | |
if (n === 0 && | |
state.needReadable && | |
(state.length >= state.highWaterMark || state.ended)) { | |
emitReadable(this); | |
return null; | |
} | |
n = howMuchToRead(n, state); | |
// if we've ended, and we're now clear, then finish it up. | |
if (n === 0 && state.ended) { | |
if (state.length === 0) | |
endReadable(this); | |
return null; | |
} | |
// All the actual chunk generation logic needs to be | |
// *below* the call to _read. The reason is that in certain | |
// synthetic stream cases, such as passthrough streams, _read | |
// may be a completely synchronous operation which may change | |
// the state of the read buffer, providing enough data when | |
// before there was *not* enough. | |
// | |
// So, the steps are: | |
// 1. Figure out what the state of things will be after we do | |
// a read from the buffer. | |
// | |
// 2. If that resulting state will trigger a _read, then call _read. | |
// Note that this may be asynchronous, or synchronous. Yes, it is | |
// deeply ugly to write APIs this way, but that still doesn't mean | |
// that the Readable class should behave improperly, as streams are | |
// designed to be sync/async agnostic. | |
// Take note if the _read call is sync or async (ie, if the read call | |
// has returned yet), so that we know whether or not it's safe to emit | |
// 'readable' etc. | |
// | |
// 3. Actually pull the requested chunks out of the buffer and return. | |
// if we need a readable event, then we need to do some reading. | |
var doRead = state.needReadable; | |
// if we currently have less than the highWaterMark, then also read some | |
if (state.length - n <= state.highWaterMark) | |
doRead = true; | |
// however, if we've ended, then there's no point, and if we're already | |
// reading, then it's unnecessary. | |
if (state.ended || state.reading) | |
doRead = false; | |
if (doRead) { | |
state.reading = true; | |
state.sync = true; | |
// if the length is currently zero, then we *need* a readable event. | |
if (state.length === 0) | |
state.needReadable = true; | |
// call internal read method | |
this._read(state.highWaterMark); | |
state.sync = false; | |
} | |
// If _read called its callback synchronously, then `reading` | |
// will be false, and we need to re-evaluate how much data we | |
// can return to the user. | |
if (doRead && !state.reading) | |
n = howMuchToRead(nOrig, state); | |
var ret; | |
if (n > 0) | |
ret = fromList(n, state); | |
else | |
ret = null; | |
if (ret === null) { | |
state.needReadable = true; | |
n = 0; | |
} | |
state.length -= n; | |
// If we have nothing in the buffer, then we want to know | |
// as soon as we *do* get something into the buffer. | |
if (state.length === 0 && !state.ended) | |
state.needReadable = true; | |
// If we happened to read() exactly the remaining amount in the | |
// buffer, and the EOF has been seen at this point, then make sure | |
// that we emit 'end' on the very next tick. | |
if (state.ended && !state.endEmitted && state.length === 0) | |
endReadable(this); | |
return ret; | |
}; | |
function chunkInvalid(state, chunk) { | |
var er = null; | |
if (!Buffer.isBuffer(chunk) && | |
'string' !== typeof chunk && | |
chunk !== null && | |
chunk !== undefined && | |
!state.objectMode && | |
!er) { | |
er = new TypeError('Invalid non-string/buffer chunk'); | |
} | |
return er; | |
} | |
function onEofChunk(stream, state) { | |
if (state.decoder && !state.ended) { | |
var chunk = state.decoder.end(); | |
if (chunk && chunk.length) { | |
state.buffer.push(chunk); | |
state.length += state.objectMode ? 1 : chunk.length; | |
} | |
} | |
state.ended = true; | |
// if we've ended and we have some data left, then emit | |
// 'readable' now to make sure it gets picked up. | |
if (state.length > 0) | |
emitReadable(stream); | |
else | |
endReadable(stream); | |
} | |
// Don't emit readable right away in sync mode, because this can trigger | |
// another read() call => stack overflow. This way, it might trigger | |
// a nextTick recursion warning, but that's not so bad. | |
function emitReadable(stream) { | |
var state = stream._readableState; | |
state.needReadable = false; | |
if (state.emittedReadable) | |
return; | |
state.emittedReadable = true; | |
if (state.sync) | |
process.nextTick(function() { | |
emitReadable_(stream); | |
}); | |
else | |
emitReadable_(stream); | |
} | |
function emitReadable_(stream) { | |
stream.emit('readable'); | |
} | |
// at this point, the user has presumably seen the 'readable' event, | |
// and called read() to consume some data. that may have triggered | |
// in turn another _read(n) call, in which case reading = true if | |
// it's in progress. | |
// However, if we're not ended, or reading, and the length < hwm, | |
// then go ahead and try to read some more preemptively. | |
function maybeReadMore(stream, state) { | |
if (!state.readingMore) { | |
state.readingMore = true; | |
process.nextTick(function() { | |
maybeReadMore_(stream, state); | |
}); | |
} | |
} | |
function maybeReadMore_(stream, state) { | |
var len = state.length; | |
while (!state.reading && !state.flowing && !state.ended && | |
state.length < state.highWaterMark) { | |
stream.read(0); | |
if (len === state.length) | |
// didn't get any data, stop spinning. | |
break; | |
else | |
len = state.length; | |
} | |
state.readingMore = false; | |
} | |
// abstract method. to be overridden in specific implementation classes. | |
// call cb(er, data) where data is <= n in length. | |
// for virtual (non-string, non-buffer) streams, "length" is somewhat | |
// arbitrary, and perhaps not very meaningful. | |
Readable.prototype._read = function(n) { | |
this.emit('error', new Error('not implemented')); | |
}; | |
Readable.prototype.pipe = function(dest, pipeOpts) { | |
var src = this; | |
var state = this._readableState; | |
switch (state.pipesCount) { | |
case 0: | |
state.pipes = dest; | |
break; | |
case 1: | |
state.pipes = [state.pipes, dest]; | |
break; | |
default: | |
state.pipes.push(dest); | |
break; | |
} | |
state.pipesCount += 1; | |
var doEnd = (!pipeOpts || pipeOpts.end !== false) && | |
dest !== process.stdout && | |
dest !== process.stderr; | |
var endFn = doEnd ? onend : cleanup; | |
if (state.endEmitted) | |
process.nextTick(endFn); | |
else | |
src.once('end', endFn); | |
dest.on('unpipe', onunpipe); | |
function onunpipe(readable) { | |
if (readable !== src) return; | |
cleanup(); | |
} | |
function onend() { | |
dest.end(); | |
} | |
// when the dest drains, it reduces the awaitDrain counter | |
// on the source. This would be more elegant with a .once() | |
// handler in flow(), but adding and removing repeatedly is | |
// too slow. | |
var ondrain = pipeOnDrain(src); | |
dest.on('drain', ondrain); | |
function cleanup() { | |
// cleanup event handlers once the pipe is broken | |
dest.removeListener('close', onclose); | |
dest.removeListener('finish', onfinish); | |
dest.removeListener('drain', ondrain); | |
dest.removeListener('error', onerror); | |
dest.removeListener('unpipe', onunpipe); | |
src.removeListener('end', onend); | |
src.removeListener('end', cleanup); | |
// if the reader is waiting for a drain event from this | |
// specific writer, then it would cause it to never start | |
// flowing again. | |
// So, if this is awaiting a drain, then we just call it now. | |
// If we don't know, then assume that we are waiting for one. | |
if (!dest._writableState || dest._writableState.needDrain) | |
ondrain(); | |
} | |
// if the dest has an error, then stop piping into it. | |
// however, don't suppress the throwing behavior for this. | |
function onerror(er) { | |
unpipe(); | |
dest.removeListener('error', onerror); | |
if (EE.listenerCount(dest, 'error') === 0) | |
dest.emit('error', er); | |
} | |
// This is a brutally ugly hack to make sure that our error handler | |
// is attached before any userland ones. NEVER DO THIS. | |
if (!dest._events || !dest._events.error) | |
dest.on('error', onerror); | |
else if (isArray(dest._events.error)) | |
dest._events.error.unshift(onerror); | |
else | |
dest._events.error = [onerror, dest._events.error]; | |
// Both close and finish should trigger unpipe, but only once. | |
function onclose() { | |
dest.removeListener('finish', onfinish); | |
unpipe(); | |
} | |
dest.once('close', onclose); | |
function onfinish() { | |
dest.removeListener('close', onclose); | |
unpipe(); | |
} | |
dest.once('finish', onfinish); | |
function unpipe() { | |
src.unpipe(dest); | |
} | |
// tell the dest that it's being piped to | |
dest.emit('pipe', src); | |
// start the flow if it hasn't been started already. | |
if (!state.flowing) { | |
// the handler that waits for readable events after all | |
// the data gets sucked out in flow. | |
// This would be easier to follow with a .once() handler | |
// in flow(), but that is too slow. | |
this.on('readable', pipeOnReadable); | |
state.flowing = true; | |
process.nextTick(function() { | |
flow(src); | |
}); | |
} | |
return dest; | |
}; | |
function pipeOnDrain(src) { | |
return function() { | |
var dest = this; | |
var state = src._readableState; | |
state.awaitDrain--; | |
if (state.awaitDrain === 0) | |
flow(src); | |
}; | |
} | |
function flow(src) { | |
var state = src._readableState; | |
var chunk; | |
state.awaitDrain = 0; | |
function write(dest, i, list) { | |
var written = dest.write(chunk); | |
if (false === written) { | |
state.awaitDrain++; | |
} | |
} | |
while (state.pipesCount && null !== (chunk = src.read())) { | |
if (state.pipesCount === 1) | |
write(state.pipes, 0, null); | |
else | |
forEach(state.pipes, write); | |
src.emit('data', chunk); | |
// if anyone needs a drain, then we have to wait for that. | |
if (state.awaitDrain > 0) | |
return; | |
} | |
// if every destination was unpiped, either before entering this | |
// function, or in the while loop, then stop flowing. | |
// | |
// NB: This is a pretty rare edge case. | |
if (state.pipesCount === 0) { | |
state.flowing = false; | |
// if there were data event listeners added, then switch to old mode. | |
if (EE.listenerCount(src, 'data') > 0) | |
emitDataEvents(src); | |
return; | |
} | |
// at this point, no one needed a drain, so we just ran out of data | |
// on the next readable event, start it over again. | |
state.ranOut = true; | |
} | |
function pipeOnReadable() { | |
if (this._readableState.ranOut) { | |
this._readableState.ranOut = false; | |
flow(this); | |
} | |
} | |
Readable.prototype.unpipe = function(dest) { | |
var state = this._readableState; | |
// if we're not piping anywhere, then do nothing. | |
if (state.pipesCount === 0) | |
return this; | |
// just one destination. most common case. | |
if (state.pipesCount === 1) { | |
// passed in one, but it's not the right one. | |
if (dest && dest !== state.pipes) | |
return this; | |
if (!dest) | |
dest = state.pipes; | |
// got a match. | |
state.pipes = null; | |
state.pipesCount = 0; | |
this.removeListener('readable', pipeOnReadable); | |
state.flowing = false; | |
if (dest) | |
dest.emit('unpipe', this); | |
return this; | |
} | |
// slow case. multiple pipe destinations. | |
if (!dest) { | |
// remove all. | |
var dests = state.pipes; | |
var len = state.pipesCount; | |
state.pipes = null; | |
state.pipesCount = 0; | |
this.removeListener('readable', pipeOnReadable); | |
state.flowing = false; | |
for (var i = 0; i < len; i++) | |
dests[i].emit('unpipe', this); | |
return this; | |
} | |
// try to find the right one. | |
var i = indexOf(state.pipes, dest); | |
if (i === -1) | |
return this; | |
state.pipes.splice(i, 1); | |
state.pipesCount -= 1; | |
if (state.pipesCount === 1) | |
state.pipes = state.pipes[0]; | |
dest.emit('unpipe', this); | |
return this; | |
}; | |
// set up data events if they are asked for | |
// Ensure readable listeners eventually get something | |
Readable.prototype.on = function(ev, fn) { | |
var res = Stream.prototype.on.call(this, ev, fn); | |
if (ev === 'data' && !this._readableState.flowing) | |
emitDataEvents(this); | |
if (ev === 'readable' && this.readable) { | |
var state = this._readableState; | |
if (!state.readableListening) { | |
state.readableListening = true; | |
state.emittedReadable = false; | |
state.needReadable = true; | |
if (!state.reading) { | |
this.read(0); | |
} else if (state.length) { | |
emitReadable(this, state); | |
} | |
} | |
} | |
return res; | |
}; | |
Readable.prototype.addListener = Readable.prototype.on; | |
// pause() and resume() are remnants of the legacy readable stream API | |
// If the user uses them, then switch into old mode. | |
Readable.prototype.resume = function() { | |
emitDataEvents(this); | |
this.read(0); | |
this.emit('resume'); | |
}; | |
Readable.prototype.pause = function() { | |
emitDataEvents(this, true); | |
this.emit('pause'); | |
}; | |
function emitDataEvents(stream, startPaused) { | |
var state = stream._readableState; | |
if (state.flowing) { | |
// https://github.com/isaacs/readable-stream/issues/16 | |
throw new Error('Cannot switch to old mode now.'); | |
} | |
var paused = startPaused || false; | |
var readable = false; | |
// convert to an old-style stream. | |
stream.readable = true; | |
stream.pipe = Stream.prototype.pipe; | |
stream.on = stream.addListener = Stream.prototype.on; | |
stream.on('readable', function() { | |
readable = true; | |
var c; | |
while (!paused && (null !== (c = stream.read()))) | |
stream.emit('data', c); | |
if (c === null) { | |
readable = false; | |
stream._readableState.needReadable = true; | |
} | |
}); | |
stream.pause = function() { | |
paused = true; | |
this.emit('pause'); | |
}; | |
stream.resume = function() { | |
paused = false; | |
if (readable) | |
process.nextTick(function() { | |
stream.emit('readable'); | |
}); | |
else | |
this.read(0); | |
this.emit('resume'); | |
}; | |
// now make it start, just in case it hadn't already. | |
stream.emit('readable'); | |
} | |
// wrap an old-style stream as the async data source. | |
// This is *not* part of the readable stream interface. | |
// It is an ugly unfortunate mess of history. | |
Readable.prototype.wrap = function(stream) { | |
var state = this._readableState; | |
var paused = false; | |
var self = this; | |
stream.on('end', function() { | |
if (state.decoder && !state.ended) { | |
var chunk = state.decoder.end(); | |
if (chunk && chunk.length) | |
self.push(chunk); | |
} | |
self.push(null); | |
}); | |
stream.on('data', function(chunk) { | |
if (state.decoder) | |
chunk = state.decoder.write(chunk); | |
if (!chunk || !state.objectMode && !chunk.length) | |
return; | |
var ret = self.push(chunk); | |
if (!ret) { | |
paused = true; | |
stream.pause(); | |
} | |
}); | |
// proxy all the other methods. | |
// important when wrapping filters and duplexes. | |
for (var i in stream) { | |
if (typeof stream[i] === 'function' && | |
typeof this[i] === 'undefined') { | |
this[i] = function(method) { return function() { | |
return stream[method].apply(stream, arguments); | |
}}(i); | |
} | |
} | |
// proxy certain important events. | |
var events = ['error', 'close', 'destroy', 'pause', 'resume']; | |
forEach(events, function(ev) { | |
stream.on(ev, self.emit.bind(self, ev)); | |
}); | |
// when we try to consume some more bytes, simply unpause the | |
// underlying stream. | |
self._read = function(n) { | |
if (paused) { | |
paused = false; | |
stream.resume(); | |
} | |
}; | |
return self; | |
}; | |
// exposed for testing purposes only. | |
Readable._fromList = fromList; | |
// Pluck off n bytes from an array of buffers. | |
// Length is the combined lengths of all the buffers in the list. | |
function fromList(n, state) { | |
var list = state.buffer; | |
var length = state.length; | |
var stringMode = !!state.decoder; | |
var objectMode = !!state.objectMode; | |
var ret; | |
// nothing in the list, definitely empty. | |
if (list.length === 0) | |
return null; | |
if (length === 0) | |
ret = null; | |
else if (objectMode) | |
ret = list.shift(); | |
else if (!n || n >= length) { | |
// read it all, truncate the array. | |
if (stringMode) | |
ret = list.join(''); | |
else | |
ret = Buffer.concat(list, length); | |
list.length = 0; | |
} else { | |
// read just some of it. | |
if (n < list[0].length) { | |
// just take a part of the first list item. | |
// slice is the same for buffers and strings. | |
var buf = list[0]; | |
ret = buf.slice(0, n); | |
list[0] = buf.slice(n); | |
} else if (n === list[0].length) { | |
// first list is a perfect match | |
ret = list.shift(); | |
} else { | |
// complex case. | |
// we have enough to cover it, but it spans past the first buffer. | |
if (stringMode) | |
ret = ''; | |
else | |
ret = new Buffer(n); | |
var c = 0; | |
for (var i = 0, l = list.length; i < l && c < n; i++) { | |
var buf = list[0]; | |
var cpy = Math.min(n - c, buf.length); | |
if (stringMode) | |
ret += buf.slice(0, cpy); | |
else | |
buf.copy(ret, c, 0, cpy); | |
if (cpy < buf.length) | |
list[0] = buf.slice(cpy); | |
else | |
list.shift(); | |
c += cpy; | |
} | |
} | |
} | |
return ret; | |
} | |
function endReadable(stream) { | |
var state = stream._readableState; | |
// If we get here before consuming all the bytes, then that is a | |
// bug in node. Should never happen. | |
if (state.length > 0) | |
throw new Error('endReadable called on non-empty stream'); | |
if (!state.endEmitted && state.calledRead) { | |
state.ended = true; | |
process.nextTick(function() { | |
// Check that we didn't get one last unshift. | |
if (!state.endEmitted && state.length === 0) { | |
state.endEmitted = true; | |
stream.readable = false; | |
stream.emit('end'); | |
} | |
}); | |
} | |
} | |
function forEach (xs, f) { | |
for (var i = 0, l = xs.length; i < l; i++) { | |
f(xs[i], i); | |
} | |
} | |
function indexOf (xs, x) { | |
for (var i = 0, l = xs.length; i < l; i++) { | |
if (xs[i] === x) return i; | |
} | |
return -1; | |
} | |
}).call(this,require('_process')) | |
},{"_process":16,"buffer":9,"core-util-is":23,"events":12,"inherits":13,"isarray":14,"stream":29,"string_decoder/":24}],21:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
// a transform stream is a readable/writable stream where you do | |
// something with the data. Sometimes it's called a "filter", | |
// but that's not a great name for it, since that implies a thing where | |
// some bits pass through, and others are simply ignored. (That would | |
// be a valid example of a transform, of course.) | |
// | |
// While the output is causally related to the input, it's not a | |
// necessarily symmetric or synchronous transformation. For example, | |
// a zlib stream might take multiple plain-text writes(), and then | |
// emit a single compressed chunk some time in the future. | |
// | |
// Here's how this works: | |
// | |
// The Transform stream has all the aspects of the readable and writable | |
// stream classes. When you write(chunk), that calls _write(chunk,cb) | |
// internally, and returns false if there's a lot of pending writes | |
// buffered up. When you call read(), that calls _read(n) until | |
// there's enough pending readable data buffered up. | |
// | |
// In a transform stream, the written data is placed in a buffer. When | |
// _read(n) is called, it transforms the queued up data, calling the | |
// buffered _write cb's as it consumes chunks. If consuming a single | |
// written chunk would result in multiple output chunks, then the first | |
// outputted bit calls the readcb, and subsequent chunks just go into | |
// the read buffer, and will cause it to emit 'readable' if necessary. | |
// | |
// This way, back-pressure is actually determined by the reading side, | |
// since _read has to be called to start processing a new chunk. However, | |
// a pathological inflate type of transform can cause excessive buffering | |
// here. For example, imagine a stream where every byte of input is | |
// interpreted as an integer from 0-255, and then results in that many | |
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in | |
// 1kb of data being output. In this case, you could write a very small | |
// amount of input, and end up with a very large amount of output. In | |
// such a pathological inflating mechanism, there'd be no way to tell | |
// the system to stop doing the transform. A single 4MB write could | |
// cause the system to run out of memory. | |
// | |
// However, even in such a pathological case, only a single written chunk | |
// would be consumed, and then the rest would wait (un-transformed) until | |
// the results of the previous transformed chunk were consumed. | |
module.exports = Transform; | |
var Duplex = require('./_stream_duplex'); | |
/*<replacement>*/ | |
var util = require('core-util-is'); | |
util.inherits = require('inherits'); | |
/*</replacement>*/ | |
util.inherits(Transform, Duplex); | |
function TransformState(options, stream) { | |
this.afterTransform = function(er, data) { | |
return afterTransform(stream, er, data); | |
}; | |
this.needTransform = false; | |
this.transforming = false; | |
this.writecb = null; | |
this.writechunk = null; | |
} | |
function afterTransform(stream, er, data) { | |
var ts = stream._transformState; | |
ts.transforming = false; | |
var cb = ts.writecb; | |
if (!cb) | |
return stream.emit('error', new Error('no writecb in Transform class')); | |
ts.writechunk = null; | |
ts.writecb = null; | |
if (data !== null && data !== undefined) | |
stream.push(data); | |
if (cb) | |
cb(er); | |
var rs = stream._readableState; | |
rs.reading = false; | |
if (rs.needReadable || rs.length < rs.highWaterMark) { | |
stream._read(rs.highWaterMark); | |
} | |
} | |
function Transform(options) { | |
if (!(this instanceof Transform)) | |
return new Transform(options); | |
Duplex.call(this, options); | |
var ts = this._transformState = new TransformState(options, this); | |
// when the writable side finishes, then flush out anything remaining. | |
var stream = this; | |
// start out asking for a readable event once data is transformed. | |
this._readableState.needReadable = true; | |
// we have implemented the _read method, and done the other things | |
// that Readable wants before the first _read call, so unset the | |
// sync guard flag. | |
this._readableState.sync = false; | |
this.once('finish', function() { | |
if ('function' === typeof this._flush) | |
this._flush(function(er) { | |
done(stream, er); | |
}); | |
else | |
done(stream); | |
}); | |
} | |
Transform.prototype.push = function(chunk, encoding) { | |
this._transformState.needTransform = false; | |
return Duplex.prototype.push.call(this, chunk, encoding); | |
}; | |
// This is the part where you do stuff! | |
// override this function in implementation classes. | |
// 'chunk' is an input chunk. | |
// | |
// Call `push(newChunk)` to pass along transformed output | |
// to the readable side. You may call 'push' zero or more times. | |
// | |
// Call `cb(err)` when you are done with this chunk. If you pass | |
// an error, then that'll put the hurt on the whole operation. If you | |
// never call cb(), then you'll never get another chunk. | |
Transform.prototype._transform = function(chunk, encoding, cb) { | |
throw new Error('not implemented'); | |
}; | |
Transform.prototype._write = function(chunk, encoding, cb) { | |
var ts = this._transformState; | |
ts.writecb = cb; | |
ts.writechunk = chunk; | |
ts.writeencoding = encoding; | |
if (!ts.transforming) { | |
var rs = this._readableState; | |
if (ts.needTransform || | |
rs.needReadable || | |
rs.length < rs.highWaterMark) | |
this._read(rs.highWaterMark); | |
} | |
}; | |
// Doesn't matter what the args are here. | |
// _transform does all the work. | |
// That we got here means that the readable side wants more data. | |
Transform.prototype._read = function(n) { | |
var ts = this._transformState; | |
if (ts.writechunk !== null && ts.writecb && !ts.transforming) { | |
ts.transforming = true; | |
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); | |
} else { | |
// mark that we need a transform, so that any data that comes in | |
// will get processed, now that we've asked for it. | |
ts.needTransform = true; | |
} | |
}; | |
function done(stream, er) { | |
if (er) | |
return stream.emit('error', er); | |
// if there's nothing in the write buffer, then that means | |
// that nothing more will ever be provided | |
var ws = stream._writableState; | |
var rs = stream._readableState; | |
var ts = stream._transformState; | |
if (ws.length) | |
throw new Error('calling transform done when ws.length != 0'); | |
if (ts.transforming) | |
throw new Error('calling transform done when still transforming'); | |
return stream.push(null); | |
} | |
},{"./_stream_duplex":18,"core-util-is":23,"inherits":13}],22:[function(require,module,exports){ | |
(function (process){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
// A bit simpler than readable streams. | |
// Implement an async ._write(chunk, cb), and it'll handle all | |
// the drain event emission and buffering. | |
module.exports = Writable; | |
/*<replacement>*/ | |
var Buffer = require('buffer').Buffer; | |
/*</replacement>*/ | |
Writable.WritableState = WritableState; | |
/*<replacement>*/ | |
var util = require('core-util-is'); | |
util.inherits = require('inherits'); | |
/*</replacement>*/ | |
var Stream = require('stream'); | |
util.inherits(Writable, Stream); | |
function WriteReq(chunk, encoding, cb) { | |
this.chunk = chunk; | |
this.encoding = encoding; | |
this.callback = cb; | |
} | |
function WritableState(options, stream) { | |
options = options || {}; | |
// the point at which write() starts returning false | |
// Note: 0 is a valid value, means that we always return false if | |
// the entire buffer is not flushed immediately on write() | |
var hwm = options.highWaterMark; | |
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024; | |
// object stream flag to indicate whether or not this stream | |
// contains buffers or objects. | |
this.objectMode = !!options.objectMode; | |
// cast to ints. | |
this.highWaterMark = ~~this.highWaterMark; | |
this.needDrain = false; | |
// at the start of calling end() | |
this.ending = false; | |
// when end() has been called, and returned | |
this.ended = false; | |
// when 'finish' is emitted | |
this.finished = false; | |
// should we decode strings into buffers before passing to _write? | |
// this is here so that some node-core streams can optimize string | |
// handling at a lower level. | |
var noDecode = options.decodeStrings === false; | |
this.decodeStrings = !noDecode; | |
// Crypto is kind of old and crusty. Historically, its default string | |
// encoding is 'binary' so we have to make this configurable. | |
// Everything else in the universe uses 'utf8', though. | |
this.defaultEncoding = options.defaultEncoding || 'utf8'; | |
// not an actual buffer we keep track of, but a measurement | |
// of how much we're waiting to get pushed to some underlying | |
// socket or file. | |
this.length = 0; | |
// a flag to see when we're in the middle of a write. | |
this.writing = false; | |
// a flag to be able to tell if the onwrite cb is called immediately, | |
// or on a later tick. We set this to true at first, becuase any | |
// actions that shouldn't happen until "later" should generally also | |
// not happen before the first write call. | |
this.sync = true; | |
// a flag to know if we're processing previously buffered items, which | |
// may call the _write() callback in the same tick, so that we don't | |
// end up in an overlapped onwrite situation. | |
this.bufferProcessing = false; | |
// the callback that's passed to _write(chunk,cb) | |
this.onwrite = function(er) { | |
onwrite(stream, er); | |
}; | |
// the callback that the user supplies to write(chunk,encoding,cb) | |
this.writecb = null; | |
// the amount that is being written when _write is called. | |
this.writelen = 0; | |
this.buffer = []; | |
// True if the error was already emitted and should not be thrown again | |
this.errorEmitted = false; | |
} | |
function Writable(options) { | |
var Duplex = require('./_stream_duplex'); | |
// Writable ctor is applied to Duplexes, though they're not | |
// instanceof Writable, they're instanceof Readable. | |
if (!(this instanceof Writable) && !(this instanceof Duplex)) | |
return new Writable(options); | |
this._writableState = new WritableState(options, this); | |
// legacy. | |
this.writable = true; | |
Stream.call(this); | |
} | |
// Otherwise people can pipe Writable streams, which is just wrong. | |
Writable.prototype.pipe = function() { | |
this.emit('error', new Error('Cannot pipe. Not readable.')); | |
}; | |
function writeAfterEnd(stream, state, cb) { | |
var er = new Error('write after end'); | |
// TODO: defer error events consistently everywhere, not just the cb | |
stream.emit('error', er); | |
process.nextTick(function() { | |
cb(er); | |
}); | |
} | |
// If we get something that is not a buffer, string, null, or undefined, | |
// and we're not in objectMode, then that's an error. | |
// Otherwise stream chunks are all considered to be of length=1, and the | |
// watermarks determine how many objects to keep in the buffer, rather than | |
// how many bytes or characters. | |
function validChunk(stream, state, chunk, cb) { | |
var valid = true; | |
if (!Buffer.isBuffer(chunk) && | |
'string' !== typeof chunk && | |
chunk !== null && | |
chunk !== undefined && | |
!state.objectMode) { | |
var er = new TypeError('Invalid non-string/buffer chunk'); | |
stream.emit('error', er); | |
process.nextTick(function() { | |
cb(er); | |
}); | |
valid = false; | |
} | |
return valid; | |
} | |
Writable.prototype.write = function(chunk, encoding, cb) { | |
var state = this._writableState; | |
var ret = false; | |
if (typeof encoding === 'function') { | |
cb = encoding; | |
encoding = null; | |
} | |
if (Buffer.isBuffer(chunk)) | |
encoding = 'buffer'; | |
else if (!encoding) | |
encoding = state.defaultEncoding; | |
if (typeof cb !== 'function') | |
cb = function() {}; | |
if (state.ended) | |
writeAfterEnd(this, state, cb); | |
else if (validChunk(this, state, chunk, cb)) | |
ret = writeOrBuffer(this, state, chunk, encoding, cb); | |
return ret; | |
}; | |
function decodeChunk(state, chunk, encoding) { | |
if (!state.objectMode && | |
state.decodeStrings !== false && | |
typeof chunk === 'string') { | |
chunk = new Buffer(chunk, encoding); | |
} | |
return chunk; | |
} | |
// if we're already writing something, then just put this | |
// in the queue, and wait our turn. Otherwise, call _write | |
// If we return false, then we need a drain event, so set that flag. | |
function writeOrBuffer(stream, state, chunk, encoding, cb) { | |
chunk = decodeChunk(state, chunk, encoding); | |
if (Buffer.isBuffer(chunk)) | |
encoding = 'buffer'; | |
var len = state.objectMode ? 1 : chunk.length; | |
state.length += len; | |
var ret = state.length < state.highWaterMark; | |
// we must ensure that previous needDrain will not be reset to false. | |
if (!ret) | |
state.needDrain = true; | |
if (state.writing) | |
state.buffer.push(new WriteReq(chunk, encoding, cb)); | |
else | |
doWrite(stream, state, len, chunk, encoding, cb); | |
return ret; | |
} | |
function doWrite(stream, state, len, chunk, encoding, cb) { | |
state.writelen = len; | |
state.writecb = cb; | |
state.writing = true; | |
state.sync = true; | |
stream._write(chunk, encoding, state.onwrite); | |
state.sync = false; | |
} | |
function onwriteError(stream, state, sync, er, cb) { | |
if (sync) | |
process.nextTick(function() { | |
cb(er); | |
}); | |
else | |
cb(er); | |
stream._writableState.errorEmitted = true; | |
stream.emit('error', er); | |
} | |
function onwriteStateUpdate(state) { | |
state.writing = false; | |
state.writecb = null; | |
state.length -= state.writelen; | |
state.writelen = 0; | |
} | |
function onwrite(stream, er) { | |
var state = stream._writableState; | |
var sync = state.sync; | |
var cb = state.writecb; | |
onwriteStateUpdate(state); | |
if (er) | |
onwriteError(stream, state, sync, er, cb); | |
else { | |
// Check if we're actually ready to finish, but don't emit yet | |
var finished = needFinish(stream, state); | |
if (!finished && !state.bufferProcessing && state.buffer.length) | |
clearBuffer(stream, state); | |
if (sync) { | |
process.nextTick(function() { | |
afterWrite(stream, state, finished, cb); | |
}); | |
} else { | |
afterWrite(stream, state, finished, cb); | |
} | |
} | |
} | |
function afterWrite(stream, state, finished, cb) { | |
if (!finished) | |
onwriteDrain(stream, state); | |
cb(); | |
if (finished) | |
finishMaybe(stream, state); | |
} | |
// Must force callback to be called on nextTick, so that we don't | |
// emit 'drain' before the write() consumer gets the 'false' return | |
// value, and has a chance to attach a 'drain' listener. | |
function onwriteDrain(stream, state) { | |
if (state.length === 0 && state.needDrain) { | |
state.needDrain = false; | |
stream.emit('drain'); | |
} | |
} | |
// if there's something in the buffer waiting, then process it | |
function clearBuffer(stream, state) { | |
state.bufferProcessing = true; | |
for (var c = 0; c < state.buffer.length; c++) { | |
var entry = state.buffer[c]; | |
var chunk = entry.chunk; | |
var encoding = entry.encoding; | |
var cb = entry.callback; | |
var len = state.objectMode ? 1 : chunk.length; | |
doWrite(stream, state, len, chunk, encoding, cb); | |
// if we didn't call the onwrite immediately, then | |
// it means that we need to wait until it does. | |
// also, that means that the chunk and cb are currently | |
// being processed, so move the buffer counter past them. | |
if (state.writing) { | |
c++; | |
break; | |
} | |
} | |
state.bufferProcessing = false; | |
if (c < state.buffer.length) | |
state.buffer = state.buffer.slice(c); | |
else | |
state.buffer.length = 0; | |
} | |
Writable.prototype._write = function(chunk, encoding, cb) { | |
cb(new Error('not implemented')); | |
}; | |
Writable.prototype.end = function(chunk, encoding, cb) { | |
var state = this._writableState; | |
if (typeof chunk === 'function') { | |
cb = chunk; | |
chunk = null; | |
encoding = null; | |
} else if (typeof encoding === 'function') { | |
cb = encoding; | |
encoding = null; | |
} | |
if (typeof chunk !== 'undefined' && chunk !== null) | |
this.write(chunk, encoding); | |
// ignore unnecessary end() calls. | |
if (!state.ending && !state.finished) | |
endWritable(this, state, cb); | |
}; | |
function needFinish(stream, state) { | |
return (state.ending && | |
state.length === 0 && | |
!state.finished && | |
!state.writing); | |
} | |
function finishMaybe(stream, state) { | |
var need = needFinish(stream, state); | |
if (need) { | |
state.finished = true; | |
stream.emit('finish'); | |
} | |
return need; | |
} | |
function endWritable(stream, state, cb) { | |
state.ending = true; | |
finishMaybe(stream, state); | |
if (cb) { | |
if (state.finished) | |
process.nextTick(cb); | |
else | |
stream.once('finish', cb); | |
} | |
state.ended = true; | |
} | |
}).call(this,require('_process')) | |
},{"./_stream_duplex":18,"_process":16,"buffer":9,"core-util-is":23,"inherits":13,"stream":29}],23:[function(require,module,exports){ | |
(function (Buffer){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
// NOTE: These type checking functions intentionally don't use `instanceof` | |
// because it is fragile and can be easily faked with `Object.create()`. | |
function isArray(ar) { | |
return Array.isArray(ar); | |
} | |
exports.isArray = isArray; | |
function isBoolean(arg) { | |
return typeof arg === 'boolean'; | |
} | |
exports.isBoolean = isBoolean; | |
function isNull(arg) { | |
return arg === null; | |
} | |
exports.isNull = isNull; | |
function isNullOrUndefined(arg) { | |
return arg == null; | |
} | |
exports.isNullOrUndefined = isNullOrUndefined; | |
function isNumber(arg) { | |
return typeof arg === 'number'; | |
} | |
exports.isNumber = isNumber; | |
function isString(arg) { | |
return typeof arg === 'string'; | |
} | |
exports.isString = isString; | |
function isSymbol(arg) { | |
return typeof arg === 'symbol'; | |
} | |
exports.isSymbol = isSymbol; | |
function isUndefined(arg) { | |
return arg === void 0; | |
} | |
exports.isUndefined = isUndefined; | |
function isRegExp(re) { | |
return isObject(re) && objectToString(re) === '[object RegExp]'; | |
} | |
exports.isRegExp = isRegExp; | |
function isObject(arg) { | |
return typeof arg === 'object' && arg !== null; | |
} | |
exports.isObject = isObject; | |
function isDate(d) { | |
return isObject(d) && objectToString(d) === '[object Date]'; | |
} | |
exports.isDate = isDate; | |
function isError(e) { | |
return isObject(e) && | |
(objectToString(e) === '[object Error]' || e instanceof Error); | |
} | |
exports.isError = isError; | |
function isFunction(arg) { | |
return typeof arg === 'function'; | |
} | |
exports.isFunction = isFunction; | |
function isPrimitive(arg) { | |
return arg === null || | |
typeof arg === 'boolean' || | |
typeof arg === 'number' || | |
typeof arg === 'string' || | |
typeof arg === 'symbol' || // ES6 symbol | |
typeof arg === 'undefined'; | |
} | |
exports.isPrimitive = isPrimitive; | |
function isBuffer(arg) { | |
return Buffer.isBuffer(arg); | |
} | |
exports.isBuffer = isBuffer; | |
function objectToString(o) { | |
return Object.prototype.toString.call(o); | |
} | |
}).call(this,require("buffer").Buffer) | |
},{"buffer":9}],24:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var Buffer = require('buffer').Buffer; | |
var isBufferEncoding = Buffer.isEncoding | |
|| function(encoding) { | |
switch (encoding && encoding.toLowerCase()) { | |
case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': case 'raw': return true; | |
default: return false; | |
} | |
} | |
function assertEncoding(encoding) { | |
if (encoding && !isBufferEncoding(encoding)) { | |
throw new Error('Unknown encoding: ' + encoding); | |
} | |
} | |
var StringDecoder = exports.StringDecoder = function(encoding) { | |
this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); | |
assertEncoding(encoding); | |
switch (this.encoding) { | |
case 'utf8': | |
// CESU-8 represents each of Surrogate Pair by 3-bytes | |
this.surrogateSize = 3; | |
break; | |
case 'ucs2': | |
case 'utf16le': | |
// UTF-16 represents each of Surrogate Pair by 2-bytes | |
this.surrogateSize = 2; | |
this.detectIncompleteChar = utf16DetectIncompleteChar; | |
break; | |
case 'base64': | |
// Base-64 stores 3 bytes in 4 chars, and pads the remainder. | |
this.surrogateSize = 3; | |
this.detectIncompleteChar = base64DetectIncompleteChar; | |
break; | |
default: | |
this.write = passThroughWrite; | |
return; | |
} | |
this.charBuffer = new Buffer(6); | |
this.charReceived = 0; | |
this.charLength = 0; | |
}; | |
StringDecoder.prototype.write = function(buffer) { | |
var charStr = ''; | |
var offset = 0; | |
// if our last write ended with an incomplete multibyte character | |
while (this.charLength) { | |
// determine how many remaining bytes this buffer has to offer for this char | |
var i = (buffer.length >= this.charLength - this.charReceived) ? | |
this.charLength - this.charReceived : | |
buffer.length; | |
// add the new bytes to the char buffer | |
buffer.copy(this.charBuffer, this.charReceived, offset, i); | |
this.charReceived += (i - offset); | |
offset = i; | |
if (this.charReceived < this.charLength) { | |
// still not enough chars in this buffer? wait for more ... | |
return ''; | |
} | |
// get the character that was split | |
charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); | |
// lead surrogate (D800-DBFF) is also the incomplete character | |
var charCode = charStr.charCodeAt(charStr.length - 1); | |
if (charCode >= 0xD800 && charCode <= 0xDBFF) { | |
this.charLength += this.surrogateSize; | |
charStr = ''; | |
continue; | |
} | |
this.charReceived = this.charLength = 0; | |
// if there are no more bytes in this buffer, just emit our char | |
if (i == buffer.length) return charStr; | |
// otherwise cut off the characters end from the beginning of this buffer | |
buffer = buffer.slice(i, buffer.length); | |
break; | |
} | |
var lenIncomplete = this.detectIncompleteChar(buffer); | |
var end = buffer.length; | |
if (this.charLength) { | |
// buffer the incomplete character bytes we got | |
buffer.copy(this.charBuffer, 0, buffer.length - lenIncomplete, end); | |
this.charReceived = lenIncomplete; | |
end -= lenIncomplete; | |
} | |
charStr += buffer.toString(this.encoding, 0, end); | |
var end = charStr.length - 1; | |
var charCode = charStr.charCodeAt(end); | |
// lead surrogate (D800-DBFF) is also the incomplete character | |
if (charCode >= 0xD800 && charCode <= 0xDBFF) { | |
var size = this.surrogateSize; | |
this.charLength += size; | |
this.charReceived += size; | |
this.charBuffer.copy(this.charBuffer, size, 0, size); | |
this.charBuffer.write(charStr.charAt(charStr.length - 1), this.encoding); | |
return charStr.substring(0, end); | |
} | |
// or just emit the charStr | |
return charStr; | |
}; | |
StringDecoder.prototype.detectIncompleteChar = function(buffer) { | |
// determine how many bytes we have to check at the end of this buffer | |
var i = (buffer.length >= 3) ? 3 : buffer.length; | |
// Figure out if one of the last i bytes of our buffer announces an | |
// incomplete char. | |
for (; i > 0; i--) { | |
var c = buffer[buffer.length - i]; | |
// See http://en.wikipedia.org/wiki/UTF-8#Description | |
// 110XXXXX | |
if (i == 1 && c >> 5 == 0x06) { | |
this.charLength = 2; | |
break; | |
} | |
// 1110XXXX | |
if (i <= 2 && c >> 4 == 0x0E) { | |
this.charLength = 3; | |
break; | |
} | |
// 11110XXX | |
if (i <= 3 && c >> 3 == 0x1E) { | |
this.charLength = 4; | |
break; | |
} | |
} | |
return i; | |
}; | |
StringDecoder.prototype.end = function(buffer) { | |
var res = ''; | |
if (buffer && buffer.length) | |
res = this.write(buffer); | |
if (this.charReceived) { | |
var cr = this.charReceived; | |
var buf = this.charBuffer; | |
var enc = this.encoding; | |
res += buf.slice(0, cr).toString(enc); | |
} | |
return res; | |
}; | |
function passThroughWrite(buffer) { | |
return buffer.toString(this.encoding); | |
} | |
function utf16DetectIncompleteChar(buffer) { | |
var incomplete = this.charReceived = buffer.length % 2; | |
this.charLength = incomplete ? 2 : 0; | |
return incomplete; | |
} | |
function base64DetectIncompleteChar(buffer) { | |
var incomplete = this.charReceived = buffer.length % 3; | |
this.charLength = incomplete ? 3 : 0; | |
return incomplete; | |
} | |
},{"buffer":9}],25:[function(require,module,exports){ | |
module.exports = require("./lib/_stream_passthrough.js") | |
},{"./lib/_stream_passthrough.js":19}],26:[function(require,module,exports){ | |
exports = module.exports = require('./lib/_stream_readable.js'); | |
exports.Readable = exports; | |
exports.Writable = require('./lib/_stream_writable.js'); | |
exports.Duplex = require('./lib/_stream_duplex.js'); | |
exports.Transform = require('./lib/_stream_transform.js'); | |
exports.PassThrough = require('./lib/_stream_passthrough.js'); | |
},{"./lib/_stream_duplex.js":18,"./lib/_stream_passthrough.js":19,"./lib/_stream_readable.js":20,"./lib/_stream_transform.js":21,"./lib/_stream_writable.js":22}],27:[function(require,module,exports){ | |
module.exports = require("./lib/_stream_transform.js") | |
},{"./lib/_stream_transform.js":21}],28:[function(require,module,exports){ | |
module.exports = require("./lib/_stream_writable.js") | |
},{"./lib/_stream_writable.js":22}],29:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
module.exports = Stream; | |
var EE = require('events').EventEmitter; | |
var inherits = require('inherits'); | |
inherits(Stream, EE); | |
Stream.Readable = require('readable-stream/readable.js'); | |
Stream.Writable = require('readable-stream/writable.js'); | |
Stream.Duplex = require('readable-stream/duplex.js'); | |
Stream.Transform = require('readable-stream/transform.js'); | |
Stream.PassThrough = require('readable-stream/passthrough.js'); | |
// Backwards-compat with node 0.4.x | |
Stream.Stream = Stream; | |
// old-style streams. Note that the pipe method (the only relevant | |
// part of this class) is overridden in the Readable class. | |
function Stream() { | |
EE.call(this); | |
} | |
Stream.prototype.pipe = function(dest, options) { | |
var source = this; | |
function ondata(chunk) { | |
if (dest.writable) { | |
if (false === dest.write(chunk) && source.pause) { | |
source.pause(); | |
} | |
} | |
} | |
source.on('data', ondata); | |
function ondrain() { | |
if (source.readable && source.resume) { | |
source.resume(); | |
} | |
} | |
dest.on('drain', ondrain); | |
// If the 'end' option is not supplied, dest.end() will be called when | |
// source gets the 'end' or 'close' events. Only dest.end() once. | |
if (!dest._isStdio && (!options || options.end !== false)) { | |
source.on('end', onend); | |
source.on('close', onclose); | |
} | |
var didOnEnd = false; | |
function onend() { | |
if (didOnEnd) return; | |
didOnEnd = true; | |
dest.end(); | |
} | |
function onclose() { | |
if (didOnEnd) return; | |
didOnEnd = true; | |
if (typeof dest.destroy === 'function') dest.destroy(); | |
} | |
// don't leave dangling pipes when there are errors. | |
function onerror(er) { | |
cleanup(); | |
if (EE.listenerCount(this, 'error') === 0) { | |
throw er; // Unhandled stream error in pipe. | |
} | |
} | |
source.on('error', onerror); | |
dest.on('error', onerror); | |
// remove all the event listeners that were added. | |
function cleanup() { | |
source.removeListener('data', ondata); | |
dest.removeListener('drain', ondrain); | |
source.removeListener('end', onend); | |
source.removeListener('close', onclose); | |
source.removeListener('error', onerror); | |
dest.removeListener('error', onerror); | |
source.removeListener('end', cleanup); | |
source.removeListener('close', cleanup); | |
dest.removeListener('close', cleanup); | |
} | |
source.on('end', cleanup); | |
source.on('close', cleanup); | |
dest.on('close', cleanup); | |
dest.emit('pipe', source); | |
// Allow for unix-like usage: A.pipe(B).pipe(C) | |
return dest; | |
}; | |
},{"events":12,"inherits":13,"readable-stream/duplex.js":17,"readable-stream/passthrough.js":25,"readable-stream/readable.js":26,"readable-stream/transform.js":27,"readable-stream/writable.js":28}],30:[function(require,module,exports){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var Buffer = require('buffer').Buffer; | |
function assertEncoding(encoding) { | |
if (encoding && !Buffer.isEncoding(encoding)) { | |
throw new Error('Unknown encoding: ' + encoding); | |
} | |
} | |
var StringDecoder = exports.StringDecoder = function(encoding) { | |
this.encoding = (encoding || 'utf8').toLowerCase().replace(/[-_]/, ''); | |
assertEncoding(encoding); | |
switch (this.encoding) { | |
case 'utf8': | |
// CESU-8 represents each of Surrogate Pair by 3-bytes | |
this.surrogateSize = 3; | |
break; | |
case 'ucs2': | |
case 'utf16le': | |
// UTF-16 represents each of Surrogate Pair by 2-bytes | |
this.surrogateSize = 2; | |
this.detectIncompleteChar = utf16DetectIncompleteChar; | |
break; | |
case 'base64': | |
// Base-64 stores 3 bytes in 4 chars, and pads the remainder. | |
this.surrogateSize = 3; | |
this.detectIncompleteChar = base64DetectIncompleteChar; | |
break; | |
default: | |
this.write = passThroughWrite; | |
return; | |
} | |
this.charBuffer = new Buffer(6); | |
this.charReceived = 0; | |
this.charLength = 0; | |
}; | |
StringDecoder.prototype.write = function(buffer) { | |
var charStr = ''; | |
var offset = 0; | |
// if our last write ended with an incomplete multibyte character | |
while (this.charLength) { | |
// determine how many remaining bytes this buffer has to offer for this char | |
var i = (buffer.length >= this.charLength - this.charReceived) ? | |
this.charLength - this.charReceived : | |
buffer.length; | |
// add the new bytes to the char buffer | |
buffer.copy(this.charBuffer, this.charReceived, offset, i); | |
this.charReceived += (i - offset); | |
offset = i; | |
if (this.charReceived < this.charLength) { | |
// still not enough chars in this buffer? wait for more ... | |
return ''; | |
} | |
// get the character that was split | |
charStr = this.charBuffer.slice(0, this.charLength).toString(this.encoding); | |
// lead surrogate (D800-DBFF) is also the incomplete character | |
var charCode = charStr.charCodeAt(charStr.length - 1); | |
if (charCode >= 0xD800 && charCode <= 0xDBFF) { | |
this.charLength += this.surrogateSize; | |
charStr = ''; | |
continue; | |
} | |
this.charReceived = this.charLength = 0; | |
// if there are no more bytes in this buffer, just emit our char | |
if (i == buffer.length) return charStr; | |
// otherwise cut off the characters end from the beginning of this buffer | |
buffer = buffer.slice(i, buffer.length); | |
break; | |
} | |
var lenIncomplete = this.detectIncompleteChar(buffer); | |
var end = buffer.length; | |
if (this.charLength) { | |
// buffer the incomplete character bytes we got | |
buffer.copy(this.charBuffer, 0, buffer.length - lenIncomplete, end); | |
this.charReceived = lenIncomplete; | |
end -= lenIncomplete; | |
} | |
charStr += buffer.toString(this.encoding, 0, end); | |
var end = charStr.length - 1; | |
var charCode = charStr.charCodeAt(end); | |
// lead surrogate (D800-DBFF) is also the incomplete character | |
if (charCode >= 0xD800 && charCode <= 0xDBFF) { | |
var size = this.surrogateSize; | |
this.charLength += size; | |
this.charReceived += size; | |
this.charBuffer.copy(this.charBuffer, size, 0, size); | |
this.charBuffer.write(charStr.charAt(charStr.length - 1), this.encoding); | |
return charStr.substring(0, end); | |
} | |
// or just emit the charStr | |
return charStr; | |
}; | |
StringDecoder.prototype.detectIncompleteChar = function(buffer) { | |
// determine how many bytes we have to check at the end of this buffer | |
var i = (buffer.length >= 3) ? 3 : buffer.length; | |
// Figure out if one of the last i bytes of our buffer announces an | |
// incomplete char. | |
for (; i > 0; i--) { | |
var c = buffer[buffer.length - i]; | |
// See http://en.wikipedia.org/wiki/UTF-8#Description | |
// 110XXXXX | |
if (i == 1 && c >> 5 == 0x06) { | |
this.charLength = 2; | |
break; | |
} | |
// 1110XXXX | |
if (i <= 2 && c >> 4 == 0x0E) { | |
this.charLength = 3; | |
break; | |
} | |
// 11110XXX | |
if (i <= 3 && c >> 3 == 0x1E) { | |
this.charLength = 4; | |
break; | |
} | |
} | |
return i; | |
}; | |
StringDecoder.prototype.end = function(buffer) { | |
var res = ''; | |
if (buffer && buffer.length) | |
res = this.write(buffer); | |
if (this.charReceived) { | |
var cr = this.charReceived; | |
var buf = this.charBuffer; | |
var enc = this.encoding; | |
res += buf.slice(0, cr).toString(enc); | |
} | |
return res; | |
}; | |
function passThroughWrite(buffer) { | |
return buffer.toString(this.encoding); | |
} | |
function utf16DetectIncompleteChar(buffer) { | |
var incomplete = this.charReceived = buffer.length % 2; | |
this.charLength = incomplete ? 2 : 0; | |
return incomplete; | |
} | |
function base64DetectIncompleteChar(buffer) { | |
var incomplete = this.charReceived = buffer.length % 3; | |
this.charLength = incomplete ? 3 : 0; | |
return incomplete; | |
} | |
},{"buffer":9}],31:[function(require,module,exports){ | |
module.exports = function isBuffer(arg) { | |
return arg && typeof arg === 'object' | |
&& typeof arg.copy === 'function' | |
&& typeof arg.fill === 'function' | |
&& typeof arg.readUInt8 === 'function'; | |
} | |
},{}],32:[function(require,module,exports){ | |
(function (process,global){ | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
var formatRegExp = /%[sdj%]/g; | |
exports.format = function(f) { | |
if (!isString(f)) { | |
var objects = []; | |
for (var i = 0; i < arguments.length; i++) { | |
objects.push(inspect(arguments[i])); | |
} | |
return objects.join(' '); | |
} | |
var i = 1; | |
var args = arguments; | |
var len = args.length; | |
var str = String(f).replace(formatRegExp, function(x) { | |
if (x === '%%') return '%'; | |
if (i >= len) return x; | |
switch (x) { | |
case '%s': return String(args[i++]); | |
case '%d': return Number(args[i++]); | |
case '%j': | |
try { | |
return JSON.stringify(args[i++]); | |
} catch (_) { | |
return '[Circular]'; | |
} | |
default: | |
return x; | |
} | |
}); | |
for (var x = args[i]; i < len; x = args[++i]) { | |
if (isNull(x) || !isObject(x)) { | |
str += ' ' + x; | |
} else { | |
str += ' ' + inspect(x); | |
} | |
} | |
return str; | |
}; | |
// Mark that a method should not be used. | |
// Returns a modified function which warns once by default. | |
// If --no-deprecation is set, then it is a no-op. | |
exports.deprecate = function(fn, msg) { | |
// Allow for deprecating things in the process of starting up. | |
if (isUndefined(global.process)) { | |
return function() { | |
return exports.deprecate(fn, msg).apply(this, arguments); | |
}; | |
} | |
if (process.noDeprecation === true) { | |
return fn; | |
} | |
var warned = false; | |
function deprecated() { | |
if (!warned) { | |
if (process.throwDeprecation) { | |
throw new Error(msg); | |
} else if (process.traceDeprecation) { | |
console.trace(msg); | |
} else { | |
console.error(msg); | |
} | |
warned = true; | |
} | |
return fn.apply(this, arguments); | |
} | |
return deprecated; | |
}; | |
var debugs = {}; | |
var debugEnviron; | |
exports.debuglog = function(set) { | |
if (isUndefined(debugEnviron)) | |
debugEnviron = process.env.NODE_DEBUG || ''; | |
set = set.toUpperCase(); | |
if (!debugs[set]) { | |
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { | |
var pid = process.pid; | |
debugs[set] = function() { | |
var msg = exports.format.apply(exports, arguments); | |
console.error('%s %d: %s', set, pid, msg); | |
}; | |
} else { | |
debugs[set] = function() {}; | |
} | |
} | |
return debugs[set]; | |
}; | |
/** | |
* Echos the value of a value. Trys to print the value out | |
* in the best way possible given the different types. | |
* | |
* @param {Object} obj The object to print out. | |
* @param {Object} opts Optional options object that alters the output. | |
*/ | |
/* legacy: obj, showHidden, depth, colors*/ | |
function inspect(obj, opts) { | |
// default options | |
var ctx = { | |
seen: [], | |
stylize: stylizeNoColor | |
}; | |
// legacy... | |
if (arguments.length >= 3) ctx.depth = arguments[2]; | |
if (arguments.length >= 4) ctx.colors = arguments[3]; | |
if (isBoolean(opts)) { | |
// legacy... | |
ctx.showHidden = opts; | |
} else if (opts) { | |
// got an "options" object | |
exports._extend(ctx, opts); | |
} | |
// set default options | |
if (isUndefined(ctx.showHidden)) ctx.showHidden = false; | |
if (isUndefined(ctx.depth)) ctx.depth = 2; | |
if (isUndefined(ctx.colors)) ctx.colors = false; | |
if (isUndefined(ctx.customInspect)) ctx.customInspect = true; | |
if (ctx.colors) ctx.stylize = stylizeWithColor; | |
return formatValue(ctx, obj, ctx.depth); | |
} | |
exports.inspect = inspect; | |
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics | |
inspect.colors = { | |
'bold' : [1, 22], | |
'italic' : [3, 23], | |
'underline' : [4, 24], | |
'inverse' : [7, 27], | |
'white' : [37, 39], | |
'grey' : [90, 39], | |
'black' : [30, 39], | |
'blue' : [34, 39], | |
'cyan' : [36, 39], | |
'green' : [32, 39], | |
'magenta' : [35, 39], | |
'red' : [31, 39], | |
'yellow' : [33, 39] | |
}; | |
// Don't use 'blue' not visible on cmd.exe | |
inspect.styles = { | |
'special': 'cyan', | |
'number': 'yellow', | |
'boolean': 'yellow', | |
'undefined': 'grey', | |
'null': 'bold', | |
'string': 'green', | |
'date': 'magenta', | |
// "name": intentionally not styling | |
'regexp': 'red' | |
}; | |
function stylizeWithColor(str, styleType) { | |
var style = inspect.styles[styleType]; | |
if (style) { | |
return '\u001b[' + inspect.colors[style][0] + 'm' + str + | |
'\u001b[' + inspect.colors[style][1] + 'm'; | |
} else { | |
return str; | |
} | |
} | |
function stylizeNoColor(str, styleType) { | |
return str; | |
} | |
function arrayToHash(array) { | |
var hash = {}; | |
array.forEach(function(val, idx) { | |
hash[val] = true; | |
}); | |
return hash; | |
} | |
function formatValue(ctx, value, recurseTimes) { | |
// Provide a hook for user-specified inspect functions. | |
// Check that value is an object with an inspect function on it | |
if (ctx.customInspect && | |
value && | |
isFunction(value.inspect) && | |
// Filter out the util module, it's inspect function is special | |
value.inspect !== exports.inspect && | |
// Also filter out any prototype objects using the circular check. | |
!(value.constructor && value.constructor.prototype === value)) { | |
var ret = value.inspect(recurseTimes, ctx); | |
if (!isString(ret)) { | |
ret = formatValue(ctx, ret, recurseTimes); | |
} | |
return ret; | |
} | |
// Primitive types cannot have properties | |
var primitive = formatPrimitive(ctx, value); | |
if (primitive) { | |
return primitive; | |
} | |
// Look up the keys of the object. | |
var keys = Object.keys(value); | |
var visibleKeys = arrayToHash(keys); | |
if (ctx.showHidden) { | |
keys = Object.getOwnPropertyNames(value); | |
} | |
// IE doesn't make error fields non-enumerable | |
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx | |
if (isError(value) | |
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { | |
return formatError(value); | |
} | |
// Some type of object without properties can be shortcutted. | |
if (keys.length === 0) { | |
if (isFunction(value)) { | |
var name = value.name ? ': ' + value.name : ''; | |
return ctx.stylize('[Function' + name + ']', 'special'); | |
} | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} | |
if (isDate(value)) { | |
return ctx.stylize(Date.prototype.toString.call(value), 'date'); | |
} | |
if (isError(value)) { | |
return formatError(value); | |
} | |
} | |
var base = '', array = false, braces = ['{', '}']; | |
// Make Array say that they are Array | |
if (isArray(value)) { | |
array = true; | |
braces = ['[', ']']; | |
} | |
// Make functions say that they are functions | |
if (isFunction(value)) { | |
var n = value.name ? ': ' + value.name : ''; | |
base = ' [Function' + n + ']'; | |
} | |
// Make RegExps say that they are RegExps | |
if (isRegExp(value)) { | |
base = ' ' + RegExp.prototype.toString.call(value); | |
} | |
// Make dates with properties first say the date | |
if (isDate(value)) { | |
base = ' ' + Date.prototype.toUTCString.call(value); | |
} | |
// Make error with message first say the error | |
if (isError(value)) { | |
base = ' ' + formatError(value); | |
} | |
if (keys.length === 0 && (!array || value.length == 0)) { | |
return braces[0] + base + braces[1]; | |
} | |
if (recurseTimes < 0) { | |
if (isRegExp(value)) { | |
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); | |
} else { | |
return ctx.stylize('[Object]', 'special'); | |
} | |
} | |
ctx.seen.push(value); | |
var output; | |
if (array) { | |
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); | |
} else { | |
output = keys.map(function(key) { | |
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); | |
}); | |
} | |
ctx.seen.pop(); | |
return reduceToSingleString(output, base, braces); | |
} | |
function formatPrimitive(ctx, value) { | |
if (isUndefined(value)) | |
return ctx.stylize('undefined', 'undefined'); | |
if (isString(value)) { | |
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') | |
.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') + '\''; | |
return ctx.stylize(simple, 'string'); | |
} | |
if (isNumber(value)) | |
return ctx.stylize('' + value, 'number'); | |
if (isBoolean(value)) | |
return ctx.stylize('' + value, 'boolean'); | |
// For some reason typeof null is "object", so special case here. | |
if (isNull(value)) | |
return ctx.stylize('null', 'null'); | |
} | |
function formatError(value) { | |
return '[' + Error.prototype.toString.call(value) + ']'; | |
} | |
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { | |
var output = []; | |
for (var i = 0, l = value.length; i < l; ++i) { | |
if (hasOwnProperty(value, String(i))) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
String(i), true)); | |
} else { | |
output.push(''); | |
} | |
} | |
keys.forEach(function(key) { | |
if (!key.match(/^\d+$/)) { | |
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, | |
key, true)); | |
} | |
}); | |
return output; | |
} | |
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { | |
var name, str, desc; | |
desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; | |
if (desc.get) { | |
if (desc.set) { | |
str = ctx.stylize('[Getter/Setter]', 'special'); | |
} else { | |
str = ctx.stylize('[Getter]', 'special'); | |
} | |
} else { | |
if (desc.set) { | |
str = ctx.stylize('[Setter]', 'special'); | |
} | |
} | |
if (!hasOwnProperty(visibleKeys, key)) { | |
name = '[' + key + ']'; | |
} | |
if (!str) { | |
if (ctx.seen.indexOf(desc.value) < 0) { | |
if (isNull(recurseTimes)) { | |
str = formatValue(ctx, desc.value, null); | |
} else { | |
str = formatValue(ctx, desc.value, recurseTimes - 1); | |
} | |
if (str.indexOf('\n') > -1) { | |
if (array) { | |
str = str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n').substr(2); | |
} else { | |
str = '\n' + str.split('\n').map(function(line) { | |
return ' ' + line; | |
}).join('\n'); | |
} | |
} | |
} else { | |
str = ctx.stylize('[Circular]', 'special'); | |
} | |
} | |
if (isUndefined(name)) { | |
if (array && key.match(/^\d+$/)) { | |
return str; | |
} | |
name = JSON.stringify('' + key); | |
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { | |
name = name.substr(1, name.length - 2); | |
name = ctx.stylize(name, 'name'); | |
} else { | |
name = name.replace(/'/g, "\\'") | |
.replace(/\\"/g, '"') | |
.replace(/(^"|"$)/g, "'"); | |
name = ctx.stylize(name, 'string'); | |
} | |
} | |
return name + ': ' + str; | |
} | |
function reduceToSingleString(output, base, braces) { | |
var numLinesEst = 0; | |
var length = output.reduce(function(prev, cur) { | |
numLinesEst++; | |
if (cur.indexOf('\n') >= 0) numLinesEst++; | |
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; | |
}, 0); | |
if (length > 60) { | |
return braces[0] + | |
(base === '' ? '' : base + '\n ') + | |
' ' + | |
output.join(',\n ') + | |
' ' + | |
braces[1]; | |
} | |
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; | |
} | |
// NOTE: These type checking functions intentionally don't use `instanceof` | |
// because it is fragile and can be easily faked with `Object.create()`. | |
function isArray(ar) { | |
return Array.isArray(ar); | |
} | |
exports.isArray = isArray; | |
function isBoolean(arg) { | |
return typeof arg === 'boolean'; | |
} | |
exports.isBoolean = isBoolean; | |
function isNull(arg) { | |
return arg === null; | |
} | |
exports.isNull = isNull; | |
function isNullOrUndefined(arg) { | |
return arg == null; | |
} | |
exports.isNullOrUndefined = isNullOrUndefined; | |
function isNumber(arg) { | |
return typeof arg === 'number'; | |
} | |
exports.isNumber = isNumber; | |
function isString(arg) { | |
return typeof arg === 'string'; | |
} | |
exports.isString = isString; | |
function isSymbol(arg) { | |
return typeof arg === 'symbol'; | |
} | |
exports.isSymbol = isSymbol; | |
function isUndefined(arg) { | |
return arg === void 0; | |
} | |
exports.isUndefined = isUndefined; | |
function isRegExp(re) { | |
return isObject(re) && objectToString(re) === '[object RegExp]'; | |
} | |
exports.isRegExp = isRegExp; | |
function isObject(arg) { | |
return typeof arg === 'object' && arg !== null; | |
} | |
exports.isObject = isObject; | |
function isDate(d) { | |
return isObject(d) && objectToString(d) === '[object Date]'; | |
} | |
exports.isDate = isDate; | |
function isError(e) { | |
return isObject(e) && | |
(objectToString(e) === '[object Error]' || e instanceof Error); | |
} | |
exports.isError = isError; | |
function isFunction(arg) { | |
return typeof arg === 'function'; | |
} | |
exports.isFunction = isFunction; | |
function isPrimitive(arg) { | |
return arg === null || | |
typeof arg === 'boolean' || | |
typeof arg === 'number' || | |
typeof arg === 'string' || | |
typeof arg === 'symbol' || // ES6 symbol | |
typeof arg === 'undefined'; | |
} | |
exports.isPrimitive = isPrimitive; | |
exports.isBuffer = require('./support/isBuffer'); | |
function objectToString(o) { | |
return Object.prototype.toString.call(o); | |
} | |
function pad(n) { | |
return n < 10 ? '0' + n.toString(10) : n.toString(10); | |
} | |
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', | |
'Oct', 'Nov', 'Dec']; | |
// 26 Feb 16:19:34 | |
function timestamp() { | |
var d = new Date(); | |
var time = [pad(d.getHours()), | |
pad(d.getMinutes()), | |
pad(d.getSeconds())].join(':'); | |
return [d.getDate(), months[d.getMonth()], time].join(' '); | |
} | |
// log is just a thin wrapper to console.log that prepends a timestamp | |
exports.log = function() { | |
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); | |
}; | |
/** | |
* Inherit the prototype methods from one constructor into another. | |
* | |
* The Function.prototype.inherits from lang.js rewritten as a standalone | |
* function (not on Function.prototype). NOTE: If this file is to be loaded | |
* during bootstrapping this function needs to be rewritten using some native | |
* functions as prototype setup using normal JavaScript does not work as | |
* expected during bootstrapping (see mirror.js in r114903). | |
* | |
* @param {function} ctor Constructor function which needs to inherit the | |
* prototype. | |
* @param {function} superCtor Constructor function to inherit prototype from. | |
*/ | |
exports.inherits = require('inherits'); | |
exports._extend = function(origin, add) { | |
// Don't do anything if add isn't an object | |
if (!add || !isObject(add)) return origin; | |
var keys = Object.keys(add); | |
var i = keys.length; | |
while (i--) { | |
origin[keys[i]] = add[keys[i]]; | |
} | |
return origin; | |
}; | |
function hasOwnProperty(obj, prop) { | |
return Object.prototype.hasOwnProperty.call(obj, prop); | |
} | |
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{"./support/isBuffer":31,"_process":16,"inherits":13}]},{},[2])(2) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment