<commit_before>var sqlite = require('sqlite');
var Step = require('step');
var crypto = require('crypto');
var compress = require('compress');

function deflate(buffer, callback) {
    var gz = new compress.Gzip();
    var data = '';
    gz.write(buffer, function(err, chunk) {
        if (err) {
            callback(err);
            callback = undefined;
        }
        else data += chunk;
    });
    gz.close(function(err, chunk) {
        if (err) {
            if (callback) callback(err);
        }
        else data = callback(null, data + chunk);
    });
}

function step(callback) {
    return function(err, statement) {
        if (err) throw err;
        statement.step(callback);
    };
}

function bind(data, callback) {
    return function(err, statement) {
        if (err) throw err;
        statement.bindArray(data, function(err) {
            if (err) throw err;
            statement.step(function(err) {
                if (err) throw sqlite.sanitizeError(err, data);
                callback(err);
            });
        });
    };
}

function MBTiles(filename, options, callback) {
  this.filename = filename;
  this.options = options || {};
  this.db = new sqlite.Database();
  var that = this;
  this.db.open(this.filename, function() {
      if (typeof callback === 'function') {
          callback(that);
      }
  });
}

// Setup schema, indices, views for a new mbtiles database.
MBTiles.prototype.setup = function(callback) {
    var that = this;
    Step(
        function() {
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS map ('
                + 'zoom_level integer, '
                + 'tile_column integer, '
                + 'tile_row integer, '
                + 'tile_id TEXT, '
                + 'grid_id TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS map_index on map '
                + '(zoom_level, tile_column, tile_row);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS grid_key ('
                + 'grid_id TEXT, '
                + 'key_name TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS grid_key_lookup on grid_key '
                + '(grid_id, key_name);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS keymap ('
                + 'key_name TEXT, '
                + 'key_json TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS keymap_lookup on keymap '
                + '(key_name);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS grid_utfgrid ('
                + 'grid_id TEXT, '
                + 'grid_utfgrid TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS grid_utfgrid_lookup on grid_utfgrid '
                + '(grid_id);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS images ('
                + 'tile_data blob, '
                + 'tile_id text);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS images_id on images '
                + '(tile_id);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE view IF NOT EXISTS tiles AS SELECT '
                + 'map.zoom_level AS zoom_level, '
                + 'map.tile_column as tile_column, '
                + 'map.tile_row as tile_row, '
                + 'images.tile_data as tile_data '
                + 'FROM map '
                + 'JOIN images ON images.tile_id = map.tile_id',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE view IF NOT EXISTS grids AS SELECT '
                + 'map.zoom_level AS zoom_level, '
                + 'map.tile_column as tile_column, '
                + 'map.tile_row as tile_row, '
                + 'grid_utfgrid.grid_utfgrid as grid '
                + 'FROM map '
                + 'JOIN grid_utfgrid ON grid_utfgrid.grid_id = map.grid_id',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS metadata ('
                + 'name text,'
                + 'value text);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS name on metadata '
                + '(name);',
                step(this)
            );
        },
        // Set the synchronous flag to NORMAL for (much) faster inserts.
        // See http://www.sqlite.org/pragma.html#pragma_synchronous
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'PRAGMA synchronous = 1',
                step(this)
            );
        },
        callback
    );
};

// Insert a set of tiles into an mbtiles database.
//
// - @param {Array} tiles array of tile [z, x, y] coordinates to be inserted.
// - @param {Array} renders array of tile images to be inserted. Indices should
//   correspond to the coordinates of the tiles array.
// - @param {Boolean} compress whether to "compress" the mbtiles database by
//   inserting only unique tile renders.
// - @param {Function} callback
MBTiles.prototype.insertGrids = function(tiles, renders, compress, callback) {
    var that = this,
        map = [],
        grids = [],
        grid_keys = [],
        ids = [],
        inserted = [];
    for (var i = 0; i < tiles.length; i++) {
        var tile_id;
        if (compress) {
            // Generate ID from MD5 hash of actual image data.
            grid_id = crypto.createHash('md5').update(renders[i][0]).digest('hex');
        } else {
            // Generate ID from tile coordinates (unique).
            grid_id = crypto.createHash('md5').update(JSON.stringify(tiles[i])).digest('hex');
        }
        if (ids.indexOf(grid_id) === -1) {
            ids.push(grid_id);
            grids.push({
                grid_id: grid_id,
                grid_utfgrid: renders[i][0]
            });
        }
        renders[i][2].keys.forEach(function(k) {
            grid_keys.push({
                grid_id: grid_id,
                key_name: k
            });
        });
        map.push({
            grid_id: grid_id,
            zoom_level: tiles[i][0],
            tile_column: tiles[i][1],
            tile_row: tiles[i][2]
        });
    }

    Step(
        function() {
            var group = this.group();
            for (var i = 0; i < grids.length; i++) {
                that.insertGrid(grids[i], group());
            }
            for (var i = 0; i < grid_keys.length; i++) {
                that.insertGridKey(grid_keys[i], group());
            }
            for (var i = 0; i < map.length; i++) {
                that.insertGridTile(map[i], group());
            }
        },
        callback
    );
};

// Insert a set of tiles into an mbtiles database.
//
// - @param {Array} tiles array of tile [z, x, y] coordinates to be inserted.
// - @param {Array} renders array of tile images to be inserted. Indices should
//   correspond to the coordinates of the tiles array.
// - @param {Boolean} compress whether to "compress" the mbtiles database by
//   inserting only unique tile renders.
// - @param {Function} callback
MBTiles.prototype.insertTiles = function(tiles, renders, compress, callback) {
    var that = this,
        map = [],
        images = [],
        ids = [],
        inserted = [];
    for (var i = 0; i < tiles.length; i++) {
        var tile_id;
        if (compress) {
            // Generate ID from MD5 hash of actual image data.
            tile_id = crypto.createHash('md5').update(renders[i]).digest('hex');
        } else {
            // Generate ID from tile coordinates (unique).
            tile_id = crypto.createHash('md5').update(JSON.stringify(tiles[i])).digest('hex');
        }
        if (ids.indexOf(tile_id) === -1) {
            ids.push(tile_id);
            images.push({
                tile_id: tile_id,
                tile_data: renders[i]
            });
        }
        map.push({
            tile_id: tile_id,
            zoom_level: tiles[i][0],
            tile_column: tiles[i][1],
            tile_row: tiles[i][2]
        });
    }

    Step(
        function() {
            var group = this.group();
            for (var i = 0; i < images.length; i++) {
                that.insertImage(images[i], group());
            }
            for (var i = 0; i < map.length; i++) {
                that.insertTile(map[i], group());
            }
        },
        callback
    );
};

MBTiles.prototype.insertGridKey = function(grid_key, callback) {
    this.db.prepare(
        'INSERT OR ABORT INTO grid_key ('
        + 'grid_id, '
        + 'key_name) '
        + 'VALUES (?, ?);',
        bind([
            grid_key.grid_id,
            grid_key.key_name
        ], callback)
    );
};

// Insert a single feature into the grid_data table,
// with the key/axis of `key`
MBTiles.prototype.insertGridData = function(data, key_name, callback) {
    this.db.prepare(
        'INSERT OR IGNORE INTO keymap ('
        + 'key_name, '
        + 'key_json) '
        + 'VALUES (?, ?);',
        bind([
            data[key_name],
            JSON.stringify(data)
        ], callback)
    );
};

// Insert a single tile into the mbtiles database.
//
// - @param {Object} tile tile object to be inserted.
// - @param {Function} callback
MBTiles.prototype.insertTile = function(tile, callback) {
    this.db.prepare(
        'INSERT INTO map ('
        + 'tile_id, '
        + 'zoom_level, '
        + 'tile_column, '
        + 'tile_row) '
        + 'VALUES (?, ?, ?, ?);',
        bind([
            tile.tile_id,
            tile.zoom_level,
            tile.tile_column,
            tile.tile_row
        ], callback)
    );
};

// Insert a single tile into the mbtiles database.
//
// - @param {Object} tile tile object to be inserted.
// - @param {Function} callback
MBTiles.prototype.insertGridTile = function(tile, callback) {
    this.db.prepare(
        'UPDATE OR REPLACE map SET grid_id = ? WHERE '
        + ' zoom_level = ? AND'
        + ' tile_column = ? AND'
        + ' tile_row = ?;',
        bind([
            tile.grid_id,
            tile.zoom_level,
            tile.tile_column,
            tile.tile_row
        ], callback)
    );
};

// Insert a single grid into the mbtiles database.
//
// - @param {Object} image object to be inserted.
// - @param {Function} callback
MBTiles.prototype.insertGrid = function(grid, callback) {
    var that = this;
    deflate(grid.grid_utfgrid, function(err, data) {
        if (err) throw err;
        that.db.prepare(
            'INSERT OR ABORT INTO grid_utfgrid ('
            + 'grid_id, '
            + 'grid_utfgrid) '
            + 'VALUES (?, ?);',
            bind([
                grid.grid_id,
                data
            ], callback)
        );
    });
};

