├── test ├── data │ ├── small.csv │ ├── invalid-blank.csv │ ├── empty-shapefile │ │ └── empty.shp │ ├── invalid-type.json │ ├── invalid.tilejson │ ├── bom.kml │ ├── invalid_geometries.csv │ ├── blank_rows.csv │ ├── valid.tm2z │ ├── atiff.tif.gz │ ├── valid.mbtiles │ ├── valid-bigtiff.tif │ ├── valid-paxheader.gz │ ├── valid-serialtiles.gz │ ├── valid-not-csv.serialtiles.gz │ ├── valid-geometries.json │ ├── valid-coordinates.json │ ├── valid-empty_rows.csv │ ├── valid-one-field.csv │ ├── crs-geojson.json │ ├── valid-points.csv │ ├── valid.tilejson │ ├── valid-lines.csv │ ├── xtracharacters.json │ ├── valid-nested-type.json │ ├── invalid-empty.tm2z │ └── invalid-malformed.tm2z └── index.test.js ├── .gitignore ├── .npmignore ├── mapbox-file-sniff.jpg ├── .eslintrc ├── lib └── invalid.js ├── bin └── mapbox-file-sniff.js ├── package.json ├── README.md ├── CHANGELOG.md └── index.js /test/data/small.csv: -------------------------------------------------------------------------------- 1 | s 2 | -------------------------------------------------------------------------------- /test/data/invalid-blank.csv: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/data/empty-shapefile/empty.shp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/data/invalid-type.json: -------------------------------------------------------------------------------- 1 | {"type": 42} 2 | -------------------------------------------------------------------------------- /test/data/invalid.tilejson: -------------------------------------------------------------------------------- 1 | { 2 | "invalid": true 3 | } -------------------------------------------------------------------------------- /test/data/bom.kml: -------------------------------------------------------------------------------- 1 | { 60 | zlib.gunzip(buffer, {finishFlush: zlib.Z_SYNC_FLUSH }, (err, data) => { 61 | if (err) return reject(err); 62 | return resolve(data); 63 | }); 64 | }); 65 | }; 66 | 67 | /** 68 | * Check if a buffer is compressed. 69 | * From: http://www.zlib.org/rfc-gzip.html 70 | * The three first value of the buffer should be: 71 | * - ID1 = 31 (0x1f) 72 | * - ID2 = 139 (0x8b) 73 | * - CM = 8 (0x08) 74 | * 75 | * @param {buffer} buffer - 76 | * @returns {bool} 77 | */ 78 | function isGzip (buffer) { 79 | return buffer[0] === 0x1F 80 | && buffer[1] === 0x8B; 81 | } 82 | 83 | 84 | function detect(buffer, callback) { 85 | 86 | if (isGzip(buffer)) { 87 | 88 | gunzipPromise(buffer) 89 | .then(data => { 90 | //check for tm2z 91 | if (data.toString('ascii', 257, 262) === 'ustar') return callback(null, 'tm2z'); 92 | 93 | //check for serial tiles 94 | const head = data.slice(0, 50); 95 | if (head.toString().indexOf('JSONBREAKFASTTIME') === 0) return callback(null, 'serialtiles'); 96 | 97 | //check for tif+gz 98 | if ( 99 | (data.slice(0, 2).toString() === 'II' || data.slice(0, 2).toString() === 'MM') 100 | && ((data[2] === 42) || data[3] === 42 || data[2] === 43)) { 101 | return callback(null, 'tif+gz'); 102 | } 103 | return callback(invalid('Unknown filetype')); 104 | }) 105 | .catch(err => { 106 | return callback(invalid('Unknown filetype')); 107 | }) 108 | 109 | } else { 110 | 111 | var header = buffer.toString().substring(0, 400); 112 | 113 | // check for topojson/geojson 114 | if (header.trim().indexOf('{') == 0) { 115 | 116 | // Remove spaces 117 | var str = JSON.stringify(header); 118 | var nospaces = str.replace(/\s/g, ''); 119 | header = JSON.parse(nospaces); 120 | 121 | if (header.indexOf('\"tilejson\":') !== -1) return callback(null, 'tilejson'); 122 | if ((header.indexOf('\"arcs\":') !== -1) || (header.indexOf('\"objects\":') !== -1)) return callback(null, 'topojson'); 123 | if ((header.indexOf('\"features\":') !== -1) || (header.indexOf('\"geometries\":') !== -1) || (header.indexOf('\"coordinates\":') !== -1)) return callback(null, 'geojson'); 124 | if (header.indexOf('\"type\":') !== -1) { 125 | var m = /"type":\s?"(.+?)"/.exec(header); 126 | if (!m) { 127 | return callback(invalid('Unknown filetype')); 128 | } 129 | if (m[1] === 'Topology') return callback(null, 'topojson'); 130 | if (m[1] === 'Feature' || 131 | m[1] === 'FeatureCollection' || 132 | m[1] === 'Point' || 133 | m[1] === 'MultiPoint' || 134 | m[1] === 'LineString' || 135 | m[1] === 'MultiLineString' || 136 | m[1] === 'Polygon' || 137 | m[1] === 'MultiPolygon' || 138 | m[1] === 'GeometryCollection') return callback(null, 'geojson'); 139 | } 140 | return callback(invalid('Unknown filetype')); 141 | } 142 | 143 | var head = header.substring(0, 100); 144 | if (head.indexOf('SQLite format 3') === 0) { 145 | return callback(null, 'mbtiles'); 146 | } 147 | if ((head[0] + head[1]) === 'PK') { 148 | return callback(null, 'zip'); 149 | } 150 | // check if geotiff/bigtiff 151 | // matches gdal validation logic: https://github.com/OSGeo/gdal/blob/trunk/gdal/frmts/gtiff/geotiff.cpp#L6892-L6893 152 | if ((head.slice(0, 2).toString() === 'II' || head.slice(0, 2).toString() === 'MM') && ((buffer[2] === 42) || buffer[3] === 42 || buffer[2] === 43)) { 153 | return callback(null, 'tif'); 154 | } 155 | // take into account BOM char at index 0 156 | if (((head.indexOf(' 32 && buffer.readUInt32BE(0) === 9994) { 168 | return callback(null, 'shp'); 169 | } 170 | 171 | // Check for geocsv 172 | if (isgeocsv(buffer)) { 173 | return callback(null, 'csv'); 174 | } 175 | 176 | // If not one of the test is valid then return "Unknown" 177 | return callback(invalid('Unknown filetype')); 178 | } 179 | } 180 | 181 | function getProtocol(type) { 182 | var mapping = { 183 | csv: 'omnivore:', 184 | mbtiles: 'mbtiles:', 185 | shp: 'omnivore:', 186 | zip: 'omnivore:', 187 | tif: 'omnivore:', 188 | 'tif+gz': 'omnivore:', 189 | vrt: 'omnivore:', 190 | geojson: 'omnivore:', 191 | topojson: 'omnivore:', 192 | kml: 'omnivore:', 193 | gpx: 'omnivore:', 194 | tilejson: 'tilejson:', 195 | tm2z: 'tm2z:', 196 | serialtiles: 'serialtiles:' 197 | }; 198 | 199 | return mapping[type]; 200 | } 201 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | var tape = require('tape'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var exec = require('child_process').exec; 5 | var testData = path.dirname(require.resolve('mapnik-test-data')); 6 | var sniffer = require('../index.js'); 7 | 8 | function getBuffer(path) { 9 | var buffer; 10 | try { 11 | fs.statSync(path); 12 | buffer = new Buffer(512); 13 | var fd = fs.openSync(path, 'r'); 14 | fs.readSync(fd, buffer, 0, 512, 0); 15 | fs.closeSync(fd); 16 | return buffer; 17 | } catch (err) { 18 | throw err; 19 | } 20 | } 21 | 22 | /** 23 | * Testing filesniffer 24 | */ 25 | tape('error: no callback', function(assert) { 26 | var filepath = testData + '/data/kml/1week_earthquake.kml'; 27 | try { 28 | sniffer.fromFile(filepath); 29 | assert.fail(); // do not get here 30 | } catch (err) { 31 | assert.ok(err); 32 | assert.equal(err.message, 'Invalid callback. Must be a function.', 'expected error message'); 33 | assert.end(); 34 | } 35 | }); 36 | tape('error: file does not exist', function(assert) { 37 | var filepath = 'not/here/file.geojson'; 38 | sniffer.fromFile(filepath, function(err, result) { 39 | assert.ok(err); 40 | assert.equal(err.code, 'ENOENT'); 41 | assert.equal(err.message, 'ENOENT: no such file or directory, open \'not/here/file.geojson\'', 'expected message'); 42 | assert.end(); 43 | }); 44 | }); 45 | tape('error: small file', function(assert) { 46 | var filepath = path.resolve('./test/data/small.csv'); 47 | assert.notOk(Buffer.isBuffer(filepath)); 48 | sniffer.fromFile(filepath, function(err, result) { 49 | assert.ok(err); 50 | assert.equal(err.message, 'File too small', 'expected error message'); 51 | assert.equal(err.code, 'EINVALID', 'expected error code'); 52 | assert.end(); 53 | }); 54 | }); 55 | tape('error: zero byte file', function(assert) { 56 | var filepath = path.resolve('./test/data/empty-shapefile/empty.shp'); 57 | assert.notOk(Buffer.isBuffer(filepath)); 58 | sniffer.fromFile(filepath, function(err, result) { 59 | assert.ok(err); 60 | assert.equal(err.message, 'File is zero bytes.', 'expected error message'); 61 | assert.equal(err.code, 'EINVALID', 'expected error code'); 62 | assert.end(); 63 | }); 64 | }); 65 | 66 | tape('[KML] success: file path', function(assert) { 67 | var filepath = testData + '/data/kml/1week_earthquake.kml'; 68 | assert.notOk(Buffer.isBuffer(filepath)); 69 | sniffer.fromFile(filepath, function(err, result) { 70 | assert.notOk(err, 'no error'); 71 | assert.equal(result.type, 'kml', 'expected file type'); 72 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 73 | assert.end(); 74 | }); 75 | }); 76 | tape('[KML] success: buffer', function(assert) { 77 | var filepath = testData + '/data/kml/1week_earthquake.kml'; 78 | var buffer = getBuffer(filepath); 79 | assert.ok(Buffer.isBuffer(buffer)); 80 | sniffer.fromBuffer(buffer, function(err, result) { 81 | assert.notOk(err, 'no error'); 82 | assert.equal(result.type, 'kml', 'expected file type'); 83 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 84 | assert.end(); 85 | }); 86 | }); 87 | tape('[KML BOM] success: file path', function(assert) { 88 | var filepath = path.resolve('./test/data/bom.kml'); 89 | assert.notOk(Buffer.isBuffer(filepath)); 90 | sniffer.fromFile(filepath, function(err, result) { 91 | assert.notOk(err, 'no error'); 92 | assert.equal(result.type, 'kml', 'expected file type'); 93 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 94 | assert.end(); 95 | }); 96 | }); 97 | tape('[KML BOM] success: buffer', function(assert) { 98 | var filepath = path.resolve('./test/data/bom.kml'); 99 | var buffer = getBuffer(filepath); 100 | assert.ok(Buffer.isBuffer(buffer)); 101 | sniffer.fromBuffer(buffer, function(err, result) { 102 | assert.notOk(err, 'no error'); 103 | assert.equal(result.type, 'kml', 'expected file type'); 104 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 105 | assert.end(); 106 | }); 107 | }); 108 | 109 | tape('[VRT] success: file path', function(assert) { 110 | var filepath = testData + '/data/vrt/sample.vrt'; 111 | assert.notOk(Buffer.isBuffer(filepath)); 112 | sniffer.fromFile(filepath, function(err, result) { 113 | assert.notOk(err, 'no error'); 114 | assert.equal(result.type, 'vrt', 'expected file type'); 115 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 116 | assert.end(); 117 | }); 118 | }); 119 | tape('[VRT] success: buffer', function(assert) { 120 | var filepath = testData + '/data/vrt/sample.vrt'; 121 | var buffer = getBuffer(filepath); 122 | assert.ok(Buffer.isBuffer(buffer)); 123 | sniffer.fromBuffer(buffer, function(err, result) { 124 | assert.notOk(err, 'no error'); 125 | assert.equal(result.type, 'vrt', 'expected file type'); 126 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 127 | assert.end(); 128 | }); 129 | }); 130 | 131 | tape('[GEOJSON - basic] success: file path', function(assert) { 132 | var filepath = testData + '/data/geojson/DC_polygon.geo.json'; 133 | assert.notOk(Buffer.isBuffer(filepath)); 134 | sniffer.fromFile(filepath, function(err, result) { 135 | assert.notOk(err, 'no error'); 136 | assert.equal(result.type, 'geojson', 'expected file type'); 137 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 138 | assert.end(); 139 | }); 140 | }); 141 | tape('[GEOJSON - basic] success: buffer', function(assert) { 142 | var filepath = testData + '/data/geojson/DC_polygon.geo.json'; 143 | var buffer = getBuffer(filepath); 144 | assert.ok(Buffer.isBuffer(buffer)); 145 | sniffer.fromBuffer(buffer, function(err, result) { 146 | assert.notOk(err, 'no error'); 147 | assert.equal(result.type, 'geojson', 'expected file type'); 148 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 149 | assert.end(); 150 | }); 151 | }); 152 | 153 | tape('[GEOJSON - crs] success: file path', function(assert) { 154 | var filepath = path.resolve('./test/data/crs-geojson.json'); 155 | assert.notOk(Buffer.isBuffer(filepath)); 156 | sniffer.fromFile(filepath, function(err, result) { 157 | assert.notOk(err, 'no error'); 158 | assert.equal(result.type, 'geojson', 'expected file type'); 159 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 160 | assert.end(); 161 | }); 162 | }); 163 | tape('[GEOJSON - crs] success: buffer', function(assert) { 164 | var filepath = path.resolve('./test/data/crs-geojson.json'); 165 | var buffer = getBuffer(filepath); 166 | assert.ok(Buffer.isBuffer(buffer)); 167 | sniffer.fromBuffer(buffer, function(err, result) { 168 | assert.notOk(err, 'no error'); 169 | assert.equal(result.type, 'geojson', 'expected file type'); 170 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 171 | assert.end(); 172 | }); 173 | }); 174 | 175 | tape('[GEOJSON - geometries] success: file path', function(assert) { 176 | var filepath = path.resolve('./test/data/valid-geometries.json'); 177 | assert.notOk(Buffer.isBuffer(filepath)); 178 | sniffer.fromFile(filepath, function(err, result) { 179 | assert.notOk(err, 'no error'); 180 | assert.equal(result.type, 'geojson', 'expected file type'); 181 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 182 | assert.end(); 183 | }); 184 | }); 185 | tape('[GEOJSON - geometries] success: buffer', function(assert) { 186 | var filepath = path.resolve('./test/data/valid-geometries.json'); 187 | var buffer = getBuffer(filepath); 188 | assert.ok(Buffer.isBuffer(buffer)); 189 | sniffer.fromBuffer(buffer, function(err, result) { 190 | assert.notOk(err, 'no error'); 191 | assert.equal(result.type, 'geojson', 'expected file type'); 192 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 193 | assert.end(); 194 | }); 195 | }); 196 | 197 | tape('[GEOJSON - coordinates] success: file path', function(assert) { 198 | var filepath = path.resolve('./test/data/valid-coordinates.json'); 199 | assert.notOk(Buffer.isBuffer(filepath)); 200 | sniffer.fromFile(filepath, function(err, result) { 201 | assert.notOk(err, 'no error'); 202 | assert.equal(result.type, 'geojson', 'expected file type'); 203 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 204 | assert.end(); 205 | }); 206 | }); 207 | tape('[GEOJSON - coordinates] success: buffer', function(assert) { 208 | var filepath = path.resolve('./test/data/valid-coordinates.json'); 209 | var buffer = getBuffer(filepath); 210 | assert.ok(Buffer.isBuffer(buffer)); 211 | sniffer.fromBuffer(buffer, function(err, result) { 212 | assert.notOk(err, 'no error'); 213 | assert.equal(result.type, 'geojson', 'expected file type'); 214 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 215 | assert.end(); 216 | }); 217 | }); 218 | 219 | tape('[GEOJSON - extra characters] success: file path', function(assert) { 220 | var filepath = path.resolve('./test/data/xtracharacters.json'); 221 | assert.notOk(Buffer.isBuffer(filepath)); 222 | sniffer.fromFile(filepath, function(err, result) { 223 | assert.notOk(err, 'no error'); 224 | assert.equal(result.type, 'geojson', 'expected file type'); 225 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 226 | assert.end(); 227 | }); 228 | }); 229 | tape('[GEOJSON - extra characters] success: buffer', function(assert) { 230 | var filepath = path.resolve('./test/data/xtracharacters.json'); 231 | var buffer = getBuffer(filepath); 232 | assert.ok(Buffer.isBuffer(buffer)); 233 | sniffer.fromBuffer(buffer, function(err, result) { 234 | assert.notOk(err, 'no error'); 235 | assert.equal(result.type, 'geojson', 'expected file type'); 236 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 237 | assert.end(); 238 | }); 239 | }); 240 | 241 | tape('[GEOJSON - valid nested type] success: file path', function(assert) { 242 | var filepath = path.resolve('./test/data/valid-nested-type.json'); 243 | assert.notOk(Buffer.isBuffer(filepath)); 244 | sniffer.fromFile(filepath, function(err, result) { 245 | assert.notOk(err, 'no error'); 246 | assert.equal(result.type, 'geojson', 'expected file type'); 247 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 248 | assert.end(); 249 | }); 250 | }); 251 | tape('[GEOJSON - valid nested type] success: buffer', function(assert) { 252 | var filepath = path.resolve('./test/data/valid-nested-type.json'); 253 | var buffer = getBuffer(filepath); 254 | assert.ok(Buffer.isBuffer(buffer)); 255 | sniffer.fromBuffer(buffer, function(err, result) { 256 | assert.notOk(err, 'no error'); 257 | assert.equal(result.type, 'geojson', 'expected file type'); 258 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 259 | assert.end(); 260 | }); 261 | }); 262 | tape('[GEOJSON - invalid type value] errors', function(assert) { 263 | var filepath = path.resolve('./test/data/invalid-type.json'); 264 | var buffer = getBuffer(filepath); 265 | assert.ok(Buffer.isBuffer(buffer)); 266 | sniffer.fromBuffer(buffer, function(err, result) { 267 | assert.ok(err); 268 | assert.equal(err.message, 'Unknown filetype', 'expected error message'); 269 | assert.equal(err.code, 'EINVALID', 'expected error code'); 270 | assert.end(); 271 | }); 272 | }); 273 | 274 | tape('[TOPOJSON] success: file path', function(assert) { 275 | var filepath = testData + '/data/topojson/topo.json'; 276 | assert.notOk(Buffer.isBuffer(filepath)); 277 | sniffer.fromFile(filepath, function(err, result) { 278 | assert.notOk(err, 'no error'); 279 | assert.equal(result.type, 'topojson', 'expected file type'); 280 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 281 | assert.end(); 282 | }); 283 | }); 284 | tape('[TOPOJSON] success: buffer', function(assert) { 285 | var filepath = testData + '/data/topojson/topo.json'; 286 | var buffer = getBuffer(filepath); 287 | assert.ok(Buffer.isBuffer(buffer)); 288 | sniffer.fromBuffer(buffer, function(err, result) { 289 | assert.notOk(err, 'no error'); 290 | assert.equal(result.type, 'topojson', 'expected file type'); 291 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 292 | assert.end(); 293 | }); 294 | }); 295 | 296 | tape('[GPX] success: file path', function(assert) { 297 | var filepath = testData + '/data/gpx/fells_loop.gpx'; 298 | assert.notOk(Buffer.isBuffer(filepath)); 299 | sniffer.fromFile(filepath, function(err, result) { 300 | assert.notOk(err, 'no error'); 301 | assert.equal(result.type, 'gpx', 'expected file type'); 302 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 303 | assert.end(); 304 | }); 305 | }); 306 | tape('[GPX] success: buffer', function(assert) { 307 | var filepath = testData + '/data/gpx/fells_loop.gpx'; 308 | var buffer = getBuffer(filepath); 309 | assert.ok(Buffer.isBuffer(buffer)); 310 | sniffer.fromBuffer(buffer, function(err, result) { 311 | assert.notOk(err, 'no error'); 312 | assert.equal(result.type, 'gpx', 'expected file type'); 313 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 314 | assert.end(); 315 | }); 316 | }); 317 | 318 | tape('[ZIP] success: file path', function(assert) { 319 | var filepath = testData + '/data/zip/us_states.zip'; 320 | assert.notOk(Buffer.isBuffer(filepath)); 321 | sniffer.fromFile(filepath, function(err, result) { 322 | assert.notOk(err, 'no error'); 323 | assert.equal(result.type, 'zip', 'expected file type'); 324 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 325 | assert.end(); 326 | }); 327 | }); 328 | tape('[ZIP] success: buffer', function(assert) { 329 | var filepath = testData + '/data/zip/us_states.zip'; 330 | var buffer = getBuffer(filepath); 331 | assert.ok(Buffer.isBuffer(buffer)); 332 | sniffer.fromBuffer(buffer, function(err, result) { 333 | assert.notOk(err, 'no error'); 334 | assert.equal(result.type, 'zip', 'expected file type'); 335 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 336 | assert.end(); 337 | }); 338 | }); 339 | 340 | tape('[SHP] success: file path', function(assert) { 341 | var filepath = testData + '/data/shp/world_merc/world_merc.shp'; 342 | assert.notOk(Buffer.isBuffer(filepath)); 343 | sniffer.fromFile(filepath, function(err, result) { 344 | assert.notOk(err, 'no error'); 345 | assert.equal(result.type, 'shp', 'expected file type'); 346 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 347 | assert.end(); 348 | }); 349 | }); 350 | tape('[SHP] success: buffer', function(assert) { 351 | var filepath = testData + '/data/shp/world_merc/world_merc.shp'; 352 | var buffer = getBuffer(filepath); 353 | assert.ok(Buffer.isBuffer(buffer)); 354 | sniffer.fromBuffer(buffer, function(err, result) { 355 | assert.notOk(err, 'no error'); 356 | assert.equal(result.type, 'shp', 'expected file type'); 357 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 358 | assert.end(); 359 | }); 360 | }); 361 | 362 | tape('[TIF] success: file path', function(assert) { 363 | var filepath = testData + '/data/geotiff/sample.tif'; 364 | assert.notOk(Buffer.isBuffer(filepath)); 365 | sniffer.fromFile(filepath, function(err, result) { 366 | assert.notOk(err, 'no error'); 367 | assert.equal(result.type, 'tif', 'expected file type'); 368 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 369 | assert.end(); 370 | }); 371 | }); 372 | tape('[TIF] success: buffer', function(assert) { 373 | var filepath = testData + '/data/geotiff/sample.tif'; 374 | var buffer = getBuffer(filepath); 375 | assert.ok(Buffer.isBuffer(buffer)); 376 | sniffer.fromBuffer(buffer, function(err, result) { 377 | assert.notOk(err, 'no error'); 378 | assert.equal(result.type, 'tif', 'expected file type'); 379 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 380 | assert.end(); 381 | }); 382 | }); 383 | 384 | tape('[TIF - gzip] success: file path', function(assert) { 385 | var filepath = path.resolve('./test/data/atiff.tif.gz'); 386 | assert.notOk(Buffer.isBuffer(filepath)); 387 | sniffer.fromFile(filepath, function(err, result) { 388 | assert.notOk(err, 'no error'); 389 | assert.equal(result.type, 'tif+gz', 'expected file type'); 390 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 391 | assert.end(); 392 | }); 393 | }); 394 | tape('[TIF - gzip] success: buffer', function(assert) { 395 | var filepath = path.resolve('./test/data/atiff.tif.gz'); 396 | var buffer = getBuffer(filepath); 397 | assert.ok(Buffer.isBuffer(buffer)); 398 | sniffer.fromBuffer(buffer, function(err, result) { 399 | assert.notOk(err, 'no error'); 400 | assert.equal(result.type, 'tif+gz', 'expected file type'); 401 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 402 | assert.end(); 403 | }); 404 | }); 405 | 406 | tape('[BIGTIFF] success: file path', function(assert) { 407 | var filepath = path.resolve('./test/data/valid-bigtiff.tif'); 408 | assert.notOk(Buffer.isBuffer(filepath)); 409 | sniffer.fromFile(filepath, function(err, result) { 410 | assert.notOk(err, 'no error'); 411 | assert.equal(result.type, 'tif', 'expected file type'); 412 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 413 | assert.end(); 414 | }); 415 | }); 416 | tape('[BIGTIFF] success: buffer', function(assert) { 417 | var filepath = path.resolve('./test/data/valid-bigtiff.tif'); 418 | var buffer = getBuffer(filepath); 419 | assert.ok(Buffer.isBuffer(buffer)); 420 | sniffer.fromBuffer(buffer, function(err, result) { 421 | assert.notOk(err, 'no error'); 422 | assert.equal(result.type, 'tif', 'expected file type'); 423 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 424 | assert.end(); 425 | }); 426 | }); 427 | 428 | tape('[MBTILES] success: file path', function(assert) { 429 | var filepath = path.resolve('./test/data/valid.mbtiles'); 430 | assert.notOk(Buffer.isBuffer(filepath)); 431 | sniffer.fromFile(filepath, function(err, result) { 432 | assert.notOk(err, 'no error'); 433 | assert.equal(result.type, 'mbtiles', 'expected file type'); 434 | assert.equal(result.protocol, 'mbtiles:', 'expected protocol'); 435 | assert.end(); 436 | }); 437 | }); 438 | tape('[MBTILES] success: buffer', function(assert) { 439 | var filepath = path.resolve('./test/data/valid.mbtiles'); 440 | var buffer = getBuffer(filepath); 441 | assert.ok(Buffer.isBuffer(buffer)); 442 | sniffer.fromBuffer(buffer, function(err, result) { 443 | assert.notOk(err, 'no error'); 444 | assert.equal(result.type, 'mbtiles', 'expected file type'); 445 | assert.equal(result.protocol, 'mbtiles:', 'expected protocol'); 446 | assert.end(); 447 | }); 448 | }); 449 | 450 | tape('[TILEJSON - valid] success: file path', function(assert) { 451 | var filepath = path.resolve('./test/data/valid.tilejson'); 452 | assert.notOk(Buffer.isBuffer(filepath)); 453 | sniffer.fromFile(filepath, function(err, result) { 454 | assert.notOk(err, 'no error'); 455 | assert.equal(result.type, 'tilejson', 'expected file type'); 456 | assert.equal(result.protocol, 'tilejson:', 'expected protocol'); 457 | assert.end(); 458 | }); 459 | }); 460 | tape('[TILEJSON - valid] success: buffer', function(assert) { 461 | var filepath = path.resolve('./test/data/valid.tilejson'); 462 | var buffer = getBuffer(filepath); 463 | assert.ok(Buffer.isBuffer(buffer)); 464 | sniffer.fromBuffer(buffer, function(err, result) { 465 | assert.notOk(err, 'no error'); 466 | assert.equal(result.type, 'tilejson', 'expected file type'); 467 | assert.equal(result.protocol, 'tilejson:', 'expected protocol'); 468 | assert.end(); 469 | }); 470 | }); 471 | tape('[TILEJSON - invalid] errors', function(assert) { 472 | var filepath = path.resolve('./test/data/invalid.tilejson'); 473 | var buffer = getBuffer(filepath); 474 | assert.ok(Buffer.isBuffer(buffer)); 475 | sniffer.fromBuffer(buffer, function(err, result) { 476 | assert.ok(err); 477 | assert.equal(err.message, 'Unknown filetype', 'expected file type'); 478 | assert.end(); 479 | }); 480 | }); 481 | 482 | tape('[SERIALTILES] success: file path', function(assert) { 483 | var filepath = path.resolve('./test/data/valid-serialtiles.gz'); 484 | assert.notOk(Buffer.isBuffer(filepath)); 485 | sniffer.fromFile(filepath, function(err, result) { 486 | assert.notOk(err, 'no error'); 487 | assert.equal(result.type, 'serialtiles', 'expected file type'); 488 | assert.equal(result.protocol, 'serialtiles:', 'expected protocol'); 489 | assert.end(); 490 | }); 491 | }); 492 | tape('[SERIALTILES] success: buffer', function(assert) { 493 | var filepath = path.resolve('./test/data/valid-serialtiles.gz'); 494 | var buffer = getBuffer(filepath); 495 | assert.ok(Buffer.isBuffer(buffer)); 496 | sniffer.fromBuffer(buffer, function(err, result) { 497 | assert.notOk(err, 'no error'); 498 | assert.equal(result.type, 'serialtiles', 'expected file type'); 499 | assert.equal(result.protocol, 'serialtiles:', 'expected protocol'); 500 | assert.end(); 501 | }); 502 | }); 503 | 504 | 505 | tape('[SERIALTILES - not csv] success: file path', function(assert) { 506 | var filepath = path.resolve('./test/data/valid-not-csv.serialtiles.gz'); 507 | assert.notOk(Buffer.isBuffer(filepath)); 508 | sniffer.fromFile(filepath, function(err, result) { 509 | assert.notOk(err, 'no error'); 510 | assert.equal(result.type, 'serialtiles', 'expected file type'); 511 | assert.equal(result.protocol, 'serialtiles:', 'expected protocol'); 512 | assert.end(); 513 | }); 514 | }); 515 | tape('[SERIALTILES - not csv] success: buffer', function(assert) { 516 | var filepath = path.resolve('./test/data/valid-not-csv.serialtiles.gz'); 517 | var buffer = getBuffer(filepath); 518 | assert.ok(Buffer.isBuffer(buffer)); 519 | sniffer.fromBuffer(buffer, function(err, result) { 520 | assert.notOk(err, 'no error'); 521 | assert.equal(result.type, 'serialtiles', 'expected file type'); 522 | assert.equal(result.protocol, 'serialtiles:', 'expected protocol'); 523 | assert.end(); 524 | }); 525 | }); 526 | 527 | 528 | tape('[TM2Z] success: file path', function(assert) { 529 | var filepath = path.resolve('./test/data/valid.tm2z'); 530 | assert.notOk(Buffer.isBuffer(filepath)); 531 | sniffer.fromFile(filepath, function(err, result) { 532 | assert.notOk(err, 'no error'); 533 | assert.equal(result.type, 'tm2z', 'expected file type'); 534 | assert.equal(result.protocol, 'tm2z:', 'expected protocol'); 535 | assert.end(); 536 | }); 537 | }); 538 | tape('[TM2Z] success: buffer', function(assert) { 539 | var filepath = path.resolve('./test/data/valid.tm2z'); 540 | var buffer = getBuffer(filepath); 541 | assert.ok(Buffer.isBuffer(buffer)); 542 | sniffer.fromBuffer(buffer, function(err, result) { 543 | assert.notOk(err, 'no error'); 544 | assert.equal(result.type, 'tm2z', 'expected file type'); 545 | assert.equal(result.protocol, 'tm2z:', 'expected protocol'); 546 | assert.end(); 547 | }); 548 | }); 549 | tape('[TM2Z - valid paxheader] success: file path', function(assert) { 550 | var filepath = path.resolve('./test/data/valid-paxheader.gz'); 551 | assert.notOk(Buffer.isBuffer(filepath)); 552 | sniffer.fromFile(filepath, function(err, result) { 553 | assert.notOk(err, 'no error'); 554 | assert.equal(result.type, 'tm2z', 'expected file type'); 555 | assert.equal(result.protocol, 'tm2z:', 'expected protocol'); 556 | assert.end(); 557 | }); 558 | }); 559 | tape('[TM2Z - valid paxheader] success: buffer', function(assert) { 560 | var filepath = path.resolve('./test/data/valid-paxheader.gz'); 561 | var buffer = getBuffer(filepath); 562 | assert.ok(Buffer.isBuffer(buffer)); 563 | sniffer.fromBuffer(buffer, function(err, result) { 564 | assert.notOk(err, 'no error'); 565 | assert.equal(result.type, 'tm2z', 'expected file type'); 566 | assert.equal(result.protocol, 'tm2z:', 'expected protocol'); 567 | assert.end(); 568 | }); 569 | }); 570 | tape('[TM2Z - invalid malformed] errors', function(assert) { 571 | var filepath = path.resolve('./test/data/invalid-malformed.tm2z'); 572 | var buffer = getBuffer(filepath); 573 | assert.ok(Buffer.isBuffer(buffer)); 574 | sniffer.fromBuffer(buffer, function(err, result) { 575 | assert.ok(err); 576 | assert.equal(err.message, 'Unknown filetype', 'expected error message'); 577 | assert.end(); 578 | }); 579 | }); 580 | tape('[TM2Z - invalid empty] errors', function(assert) { 581 | var filepath = path.resolve('./test/data/invalid-empty.tm2z'); 582 | var buffer = getBuffer(filepath); 583 | assert.ok(Buffer.isBuffer(buffer)); 584 | sniffer.fromBuffer(buffer, function(err, result) { 585 | assert.ok(err); 586 | assert.equal(err.message, 'Unknown filetype', 'expected error message'); 587 | assert.end(); 588 | }); 589 | }); 590 | 591 | tape('[CSV] success: file path', function(assert) { 592 | var filepath = path.resolve('./test/data/valid-points.csv'); 593 | assert.notOk(Buffer.isBuffer(filepath)); 594 | sniffer.fromFile(filepath, function(err, result) { 595 | assert.notOk(err, 'no error'); 596 | assert.equal(result.type, 'csv', 'expected file type'); 597 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 598 | assert.end(); 599 | }); 600 | }); 601 | tape('[CSV] success: buffer', function(assert) { 602 | var filepath = path.resolve('./test/data/valid-points.csv'); 603 | var buffer = getBuffer(filepath); 604 | assert.ok(Buffer.isBuffer(buffer)); 605 | sniffer.fromBuffer(buffer, function(err, result) { 606 | assert.notOk(err, 'no error'); 607 | assert.equal(result.type, 'csv', 'expected file type'); 608 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 609 | assert.end(); 610 | }); 611 | }); 612 | tape('[CSV - blank rows] success: file path', function(assert) { 613 | var filepath = path.resolve('./test/data/blank_rows.csv'); 614 | assert.notOk(Buffer.isBuffer(filepath)); 615 | sniffer.fromFile(filepath, function(err, result) { 616 | assert.notOk(err, 'no error'); 617 | assert.equal(result.type, 'csv', 'expected file type'); 618 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 619 | assert.end(); 620 | }); 621 | }); 622 | tape('[CSV - blank rows] success: buffer', function(assert) { 623 | var filepath = path.resolve('./test/data/blank_rows.csv'); 624 | var buffer = getBuffer(filepath); 625 | assert.ok(Buffer.isBuffer(buffer)); 626 | sniffer.fromBuffer(buffer, function(err, result) { 627 | assert.notOk(err, 'no error'); 628 | assert.equal(result.type, 'csv', 'expected file type'); 629 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 630 | assert.end(); 631 | }); 632 | }); 633 | tape('[CSV - valid one field] success: file path', function(assert) { 634 | var filepath = path.resolve('./test/data/valid-one-field.csv'); 635 | assert.notOk(Buffer.isBuffer(filepath)); 636 | sniffer.fromFile(filepath, function(err, result) { 637 | assert.notOk(err, 'no error'); 638 | assert.equal(result.type, 'csv', 'expected file type'); 639 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 640 | assert.end(); 641 | }); 642 | }); 643 | tape('[CSV - valid one field] success: buffer', function(assert) { 644 | var filepath = path.resolve('./test/data/valid-one-field.csv'); 645 | var buffer = getBuffer(filepath); 646 | assert.ok(Buffer.isBuffer(buffer)); 647 | sniffer.fromBuffer(buffer, function(err, result) { 648 | assert.notOk(err, 'no error'); 649 | assert.equal(result.type, 'csv', 'expected file type'); 650 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 651 | assert.end(); 652 | }); 653 | }); 654 | tape('[CSV - valid empty rows] success: file path', function(assert) { 655 | var filepath = path.resolve('./test/data/valid-empty_rows.csv'); 656 | assert.notOk(Buffer.isBuffer(filepath)); 657 | sniffer.fromFile(filepath, function(err, result) { 658 | assert.notOk(err, 'no error'); 659 | assert.equal(result.type, 'csv', 'expected file type'); 660 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 661 | assert.end(); 662 | }); 663 | }); 664 | tape('[CSV - valid empty rows] success: buffer', function(assert) { 665 | var filepath = path.resolve('./test/data/valid-empty_rows.csv'); 666 | var buffer = getBuffer(filepath); 667 | assert.ok(Buffer.isBuffer(buffer)); 668 | sniffer.fromBuffer(buffer, function(err, result) { 669 | assert.notOk(err, 'no error'); 670 | assert.equal(result.type, 'csv', 'expected file type'); 671 | assert.equal(result.protocol, 'omnivore:', 'expected protocol'); 672 | assert.end(); 673 | }); 674 | }); 675 | tape('[CSV - invalid empty] errors filepath', function(assert) { 676 | var filepath = path.resolve('./test/data/invalid-blank.csv'); 677 | assert.notOk(Buffer.isBuffer(filepath)); 678 | sniffer.fromFile(filepath, function(err, result) { 679 | assert.ok(err); 680 | assert.equal(err.message, 'File too small', 'expected error message'); 681 | assert.equal(err.code, 'EINVALID', 'expected error code'); 682 | assert.end(); 683 | }); 684 | }); 685 | tape('[CSV - invalid empty] errors buffer', function(assert) { 686 | var filepath = path.resolve('./test/data/invalid-blank.csv'); 687 | var buffer = getBuffer(filepath); 688 | assert.ok(Buffer.isBuffer(buffer)); 689 | sniffer.fromBuffer(buffer, function(err, result) { 690 | assert.ok(err); 691 | assert.equal(err.message, 'Unknown filetype', 'expected error message'); 692 | assert.equal(err.code, 'EINVALID', 'expected error code'); 693 | assert.end(); 694 | }); 695 | }); 696 | tape('[CSV - invalid geometries] errors filepath', function(assert) { 697 | var filepath = path.resolve('./test/data/invalid_geometries.csv'); 698 | assert.notOk(Buffer.isBuffer(filepath)); 699 | sniffer.fromFile(filepath, function(err, result) { 700 | assert.ok(err); 701 | assert.equal(err.message, 'Unknown filetype', 'expected error message'); 702 | assert.equal(err.code, 'EINVALID', 'expected error code'); 703 | assert.end(); 704 | }); 705 | }); 706 | tape('[CSV - invalid geometries] errors buffer', function(assert) { 707 | var filepath = path.resolve('./test/data/invalid_geometries.csv'); 708 | var buffer = getBuffer(filepath); 709 | assert.ok(Buffer.isBuffer(buffer)); 710 | sniffer.fromBuffer(buffer, function(err, result) { 711 | assert.ok(err); 712 | assert.equal(err.message, 'Unknown filetype', 'expected error message'); 713 | assert.equal(err.code, 'EINVALID', 'expected error code'); 714 | assert.end(); 715 | }); 716 | }); 717 | 718 | tape('cli - success', function(assert) { 719 | var filepath = path.resolve('./test/data/valid-lines.csv'); 720 | exec([__dirname + '/../bin/mapbox-file-sniff.js', filepath].join(' '), { 721 | env: process.env, 722 | timeout: 2000 723 | }, function(err, stdout) { 724 | assert.ifError(err); 725 | assert.equal(stdout, '{"type":"csv","protocol":"omnivore:"}\n', 'expected output'); 726 | assert.end(); 727 | }); 728 | }); 729 | tape('cli - success, --protocol flag', function(assert) { 730 | var filepath = path.resolve('./test/data/valid-lines.csv'); 731 | exec([__dirname + '/../bin/mapbox-file-sniff.js', filepath, '--protocol'].join(' '), { 732 | env: process.env, 733 | timeout: 2000 734 | }, function(err, stdout) { 735 | assert.ifError(err); 736 | assert.equal(stdout, 'omnivore:\n', 'expected output'); 737 | assert.end(); 738 | }); 739 | }); 740 | tape('cli - success, --type flag', function(assert) { 741 | var filepath = path.resolve('./test/data/valid-lines.csv'); 742 | exec([__dirname + '/../bin/mapbox-file-sniff.js', filepath, '--type'].join(' '), { 743 | env: process.env, 744 | timeout: 2000 745 | }, function(err, stdout) { 746 | assert.ifError(err); 747 | assert.equal(stdout, 'csv\n', 'expected output'); 748 | assert.end(); 749 | }); 750 | }); 751 | tape('cli - error, invalid file', function(assert) { 752 | var filepath = path.resolve('./test/data/invalid_geometries.csv'); 753 | exec([__dirname + '/../bin/mapbox-file-sniff.js', filepath].join(' '), { 754 | env: process.env, 755 | timeout: 2000 756 | }, function(err, stdout, stderr) { 757 | assert.ok(err); 758 | assert.notOk(stdout); 759 | assert.equal(stderr, 'Unknown filetype\n', 'expected output'); 760 | assert.end(); 761 | }); 762 | }); 763 | -------------------------------------------------------------------------------- /test/data/invalid-empty.tm2z: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | unpacker/invalid-tm2z-empty.tm2z at master · mapbox/unpacker 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Skip to content 67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
203 |
204 |
205 | 206 |
207 |
208 |
209 | 210 |
    211 | 212 |
  • 213 |
    214 | 215 |
    216 | 219 | 221 | 222 | 223 | Unwatch 224 | 225 | 226 | 227 |
    228 | 278 |
    279 |
    280 | 281 |
    282 |
  • 283 | 284 |
  • 285 | 286 |
    287 | 288 |
    289 | 295 | 298 |
    299 |
    300 | 306 | 309 |
    310 | 311 |
  • 312 | 313 | 314 |
  • 315 | 316 | 317 | Fork 318 | 319 | 320 |
  • 321 | 322 |
323 | 324 |

325 | 326 | /unpacker 329 | private 330 | 331 | 332 | 333 | 334 | 335 |

336 |
337 |
338 | 339 |
340 |
341 |
342 | 343 | 395 | 396 |
397 | 398 | 399 |
402 |

HTTPS clone URL

403 |
404 | 406 | 407 | 408 | 409 |
410 |
411 | 412 | 413 |
416 |

SSH clone URL

417 |
418 | 420 | 421 | 422 | 423 |
424 |
425 | 426 | 427 |
430 |

Subversion checkout URL

431 |
432 | 434 | 435 | 436 | 437 |
438 |
439 | 440 | 441 |

You can clone with 442 | HTTPS, 443 | SSH, 444 | or Subversion. 445 | 446 | 447 | 448 |

449 | 450 | 451 | 452 | Clone in Desktop 453 | 454 | 455 | 456 | 461 | 462 | Download ZIP 463 | 464 |
465 |
466 | 467 |
468 | 469 | 470 | 471 | 472 | 473 | 474 |
475 | 476 |
477 | 482 | 483 | branch: 484 | master 485 | 486 | 487 | 587 |
588 | 589 |
590 | 595 | 596 | 597 | 603 |
604 | 605 | 608 |
609 | 610 | 611 |
612 |
613 | Mike Morris 614 | mikemorris 615 | 616 | 619 |
620 | 621 |
622 |

623 | 624 | 1 625 | contributor 626 | 627 |

628 | 629 |
630 | 639 |
640 | 641 |
642 |
643 |
644 |
645 | 0.119 kb 646 |
647 |
648 |
649 | Raw 650 | History 651 |
652 | 653 | 658 | 659 | 660 | 661 | 662 | 665 | 666 | 667 |
668 |
669 | 670 |
671 |
672 | View Raw 673 |
674 |
675 | 676 |
677 |
678 | 679 | Jump to Line 680 | 686 | 687 |
688 | 689 |
690 | 691 |
692 |
693 | 694 | 695 |
696 | 697 |
698 | 721 |
722 | 723 | 724 |
725 |
726 |
727 | 728 |
729 |
730 | 739 |
740 | 741 | 742 | 743 |
744 | 745 | 746 | Something went wrong with that request. Please try again. 747 |
748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | -------------------------------------------------------------------------------- /test/data/invalid-malformed.tm2z: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | unpacker/invalid-tm2z-malformed.tm2z at master · mapbox/unpacker 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Skip to content 67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
203 |
204 |
205 | 206 |
207 |
208 |
209 | 210 |
    211 | 212 |
  • 213 |
    214 | 215 |
    216 | 219 | 221 | 222 | 223 | Unwatch 224 | 225 | 226 | 227 |
    228 | 278 |
    279 |
    280 | 281 |
    282 |
  • 283 | 284 |
  • 285 | 286 |
    287 | 288 |
    289 | 295 | 298 |
    299 |
    300 | 306 | 309 |
    310 | 311 |
  • 312 | 313 | 314 |
  • 315 | 316 | 317 | Fork 318 | 319 | 320 |
  • 321 | 322 |
323 | 324 |

325 | 326 | /unpacker 329 | private 330 | 331 | 332 | 333 | 334 | 335 |

336 |
337 |
338 | 339 |
340 |
341 |
342 | 343 | 395 | 396 |
397 | 398 | 399 |
402 |

HTTPS clone URL

403 |
404 | 406 | 407 | 408 | 409 |
410 |
411 | 412 | 413 |
416 |

SSH clone URL

417 |
418 | 420 | 421 | 422 | 423 |
424 |
425 | 426 | 427 |
430 |

Subversion checkout URL

431 |
432 | 434 | 435 | 436 | 437 |
438 |
439 | 440 | 441 |

You can clone with 442 | HTTPS, 443 | SSH, 444 | or Subversion. 445 | 446 | 447 | 448 |

449 | 450 | 451 | 452 | Clone in Desktop 453 | 454 | 455 | 456 | 461 | 462 | Download ZIP 463 | 464 |
465 |
466 | 467 |
468 | 469 | 470 | 471 | 472 | 473 | 474 |
475 | 476 |
477 | 482 | 483 | branch: 484 | master 485 | 486 | 487 | 587 |
588 | 589 |
590 | 595 | 596 | 597 | 603 |
604 | 605 | 608 |
609 | 610 | 611 |
612 |
613 | Mike Morris 614 | mikemorris 615 | 616 | 619 |
620 | 621 |
622 |

623 | 624 | 1 625 | contributor 626 | 627 |

628 | 629 |
630 | 639 |
640 | 641 |
642 |
643 |
644 |
645 | 0.181 kb 646 |
647 |
648 |
649 | Raw 650 | History 651 |
652 | 653 | 658 | 659 | 660 | 661 | 662 | 665 | 666 | 667 |
668 |
669 | 670 |
671 |
672 | View Raw 673 |
674 |
675 | 676 |
677 |
678 | 679 | Jump to Line 680 | 686 | 687 |
688 | 689 |
690 | 691 |
692 |
693 | 694 | 695 |
696 | 697 |
698 | 721 |
722 | 723 | 724 |
725 |
726 |
727 | 728 |
729 |
730 | 739 |
740 | 741 | 742 | 743 |
744 | 745 | 746 | Something went wrong with that request. Please try again. 747 |
748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | --------------------------------------------------------------------------------