// Insert a single image into the mbtiles database.
//
// @param {Object} image object to be inserted.
// @param {Function} callback
MBTiles.prototype.insertImage = function(image, callback) {
    this.db.prepare(
        'INSERT OR ABORT INTO images ('
        + 'tile_id, '
        + 'tile_data) '
        + 'VALUES (?, ?);',
        bind([
            image.tile_id,
            image.tile_data
        ], callback)
    );
};

// Insert metadata into the mbtiles database.
//
// - @param {Object} metadata key, value hash of metadata to be inserted.
// - @param {Function} callback
MBTiles.prototype.metadata = function(metadata, callback) {
    var that = this;
    Step(
        function() {
            var group = this.group();
            for (var name in metadata) {
                that.db.prepare(
                    'INSERT INTO metadata (name, value) VALUES (?, ?);',
                    bind([
                        name,
                        metadata[name]
                    ], group())
                );
            }
        },
        callback
    );
};

// Select a tile from an mbtiles database.
//
// - @param {Number} x tile x coordinate.
// - @param {Number} y tile y coordinate.
// - @param {Number} z tile z coordinate.
// - @param {Function} callback
MBTiles.prototype.tile = function(x, y, z, callback) {
    var that = this;
    Step(
        function() {
            that.db.prepare(
                'SELECT tile_data FROM tiles WHERE '
                + 'zoom_level = ? AND '
                + 'tile_column = ? AND '
                + 'tile_row = ?;',
                bind([ z, x, y ], this)
            );
        },
        function(err, row) {
            if (err) callback(err);
            else if (!row || !row.tile_data) callback('empty row');
            else callback(null, row.tile_data);
        }
    );
};

/**
 * Select a tile from an mbtiles database.
 *
 * @param {Number} x tile x coordinate
 * @param {Number} y tile y coordinate
 * @param {Number} z tile z coordinate
 * @param {Function} callback
 */
MBTiles.prototype.grid = function(x, y, z, callback) {
    var that = this;
    Step(
        function() {
            that.db.prepare(
                'SELECT grid FROM grids WHERE '
                + 'zoom_level = ? AND '
                + 'tile_column = ? AND '
                + 'tile_row = ?;',
                bind([ z, x, y ], this)
            );
        },
        function(err, row) {
            if (err) callback(err);
            else if (!row || !row.grid) callback('empty row');
            else callback(null, row.grid);
        }
    );
};

module.exports = MBTiles;
<commit_msg>Include row in bind() callback.<commit_after>var sqlite = require('sqlite');
var Step = require('step');
var crypto = require('crypto');
var compress = require('compress');

function deflate(buffer, callback) {
    var gz = new compress.Gzip();
    var data = '';
    gz.write(buffer, function(err, chunk) {
        if (err) {
            callback(err);
            callback = undefined;
        }
        else data += chunk;
    });
    gz.close(function(err, chunk) {
        if (err) {
            if (callback) callback(err);
        }
        else data = callback(null, data + chunk);
    });
}

function step(callback) {
    return function(err, statement) {
        if (err) throw err;
        statement.step(callback);
    };
}

function bind(data, callback) {
    return function(err, statement) {
        if (err) throw err;
        statement.bindArray(data, function(err) {
            if (err) throw err;
            statement.step(function(err, row) {
                if (err) throw sqlite.sanitizeError(err, data);
                callback(err, row);
            });
        });
    };
}

function MBTiles(filename, options, callback) {
  this.filename = filename;
  this.options = options || {};
  this.db = new sqlite.Database();
  var that = this;
  this.db.open(this.filename, function() {
      if (typeof callback === 'function') {
          callback(that);
      }
  });
}

// Setup schema, indices, views for a new mbtiles database.
MBTiles.prototype.setup = function(callback) {
    var that = this;
    Step(
        function() {
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS map ('
                + 'zoom_level integer, '
                + 'tile_column integer, '
                + 'tile_row integer, '
                + 'tile_id TEXT, '
                + 'grid_id TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS map_index on map '
                + '(zoom_level, tile_column, tile_row);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS grid_key ('
                + 'grid_id TEXT, '
                + 'key_name TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS grid_key_lookup on grid_key '
                + '(grid_id, key_name);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS keymap ('
                + 'key_name TEXT, '
                + 'key_json TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS keymap_lookup on keymap '
                + '(key_name);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS grid_utfgrid ('
                + 'grid_id TEXT, '
                + 'grid_utfgrid TEXT);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS grid_utfgrid_lookup on grid_utfgrid '
                + '(grid_id);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS images ('
                + 'tile_data blob, '
                + 'tile_id text);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS images_id on images '
                + '(tile_id);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE view IF NOT EXISTS tiles AS SELECT '
                + 'map.zoom_level AS zoom_level, '
                + 'map.tile_column as tile_column, '
                + 'map.tile_row as tile_row, '
                + 'images.tile_data as tile_data '
                + 'FROM map '
                + 'JOIN images ON images.tile_id = map.tile_id',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE view IF NOT EXISTS grids AS SELECT '
                + 'map.zoom_level AS zoom_level, '
                + 'map.tile_column as tile_column, '
                + 'map.tile_row as tile_row, '
                + 'grid_utfgrid.grid_utfgrid as grid '
                + 'FROM map '
                + 'JOIN grid_utfgrid ON grid_utfgrid.grid_id = map.grid_id',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE TABLE IF NOT EXISTS metadata ('
                + 'name text,'
                + 'value text);',
                step(this)
            );
        },
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'CREATE UNIQUE INDEX IF NOT EXISTS name on metadata '
                + '(name);',
                step(this)
            );
        },
        // Set the synchronous flag to NORMAL for (much) faster inserts.
        // See http://www.sqlite.org/pragma.html#pragma_synchronous
        function(err) {
            if (err) throw err;
            that.db.prepare(
                'PRAGMA synchronous = 1',
                step(this)
            );
        },
        callback
    );
};

// Insert a set of tiles into an mbtiles database.
//
// - @param {Array} tiles array of tile [z, x, y] coordinates to be inserted.
// - @param {Array} renders array of tile images to be inserted. Indices should
//   correspond to the coordinates of the tiles array.
// - @param {Boolean} compress whether to "compress" the mbtiles database by
//   inserting only unique tile renders.
// - @param {Function} callback
MBTiles.prototype.insertGrids = function(tiles, renders, compress, callback) {
    var that = this,
        map = [],
        grids = [],
        grid_keys = [],
        ids = [],
        inserted = [];
    for (var i = 0; i < tiles.length; i++) {
        var tile_id;
        if (compress) {
            // Generate ID from MD5 hash of actual image data.
            grid_id = crypto.createHash('md5').update(renders[i][0]).digest('hex');
        } else {
            // Generate ID from tile coordinates (unique).
            grid_id = crypto.createHash('md5').update(JSON.stringify(tiles[i])).digest('hex');
        }
        if (ids.indexOf(grid_id) === -1) {
            ids.push(grid_id);
            grids.push({
                grid_id: grid_id,
                grid_utfgrid: renders[i][0]
            });
        }
        renders[i][2].keys.forEach(function(k) {
            grid_keys.push({
                grid_id: grid_id,
                key_name: k
            });
        });
        map.push({
            grid_id: grid_id,
            zoom_level: tiles[i][0],
            tile_column: tiles[i][1],
            tile_row: tiles[i][2]
        });
    }

    Step(
        function() {
            var group = this.group();
            for (var i = 0; i < grids.length; i++) {
                that.insertGrid(grids[i], group());
            }
            for (var i = 0; i < grid_keys.length; i++) {
                that.insertGridKey(grid_keys[i], group());
            }
            for (var i = 0; i < map.length; i++) {
                that.insertGridTile(map[i], group());
            }
        },
        callback
    );
};

// Insert a set of tiles into an mbtiles database.
//
// - @param {Array} tiles array of tile [z, x, y] coordinates to be inserted.
// - @param {Array} renders array of tile images to be inserted. Indices should
//   correspond to the coordinates of the tiles array.
// - @param {Boolean} compress whether to "compress" the mbtiles database by
//   inserting only unique tile renders.
// - @param {Function} callback
MBTiles.prototype.insertTiles = function(tiles, renders, compress, callback) {
    var that = this,
        map = [],
        images = [],
        ids = [],
        inserted = [];
    for (var i = 0; i < tiles.length; i++) {
        var tile_id;
        if (compress) {
            // Generate ID from MD5 hash of actual image data.
            tile_id = crypto.createHash('md5').update(renders[i]).digest('hex');
        } else {
            // Generate ID from tile coordinates (unique).
            tile_id = crypto.createHash('md5').update(JSON.stringify(tiles[i])).digest('hex');
        }
        if (ids.indexOf(tile_id) === -1) {
            ids.push(tile_id);
            images.push({
                tile_id: tile_id,
                tile_data: renders[i]
            });
        }
        map.push({
            tile_id: tile_id,
            zoom_level: tiles[i][0],
            tile_column: tiles[i][1],
            tile_row: tiles[i][2]
        });
    }

    Step(
        function() {
            var group = this.group();
            for (var i = 0; i < images.length; i++) {
                that.insertImage(images[i], group());
            }
            for (var i = 0; i < map.length; i++) {
                that.insertTile(map[i], group());
            }
        },
        callback
    );
};

MBTiles.prototype.insertGridKey = function(grid_key, callback) {
    this.db.prepare(
        'INSERT OR ABORT INTO grid_key ('
        + 'grid_id, '
        + 'key_name) '
        + 'VALUES (?, ?);',
        bind([
            grid_key.grid_id,
            grid_key.key_name
        ], callback)
    );
};

// Insert a single feature into the grid_data table,
// with the key/axis of `key`
MBTiles.prototype.insertGridData = function(data, key_name, callback) {
    this.db.prepare(
        'INSERT OR IGNORE INTO keymap ('
        + 'key_name, '
        + 'key_json) '
        + 'VALUES (?, ?);',
        bind([
            data[key_name],
            JSON.stringify(data)
        ], callback)
    );
};

// Insert a single tile into the mbtiles database.
//
// - @param {Object} tile tile object to be inserted.
// - @param {Function} callback
MBTiles.prototype.insertTile = function(tile, callback) {
    this.db.prepare(
        'INSERT INTO map ('
        + 'tile_id, '
        + 'zoom_level, '
        + 'tile_column, '
        + 'tile_row) '
        + 'VALUES (?, ?, ?, ?);',
        bind([
            tile.tile_id,
            tile.zoom_level,
            tile.tile_column,
            tile.tile_row
        ], callback)
    );
};

// Insert a single tile into the mbtiles database.
//
// - @param {Object} tile tile object to be inserted.
// - @param {Function} callback
MBTiles.prototype.insertGridTile = function(tile, callback) {
    this.db.prepare(
        'UPDATE OR REPLACE map SET grid_id = ? WHERE '
        + ' zoom_level = ? AND'
        + ' tile_column = ? AND'
        + ' tile_row = ?;',
        bind([
            tile.grid_id,
            tile.zoom_level,
            tile.tile_column,
            tile.tile_row
        ], callback)
    );
};

// Insert a single grid into the mbtiles database.
//
// - @param {Object} image object to be inserted.
// - @param {Function} callback
MBTiles.prototype.insertGrid = function(grid, callback) {
    var that = this;
    deflate(grid.grid_utfgrid, function(err, data) {
        if (err) throw err;
        that.db.prepare(
            'INSERT OR ABORT INTO grid_utfgrid ('
            + 'grid_id, '
            + 'grid_utfgrid) '
            + 'VALUES (?, ?);',
            bind([
                grid.grid_id,
                data
            ], callback)
        );
    });
};

// Insert a single image into the mbtiles database.
//
// @param {Object} image object to be inserted.
// @param {Function} callback
MBTiles.prototype.insertImage = function(image, callback) {
    this.db.prepare(
        'INSERT OR ABORT INTO images ('
        + 'tile_id, '
        + 'tile_data) '
        + 'VALUES (?, ?);',
        bind([
            image.tile_id,
            image.tile_data
        ], callback)
    );
};

// Insert metadata into the mbtiles database.
//
// - @param {Object} metadata key, value hash of metadata to be inserted.
// - @param {Function} callback
MBTiles.prototype.metadata = function(metadata, callback) {
    var that = this;
    Step(
        function() {
            var group = this.group();
            for (var name in metadata) {
                that.db.prepare(
                    'INSERT INTO metadata (name, value) VALUES (?, ?);',
                    bind([
                        name,
                        metadata[name]
                    ], group())
                );
            }
        },
        callback
    );
};

// Select a tile from an mbtiles database.
//
// - @param {Number} x tile x coordinate.
// - @param {Number} y tile y coordinate.
// - @param {Number} z tile z coordinate.
// - @param {Function} callback
MBTiles.prototype.tile = function(x, y, z, callback) {
    var that = this;
    Step(
        function() {
            that.db.prepare(
                'SELECT tile_data FROM tiles WHERE '
                + 'zoom_level = ? AND '
                + 'tile_column = ? AND '
                + 'tile_row = ?;',
                bind([ z, x, y ], this)
            );
        },
        function(err, row) {
            if (err) callback(err);
            else if (!row || !row.tile_data) callback('empty row');
            else callback(null, row.tile_data);
        }
    );
};

/**
 * Select a tile from an mbtiles database.
 *
 * @param {Number} x tile x coordinate
 * @param {Number} y tile y coordinate
 * @param {Number} z tile z coordinate
 * @param {Function} callback
 */
MBTiles.prototype.grid = function(x, y, z, callback) {
    var that = this;
    Step(
        function() {
            that.db.prepare(
                'SELECT grid FROM grids WHERE '
                + 'zoom_level = ? AND '
                + 'tile_column = ? AND '
                + 'tile_row = ?;',
                bind([ z, x, y ], this)
            );
        },
        function(err, row) {
            if (err) callback(err);
            else if (!row || !row.grid) callback('empty row');
            else callback(null, row.grid);
        }
    );
};

module.exports = MBTiles;
<|endoftext|>


<commit_before>// Copyright (C) 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Utilities for common patterns in host pages.
 *
 * @author kpreid@switchb.org
 * @requires document, ___, cajita, attachDocumentStub, valijaMaker,
 *           Q, defaultCajolerFinder,
 * @provides HostTools
 *
 * (HostTools can also work without cajita-module, i.e. without
 * defaultCajolerFinder, at the cost of sandbox.run() functionality.)
 */

var HostTools;
(function () {
  var hasModuleLoader = "defaultCajolerFinder" in window;
  var toolsInstanceCounter = 0;

  HostTools = function () {
    var toolsInstanceIndex = ++toolsInstanceCounter;
    var gadgetInstanceCounter = 0;

    // user-modifiable state
    var cajolerFinder = hasModuleLoader ? defaultCajolerFinder : null;
    var baseURL = document.location.toString();
    // TODO(kpreid): the above probably does the wrong thing in the case where
    // the document has a <base>; fix.
    var load;

    // internal functions
    function updateLoad() {
      if (!hasModuleLoader) {
        load = null;
      } else {
        // TODO(kpreid): allow subbing module id resolver
        // TODO(kpreid): Using XHR loader didn't work; why?
        load = scriptModuleLoadMaker(baseURL,
                                     defaultModuleIdResolver,
                                     cajolerFinder);
      }
    }
    updateLoad();

    // public methods

    function setCajolerService(url) {
      cajolerFinder = new CajolingServiceFinder(url);
      updateLoad();
    }

    function setBaseURL(url) {
      baseURL = url;
      updateLoad();
    }

    function Sandbox() {
      // user-modifiable state
      var imports = ___.copy(___.sharedImports);

      // public methods

      function run(mid) {
        if (load == null) {
          throw new Error("HostTools: Loaded without cajita-module.js, so " +
              "cannot dynamically load modules.");
        }

        // TODO: review error handling -- are errors propagated and descriptive?
        return Q.when(load.async(mid), function (moduleFunc) {
          //console.log("got load Q callback");
          return moduleFunc(imports);
        });
      }

      function runCajoledModuleString(js) {
        // This is primarily useful when used from caja.js (Caja in an iframe),
        // since the host page doesn't have eval of the frame

        var moduleFunc;
        var oldModuleHandler = ___.getNewModuleHandler();
        var newModuleHandler = ___.makeNormalNewModuleHandler();
        newModuleHandler.handle = ___.markFuncFreeze(
            function theHandler(module) {
              // TODO: does not support dependencies. Needs to tie in to
              // cajita-module.js for that and to give the module a proper
              // relative load function.
              moduleFunc = ___.prepareModule(module, load);
            });
        try {
          ___.setNewModuleHandler(newModuleHandler);
          eval(js);
        } finally {
          ___.setNewModuleHandler(oldModuleHandler);
        }
        return moduleFunc(imports);
      }

      function attach(vdocBody, options) {
        // Generate unique element id suffix for Domita.
        // There are two counters just to make them a little more decodable.
        var gadgetInstanceIndex = ++gadgetInstanceCounter;
        var idSuffix = "-HostToolsGadget-" + toolsInstanceIndex + "-" +
            gadgetInstanceIndex + "___";

        options = ___.copy(options ? options : {});
        if (options.valija === undefined) { options.valija = true; }

        // TODO(kpreid): do we want to reject multiple attach attempts?

        if (options.valija) { imports.outers = imports; }

        // TODO(kpreid): provide control over this
        var uriCallback = cajita.freeze({
            rewrite: function() {
                return null;
            }
        });
        attachDocumentStub(idSuffix, uriCallback, imports, vdocBody);
        imports.htmlEmitter___ = new HtmlEmitter(vdocBody, imports.document);

        if (options.valija) { imports.$v = valijaMaker.CALL___(imports.outers); }
      }

      return cajita.freeze({
        attach: attach,
        imports: imports,
        run: run,
        runCajoledModuleString: runCajoledModuleString
      });
    }

    return cajita.freeze({
      getLoad: function () { return load; },
      setBaseURL: setBaseURL,
      setCajolerService: setCajolerService,
      Sandbox: Sandbox
    });
  }
})();
<commit_msg>Have HostTools runCajoledModuleString give a nice error if the string doesn't ___.loadModule(). http://codereview.appspot.com/1942042<commit_after>// Copyright (C) 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Utilities for common patterns in host pages.
 *
 * @author kpreid@switchb.org
 * @requires document, ___, cajita, attachDocumentStub, valijaMaker,
 *           Q, defaultCajolerFinder,
 * @provides HostTools
 *
 * (HostTools can also work without cajita-module, i.e. without
 * defaultCajolerFinder, at the cost of sandbox.run() functionality.)
 */

var HostTools;
(function () {
  var hasModuleLoader = "defaultCajolerFinder" in window;
  var toolsInstanceCounter = 0;

  HostTools = function () {
    var toolsInstanceIndex = ++toolsInstanceCounter;
    var gadgetInstanceCounter = 0;

    // user-modifiable state
    var cajolerFinder = hasModuleLoader ? defaultCajolerFinder : null;
    var baseURL = document.location.toString();
    // TODO(kpreid): the above probably does the wrong thing in the case where
    // the document has a <base>; fix.
    var load;

    // internal functions
    function updateLoad() {
      if (!hasModuleLoader) {
        load = null;
      } else {
        // TODO(kpreid): allow subbing module id resolver
        // TODO(kpreid): Using XHR loader didn't work; why?
        load = scriptModuleLoadMaker(baseURL,
                                     defaultModuleIdResolver,
                                     cajolerFinder);
      }
    }
    updateLoad();

    // public methods

    function setCajolerService(url) {
      cajolerFinder = new CajolingServiceFinder(url);
      updateLoad();
    }

    function setBaseURL(url) {
      baseURL = url;
      updateLoad();
    }

    function Sandbox() {
      // user-modifiable state
      var imports = ___.copy(___.sharedImports);

      // public methods

      function run(mid) {
        if (load == null) {
          throw new Error("HostTools: Loaded without cajita-module.js, so " +
              "cannot dynamically load modules.");
        }

        // TODO: review error handling -- are errors propagated and descriptive?
        return Q.when(load.async(mid), function (moduleFunc) {
          //console.log("got load Q callback");
          return moduleFunc(imports);
        });
      }

      function runCajoledModuleString(js) {
        // This is primarily useful when used from caja.js (Caja in an iframe),
        // since the host page doesn't have eval of the frame

        var pumpkin = {};
        var moduleFunc = pumpkin;
        var oldModuleHandler = ___.getNewModuleHandler();
        var newModuleHandler = ___.makeNormalNewModuleHandler();
        newModuleHandler.handle = ___.markFuncFreeze(
            function theHandler(module) {
              // TODO: does not support dependencies. Needs to tie in to
              // cajita-module.js for that and to give the module a proper
              // relative load function.
              moduleFunc = ___.prepareModule(module, load);
            });
        try {
          ___.setNewModuleHandler(newModuleHandler);
          eval(js);
        } finally {
          ___.setNewModuleHandler(oldModuleHandler);
        }
        if (moduleFunc === pumpkin) {
          throw new Error("runCajoledModuleString: the provided code did not " +
            "invoke the new module handler");
        }
        return moduleFunc(imports);
      }

      function attach(vdocBody, options) {
        // Generate unique element id suffix for Domita.
        // There are two counters just to make them a little more decodable.
        var gadgetInstanceIndex = ++gadgetInstanceCounter;
        var idSuffix = "-HostToolsGadget-" + toolsInstanceIndex + "-" +
            gadgetInstanceIndex + "___";

        options = ___.copy(options ? options : {});
        if (options.valija === undefined) { options.valija = true; }

        // TODO(kpreid): do we want to reject multiple attach attempts?

        if (options.valija) { imports.outers = imports; }

        // TODO(kpreid): provide control over this
        var uriCallback = cajita.freeze({
            rewrite: function() {
                return null;
            }
        });
        attachDocumentStub(idSuffix, uriCallback, imports, vdocBody);
        imports.htmlEmitter___ = new HtmlEmitter(vdocBody, imports.document);

        if (options.valija) { imports.$v = valijaMaker.CALL___(imports.outers); }
      }

      return cajita.freeze({
        attach: attach,
        imports: imports,
        run: run,
        runCajoledModuleString: runCajoledModuleString
      });
    }

    return cajita.freeze({
      getLoad: function () { return load; },
      setBaseURL: setBaseURL,
      setCajolerService: setCajolerService,
      Sandbox: Sandbox
    });
  }
})();
<|endoftext|>


<commit_before>"use strict";

var Base = require('./index.js')
var define = Object.defineProperty
var proto = Base.prototype

define( proto, '$setKeyInternal', {
  value:function( key, value, field ) {
    if(field) {
      if(field._$parent !== this) {
        this[key] = new field.$Constructor( value, this )
      } else {
        field.$set( value )
      }
    } else {
      this[key] = new this.$children.$Constructor( value, this )
      this[key]._$key = key

      //adds 3% extra slowness (purely for the check)
      //try to remove constructor when instance is created and check if a normal if(!) is faster
      if(this.hasOwnProperty( '_$Constructor' )) {
        //this can be optimized
        this.$createContextGetter.call(this, key)
      }

    }
  }
})

define( proto, '$setKey', {
  value:function( key, value ) {
    var privateField = '_'+key
    var field = this[ privateField ]
    if(field) {
      this.$setKeyInternal( privateField, value, field )
    } else {
      this.$setKeyInternal( key, value, this[key] )
      // when is this[key] defined but not this[privateField] ?
    }
  }
})

define( proto, '$createContextGetter', {
  value:function( key$ ) {
    var field = this['_'+key$] = this[key$]

    for( var key$$ in field ) {
      if(key$$[0]!=='_' && !field['_'+key$$]) {
        field.$createContextGetter( key$$ )
      }
    }

    define( this, key$, {
      get:function() {
        var privateKey = '_'+key$
        var field = this[privateKey]
        if(!this.hasOwnProperty( privateKey )) {
          field._$context = this
          //can be optmized (check if context path and equal)
          field._$contextPath = [ key$ ]
        } else if( this._$context ) {
          //this has to become faster..
          //also a lot of potential to break (e.g. doing the same get twice...)
          field._$contextPath = this._$contextPath.concat([ key$ ])
          field._$context = this._$context
        } else {
          //clears context info
          field._$contextPath = null
          field._$context = null
        }
        return field
      },
      set:function(val) {
        this['_'+key$] = val
      }
    })

  }
})

define( proto, '$parent', {
  get:function() {
    if(this._$contextPath) {
      if(this._$contextPath.length === 1) {
        return this._$context
      }
    }
    return this._$parent
  },
  set:function(val) {
    this._$parent = val
  }
})

<commit_msg>privateKey<commit_after>"use strict";

var Base = require('./index.js')
var define = Object.defineProperty
var proto = Base.prototype

define( proto, '$setKeyInternal', {
  value:function( key, value, field ) {
    if(field) {
      if(field._$parent !== this) {
        this[key] = new field.$Constructor( value, this )
      } else {
        field.$set( value )
      }
    } else {
      this[key] = new this.$children.$Constructor( value, this )
      this[key]._$key = key

      //adds 3% extra slowness (purely for the check)
      //try to remove constructor when instance is created and check if a normal if(!) is faster
      if(this.hasOwnProperty( '_$Constructor' )) {
        //this can be optimized
        this.$createContextGetter.call(this, key)
      }

    }
  }
})

define( proto, '$setKey', {
  value:function( key, value ) {
    var privateKey = '_'+key
    var field = this[ privateKey ]
    if(field) {
      this.$setKeyInternal( privateKey, value, field )
    } else {
      this.$setKeyInternal( key, value, this[key] )
      // when is this[key] defined but not this[privateKey] ?
    }
  }
})

window.callcnt1 = 0
window.callcnt2 = 0

define( proto, '$createContextGetter', {
  value:function( key$ ) {
    window.callcnt1++
    var field = this['_'+key$] = this[key$]

    for( var key$$ in field ) {
      if(key$$[0]!=='_' && !field['_'+key$$]) {
        field.$createContextGetter( key$$ )
      }
    }

    define( this, key$, {
      get:function() {
        window.callcnt2++
        var privateKey = '_'+key$
        var field = this[privateKey]
        if(!this.hasOwnProperty( privateKey )) {
          field._$context = this
          //can be optmized (check if context path and equal)
          field._$contextPath = [ key$ ]
        } else if( this._$context ) {
          //this has to become faster..
          //also a lot of potential to break (e.g. doing the same get twice...)
          field._$contextPath = this._$contextPath.concat([ key$ ])
          field._$context = this._$context
        } else {
          //clears context info
          field._$contextPath = null
          field._$context = null
        }
        return field
      },
      set:function(val) {
        this['_'+key$] = val
      }
    })

  }
})

define( proto, '$parent', {
  get:function() {
    if(this._$contextPath) {
      if(this._$contextPath.length === 1) {
        return this._$context
      }
    }
    return this._$parent
  },
  set:function(val) {
    this._$parent = val
  }
})

<|endoftext|>


<commit_before>var rb = require('react-bootstrap');
var React = require('react');

var TabbedArea = rb.TabbedArea,
    TabPane = rb.TabPane;

var DataItemList = require('./dataitemlist')
var WidgetList = require('./widgetlist')

var SettingsPanel = React.createClass({
  getInitialState: function() {
    return {
      activeKey: 2
    }
  },
  render: function() {
    return (
    <TabbedArea activeKey={this.state.activeKey} onSelect={this.handleSelect}>
      <TabPane eventKey={1} tab="By Data Item">
        <DataItemList instrumentPanel={this.props.instrumentPanel}/>
      </TabPane>
      <TabPane eventKey={2} tab="By Display Widget">
        <WidgetList instrumentPanel={this.props.instrumentPanel}/>
      </TabPane>
    </TabbedArea>
    );
  },
  handleSelect: function(selectedKey) {
    this.setState({
      activeKey: selectedKey
    });
  }
});

module.exports = SettingsPanel;

<commit_msg>Reorder tabs<commit_after>var rb = require('react-bootstrap');
var React = require('react');

var TabbedArea = rb.TabbedArea,
    TabPane = rb.TabPane;

var DataItemList = require('./dataitemlist')
var WidgetList = require('./widgetlist')

var SettingsPanel = React.createClass({
  getInitialState: function() {
    return {
      activeKey: 1
    }
  },
  render: function() {
    return (
    <TabbedArea activeKey={this.state.activeKey} onSelect={this.handleSelect}>
      <TabPane eventKey={1} tab="By Display Widget">
        <WidgetList instrumentPanel={this.props.instrumentPanel}/>
      </TabPane>
      <TabPane eventKey={2} tab="By Data Item">
        <DataItemList instrumentPanel={this.props.instrumentPanel}/>
      </TabPane>
    </TabbedArea>
    );
  },
  handleSelect: function(selectedKey) {
    this.setState({
      activeKey: selectedKey
    });
  }
});

module.exports = SettingsPanel;

<|endoftext|>


<commit_before><commit_msg>webapp4<commit_after><|endoftext|>


<commit_before>import React from 'react'
import Loading from '../loading'
import Auth0Lock from 'auth0-lock'
import { connect } from 'react-redux'
import { storeId } from '../../action/user-id-actions.js'
import { login, logout } from '../../action/auth-actions.js'

class LandingContainer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      signUp: false,
    }
    this.showLock = this.showLock.bind(this)
  }

  componentWillMount() {
    const options = {
      theme: {
        logo: '../../../roomlet.png',
        primaryColor: '#3AB08F',
      },
      languageDictionary: {
        title: 'Roomlet',
      },
    }

    this.lock = new Auth0Lock(
      __AUTH0_CLIENT_ID__,
      __AUTH0_CLIENT_DOMAIN__,
      options
    )

    this.lock.on('signup submit', () => this.setState({ signUp: true }))

    this.lock.on('authenticated', authResult => {
      this.lock.getUserInfo(authResult.accessToken, (err, profile) => {
        if (err) return new Error('failed to authenticate')
        console.log('PROFILE', profile)
        this.props.login(authResult.accessToken)
        this.props.storeId(profile.user_id)
        // remove ! below to retore proper behavior
        !this.state.signUp
          ? this.props.history.push('/settings')
          : this.props.history.push('/dashboard')
      })
    })
  }
  showLock() {
    this.lock.show()
  }

  render() {
    return (

        Sign In
        <Loading lock={this.state.lock} />

    )
  }
}

export const mapStateToProps = state => ({})

export const mapDispatchToProps = dispatch => ({
  storeId: id => dispatch(storeId(id)),
  login: token => dispatch(login(token)),
  logout: () => dispatch(logout()),
})

export default connect(mapStateToProps, mapDispatchToProps)(LandingContainer)
<commit_msg>Update index.js<commit_after>import React from 'react'
import Loading from '../loading'
import Auth0Lock from 'auth0-lock'
import { connect } from 'react-redux'
import { storeId } from '../../action/user-id-actions.js'
import { login, logout } from '../../action/auth-actions.js'

class LandingContainer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      signUp: false,
    }
    this.showLock = this.showLock.bind(this)
  }

  componentWillMount() {
    const options = {
      theme: {
        logo: '../../../roomlet.png',
        primaryColor: '#3AB08F',
      },
      languageDictionary: {
        title: 'Roomlet',
      },
    }

    this.lock = new Auth0Lock(
      __AUTH0_CLIENT_ID__,
      __AUTH0_CLIENT_DOMAIN__,
      options
    )

    this.lock.on('signup submit', () => this.setState({ signUp: true }))

    this.lock.on('authenticated', authResult => {
      this.lock.getUserInfo(authResult.accessToken, (err, profile) => {
        if (err) return new Error('failed to authenticate')
        console.log('PROFILE', profile)
        this.props.login(authResult.accessToken)
        this.props.storeId(profile.user_id)

        this.state.signUp
          ? this.props.history.push('/settings')
          : this.props.history.push('/dashboard')
      })
    })
  }
  showLock() {
    this.lock.show()
  }

  render() {
    return (

        Sign In
        <Loading lock={this.state.lock} />

    )
  }
}

export const mapStateToProps = state => ({})

export const mapDispatchToProps = dispatch => ({
  storeId: id => dispatch(storeId(id)),
  login: token => dispatch(login(token)),
  logout: () => dispatch(logout()),
})

export default connect(mapStateToProps, mapDispatchToProps)(LandingContainer)
<|endoftext|>


<commit_before>// Native
import os from 'os'
import path from 'path'

// Ours
import {error} from '../error'

export default async dir => {
  if (!dir) {
    return
  }

  const home = os.homedir()
  let location

  const paths = {
    home,
    desktop: path.join(home, 'Desktop'),
    downloads: path.join(home, 'Downloads')
  }

  for (const locationPath in paths) {
    if (!{}.hasOwnProperty.call(paths, locationPath)) {
      continue
    }

    if (dir === paths[locationPath]) {
      location = locationPath
    }
  }

  let locationName

  switch (location) {
    case 'home':
      locationName = 'user directory'
      break
    case 'downloads':
      locationName = 'downloads directory'
      break
    default:
      locationName = location
  }

  error(`You're trying to deploy your ${locationName}.`)
  process.exit(1)
}
<commit_msg>Regular dirs can be deployed as usual<commit_after>// Native
import os from 'os'
import path from 'path'

// Ours
import {error} from '../error'

export default async dir => {
  if (!dir) {
    return
  }

  const home = os.homedir()
  let location

  const paths = {
    home,
    desktop: path.join(home, 'Desktop'),
    downloads: path.join(home, 'Downloads')
  }

  for (const locationPath in paths) {
    if (!{}.hasOwnProperty.call(paths, locationPath)) {
      continue
    }

    if (dir === paths[locationPath]) {
      location = locationPath
    }
  }

  if (!location) {
    return
  }

  let locationName

  switch (location) {
    case 'home':
      locationName = 'user directory'
      break
    case 'downloads':
      locationName = 'downloads directory'
      break
    default:
      locationName = location
  }

  error(`You're trying to deploy your ${locationName}.`)
  process.exit(1)
}
<|endoftext|>


<commit_before>import React from 'react';
import AuthForm from '../auth-form';
import { Link, NavLink } from 'react-router-dom'
import { connect } from 'react-redux';
import LoginForm from '../login-form'
import Navbar from '../navbar'
import * as utils from '../../lib/utils';
import { signupRequest, loginRequest, userFetchRequest } from '../../action/auth-actions';
import { stringify } from 'querystring';
import './_landing-container.scss';

class LandingContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showModal: true,
    };

  }

  close() {
    this.setState({ showModal: !this.state.showModal });
    // this.props.history.replace('/home');
  }

  render() {

    let { params } = this.props.match;
    let handleComplete = params.auth === 'login' ?
      this.props.login :
      this.props.signup;

    return (

Login

          {/* <AuthForm
            auth={params.auth}
            userFetchRequest={this.props.userFetchRequest}
            onComplete={handleComplete}
            buttonText={'submit'} /> */}

            <LoginForm
              auth={params.auth}
              userFetchRequest={this.props.userFetchRequest}
              onComplete={this.props.login}
              buttonText={'submit'}
            />

          <Link to="/">
Whoops, I actually want to sign up!
</Link>

    );
  }
}

let mapStateToProps = () => ({});
let mapDispatchToProps = dispatch => ({
  signup: user => dispatch(signupRequest(user)),
  login: user => dispatch(loginRequest(user)),
  userFetchRequest: () => dispatch(userFetchRequest()),
});

export default connect(mapStateToProps, mapDispatchToProps)(LandingContainer);
<commit_msg>semicolons<commit_after>import React from 'react';
import AuthForm from '../auth-form';
import { Link, NavLink } from 'react-router-dom';
import { connect } from 'react-redux';
import LoginForm from '../login-form';
import Navbar from '../navbar';
import * as utils from '../../lib/utils';
import { signupRequest, loginRequest, userFetchRequest } from '../../action/auth-actions';
import { stringify } from 'querystring';
import './_landing-container.scss';

class LandingContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showModal: true,
    };

  }

  close() {
    this.setState({ showModal: !this.state.showModal });
    // this.props.history.replace('/home');
  }

  render() {

    let { params } = this.props.match;
    let handleComplete = params.auth === 'login' ?
      this.props.login :
      this.props.signup;

    return (

Login

          {/* <AuthForm
            auth={params.auth}
            userFetchRequest={this.props.userFetchRequest}
            onComplete={handleComplete}
            buttonText={'submit'} /> */}

            <LoginForm
              auth={params.auth}
              userFetchRequest={this.props.userFetchRequest}
              onComplete={this.props.login}
              buttonText={'submit'}
            />

          <Link to="/">
Whoops, I actually want to sign up!
</Link>

    );
  }
}

let mapStateToProps = () => ({});
let mapDispatchToProps = dispatch => ({
  signup: user => dispatch(signupRequest(user)),
  login: user => dispatch(loginRequest(user)),
  userFetchRequest: () => dispatch(userFetchRequest()),
});

export default connect(mapStateToProps, mapDispatchToProps)(LandingContainer);
<|endoftext|>


<commit_before>var path = require('path');
var glob = require('glob');
var fs = require('fs-extra');
var async = require('async');
var parsePath = require('parse-filepath');
var _ = require('underscore');

var globPages = require('./glob-pages');

module.exports = function(program, cb) {
  var relativeDirectory = program.relativeDirectory;
  var directory = program.directory;

  return globPages(directory, function(err, pages) {

    // Async callback to copy each file.
    var copy = function(file, callback) {
      // Map file to path generated for that directory.
      // e.g. if file is in directory 2015-06-16-my-sweet-blog-post that got
      // rewritten to my-sweet-blog-post, we find that path rewrite so
      // our asset gets copied to the right directory.
      var parsed = parsePath(file);
      var relativePath = path.relative(directory + "/pages", file);
      var oldPath = parsePath(relativePath).dirname;

      // Wouldn't rewrite basePath
      if (oldPath === ".") {
        oldPath = "/";
        var newPath = "/" + parsed.basename;
      }

      if (!(oldPath === "/")) {
        var page = _.find(pages, function(page) {
          // Ignore files that start with underscore (they're not pages).
          if (page.file.name.slice(0,1) !== '_') {
            return parsePath(page.requirePath).dirname === oldPath;
          }
        });

        newPath = parsePath(page.path).dirname + parsed.basename;
      }

      newPath = directory + "/public/" + newPath;
      return fs.copy(file, newPath, function(err) {
        return callback(err);
      }
      );
    };

    // Copy static assets to public folder.
    return glob(directory + '/pages/**/?(*.jpg|*.png|*.pdf|*.gif|*.ico|*.svg)', null, function(err, files) {
      return async.map(files, copy, function(err, results) {
        return cb(err, results);
      });
    });
  });
};
<commit_msg>When copying static assets, don't try to rewrite directory names if file in directory not associated with a page<commit_after>var path = require('path');
var glob = require('glob');
var fs = require('fs-extra');
var async = require('async');
var parsePath = require('parse-filepath');
var _ = require('underscore');

var globPages = require('./glob-pages');

module.exports = function(program, cb) {
  var relativeDirectory = program.relativeDirectory;
  var directory = program.directory;

  return globPages(directory, function(err, pages) {

    // Async callback to copy each file.
    var copy = function(file, callback) {
      // Map file to path generated for that directory.
      // e.g. if file is in directory 2015-06-16-my-sweet-blog-post that got
      // rewritten to my-sweet-blog-post, we find that path rewrite so
      // our asset gets copied to the right directory.
      var parsed = parsePath(file);
      var relativePath = path.relative(directory + "/pages", file);
      var oldDirectory = parsePath(relativePath).dirname;

      // Wouldn't rewrite basePath
      if (oldDirectory === ".") {
        oldDirectory = "/";
        var newPath = "/" + parsed.basename;
      }

      if (!(oldDirectory === "/")) {
        var page = _.find(pages, function(page) {
          // Ignore files that start with underscore (they're not pages).
          if (page.file.name.slice(0,1) !== '_') {
            return parsePath(page.requirePath).dirname === oldDirectory;
          }
        });

        if (page) {
          newPath = parsePath(page.path).dirname + parsed.basename;
        }
        // We couldn't find a page associated with this file. Probably
        // the file is in a directory of static files. In any case,
        // we'll leave the file directory alone.
        else {
          newPath = relativePath;
        }
      }

      newPath = directory + "/public/" + newPath;
      return fs.copy(file, newPath, function(err) {
        return callback(err);
      }
      );
    };

    // Copy static assets to public folder.
    return glob(directory + '/pages/**/?(*.jpg|*.png|*.pdf|*.gif|*.ico|*.svg)', null, function(err, files) {
      return async.map(files, copy, function(err, results) {
        return cb(err, results);
      });
    });
  });
};
<|endoftext|>


<commit_before>/**
 * Created by Aus on 2017/5/25.
 */
import React from 'react'
import ZScroller from 'zscroller'
import classNames from 'classnames'

// picker-view 中的列
class PickerColumn extends React.Component {
    componentDidMount () {
        // 绑定事件
        this.bindScrollEvent();
        // 列表滚到对应位置
        this.scrollToPosition();
    }
    componentDidUpdate() {
        this.zscroller.reflow();
        this.scrollToPosition();
    }
    componentWillUnmount() {
        this.zscroller.destroy();
    }
    bindScrollEvent () {
        // 绑定滚动的事件
        const content = this.refs.content;
        // getBoundingClientRect js原生方法
        this.itemHeight = this.refs.indicator.getBoundingClientRect().height;

        // 最后还是用了何一鸣的zscroll插件
        // 但是这个插件并没有太多的文档介绍 gg
        // 插件demo地址：http://yiminghe.me/zscroller/examples/demo.html
        let t = this;
        this.zscroller = new ZScroller(content, {
            scrollbars: false,
            scrollingX: false,
            snapping: true, // 滚动结束之后 滑动对应的位置
            penetrationDeceleration: .1,
            minVelocityToKeepDecelerating: 0.5,
            scrollingComplete () {
                // 滚动结束 回调
                t.scrollingComplete();
            }
        });

        // 设置每个格子的高度 这样滚动结束 自动滚到对应格子上
        // 单位必须是px 所以要动态取一下
        this.zscroller.scroller.setSnapSize(0, this.itemHeight);
    }
    scrollingComplete () {
        // 滚动结束 判断当前选中值
        const { top } = this.zscroller.scroller.getValues();
        const {data, value, index, onValueChange} = this.props;

        let currentIndex = top / this.itemHeight;
        const floor = Math.floor(currentIndex);
        if (currentIndex - floor > 0.5) {
            currentIndex = floor + 1;
        } else {
            currentIndex = floor;
        }

        const selectedValue = data[currentIndex].value;

        if(selectedValue != value){
            // 值发生变化 通知父组件
            onValueChange(selectedValue, index);
        }
    }
    scrollToPosition () {
        // 滚动到选中的位置
        let {data, value} = this.props;

        data.map((item)=>{
            if(item.value == value){
                this.selectByIndex();
                return;
            }
        });

        for(let i = 0; i < data.length; i++){
            if(data[i].value == value){
                this.selectByIndex(i);
                return;
            }
        }

        this.selectByIndex(0);
    }
    selectByIndex (index) {
        // 滚动到index对应的位置
        let top = this.itemHeight * index;

        this.zscroller.scroller.scrollTo(0, top);
    }
    getCols () {
        // 根据value 和 index 获取到对应的data
        let {data, value, index} = this.props;
        let result = [];

        for(let i = 0; i < data.length; i++){
            result.push(
{data[i].label}
);
        }

        return result;
    }
    render () {
        let cols = this.getCols();

        return (

                        {cols}

        )
    }
}

export default PickerColumn;<commit_msg>增加PickerColumn参数校验<commit_after>/**
 * Created by Aus on 2017/5/25.
 */
import React from 'react'
import ZScroller from 'zscroller'
import classNames from 'classnames'

// picker-view 中的列
class PickerColumn extends React.Component {
    static propTypes = {
        index: React.PropTypes.number,
        data: React.PropTypes.array,
        value: React.PropTypes.array,
        onValueChange: React.PropTypes.func
    };
    componentDidMount () {
        // 绑定事件
        this.bindScrollEvent();
        // 列表滚到对应位置
        this.scrollToPosition();
    }
    componentDidUpdate() {
        this.zscroller.reflow();
        this.scrollToPosition();
    }
    componentWillUnmount() {
        this.zscroller.destroy();
    }
    bindScrollEvent () {
        // 绑定滚动的事件
        const content = this.refs.content;
        // getBoundingClientRect js原生方法
        this.itemHeight = this.refs.indicator.getBoundingClientRect().height;

        // 最后还是用了何一鸣的zscroll插件
        // 但是这个插件并没有太多的文档介绍 gg
        // 插件demo地址：http://yiminghe.me/zscroller/examples/demo.html
        let t = this;
        this.zscroller = new ZScroller(content, {
            scrollbars: false,
            scrollingX: false,
            snapping: true, // 滚动结束之后 滑动对应的位置
            penetrationDeceleration: .1,
            minVelocityToKeepDecelerating: 0.5,
            scrollingComplete () {
                // 滚动结束 回调
                t.scrollingComplete();
            }
        });

        // 设置每个格子的高度 这样滚动结束 自动滚到对应格子上
        // 单位必须是px 所以要动态取一下
        this.zscroller.scroller.setSnapSize(0, this.itemHeight);
    }
    scrollingComplete () {
        // 滚动结束 判断当前选中值
        const { top } = this.zscroller.scroller.getValues();
        const {data, value, index, onValueChange} = this.props;

        let currentIndex = top / this.itemHeight;
        const floor = Math.floor(currentIndex);
        if (currentIndex - floor > 0.5) {
            currentIndex = floor + 1;
        } else {
            currentIndex = floor;
        }

        const selectedValue = data[currentIndex].value;

        if(selectedValue != value){
            // 值发生变化 通知父组件
            onValueChange(selectedValue, index);
        }
    }
    scrollToPosition () {
        // 滚动到选中的位置
        let {data, value} = this.props;

        data.map((item)=>{
            if(item.value == value){
                this.selectByIndex();
                return;
            }
        });

        for(let i = 0; i < data.length; i++){
            if(data[i].value == value){
                this.selectByIndex(i);
                return;
            }
        }

        this.selectByIndex(0);
    }
    selectByIndex (index) {
        // 滚动到index对应的位置
        let top = this.itemHeight * index;

        this.zscroller.scroller.scrollTo(0, top);
    }
    getCols () {
        // 根据value 和 index 获取到对应的data
        let {data, value, index} = this.props;
        let result = [];

        for(let i = 0; i < data.length; i++){
            result.push(
{data[i].label}
);
        }

        return result;
    }
    render () {
        let cols = this.getCols();

        return (

                        {cols}

        )
    }
}

export default PickerColumn;<|endoftext|>


<commit_before>// @flow
/** Utilities for working with r5 versions */

/** Determine if a particular version is a release version */
export const RELEASE_VERSION_REGEX = /^v[0-9]+\.[0-9]+\.[0-9]+$/

/** Parse a version into its constituent pieces (major, minor, patch, distance from last release, commit) */
const VERSION_PARSE_REGEX = /^v([0-9]+).([0-9]+).([0-9]+)-?([0-9]+)?-?(.*)?$/

/** Compare two R5 versions to determine which is more recent */
export function compareR5Versions (a: string, b: string): number {
  const [, amajor, aminor, apatch, acommit] = a.match(VERSION_PARSE_REGEX)
  const [, bmajor, bminor, bpatch, bcommit] = b.match(VERSION_PARSE_REGEX)

  const intDiff = (a, b) => parseInt(a) - parseInt(b)

  const diffs = [intDiff(amajor, bmajor), intDiff(aminor, bminor), intDiff(apatch, bpatch)]
  for (const diff of diffs) {
    if (diff !== 0) return diff < 0 ? -1 : 1
  }

  // commit versions go above their release versions to keep chronology
  if (acommit == null && bcommit == null) return 0
  if (acommit == null) return -1
  if (bcommit == null) return 1

  const commitDiff = intDiff(acommit, bcommit)
  if (commitDiff < 0) return -1
  else if (commitDiff > 0) return 1

  return 0 // this should not happen, no two versions should be identical
}
<commit_msg>test(flow): Fix flow errors<commit_after>// @flow
/** Utilities for working with r5 versions */

/** Determine if a particular version is a release version */
export const RELEASE_VERSION_REGEX = /^v[0-9]+\.[0-9]+\.[0-9]+$/

/** Parse a version into its constituent pieces (major, minor, patch, distance from last release, commit) */
const VERSION_PARSE_REGEX = /^v([0-9]+).([0-9]+).([0-9]+)-?([0-9]+)?-?(.*)?$/

/** Compare two R5 versions to determine which is more recent */
export function compareR5Versions (a: string, b: string): number {
  const aMatch = a.match(VERSION_PARSE_REGEX)
  const bMatch = b.match(VERSION_PARSE_REGEX)
  if (aMatch && bMatch) {
    const [, amajor, aminor, apatch, acommit] = aMatch
    const [, bmajor, bminor, bpatch, bcommit] = bMatch

    const intDiff = (a, b) => parseInt(a) - parseInt(b)

    const diffs = [intDiff(amajor, bmajor), intDiff(aminor, bminor), intDiff(apatch, bpatch)]
    for (const diff of diffs) {
      if (diff !== 0) return diff < 0 ? -1 : 1
    }

    // commit versions go above their release versions to keep chronology
    if (acommit == null && bcommit == null) return 0
    if (acommit == null) return -1
    if (bcommit == null) return 1

    const commitDiff = intDiff(acommit, bcommit)
    if (commitDiff < 0) return -1
    else if (commitDiff > 0) return 1
  }

  return 0 // this should not happen, no two versions should be identical
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import Paper from 'material-ui/Paper';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import './SearchResult.css';

class SearchResultItem extends Component {
  handleClick() {
    let step = '';
    switch (this.props.searchResult.state) {
      case 'Open' || 'Auction':
        // 1. aution / open go to start acution
        step = 'StartAuction';
        break;
      case 'Reveal':
        // 2. reveal go to reveal auction
        step = 'RevealAuction';
        break;
      case 'Owned':
        // 3. owned go to finialze auction
        // TODO check if the person should be the one finalize it
        step = 'FinalizeAuction';
        break;
      default:
        // 4. forbidden / not yet available do nothing
        alert('the domain name service is not available');
    }

    this.props.setStep(step);
    this.props.switchPage('auction');
  }

  render() {
    const actionBtn = (this.props.searchResult.state === 'Auction' ||
      this.props.searchResult.state === 'Open' ||
      this.props.searchResult.state === 'Reveal') ?

 this.handleClick()}>
        <IconButton aria-label="Buy Now">
          shopping_cart
        </IconButton>

 :

        <IconButton aria-label="Owned">
          not_interested
        </IconButton>

;

    return (
      <Paper className="SearchResult-paper">
        <Typography type="title" component="p" className="SearchResult-typography SearchResult-typography-front">
          {this.props.searchResult.searchName}.eth
        </Typography>
        <Typography type="title" component="p" className="SearchResult-typography SearchResult-typography-status">

Status

{this.props.searchResult.state}

        </Typography>
        {actionBtn}
      </Paper>
    );
  }
}

export const SearchResult = (props) => {
  const result = props.searchResult;
  return result &&
    (

        <SearchResultItem
          {...props}
        />

    );
};<commit_msg>Fix switch<commit_after>import React, {Component} from 'react';
import Paper from 'material-ui/Paper';
import Typography from 'material-ui/Typography';
import IconButton from 'material-ui/IconButton';
import './SearchResult.css';

class SearchResultItem extends Component {
  handleClick() {
    let step = '';
    switch (this.props.searchResult.state) {
      case 'Open':
      case 'Auction':
        // 1. aution / open go to start acution
        step = 'StartAuction';
        break;
      case 'Reveal':
        // 2. reveal go to reveal auction
        step = 'RevealAuction';
        break;
      case 'Owned':
        // 3. owned go to finialze auction
        // TODO check if the person should be the one finalize it
        step = 'FinalizeAuction';
        break;
      default:
        // 4. forbidden / not yet available do nothing
        alert('the domain name service is not available');
    }

    this.props.setStep(step);
    this.props.switchPage('auction');
  }

  render() {
    const actionBtn = (this.props.searchResult.state === 'Auction' ||
      this.props.searchResult.state === 'Open' ||
      this.props.searchResult.state === 'Reveal') ?

 this.handleClick()}>
        <IconButton aria-label="Buy Now">
          shopping_cart
        </IconButton>

 :

        <IconButton aria-label="Owned">
          not_interested
        </IconButton>

;

    return (
      <Paper className="SearchResult-paper">
        <Typography type="title" component="p" className="SearchResult-typography SearchResult-typography-front">
          {this.props.searchResult.searchName}.eth
        </Typography>
        <Typography type="title" component="p" className="SearchResult-typography SearchResult-typography-status">

Status

{this.props.searchResult.state}

        </Typography>
        {actionBtn}
      </Paper>
    );
  }
}

export const SearchResult = (props) => {
  const result = props.searchResult;
  return result &&
    (

        <SearchResultItem
          {...props}
        />

    );
};<|endoftext|>


<commit_before><commit_msg>added regexp for splitting menu lines<commit_after><|endoftext|>


<commit_before>'use babel';

import { dirname } from 'path';
import { existsSync, isFileSync, makeTreeSync, moveSync, statSync } from 'fs-plus';

/*
  code pulled from:
  https://github.com/atom/tree-view/blob/492e344149bd42755d90765607743173bb9b7c0a/lib/move-dialog.coffee
*/

function isNewPathValid(previousPath, nextPath) {
  if (!isFileSync(nextPath)) return true;

  /*
  New path exists so check if it points to the same file as the initial
  path to see if the case of the file name is being changed on a on a
  case insensitive filesystem.
  */
  const oldStat = statSync(previousPath);
  const newStat = statSync(nextPath);
  const haveSamePath = previousPath.toLowerCase() === nextPath.toLowerCase();
  const haveSameDev = oldStat.dev === newStat.dev;
  const haveSameIno = oldStat.ino === newStat.ino;

  return !(haveSamePath && haveSameDev && haveSameIno);
}

export default function renameFile(previousPath, nextPath) {
  if (!isNewPathValid(previousPath, nextPath)) return false;
  const directoryPath = dirname(nextPath);
  try {
    if (!existsSync(directoryPath)) {
      makeTreeSync(directoryPath);
    }
    moveSync(previousPath, nextPath);
    return true;
  } catch (e) {
    return false;
  }
}
<commit_msg>Remove unnecessary mkdirp before moving a file, since fs-plus handles it<commit_after>'use babel';

import { isFileSync, moveSync, statSync } from 'fs-plus';

/*
  code pulled from:
  https://github.com/atom/tree-view/blob/492e344149bd42755d90765607743173bb9b7c0a/lib/move-dialog.coffee
*/

function isNewPathValid(previousPath, nextPath) {
  if (!isFileSync(nextPath)) return true;

  /*
  New path exists so check if it points to the same file as the initial
  path to see if the case of the file name is being changed on a on a
  case insensitive filesystem.
  */
  const oldStat = statSync(previousPath);
  const newStat = statSync(nextPath);
  const haveSamePath = previousPath.toLowerCase() === nextPath.toLowerCase();
  const haveSameDev = oldStat.dev === newStat.dev;
  const haveSameIno = oldStat.ino === newStat.ino;

  return !(haveSamePath && haveSameDev && haveSameIno);
}

export default function renameFile(previousPath, nextPath) {
  if (!isNewPathValid(previousPath, nextPath)) return false;

  try {
    moveSync(previousPath, nextPath);
    return true;
  } catch (e) {
    atom.notifications.addError(e.message, {
      dismissable: true,
    });
    return false;
  }
}
<|endoftext|>


<commit_before>
var RRule = require('../lib/icalendar').RRule;

describe("RRule", function() {
    it("should parse RRULEs correctly", function() {
        expect(new RRule(RRule.parse('FREQ=YEARLY;BYMONTH=11;BYDAY=1SU')).valueOf())
            .toEqual({FREQ: 'YEARLY', BYMONTH: 11, BYDAY: [[1,0]]});
    });

    it("handles yearly recurrence", function() {
        var start = new Date(1970,2,8,2,0,0);
        var rrule = new RRule(RRule.parse('FREQ=YEARLY;BYMONTH=3;BYDAY=1SU'), start);
        var rrule5 = new RRule(RRule.parse('FREQ=YEARLY;INTERVAL=5;BYMONTH=3;BYDAY=1SU'), start);

        expect(rrule.next(new Date(2011,1,4)))
                .toEqual(new Date(2011,2,6,2,0,0));

        expect(rrule.next(new Date(2011,3,7)))
                .toEqual(new Date(2012,2,4,2,0,0));

        expect(rrule.next(new Date(2011,2,6,2,0,0)))
                .toEqual(new Date(2012,2,4,2,0,0));

        expect(rrule5.next(new Date(2011,1,4)))
                .toEqual(new Date(2015,2,1,2,0,0));
    });

    it("respects UNTIL parts", function() {
        var start = new Date(2011,0,1,2,0,0);
        var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU;UNTIL=20110201'), start);

        expect(rrule.next(new Date(2011,0,1,2,0,0)))
                .toEqual(new Date(2011,0,2,2,0,0));

        expect(rrule.next(new Date(2011,0,2,2,0,0)))
                .toEqual(null);
    });

    it("handles monthly recurrence", function() {
        var start = new Date(2011,0,1,2,0,0);
        var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU'), start);

        expect(rrule.next(new Date(2011,0,1,2,0,0)))
                .toEqual(new Date(2011,0,2,2,0,0));

        expect(rrule.next(new Date(2011,0,2,2,0,0)))
                .toEqual(new Date(2011,1,6,2,0,0));
    });

    it("handles monthly recurrence with an interval", function() {
        var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU;INTERVAL=3'),
                    new Date(2011,0,1,2,0,0));

        expect(rrule.nextOccurances(new Date(2011,0,1,2,0,0), 3))
                .toEqual([
                    new Date(2011,0,2,2,0,0),
                    new Date(2011,3,3,2,0,0),
                    new Date(2011,6,3,2,0,0)
                    ]);
    });

    it("handles daily recurrence", function() {
        var rrule = new RRule('FREQ=DAILY;BYDAY=1MO,2TU,3WE', new Date(2012,0,1));

        expect(rrule.nextOccurances(new Date(2012,0,1), 3))
                .toEqual([new Date(2012,0,2),
                    new Date(2012,0,10),
                    new Date(2012,0,18)]);
    });
});

<commit_msg>Refactored tests<commit_after>
var RRule = require('../lib/icalendar').RRule;

describe("RRule", function() {
    it("should parse RRULEs correctly", function() {
        expect(new RRule(RRule.parse('FREQ=YEARLY;BYMONTH=11;BYDAY=1SU')).valueOf())
            .toEqual({FREQ: 'YEARLY', BYMONTH: 11, BYDAY: [[1,0]]});
    });

    it("respects UNTIL parts", function() {
        var start = new Date(2011,0,1,2,0,0);
        var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU;UNTIL=20110201'), start);

        expect(rrule.next(new Date(2011,0,1,2,0,0)))
                .toEqual(new Date(2011,0,2,2,0,0));

        expect(rrule.next(new Date(2011,0,2,2,0,0)))
                .toEqual(null);
    });

    describe("yearly recurrence", function() {
        it("handles yearly recurrence", function() {
            var start = new Date(1970,2,8,2,0,0);
            var rrule = new RRule('FREQ=YEARLY;BYMONTH=3;BYDAY=1SU', new Date(1970,2,8,2,0,0));

            expect(rrule.next(new Date(2011,1,4)))
                    .toEqual(new Date(2011,2,6,2,0,0));

            expect(rrule.next(new Date(2011,3,7)))
                    .toEqual(new Date(2012,2,4,2,0,0));

            expect(rrule.next(new Date(2011,2,6,2,0,0)))
                    .toEqual(new Date(2012,2,4,2,0,0));
        });

        it("handles yearly recurrence with an interval", function() {
            var rrule5 = new RRule('FREQ=YEARLY;INTERVAL=5;BYMONTH=3;BYDAY=1SU', new Date(1970,2,8,2,0,0));

            expect(rrule5.next(new Date(2011,1,4)))
                    .toEqual(new Date(2015,2,1,2,0,0));
        });
    });

    describe("monthly recurrence", function() {
        it("handles monthly recurrence", function() {
            var start = new Date(2011,0,1,2,0,0);
            var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU'), start);

            expect(rrule.next(new Date(2011,0,1,2,0,0)))
                    .toEqual(new Date(2011,0,2,2,0,0));

            expect(rrule.next(new Date(2011,0,2,2,0,0)))
                    .toEqual(new Date(2011,1,6,2,0,0));
        });

        it("handles monthly recurrence with an interval", function() {
            var rrule = new RRule(RRule.parse('FREQ=MONTHLY;BYDAY=1SU;INTERVAL=3'),
                        new Date(2011,0,1,2,0,0));

            expect(rrule.nextOccurances(new Date(2011,0,1,2,0,0), 3))
                    .toEqual([
                        new Date(2011,0,2,2,0,0),
                        new Date(2011,3,3,2,0,0),
                        new Date(2011,6,3,2,0,0)
                        ]);
        });
    });

    describe("daily recurrence", function() {
        it("handles daily recurrence", function() {
            var rrule = new RRule('FREQ=DAILY;BYDAY=1MO,2TU,3WE', new Date(2012,0,1));

            expect(rrule.nextOccurances(new Date(2012,0,1), 3))
                    .toEqual([new Date(2012,0,2),
                        new Date(2012,0,10),
                        new Date(2012,0,18)]);
        });
    });
});

<|endoftext|>