<commit_before>/**
 * Created by kiettv on 4/17/15.
 */
var Promise = require('bluebird');
var S3 = require('aws-sdk').S3;
var _ = require('lodash');

var Image = require('./image');

function Upload(awsBucketName, options) {
    this.opts = options ? options : {};
    if (!awsBucketName) {
        throw new TypeError('Bucket name can not be undefined');
    }

    if (this.opts.aws == null) {
        this.opts.aws = {};
    }
    if (this.opts.aws.region == null) {
        this.opts.aws.region = 'us-east-1';
    }
    if (this.opts.aws.path == null) {
        this.opts.path = '';
    }
    if (this.opts.aws.acl == null) {
        this.opts.aws.acl = 'private';
    }
    if (this.opts.aws.sslEnabled == null) {
        this.opts.aws.sslEnabled = true;
    }
    if (this.opts.aws.maxRetries == null) {
        this.opts.aws.maxRetries = 3;
    }
    if (this.opts.aws.accessKeyId == null) {
        throw new TypeError('Access Key Id can not be undefined');
    }
    if (this.opts.aws.secretAccessKey == null) {
        throw new TypeError('Secret Access Key can not be undefined');
    }
    if (this.opts.aws.params == null) {
        this.opts.aws.params = {};
    }
    this.opts.aws.params.Bucket = awsBucketName;
    if (this.opts.aws.httpOptions == null) {
        this.opts.aws.httpOptions = {};
    }
    if (this.opts.aws.httpOptions.timeout == null) {
        this.opts.aws.httpOptions.timeout = 10000;
    }
    if (this.opts.versions == null) {
        this.opts.versions = [];
    }
    if (this.opts.resizeQuality == null) {
        this.opts.resizeQuality = 70;
    }
    if(this.opts.returnExif == null){
        this.opts.returnExif = false;
    }
    if(this.opts.tmpDir == null){
        this.opts.tmpDir = require('os').tmpdir() + '/';
    }
    if(this.opts.tmpPrefix == null){
        this.opts.tmpPrefix = 'gm-'
    }
    if(!_.isFunction(this.opts.pathGenerator)){
        this.opts.pathGenerator = this.getRandomPath;
    }
    this.opts.url = "https://s3-" + this.opts.aws.region + ".amazonaws.com/" + this.opts.aws.params.Bucket + "/";

    this.s3 = new Promise.promisifyAll(new S3(this.opts.aws));
    return this;
}

Upload.prototype.getRandomPath = function () {
    var i, input, j, res, x, y;
    input = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    res = [];
    for (i = j = 1; j <= 3; i = ++j) {
        x = input[Math.floor(Math.random() * input.length)];
        y = input[Math.floor(Math.random() * input.length)];
        res.push(x + y);
    }
    return res.join('/');
};

Upload.prototype.uploadPathIsAvailable = function (path) {
    return this.s3.listObjectsAsync({Prefix: path})
        .then(function(data){
            return data.Contents.length === 0;
        });
};

Upload.prototype.uploadGeneratePath = function (prefix) {
    var path = prefix + this.opts.pathGenerator.call();
    if(!_.endsWith(path, '/')){
        path = path + '/';
    }
    return this.uploadPathIsAvailable(prefix + this.getRandomPath())
        .then(function(isAvailable){
            if(!isAvailable){
                throw new Error('Path \'' + path + '\' not available!')
            }
            return path;
        });
};

Upload.prototype.upload = function (src, opts) {
    var self = this;
    var prefix = (opts != null ? opts.path : void 0) || this.opts.aws.path;
    return this.uploadGeneratePath(prefix)
        .then(function(dest){
            return new Image(src, dest, opts, self).exec();
        });
};

module.exports = Upload;<commit_msg>Clean up<commit_after>/**
 * Created by kiettv on 4/17/15.
 */
var Promise = require('bluebird');
var S3 = require('aws-sdk').S3;
var _ = require('lodash');

var Image = require('./image');

function Upload(awsBucketName, options) {
    this.opts = options ? options : {};
    if (!awsBucketName) {
        throw new TypeError('Bucket name can not be undefined');
    }

    if (this.opts.aws == null) {
        this.opts.aws = {};
    }
    if (this.opts.aws.region == null) {
        this.opts.aws.region = 'us-east-1';
    }
    if (this.opts.aws.path == null) {
        this.opts.path = '';
    }
    if (this.opts.aws.acl == null) {
        this.opts.aws.acl = 'private';
    }
    if (this.opts.aws.sslEnabled == null) {
        this.opts.aws.sslEnabled = true;
    }
    if (this.opts.aws.maxRetries == null) {
        this.opts.aws.maxRetries = 3;
    }
    if (this.opts.aws.accessKeyId == null) {
        throw new TypeError('Access Key Id can not be undefined');
    }
    if (this.opts.aws.secretAccessKey == null) {
        throw new TypeError('Secret Access Key can not be undefined');
    }
    if (this.opts.aws.params == null) {
        this.opts.aws.params = {};
    }
    this.opts.aws.params.Bucket = awsBucketName;
    if (this.opts.aws.httpOptions == null) {
        this.opts.aws.httpOptions = {};
    }
    if (this.opts.aws.httpOptions.timeout == null) {
        this.opts.aws.httpOptions.timeout = 10000;
    }
    if (this.opts.versions == null) {
        this.opts.versions = [];
    }
    if (this.opts.resizeQuality == null) {
        this.opts.resizeQuality = 100;
    }
    if(this.opts.returnExif == null){
        this.opts.returnExif = false;
    }
    if(this.opts.tmpDir == null){
        this.opts.tmpDir = require('os').tmpdir() + '/';
    }
    if(this.opts.tmpPrefix == null){
        this.opts.tmpPrefix = 'gm-'
    }
    if(!_.isFunction(this.opts.pathGenerator)){
        this.opts.pathGenerator = this.getRandomPath;
    }
    if(this.opts.url == null) {
        this.opts.url = "https://s3-" + this.opts.aws.region + ".amazonaws.com/" + this.opts.aws.params.Bucket + "/";
    }

    this.s3 = new Promise.promisifyAll(new S3(this.opts.aws));
    return this;
}

Upload.prototype.getRandomPath = function () {
    var i, input, j, res, x, y;
    input = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    res = [];
    for (i = j = 1; j <= 3; i = ++j) {
        x = input[Math.floor(Math.random() * input.length)];
        y = input[Math.floor(Math.random() * input.length)];
        res.push(x + y);
    }
    return res.join('/');
};

Upload.prototype.uploadPathIsAvailable = function (path) {
    return this.s3.listObjectsAsync({Prefix: path})
        .then(function(data){
            return data.Contents.length === 0;
        });
};

Upload.prototype.uploadGeneratePath = function (prefix) {
    var path = prefix + this.opts.pathGenerator.call();
    if(!_.endsWith(path, '/')){
        path = path + '/';
    }
    return this.uploadPathIsAvailable(prefix + this.getRandomPath())
        .then(function(isAvailable){
            if(!isAvailable){
                throw new Error('Path \'' + path + '\' not available!')
            }
            return path;
        });
};

Upload.prototype.upload = function (src, opts) {
    var self = this;
    var prefix = (opts != null ? opts.path : void 0) || this.opts.aws.path;
    return this.uploadGeneratePath(prefix)
        .then(function(dest){
            return new Image(src, dest, opts, self).exec();
        });
};

module.exports = Upload;<|endoftext|>


<commit_before>'use strict';

const	EventEmitter	= require('events').EventEmitter,
	eventEmitter	= new EventEmitter(),
	dbmigration	= require('larvitdbmigration')({'tableName': 'product_db_version', 'migrationScriptsPath': __dirname + '/dbmigration'}),
	intercom	= require('larvitutils').instances.intercom,
	helpers	= require(__dirname + '/helpers.js'),
	lUtils	= require('larvitutils'),
	amsync	= require('larvitamsync'),
	async	= require('async'),
	log	= require('winston'),
	db	= require('larvitdb');

let	readyInProgress	= false,
	isReady	= false;

function listenToQueue(cb) {
	const	options	= {'exchange': exports.exchangeName};

	let	listenMethod;

	if (exports.mode === 'master') {
		listenMethod	= 'consume';
		options.exclusive	= true;	// It is important no other client tries to sneak
				// out messages from us, and we want "consume"
				// since we want the queue to persist even if this
				// minion goes offline.
	} else if (exports.mode === 'slave') {
		listenMethod = 'subscribe';
	} else {
		const	err	= new Error('Invalid exports.mode. Must be either "master" or "slave"');
		log.error('larvitproduct: dataWriter.js - listenToQueue() - ' + err.message);
		cb(err);
		return;
	}

	log.info('larvitproduct: dataWriter.js - listenToQueue() - listenMethod: ' + listenMethod);

	intercom[listenMethod](options, function(message, ack, deliveryTag) {
		exports.ready(function(err) {
			ack(err); // Ack first, if something goes wrong we log it and handle it manually

			if (err) {
				log.error('larvitproduct: dataWriter.js - listenToQueue() - intercom.' + listenMethod + '() - exports.ready() returned err: ' + err.message);
				return;
			}

			if (typeof message !== 'object') {
				log.error('larvitproduct: dataWriter.js - listenToQueue() - intercom.' + listenMethod + '() - Invalid message received, is not an object! deliveryTag: "' + deliveryTag + '"');
				return;
			}

			if (typeof exports[message.action] === 'function') {
				exports[message.action](message.params, deliveryTag, message.uuid);
			} else {
				log.warn('larvitproduct: dataWriter.js - listenToQueue() - intercom.' + listenMethod + '() - Unknown message.action received: "' + message.action + '"');
			}
		});
	}, cb);
}
// Run listenToQueue as soon as all I/O is done, this makes sure the exports.mode can be set
// by the application before listening commences
setImmediate(listenToQueue);

// This is ran before each incoming message on the queue is handeled
function ready(cb) {
	const	tasks	= [];

	if (isReady === true) { cb(); return; }

	if (readyInProgress === true) {
		eventEmitter.on('ready', cb);
		return;
	}

	readyInProgress = true;

	// We are strictly in need of the intercom!
	if ( ! (intercom instanceof require('larvitamintercom'))) {
		const	err	= new Error('larvitutils.instances.intercom is not an instance of Intercom!');
		log.error('larvitproduct: dataWriter.js - ' + err.message);
		throw err;
	}

	if (exports.mode === 'both' || exports.mode === 'slave') {
		log.verbose('larvitproduct: dataWriter.js: exports.mode: "' + exports.mode + '", so read');

		tasks.push(function(cb) {
			amsync.mariadb({'exchange': exports.exchangeName + '_dataDump'}, cb);
		});
	}

	// Migrate database
	tasks.push(function(cb) {
		dbmigration(function(err) {
			if (err) {
				log.error('larvitproduct: dataWriter.js: Database error: ' + err.message);
			}

			cb(err);
		});
	});

	async.series(tasks, function(err) {
		if (err) {
			return;
		}

		isReady	= true;
		eventEmitter.emit('ready');

		if (exports.mode === 'both' || exports.mode === 'master') {
			runDumpServer(cb);
		} else {
			cb();
		}
	});
}

function rmProduct(params, deliveryTag, msgUuid) {
	const	productUuid	= params.uuid,
		productUuidBuf	= lUtils.uuidToBuffer(productUuid),
		tasks	= [];

	// Delete attributes
	tasks.push(function(cb) {
		db.query('DELETE FROM product_product_attributes WHERE productUuid = ?;', [productUuidBuf], cb);
	});

	// Delete product
	tasks.push(function(cb) {
		db.query('DELETE FROM product_products WHERE uuid = ?;', [productUuidBuf], cb);
	});

	async.series(tasks, function(err) {
		exports.emitter.emit(msgUuid, err);
	});
}

function runDumpServer(cb) {
	const	options	= {'exchange': exports.exchangeName + '_dataDump'},
		args	= [];

	if (db.conf.host) {
		args.push('-h');
		args.push(db.conf.host);
	}

	args.push('-u');
	args.push(db.conf.user);

	if (db.conf.password) {
		args.push('-p' + db.conf.password);
	}

	args.push('--single-transaction');
	args.push(db.conf.database);

	// Tables
	args.push('product_attributes');
	args.push('product_db_version');
	args.push('product_products');
	args.push('product_product_attributes');

	options.dataDumpCmd = {
		'command':	'mysqldump',
		'args':	args
	};

	options['Content-Type'] = 'application/sql';

	new amsync.SyncServer(options, cb);
}

function writeProduct(params, deliveryTag, msgUuid) {
	const	productAttributes	= params.attributes,
		productUuid	= params.uuid,
		productUuidBuf	= lUtils.uuidToBuffer(productUuid),
		created	= params.created,
		tasks	= [];

	let	attributeUuidsByName;

	if (lUtils.formatUuid(productUuid) === false || productUuidBuf === false) {
		const err = new Error('Invalid productUuid: "' + productUuid + '"');
		log.error('larvitproduct: ./dataWriter.js - writeProduct() - ' + err.message);
		exports.emitter.emit(productUuid, err);
		return;
	}

	// Make sure the base product row exists
	tasks.push(function(cb) {
		const	sql	= 'INSERT IGNORE INTO product_products (uuid, created) VALUES(?,?)';

		db.query(sql, [productUuidBuf, created], cb);
	});

	// Clean out old attribute data
	tasks.push(function(cb) {
		db.query('DELETE FROM product_product_attributes WHERE productUuid = ?', [productUuidBuf], cb);
	});

	// By now we have a clean database, lets insert stuff!

	// Get all attribute uuids
	tasks.push(function(cb) {
		helpers.getAttributeUuidBuffers(Object.keys(productAttributes), function(err, result) {
			attributeUuidsByName = result;
			cb(err);
		});
	});

	// Insert attributes
	tasks.push(function(cb) {
		const	dbFields	= [];

		let	sql	= 'INSERT INTO product_product_attributes (productUuid, attributeUuid, `data`) VALUES';

		for (const fieldName of Object.keys(productAttributes)) {
			if ( ! (productAttributes[fieldName] instanceof Array)) {
				productAttributes[fieldName] = [productAttributes[fieldName]];
			}

			for (let i = 0; productAttributes[fieldName][i] !== undefined; i ++) {
				const	attributeData	= productAttributes[fieldName][i];
				sql += '(?,?,?),';
				dbFields.push(productUuidBuf);
				dbFields.push(attributeUuidsByName[fieldName]);
				dbFields.push(attributeData);
			}
		}

		sql = sql.substring(0, sql.length - 1) + ';';

		db.query(sql, dbFields, cb);
	});

	async.series(tasks, function(err) {
		exports.emitter.emit(msgUuid, err);
	});
}

function writeAttribute(params, deliveryTag, msgUuid) {
	const	uuid	= params.uuid,
		name	= params.name;

	db.query('INSERT IGNORE INTO product_attributes (uuid, name) VALUES(?,?)', [lUtils.uuidToBuffer(uuid), name], function(err) {
		exports.emitter.emit(msgUuid, err);
	});
}

exports.emitter	= new EventEmitter();
exports.exchangeName	= 'larvitproduct';
exports.listenToQueue	= listenToQueue;
exports.mode	= 'slave'; // or "master"
exports.ready	= ready;
exports.rmProduct	= rmProduct;
exports.writeAttribute	= writeAttribute;
exports.writeProduct	= writeProduct;
<commit_msg>Make sure ready is ran directly after listening to the queue<commit_after>'use strict';

const	EventEmitter	= require('events').EventEmitter,
	eventEmitter	= new EventEmitter(),
	dbmigration	= require('larvitdbmigration')({'tableName': 'product_db_version', 'migrationScriptsPath': __dirname + '/dbmigration'}),
	intercom	= require('larvitutils').instances.intercom,
	helpers	= require(__dirname + '/helpers.js'),
	lUtils	= require('larvitutils'),
	amsync	= require('larvitamsync'),
	async	= require('async'),
	log	= require('winston'),
	db	= require('larvitdb');

let	readyInProgress	= false,
	isReady	= false;

function listenToQueue(cb) {
	const	options	= {'exchange': exports.exchangeName};

	let	listenMethod;

	if (exports.mode === 'master') {
		listenMethod	= 'consume';
		options.exclusive	= true;	// It is important no other client tries to sneak
				// out messages from us, and we want "consume"
				// since we want the queue to persist even if this
				// minion goes offline.
	} else if (exports.mode === 'slave') {
		listenMethod = 'subscribe';
	} else {
		const	err	= new Error('Invalid exports.mode. Must be either "master" or "slave"');
		log.error('larvitproduct: dataWriter.js - listenToQueue() - ' + err.message);
		cb(err);
		return;
	}

	log.info('larvitproduct: dataWriter.js - listenToQueue() - listenMethod: ' + listenMethod);

	intercom[listenMethod](options, function(message, ack, deliveryTag) {
		exports.ready(function(err) {
			ack(err); // Ack first, if something goes wrong we log it and handle it manually

			if (err) {
				log.error('larvitproduct: dataWriter.js - listenToQueue() - intercom.' + listenMethod + '() - exports.ready() returned err: ' + err.message);
				return;
			}

			if (typeof message !== 'object') {
				log.error('larvitproduct: dataWriter.js - listenToQueue() - intercom.' + listenMethod + '() - Invalid message received, is not an object! deliveryTag: "' + deliveryTag + '"');
				return;
			}

			if (typeof exports[message.action] === 'function') {
				exports[message.action](message.params, deliveryTag, message.uuid);
			} else {
				log.warn('larvitproduct: dataWriter.js - listenToQueue() - intercom.' + listenMethod + '() - Unknown message.action received: "' + message.action + '"');
			}
		});
	}, ready);
}
// Run listenToQueue as soon as all I/O is done, this makes sure the exports.mode can be set
// by the application before listening commences
setImmediate(listenToQueue);

// This is ran before each incoming message on the queue is handeled
function ready(cb) {
	const	tasks	= [];

	if (isReady === true) { cb(); return; }

	if (readyInProgress === true) {
		eventEmitter.on('ready', cb);
		return;
	}

	readyInProgress = true;

	// We are strictly in need of the intercom!
	if ( ! (intercom instanceof require('larvitamintercom'))) {
		const	err	= new Error('larvitutils.instances.intercom is not an instance of Intercom!');
		log.error('larvitproduct: dataWriter.js - ' + err.message);
		throw err;
	}

	if (exports.mode === 'both' || exports.mode === 'slave') {
		log.verbose('larvitproduct: dataWriter.js: exports.mode: "' + exports.mode + '", so read');

		tasks.push(function(cb) {
			amsync.mariadb({'exchange': exports.exchangeName + '_dataDump'}, cb);
		});
	}

	// Migrate database
	tasks.push(function(cb) {
		dbmigration(function(err) {
			if (err) {
				log.error('larvitproduct: dataWriter.js: Database error: ' + err.message);
			}

			cb(err);
		});
	});

	async.series(tasks, function(err) {
		if (err) {
			return;
		}

		isReady	= true;
		eventEmitter.emit('ready');

		if (exports.mode === 'both' || exports.mode === 'master') {
			runDumpServer(cb);
		} else {
			cb();
		}
	});
}

function rmProduct(params, deliveryTag, msgUuid) {
	const	productUuid	= params.uuid,
		productUuidBuf	= lUtils.uuidToBuffer(productUuid),
		tasks	= [];

	// Delete attributes
	tasks.push(function(cb) {
		db.query('DELETE FROM product_product_attributes WHERE productUuid = ?;', [productUuidBuf], cb);
	});

	// Delete product
	tasks.push(function(cb) {
		db.query('DELETE FROM product_products WHERE uuid = ?;', [productUuidBuf], cb);
	});

	async.series(tasks, function(err) {
		exports.emitter.emit(msgUuid, err);
	});
}

function runDumpServer(cb) {
	const	options	= {'exchange': exports.exchangeName + '_dataDump'},
		args	= [];

	if (db.conf.host) {
		args.push('-h');
		args.push(db.conf.host);
	}

	args.push('-u');
	args.push(db.conf.user);

	if (db.conf.password) {
		args.push('-p' + db.conf.password);
	}

	args.push('--single-transaction');
	args.push(db.conf.database);

	// Tables
	args.push('product_attributes');
	args.push('product_db_version');
	args.push('product_products');
	args.push('product_product_attributes');

	options.dataDumpCmd = {
		'command':	'mysqldump',
		'args':	args
	};

	options['Content-Type'] = 'application/sql';

	new amsync.SyncServer(options, cb);
}

function writeProduct(params, deliveryTag, msgUuid) {
	const	productAttributes	= params.attributes,
		productUuid	= params.uuid,
		productUuidBuf	= lUtils.uuidToBuffer(productUuid),
		created	= params.created,
		tasks	= [];

	let	attributeUuidsByName;

	if (lUtils.formatUuid(productUuid) === false || productUuidBuf === false) {
		const err = new Error('Invalid productUuid: "' + productUuid + '"');
		log.error('larvitproduct: ./dataWriter.js - writeProduct() - ' + err.message);
		exports.emitter.emit(productUuid, err);
		return;
	}

	// Make sure the base product row exists
	tasks.push(function(cb) {
		const	sql	= 'INSERT IGNORE INTO product_products (uuid, created) VALUES(?,?)';

		db.query(sql, [productUuidBuf, created], cb);
	});

	// Clean out old attribute data
	tasks.push(function(cb) {
		db.query('DELETE FROM product_product_attributes WHERE productUuid = ?', [productUuidBuf], cb);
	});

	// By now we have a clean database, lets insert stuff!

	// Get all attribute uuids
	tasks.push(function(cb) {
		helpers.getAttributeUuidBuffers(Object.keys(productAttributes), function(err, result) {
			attributeUuidsByName = result;
			cb(err);
		});
	});

	// Insert attributes
	tasks.push(function(cb) {
		const	dbFields	= [];

		let	sql	= 'INSERT INTO product_product_attributes (productUuid, attributeUuid, `data`) VALUES';

		for (const fieldName of Object.keys(productAttributes)) {
			if ( ! (productAttributes[fieldName] instanceof Array)) {
				productAttributes[fieldName] = [productAttributes[fieldName]];
			}

			for (let i = 0; productAttributes[fieldName][i] !== undefined; i ++) {
				const	attributeData	= productAttributes[fieldName][i];
				sql += '(?,?,?),';
				dbFields.push(productUuidBuf);
				dbFields.push(attributeUuidsByName[fieldName]);
				dbFields.push(attributeData);
			}
		}

		sql = sql.substring(0, sql.length - 1) + ';';

		db.query(sql, dbFields, cb);
	});

	async.series(tasks, function(err) {
		exports.emitter.emit(msgUuid, err);
	});
}

function writeAttribute(params, deliveryTag, msgUuid) {
	const	uuid	= params.uuid,
		name	= params.name;

	db.query('INSERT IGNORE INTO product_attributes (uuid, name) VALUES(?,?)', [lUtils.uuidToBuffer(uuid), name], function(err) {
		exports.emitter.emit(msgUuid, err);
	});
}

exports.emitter	= new EventEmitter();
exports.exchangeName	= 'larvitproduct';
exports.listenToQueue	= listenToQueue;
exports.mode	= 'slave'; // or "master"
exports.ready	= ready;
exports.rmProduct	= rmProduct;
exports.writeAttribute	= writeAttribute;
exports.writeProduct	= writeProduct;
<|endoftext|>


<commit_before>'use strict';

const errors = require('./errors');
const TypedArray = require('./typed_array_util');
const deserializeInteger = require('./deserialize/integer');
const deserializeDelta = require('./deserialize/delta');
const NearCache = require('./address_caches/near');
const SameCache = require('./address_caches/same');

/**
 *
 * @param delta {Uint8Array}
 * @param source {Uint8Array}
 * @constructor
 */
function VCDiff(delta, source) {
  this.delta = delta;
  this.position = 0;
  this.source = source;
  this.targetWindows = new TypedArray.TypedArrayList();
}

VCDiff.prototype.decode = function() {
  this._consumeHeader();
  while (this._consumeWindow()) {}

  let targetLength = this.targetWindows.typedArrays.reduce((sum, uint8Array) => uint8Array.length + sum, 0);
  let target = new Uint8Array(targetLength);
  let position = 0;

  // concat all uint8arrays
  for (let arrayNum = 0; arrayNum < this.targetWindows.typedArrays.length; arrayNum++) {
    let array = this.targetWindows.typedArrays[arrayNum];
    let length = array.length;
    target.set(array, position);
    position += length;
  }

  return target;
};

VCDiff.prototype._consumeHeader = function() {

  let hasVCDiffHeader = this.delta[0] === 214 && // V
      this.delta[1] === 195 && // C
      this.delta[2] === 196 && // D
      this.delta[3] === 0; // \0

  if (!hasVCDiffHeader) {
    throw new errors.InvalidDelta('first 3 bytes not VCD');
  }

  let hdrIndicator = this.delta[4];
  // extract least significant bit
  let vcdDecompress = 1 & hdrIndicator;
  // extract second least significant bit
  let vcdCodetable = 1 & (hdrIndicator >> 1);

  // verify not using Hdr_Indicator
  if (vcdDecompress || vcdCodetable) {
    throw new errors.NotImplemented(
      'non-zero Hdr_Indicator (VCD_DECOMPRESS or VCD_CODETABLE bit is set)'
    );
  }

  this.position += 5;
};

VCDiff.prototype._consumeWindow = function() {
  let winIndicator = this.delta[this.position++];

  // extract least significant bit
  let vcdSource = 1 & winIndicator;
  // extract second least significant bit
  let vcdTarget = 1 & (winIndicator >> 1);

  if (vcdSource && vcdTarget) {
    throw new errors.InvalidDelta(
      'VCD_SOURCE and VCD_TARGET cannot both be set in Win_Indicator'
    )
  }
  else if (vcdSource) {
    let sourceSegmentLength, sourceSegmentPosition, deltaLength;
    ({ value: sourceSegmentLength, position: this.position } = deserializeInteger(this.delta, this.position));
    ({ value: sourceSegmentPosition, position: this.position } = deserializeInteger(this.delta, this.position));
    ({ value: deltaLength, position: this.position } = deserializeInteger(this.delta, this.position));

    let sourceSegment = this.source.slice(sourceSegmentPosition, sourceSegmentPosition + sourceSegmentLength);
    this._buildTargetWindow(this.position, sourceSegment);
    this.position += deltaLength;
  }
  else if (vcdTarget) {
    throw new errors.NotImplemented(
      'non-zero VCD_TARGET in Win_Indicator'
    )
  }
  else {
    let deltaLength;
    ({ value: deltaLength, position: this.position } = deserializeInteger(this.delta, this.position));

    this._buildTargetWindow(this.position);
    this.position += deltaLength;
  }

  return this.position < this.delta.length;
};

// first integer is target window length
VCDiff.prototype._buildTargetWindow = function(position, sourceSegment) {
  let window = deserializeDelta(this.delta, position);

  let T = new Uint8Array(window.targetWindowLength);

  let U = new TypedArray.TypedArrayList();
  let uTargetPosition = 0;
  if (sourceSegment) {
    U.add(sourceSegment);
    uTargetPosition = sourceSegment.length;
  }
  U.add(T);

  let targetPosition = this.source.length;
  let dataPosition = 0;

  let delta = new Delta(U, uTargetPosition, window.data, window.addresses);
  window.instructions.forEach(instruction => {
    instruction.execute(delta);
  });

  let target = U.typedArrays[1];
  this.targetWindows.add(target);
};

function Delta(U, UTargetPosition, data, addresses) {
  this.U = U;
  this.UTargetPosition = UTargetPosition;
  this.data = data;
  this.dataPosition = 0;
  this.addresses = addresses;
  this.addressesPosition = 0;
  this.nearCache = new NearCache(4);
  this.sameCache = new SameCache(3);
}

Delta.prototype.getNextAddressInteger = function() {
  let value;
  // get next address and increase the address position for the next address
  ({value, position: this.addressesPosition } = deserializeInteger(this.addresses, this.addressesPosition));
  return value;
};

Delta.prototype.getNextAddressByte = function() {
  // get next address and increase the address position for the next address
  let value = this.addresses[this.addressesPosition++];
  return value;
};

module.exports = VCDiff;<commit_msg>Allowing VCD_Source and VCD_Target to be both 0<commit_after>'use strict';

const errors = require('./errors');
const TypedArray = require('./typed_array_util');
const deserializeInteger = require('./deserialize/integer');
const deserializeDelta = require('./deserialize/delta');
const NearCache = require('./address_caches/near');
const SameCache = require('./address_caches/same');

/**
 *
 * @param delta {Uint8Array}
 * @param source {Uint8Array}
 * @constructor
 */
function VCDiff(delta, source) {
  this.delta = delta;
  this.position = 0;
  this.source = source;
  this.targetWindows = new TypedArray.TypedArrayList();
}

VCDiff.prototype.decode = function() {
  this._consumeHeader();
  while (this._consumeWindow()) {}

  let targetLength = this.targetWindows.typedArrays.reduce((sum, uint8Array) => uint8Array.length + sum, 0);
  let target = new Uint8Array(targetLength);
  let position = 0;

  // concat all uint8arrays
  for (let arrayNum = 0; arrayNum < this.targetWindows.typedArrays.length; arrayNum++) {
    let array = this.targetWindows.typedArrays[arrayNum];
    let length = array.length;
    target.set(array, position);
    position += length;
  }

  return target;
};

VCDiff.prototype._consumeHeader = function() {

  let hasVCDiffHeader = this.delta[0] === 214 && // V
      this.delta[1] === 195 && // C
      this.delta[2] === 196 && // D
      this.delta[3] === 0; // \0

  if (!hasVCDiffHeader) {
    throw new errors.InvalidDelta('first 3 bytes not VCD');
  }

  let hdrIndicator = this.delta[4];
  // extract least significant bit
  let vcdDecompress = 1 & hdrIndicator;
  // extract second least significant bit
  let vcdCodetable = 1 & (hdrIndicator >> 1);

  // verify not using Hdr_Indicator
  if (vcdDecompress || vcdCodetable) {
    throw new errors.NotImplemented(
      'non-zero Hdr_Indicator (VCD_DECOMPRESS or VCD_CODETABLE bit is set)'
    );
  }

  this.position += 5;
};

VCDiff.prototype._consumeWindow = function() {
  let winIndicator = this.delta[this.position++];

  // extract least significant bit
  let vcdSource = 1 & winIndicator;
  // extract second least significant bit
  let vcdTarget = 1 & (winIndicator >> 1);

  if (vcdSource && vcdTarget) {
    throw new errors.InvalidDelta(
      'VCD_SOURCE and VCD_TARGET cannot both be set in Win_Indicator'
    )
  }
  else if (vcdSource) {
    let sourceSegmentLength, sourceSegmentPosition, deltaLength;
    ({ value: sourceSegmentLength, position: this.position } = deserializeInteger(this.delta, this.position));
    ({ value: sourceSegmentPosition, position: this.position } = deserializeInteger(this.delta, this.position));
    ({ value: deltaLength, position: this.position } = deserializeInteger(this.delta, this.position));

    let sourceSegment = this.source.slice(sourceSegmentPosition, sourceSegmentPosition + sourceSegmentLength);
    this._buildTargetWindow(this.position, sourceSegment);
    this.position += deltaLength;
  }
  else if (vcdTarget) {
    throw new errors.NotImplemented(
      'non-zero VCD_TARGET in Win_Indicator'
    )
  }
  else {
    let deltaLength;
    ({ value: deltaLength, position: this.position } = deserializeInteger(this.delta, this.position));

    this._buildTargetWindow(this.position);
    this.position += deltaLength;
  }

  return this.position < this.delta.length;
};

// first integer is target window length
VCDiff.prototype._buildTargetWindow = function(position, sourceSegment) {
  let window = deserializeDelta(this.delta, position);

  let T = new Uint8Array(window.targetWindowLength);

  let U = new TypedArray.TypedArrayList();
  let uTargetPosition = 0;
  if (sourceSegment) {
    U.add(sourceSegment);
    uTargetPosition = sourceSegment.length;
  }
  U.add(T);

  let targetPosition = this.source.length;
  let dataPosition = 0;

  let delta = new Delta(U, uTargetPosition, window.data, window.addresses);
  window.instructions.forEach(instruction => {
    instruction.execute(delta);
  });

  this.targetWindows.add(T);
};

function Delta(U, UTargetPosition, data, addresses) {
  this.U = U;
  this.UTargetPosition = UTargetPosition;
  this.data = data;
  this.dataPosition = 0;
  this.addresses = addresses;
  this.addressesPosition = 0;
  this.nearCache = new NearCache(4);
  this.sameCache = new SameCache(3);
}

Delta.prototype.getNextAddressInteger = function() {
  let value;
  // get next address and increase the address position for the next address
  ({value, position: this.addressesPosition } = deserializeInteger(this.addresses, this.addressesPosition));
  return value;
};

Delta.prototype.getNextAddressByte = function() {
  // get next address and increase the address position for the next address
  let value = this.addresses[this.addressesPosition++];
  return value;
};

module.exports = VCDiff;<|endoftext|>


<commit_before>// Chromosome for Evolutionary Algorithms that uses floating point vectors
/*jslint node: true */
'use strict';
// * @license GPL v3
// * @package nodeo
// * @author J. J. Merelo <jjmerelo@gmail.com>

/*jshint smarttabs:true */

// Applies operators to the pool
function reproduction(  pool ) {
/*jshint validthis: true */
    var offspring = [];
    while (pool.length ) {
	var first = pool.splice( Math.floor(Math.random()*pool.length), 1 );
	var second = pool.splice( Math.floor(Math.random()*pool.length), 1 );
	var crossovers = crossover( first[0], second[0] );
	for ( var i in crossovers ) {
	    offspring.push( mutate(crossovers[i]));
	}
    }
    return offspring;
}

// Creates a single chromosome. Includes all utility functions
function Vector(vector,fitness){
    this.vector = vector;
    this.fitness = fitness;
}

// Changes a single element
function mutate (chromosome ) {

    var mutation_point = Math.floor( Math.random() * chromosome.vector.length);
    var temp = [];
    for ( var i in chromosome.vector ) {
	temp[i] = chromosome.vector[i];
    }
    temp[mutation_point] = temp[mutation_point]-0.2+Math.random()*0.1;
    return new Vector( temp );
}

// Interchanges a substring between the two parents
function crossover ( chrom1, chrom2 ) {
    var length = chrom1.vector.length;
    var xover_point = Math.floor( Math.random() * length);
    var range = 1 + Math.floor(Math.random() * (length - xover_point) );
    var new_chrom1 = chrom1.vector.splice(0,xover_point);
    var new_chrom2 = chrom2.vector.splice(0,xover_point);
    new_chrom1.push( chrom2.vector.splice(xover_point,xover_point));
    new_chrom1.push( chrom1.vector.splice(xover_point+range,length) );
    new_chrom2.push( chrom1.vector.splice(xover_point,xover_point+range) );
    new_chrom2.push( chrom2.vector.splice(xover_point+range,length));
    return [new Vector(new_chrom1), new Vector(new_chrom2)];
}

// Returns a random vector of `n` components with minimum `min` and range `range`
function random( n, min, range ) {
    var $vector = [];
    for (var i = 0; i < n; i ++ ) {
	$vector.push( min+Math.random()*range );
    }
    return $vector;
}

module.exports =  {
    version: '0.0.1',
    Vector : Vector,
    mutate: mutate,
    crossover: crossover,
    random: random,
    reproduction: reproduction };
<commit_msg>Helping hints<commit_after>// Chromosome for Evolutionary Algorithms that uses floating point vectors
/*jslint node: true */
'use strict';
// * @license GPL v3
// * @package nodeo
// * @author J. J. Merelo <jjmerelo@gmail.com>

/*jshint smarttabs:true */

// Applies operators to the pool
function reproduction(  pool ) {
    var offspring = [];
    while (pool.length ) {
	var first = pool.splice( Math.floor(Math.random()*pool.length), 1 );
	var second = pool.splice( Math.floor(Math.random()*pool.length), 1 );
	var crossovers = crossover( first[0], second[0] );
	for ( var i in crossovers ) {
	    offspring.push( mutate(crossovers[i]));
	}
    }
    return offspring;
}

// Creates a single chromosome. Includes all utility functions
function Vector(vector,fitness){
    /*jshint validthis: true */
    this.vector = vector;
    this.fitness = fitness;
}

// Changes a single element
function mutate (chromosome ) {

    var mutation_point = Math.floor( Math.random() * chromosome.vector.length);
    var temp = [];
    for ( var i in chromosome.vector ) {
	temp[i] = chromosome.vector[i];
    }
    temp[mutation_point] = temp[mutation_point]-0.2+Math.random()*0.1;
    return new Vector( temp );
}

// Interchanges a substring between the two parents
function crossover ( chrom1, chrom2 ) {
    var length = chrom1.vector.length;
    var xover_point = Math.floor( Math.random() * length);
    var range = 1 + Math.floor(Math.random() * (length - xover_point) );
    var new_chrom1 = chrom1.vector.splice(0,xover_point);
    var new_chrom2 = chrom2.vector.splice(0,xover_point);
    new_chrom1.push( chrom2.vector.splice(xover_point,xover_point));
    new_chrom1.push( chrom1.vector.splice(xover_point+range,length) );
    new_chrom2.push( chrom1.vector.splice(xover_point,xover_point+range) );
    new_chrom2.push( chrom2.vector.splice(xover_point+range,length));
    return [new Vector(new_chrom1), new Vector(new_chrom2)];
}

// Returns a random vector of `n` components with minimum `min` and range `range`
function random( n, min, range ) {
    var $vector = [];
    for (var i = 0; i < n; i ++ ) {
	$vector.push( min+Math.random()*range );
    }
    return $vector;
}

module.exports =  {
    version: '0.0.1',
    Vector : Vector,
    mutate: mutate,
    crossover: crossover,
    random: random,
    reproduction: reproduction };
<|endoftext|>


<commit_before>/**
 * A module for retrieving information about Venues from Foursquare.
 * @param {Object} config A valid configuration.
 * @module node-foursquare/Venues
 */
module.exports = function(config) {
  var core = require("./core")(config),
    logger = require('log4js')(config.log4js).getLogger("node-foursquare.Venues");

  /**
   * Retrieve a list of Venue Categories.
   * @memberof module:node-foursquare/Venues
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} accessToken The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/categories.html
   */
  function getCategories(params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getCategories");
    core.callApi("/venues/categories", accessToken, params || {}, callback);
  }

  /**
   * Explore Foursquare Venues.
   * @memberof module:node-foursquare/Venues
   * @param {String|Number} lat The latitude of the location around which to explore.
   * @param {String|Number} lng The longitude of the location around which to explore.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} accessToken The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/explore.html
   */
  function explore(lat, lng, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.explore");
    params = params || {};

    if(!lat || !lng) {
      logger.error("Lat and Lng are both required parameters.");
      callback(new Error("Venues.explore: lat and lng are both required."));
      return;
    }
    params.ll = lat + "," + lng;

    core.callApi("/venues/explore", accessToken, params, callback);
  }

  /**
   * Search Foursquare Venues.
   * @memberof module:node-foursquare/Venues
   * @param {String|Number} lat The latitude of the location around which to search.
   * @param {String|Number} lng The longitude of the location around which to search.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/search.html
   */
  function search(lat, lng, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.search");
    params = params || {};

    if(!lat || !lng) {
      logger.error("Lat and Lng are both required parameters.");
      callback(new Error("Venues.explore: lat and lng are both required."));
      return;
    }
    params.ll = lat + "," + lng;

    core.callApi("/venues/search", accessToken, params, callback);
  }

  /**
   * Return Foursquare Venues near location with the most people currently checked in.
   * @memberof module:node-foursquare/Venues
   * @param {String|Number} lat The latitude of the location around which to search.
   * @param {String|Number} lng The longitude of the location around which to search.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/trending.html
   */
  function getTrending(lat, lng, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.trending");
    params = params || {};

    if(!lat || !lng) {
      logger.error("Lat and Lng are both required parameters.");
      callback(new Error("Venues.explore: lat and lng are both required."));
      return;
    }

    params.ll = lat + "," + lng;
    core.callApi("/venues/trending", accessToken, params, callback);
  }

  /**
   * Retrieve a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/venues.html
   */
  function getVenue(venueId, accessToken, callback) {
    logger.debug("ENTERING: Venues.getVenue");

    if (!venueId) {
      logger.error("getVenue: venueId is required.");
      callback(new Error("Venues.getVenue: venueId is required."));
      return;
    }

    core.callApi("/venues/" + venueId, accessToken, null, callback);
  }

  /**
   * Retrieve a specific aspect from the Venues endpoint.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {String} aspect The aspect to retrieve. Refer to Foursquare documentation for details on currently
   * supported aspects.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} accessToken The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/index_docs.html
   */
  function getVenueAspect(venueId, aspect, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getVenueAspect");

    if (!venueId) {
      logger.error("getVenueAspect: venueId is required.");
      callback(new Error("Venues.getVenueAspect: venueId is required."));
      return;
    }

    if (!aspect) {
      logger.error("getVenueAspect: aspect is required.");
      callback(new Error("Venues.getVenueAspect: aspect is required."));
      return;
    }

    core.callApi("/venues/" + venueId + '/' + aspect, accessToken, params || {}, callback);
  }

  /**
   * Retrieve Check-ins for Users who are at a Venue "now".
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/herenow.html
   */
  function getHereNow(venueId, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getHereNow");

    if (!venueId) {
      logger.error("getHereNow: venueId is required.");
      callback(new Error("Venues.getHereNow: venueId is required."));
      return;
    }

    core.callApi("/venues/" + venueId + '/' + "herenow", accessToken, params || {}, callback);
  }

  /**
   * Retrieve Tips for a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/tips.html
   */
  function getTips(venueId, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getTips");

    if (!venueId) {
      logger.error("getTips: venueId is required.");
      callback(new Error("Venues.getTips: venueId is required."));
      return;
    }

    getVenueAspect(venueId, "tips", params, accessToken, callback)
  }

  /**
   * Retrieve Photos for a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {String} [group="venue"] The type of photos to retrieve. Refer to Foursquare documentation for details
   * on currently supported groups.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user. NOTE: This may or may
   * not be required for certain types of photos associated to the venue. Refer to the Foursquare documentation for
   * details.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/photos.html
   */
  function getPhotos(venueId, group, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getPhotos");

    if (!venueId) {
      logger.error("getPhotos: venueId is required.");
      callback(new Error("Venues.getPhotos: venueId is required."));
      return;
    }

    params = params || {};
    params.group = group || "venue";
    getVenueAspect(venueId, "photos", params, accessToken, callback)
  }

  /**
   * Retrieve Links for a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/links.html
   */
  function getLinks(venueId, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getLinks");

    if (!venueId) {
      logger.error("getLinks: venueId is required.");
      callback(new Error("Venues.getLinks: venueId is required."));
      return;
    }

    getVenueAspect(venueId, "links", params, accessToken, callback)
  }

  return {
    "getCategories" : getCategories,
    "explore" : explore,
    "search" : search,
    "getTrending" : getTrending,
    "getVenue" : getVenue,
    "getVenueAspect" : getVenueAspect,
    "getHereNow" : getHereNow,
    "getTips" : getTips,
    "getPhotos" : getPhotos,
    "getLinks" : getLinks
  }
};<commit_msg>Downstream merge<commit_after>/**
 * A module for retrieving information about Venues from Foursquare.
 * @param {Object} config A valid configuration.
 * @module node-foursquare/Venues
 */
module.exports = function(config) {
  var core = require("./core")(config),
    logger = require('log4js')(config.log4js).getLogger("node-foursquare.Venues"),
      path = require('path');

  /**
   * Retrieve a list of Venue Categories.
   * @memberof module:node-foursquare/Venues
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} accessToken The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/categories.html
   */
  function getCategories(params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getCategories");
    core.callApi("/venues/categories", accessToken, params || {}, callback);
  }

  /**
   * Explore Foursquare Venues.
   * @memberof module:node-foursquare/Venues
   * @param {String|Number} lat The latitude of the location around which to explore.
   * @param {String|Number} lng The longitude of the location around which to explore.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} accessToken The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/explore.html
   */
  function explore(lat, lng, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.explore");
    params = params || {};

    if(!lat || !lng) {
      logger.error("Lat and Lng are both required parameters.");
      callback(new Error("Venues.explore: lat and lng are both required."));
      return;
    }
    params.ll = lat + "," + lng;

    core.callApi("/venues/explore", accessToken, params, callback);
  }

  /**
   * Search Foursquare Venues.
   * @memberof module:node-foursquare/Venues
   * @param {String|Number} lat The latitude of the location around which to search.
   * @param {String|Number} lng The longitude of the location around which to search.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/search.html
   */
  function search(lat, lng, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.search");
    params = params || {};

    if(!lat || !lng) {
      logger.error("Lat and Lng are both required parameters.");
      callback(new Error("Venues.explore: lat and lng are both required."));
      return;
    }
    params.ll = lat + "," + lng;

    core.callApi("/venues/search", accessToken, params, callback);
  }

  /**
   * Return Foursquare Venues near location with the most people currently checked in.
   * @memberof module:node-foursquare/Venues
   * @param {String|Number} lat The latitude of the location around which to search.
   * @param {String|Number} lng The longitude of the location around which to search.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/trending.html
   */
  function getTrending(lat, lng, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.trending");
    params = params || {};

    if(!lat || !lng) {
      logger.error("Lat and Lng are both required parameters.");
      callback(new Error("Venues.explore: lat and lng are both required."));
      return;
    }

    params.ll = lat + "," + lng;
    core.callApi("/venues/trending", accessToken, params, callback);
  }

  /**
   * Retrieve a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/venues.html
   */
  function getVenue(venueId, accessToken, callback) {
    logger.debug("ENTERING: Venues.getVenue");

    if (!venueId) {
      logger.error("getVenue: venueId is required.");
      callback(new Error("Venues.getVenue: venueId is required."));
      return;
    }

    core.callApi(path.join("/venues", venueId), accessToken, null, callback);
  }

  /**
   * Retrieve a specific aspect from the Venues endpoint.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {String} aspect The aspect to retrieve. Refer to Foursquare documentation for details on currently
   * supported aspects.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} accessToken The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/index_docs.html
   */
  function getVenueAspect(venueId, aspect, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getVenueAspect");

    if (!venueId) {
      logger.error("getVenueAspect: venueId is required.");
      callback(new Error("Venues.getVenueAspect: venueId is required."));
      return;
    }

    if (!aspect) {
      logger.error("getVenueAspect: aspect is required.");
      callback(new Error("Venues.getVenueAspect: aspect is required."));
      return;
    }

    core.callApi(path.join("/venues", venueId,  aspect), accessToken, params || {}, callback);
  }

  /**
   * Retrieve Check-ins for Users who are at a Venue "now".
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/herenow.html
   */
  function getHereNow(venueId, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getHereNow");

    if (!venueId) {
      logger.error("getHereNow: venueId is required.");
      callback(new Error("Venues.getHereNow: venueId is required."));
      return;
    }

    core.callApi(path.join("/venues", venueId,  "herenow"), accessToken, params || {}, callback);
  }

  /**
   * Retrieve Tips for a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/tips.html
   */
  function getTips(venueId, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getTips");

    if (!venueId) {
      logger.error("getTips: venueId is required.");
      callback(new Error("Venues.getTips: venueId is required."));
      return;
    }

    getVenueAspect(venueId, "tips", params, accessToken, callback)
  }

  /**
   * Retrieve Photos for a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {String} [group="venue"] The type of photos to retrieve. Refer to Foursquare documentation for details
   * on currently supported groups.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user. NOTE: This may or may
   * not be required for certain types of photos associated to the venue. Refer to the Foursquare documentation for
   * details.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/photos.html
   */
  function getPhotos(venueId, group, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getPhotos");

    if (!venueId) {
      logger.error("getPhotos: venueId is required.");
      callback(new Error("Venues.getPhotos: venueId is required."));
      return;
    }

    params = params || {};
    params.group = group || "venue";
    getVenueAspect(venueId, "photos", params, accessToken, callback)
  }

  /**
   * Retrieve Links for a Foursquare Venue.
   * @memberof module:node-foursquare/Venues
   * @param {String} venueId The id of a Foursquare Venue.
   * @param {Object} [params] An object containing additional parameters. Refer to Foursquare documentation for details
   * on currently supported parameters.
   * @param {String} [accessToken] The access token provided by Foursquare for the current user.
   * @param {Function} callback The function to call with results, function({Error} error, {Object} results).
   * @see https://developer.foursquare.com/docs/venues/links.html
   */
  function getLinks(venueId, params, accessToken, callback) {
    logger.debug("ENTERING: Venues.getLinks");

    if (!venueId) {
      logger.error("getLinks: venueId is required.");
      callback(new Error("Venues.getLinks: venueId is required."));
      return;
    }

    getVenueAspect(venueId, "links", params, accessToken, callback)
  }

  return {
    "getCategories" : getCategories,
    "explore" : explore,
    "search" : search,
    "getTrending" : getTrending,
    "getVenue" : getVenue,
    "getVenueAspect" : getVenueAspect,
    "getHereNow" : getHereNow,
    "getTips" : getTips,
    "getPhotos" : getPhotos,
    "getLinks" : getLinks
  }
};<|endoftext|>


<commit_before>var crassObjects = require('crass').objects;

// `cb` gets called with rulesets
exports.walk = function(stylesheet, cb) {

    function walkObject(obj) {
        if (obj instanceof crassObjects.Media) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.Page) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.FontFace) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.Keyframes) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.Supports) {
            obj.blocks.forEach(walkObject);
        } else if (obj instanceof crassObjects.Ruleset ||
                   obj instanceof crassObjects.PageMargin ||
                   obj instanceof crassObjects.Keyframe) {
            cb(obj);
        }
    }

    stylesheet.content.forEach(walkObject);
};
<commit_msg>Clean up /lib/walker.js<commit_after>var crassObjects = require('crass').objects;

/**
 * Walks a CSS parse tree and calls a callback when any ruleset-like
 * node is encountered.
 * @param {Stylesheet} stylesheet A CSS stylesheet object to traverse
 * @param {function} cb A callback to fire when a ruleset is encountered.
 * @returns {void}
 */
exports.walk = function walk(stylesheet, cb) {

    function walkObject(obj) {
        if (obj instanceof crassObjects.Media) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.Page) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.FontFace) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.Keyframes) {
            obj.content.forEach(walkObject);
        } else if (obj instanceof crassObjects.Supports) {
            obj.blocks.forEach(walkObject);
        } else if (obj instanceof crassObjects.Ruleset ||
                   obj instanceof crassObjects.PageMargin ||
                   obj instanceof crassObjects.Keyframe) {
            cb(obj);
        }
    }

    stylesheet.content.forEach(walkObject);

};
<|endoftext|>


<commit_before>(function($) {

  $.declare('weekly', {
    defaults: {
      startTime: 8,
      endTime: 6,
      weekOffset: 0,
      currentDate: new Date(),
      autoRender: true,
      fitText: true,
      fitTextMin: 11,
      fitTextMax: 15,
      template: '##template##'
    },

    init: function() {
      this.events = [];

      if (this.autoRender) {
        this.update();
      }
    },

    update: function() {
      this.render({
        timef: this.timef,
        getWeekSpan: this.proxy(this.getWeekSpan),
        currentDate: this.currentDate,
        dates: this.getDates(),
        times: this.getTimes()
      });

      for(var i = 0, c = this.events.length; i < c; i++) {
        this.renderEvent(this.events[i]);
      }

      this.timeDifference = (this.endTime + 13) - this.startTime;

      this.registerClickToCreate();

      this.highlightToday();

      if(!this.weekOffset) {
        this.el.find(".weekly-change-today-button").css('display', 'none');
      } else {
        this.el.find(".weekly-change-today-button").css('display', 'block');
      }

      this.el.find('.weekly-time-navigation .weekly-previous-week .week').html(this.getWeekSpan(this.currentDate, this.weekOffset - 1));
      this.el.find('.weekly-time-navigation .weekly-next-week .week').html(this.getWeekSpan(this.currentDate, this.weekOffset + 1));

      this.el.find('.weekly-time-navigation .weekly-header').html(this.getWeekSpan(this.currentDate, this.weekOffset));

      if (this.fitText) {
        this.el.find(".weekly-days .weekly-day, .weekly-times .weekly-time").fitText(1, {
          'minFontSize': this.fitTextMin,
          'maxFontSize': this.fitTextMax
        });
      }
    },

    highlightToday: function() {
      var today = new Date();

      this.el.find('[data-date="' + this.timef('%Y-%n-%j', today) + '"]').addClass('weekly-today');
    },

    registerClickToCreate: function() {
      this.mouseDown = false;
      this.pendingEvent = null;
      this.pendingEventStart = null;

      var gridDays = this.el.find('.weekly-grid .weekly-day');

      // Make sure anything previously bound is bound no more.
      gridDays.unbind('mousedown mousemove mouseup mouseout click');

      gridDays.on('mousedown', this.proxy(function(event){
        if(event.which !== 1 || $(event.target).is('.weekly-dragger')) return;
        this.mouseDown = true;

        if(this.pendingEvent) {
          this.pendingEvent.remove();
          this.pendingEvent = null;
        }
      }));

      gridDays.on('mouseup', this.proxy(function(){
        this.mouseDown = false;

        if(this.pendingEvent) {
          var eventData = this.pendingEvent.data();

          var parsedDate = eventData.date.split('-');

          this.addEvent({
            start: new Date(parsedDate[0], parsedDate[1], parsedDate[2], eventData.starttime),
            end: new Date(parsedDate[0], parsedDate[1], parsedDate[2], eventData.endtime)
          });

          this.pendingEvent.remove();
          this.pendingEvent = null;
          this.pendingEventStart = null;
        }
      }));

      gridDays.on('mousemove', this.proxy(function(event){
        if(this.mouseDown) {
          this.createEvent(event);
        }
      }));

      gridDays.on('click', this.proxy(function(event){
        if($(event.target).is('.weekly-time,.weekly-day')) {
          this.createEvent(event);
          gridDays.trigger('mouseup');
        }
      }));

      gridDays.on('mouseleave', this.proxy(function(event){
        if(this.mouseDown) {
          gridDays.trigger('mouseup');
        }
      }));
    },

    registerModifyEvent: function() {
      this.mouseDown = false;
      this.pendingEvent = null;
      this.pendingEventStart = null;

      var eventDraggers = this.el.find('.weekly-grid .weekly-dragger');

      // Make sure anything previously bound is bound no more.
      eventDraggers.unbind('mousedown mousemove mouseup mouseout click');

      eventDraggers.on('mousedown', this.proxy(function(event){
        if(event.which !== 1) return;
        this.mouseDown = true;

        this.eventOffset = 0;
      }));

      gridDays.on('mouseup', this.proxy(function(){
        this.mouseDown = false;

        if(this.eventOffset) {
          var eventData = this.pendingEvent.data();

          this.eventOffset = 0;
        }
      }));

      gridDays.on('mousemove', this.proxy(function(event){
        if(this.mouseDown) {
          this.createEvent(event);
        }
      }));

      gridDays.on('click', this.proxy(function(event){
        if($(event.target).is('.weekly-time,.weekly-day')) {
          this.createEvent(event);
          gridDays.trigger('mouseup');
        }
      }));

      gridDays.on('mouseleave', this.proxy(function(event){
        if(this.mouseDown) {
          gridDays.trigger('mouseup');
        }
      }));
    },

    createEvent: function(event) {
      var target = $(event.currentTarget);
      var targetOffset = target.parent().offset();
      var mouseOffsetTop = event.pageY - targetOffset.top;
      var dayHeight = $(event.currentTarget).height();
      var hourHeight = Math.round(dayHeight / this.timeDifference);

      var tempStart = Math.floor(mouseOffsetTop / hourHeight) * hourHeight;
      var tempEnd = Math.ceil(mouseOffsetTop / hourHeight) * hourHeight;

      if(this.pendingEventStart === null) {
        this.pendingEventStart = tempStart;
      }

      if(this.pendingEventEnd === null || tempEnd > this.pendingEventStart) {
        this.pendingEventEnd = tempEnd;
      }

      if(!this.pendingEvent) {
        target.append('

');
        this.pendingEvent = target.find('.weekly-event-pending');
        this.pendingEvent.data('date', target.data('date'));
      }

      this.pendingEvent.css({
        top: this.pendingEventStart,
        bottom: dayHeight - this.pendingEventEnd
      });

      this.pendingEvent.data('starttime', ((this.pendingEventStart / hourHeight) || 0) + this.startTime);
      this.pendingEvent.data('endtime', ((this.pendingEventEnd / hourHeight) || 1) + this.startTime);
    },

    modifyEvent: function(event) {
      var target = $(event.currentTarget);
      var targetOffset = target.parent().offset();
      var mouseOffsetTop = event.pageY - targetOffset.top;
      var dayHeight = $(event.currentTarget).height();
      var hourHeight = Math.round(dayHeight / this.timeDifference);

      var tempStart = Math.floor(mouseOffsetTop / hourHeight) * hourHeight;
      var tempEnd = Math.ceil(mouseOffsetTop / hourHeight) * hourHeight;

      console.log(tempStart, tempEnd);
    },

    getWeekSpan: function(date, offset) {
      var first = this.getFirstDayOfWeek(date, offset);
      var last = this.getLastDayOfWeek(date, offset);

      var span = this.timef('%M %d', first) + ' - ';
      if (first.getMonth() == last.getMonth()) {
        span += this.timef('%d', last);
      } else {
        span += this.timef('%M %d', last);
      }
      return span;
    },

    getFirstDayOfWeek: function(date, offset) {
      offset = offset || 0;
      var first = date.getDate() - date.getDay();
      var newDate = new Date(date.getTime());
      newDate.setDate(first + (offset * 7));
      return newDate;
    },

    getLastDayOfWeek: function(date, weekOffset) {
      weekOffset = weekOffset || 0;
      var first = date.getDate() - date.getDay();
      var newDate = new Date(date.getTime());
      newDate.setDate(first + 6 + (weekOffset * 7));
      return newDate;
    },

    getDates: function() {
      var curr = this.currentDate;

      var daysInWeek = 7;

      var days = [];
      var sunday = this.getFirstDayOfWeek(curr, this.weekOffset);

      for (var i = 0, c = daysInWeek; i < c; i++) {
        var d = new Date(sunday.getTime());
        d.setDate(d.getDate() - d.getDay() + i);
        days.push(d);
      }
      return days;
    },

    getTimes: function() {
      var end = this.endTime + 12;

      var times = [];
      for (var i = this.startTime; i <= end; i++) {
        var hour = (i > 12) ? i - 12 : i;
        var timeString = hour+':00 ';

        timeString += (i > 11) ? 'PM' : 'AM';

        times.push(timeString);
      }

      return times;
    },

    nextWeek: function() {
      this.changeDate(1);
    },

    prevWeek: function() {
      this.changeDate(-1);
    },

    jumpToday: function() {
      this.changeDate(0);
    },

    changeDate: function(offsetWeek) {
      if(!offsetWeek) {
        this.weekOffset = 0;
      } else {
        this.weekOffset += offsetWeek;
      }

      this.update();
    },

    renderEvent: function(event) {
      var startDate = event.start.getFullYear() + "-" + event.start.getMonth() + "-" + event.start.getDate();
      var startTime = event.start.toTimeString().slice(0,5);
      var endDate = event.end.getFullYear() + "-" + event.end.getMonth() + "-" + event.end.getDate();
      var endTime = event.end.toTimeString().slice(0,5);

      var topOffset = 100 - this.getTimeOffsetPercent(startTime);
      var bottomOffset = this.getTimeOffsetPercent(endTime);

      var eventTemplate = $('

');

      eventTemplate.data(event);

      eventTemplate.css({
        top: topOffset + '%',
        bottom: bottomOffset + '%'
      }).append([
        '<button data-action="removeEvent" class="weekly-delete">×</button>',
        '
' + this.timef('%g:%i %a', event.start) + ' -
' + this.timef('%g:%i %a', event.end) + '
',
        '
' + event.name + '
',
        '
' + event.description + '
',
        '

'
      ].join(''));

      var selectedDay = this.el.find('.weekly-grid .weekly-day[data-date="' + startDate + '"]');

      selectedDay.append(eventTemplate);

      if (this.fitText) {
        selectedDay.find(".weekly-event").fitText(1, {
          'minFontSize': this.fitTextMin,
          'maxFontSize': this.fitTextMax
        });
      }
    },

    toFraction: function(time) {
      if(time.toString().indexOf(':') === -1) {
        return time;
      }

      var parts = time.toString().split(':');

      return parseFloat(parts[0] + '.' + 100/(60/parts[1]));
    },

    getTimeOffsetPercent: function(time) {
      time = this.toFraction(time) - this.startTime;

      var diff = this.timeDifference - time;

      if(diff < 0) {
        diff = time - this.timeDifference;
      }

      var percent = (diff / this.timeDifference) * 100;

      return percent;
    },

    timef: function(format, time) {
      if(!time instanceof Date) return;

      var months = 'January|February|March|April|May|June|July|August|September|October|November|December'.split('|');
      var days = 'Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday'.split('|');

      // Implements PHP's date format syntax.
      return format.replace(/%d|%D|%j|%l|%S|%w|%F|%m|%M|%n|%Y|%y|%a|%A|%g|%G|%h|%H|%i|%s|%u|%e/g, function(match) {
        switch(match) {
          case '%d':
            return ("0" + time.getDate()).substr(-2,2);
          case '%D':
            return days[time.getDay()].substr(0,3);
          case '%j':
            return time.getDate();
          case '%l':
            return days[time.getDay()];
          case '%S':
            if(time.getDate() === 1) {
              return 'st';
            } else if(time.getDate() === 2) {
              return 'nd';
            } else if(time.getDate() === 3) {
              return 'rd';
            } else {
              return 'th';
            }
            break;
          case '%w':
            return time.getDay();
          case '%F':
            return months[time.getMonth()];
          case '%m':
            return ("0" + time.getMonth()).substr(-2,2);
          case '%M':
            return months[time.getMonth()].substr(0,3);
          case '%n':
            return time.getMonth();
          case '%Y':
            return time.getFullYear();
          case '%y':
            return time.getFullYear().toString().substr(-2,2);
          case '%a':
            return time.getHours() > 11 ? 'pm' : 'am';
          case '%A':
            return time.getHours() > 11 ? 'PM' : 'AM';
          case '%g':
            return time.getHours() > 12 ? time.getHours() -12 : time.getHours();
          case '%G':
          return time.getHours();
          case '%h':
            return ("0" + (time.getHours() > 12 ? time.getHours() -12 : time.getHours())).substr(-2,2);
          case '%H':
            return ("0" + time.getHours()).substr(-2,2);
          case '%i':
            return ("0" + time.getMinutes()).substr(-2,2);
          case '%s':
            return ("0" + time.getSeconds()).substr(-2,2);
          case '%u':
            return time.getMilliseconds();
          case '%e':
            return time.getTimezoneOffset();
        }
      });
    },

    addEvent: function(event) {
      if(event instanceof Array) {
        for(var i = 0, c = event.length; i < c; i++) {
          this.addEvent(event[i]);
        }
        return;
      }

      event = $.extend({
        name: '',
        description: '',
        start: null,
        end: null
      }, event);

      event._index = this.events.length;

      this.renderEvent(event);
      this.events.push(event);

      this.el.trigger('addEvent', [event]);
    },

    removeEvent: function(e) {
      var el = $(e.target).parents('.weekly-event');
      var event = el.data();

      this.events.splice(event._index, 1);
      el.remove();

      this.el.trigger('removeEvent', event);

      return false;
    },

    clearEvents: function() {
      this.el.find('.weekly-event').remove();

      this.events = [];

      this.el.trigger('clearedEvents');
    }
  });

})(jQuery);
<commit_msg>changed clearedEvents to clearEvents<commit_after>(function($) {

  $.declare('weekly', {
    defaults: {
      startTime: 8,
      endTime: 6,
      weekOffset: 0,
      currentDate: new Date(),
      autoRender: true,
      fitText: true,
      fitTextMin: 11,
      fitTextMax: 15,
      template: '##template##'
    },

    init: function() {
      this.events = [];

      if (this.autoRender) {
        this.update();
      }
    },

    update: function() {
      this.render({
        timef: this.timef,
        getWeekSpan: this.proxy(this.getWeekSpan),
        currentDate: this.currentDate,
        dates: this.getDates(),
        times: this.getTimes()
      });

      for(var i = 0, c = this.events.length; i < c; i++) {
        this.renderEvent(this.events[i]);
      }

      this.timeDifference = (this.endTime + 13) - this.startTime;

      this.registerClickToCreate();

      this.highlightToday();

      if(!this.weekOffset) {
        this.el.find(".weekly-change-today-button").css('display', 'none');
      } else {
        this.el.find(".weekly-change-today-button").css('display', 'block');
      }

      this.el.find('.weekly-time-navigation .weekly-previous-week .week').html(this.getWeekSpan(this.currentDate, this.weekOffset - 1));
      this.el.find('.weekly-time-navigation .weekly-next-week .week').html(this.getWeekSpan(this.currentDate, this.weekOffset + 1));

      this.el.find('.weekly-time-navigation .weekly-header').html(this.getWeekSpan(this.currentDate, this.weekOffset));

      if (this.fitText) {
        this.el.find(".weekly-days .weekly-day, .weekly-times .weekly-time").fitText(1, {
          'minFontSize': this.fitTextMin,
          'maxFontSize': this.fitTextMax
        });
      }
    },

    highlightToday: function() {
      var today = new Date();

      this.el.find('[data-date="' + this.timef('%Y-%n-%j', today) + '"]').addClass('weekly-today');
    },

    registerClickToCreate: function() {
      this.mouseDown = false;
      this.pendingEvent = null;
      this.pendingEventStart = null;

      var gridDays = this.el.find('.weekly-grid .weekly-day');

      // Make sure anything previously bound is bound no more.
      gridDays.unbind('mousedown mousemove mouseup mouseout click');

      gridDays.on('mousedown', this.proxy(function(event){
        if(event.which !== 1 || $(event.target).is('.weekly-dragger')) return;
        this.mouseDown = true;

        if(this.pendingEvent) {
          this.pendingEvent.remove();
          this.pendingEvent = null;
        }
      }));

      gridDays.on('mouseup', this.proxy(function(){
        this.mouseDown = false;

        if(this.pendingEvent) {
          var eventData = this.pendingEvent.data();

          var parsedDate = eventData.date.split('-');

          this.addEvent({
            start: new Date(parsedDate[0], parsedDate[1], parsedDate[2], eventData.starttime),
            end: new Date(parsedDate[0], parsedDate[1], parsedDate[2], eventData.endtime)
          });

          this.pendingEvent.remove();
          this.pendingEvent = null;
          this.pendingEventStart = null;
        }
      }));

      gridDays.on('mousemove', this.proxy(function(event){
        if(this.mouseDown) {
          this.createEvent(event);
        }
      }));

      gridDays.on('click', this.proxy(function(event){
        if($(event.target).is('.weekly-time,.weekly-day')) {
          this.createEvent(event);
          gridDays.trigger('mouseup');
        }
      }));

      gridDays.on('mouseleave', this.proxy(function(event){
        if(this.mouseDown) {
          gridDays.trigger('mouseup');
        }
      }));
    },

    registerModifyEvent: function() {
      this.mouseDown = false;
      this.pendingEvent = null;
      this.pendingEventStart = null;

      var eventDraggers = this.el.find('.weekly-grid .weekly-dragger');

      // Make sure anything previously bound is bound no more.
      eventDraggers.unbind('mousedown mousemove mouseup mouseout click');

      eventDraggers.on('mousedown', this.proxy(function(event){
        if(event.which !== 1) return;
        this.mouseDown = true;

        this.eventOffset = 0;
      }));

      gridDays.on('mouseup', this.proxy(function(){
        this.mouseDown = false;

        if(this.eventOffset) {
          var eventData = this.pendingEvent.data();

          this.eventOffset = 0;
        }
      }));

      gridDays.on('mousemove', this.proxy(function(event){
        if(this.mouseDown) {
          this.createEvent(event);
        }
      }));

      gridDays.on('click', this.proxy(function(event){
        if($(event.target).is('.weekly-time,.weekly-day')) {
          this.createEvent(event);
          gridDays.trigger('mouseup');
        }
      }));

      gridDays.on('mouseleave', this.proxy(function(event){
        if(this.mouseDown) {
          gridDays.trigger('mouseup');
        }
      }));
    },

    createEvent: function(event) {
      var target = $(event.currentTarget);
      var targetOffset = target.parent().offset();
      var mouseOffsetTop = event.pageY - targetOffset.top;
      var dayHeight = $(event.currentTarget).height();
      var hourHeight = Math.round(dayHeight / this.timeDifference);

      var tempStart = Math.floor(mouseOffsetTop / hourHeight) * hourHeight;
      var tempEnd = Math.ceil(mouseOffsetTop / hourHeight) * hourHeight;

      if(this.pendingEventStart === null) {
        this.pendingEventStart = tempStart;
      }

      if(this.pendingEventEnd === null || tempEnd > this.pendingEventStart) {
        this.pendingEventEnd = tempEnd;
      }

      if(!this.pendingEvent) {
        target.append('

');
        this.pendingEvent = target.find('.weekly-event-pending');
        this.pendingEvent.data('date', target.data('date'));
      }

      this.pendingEvent.css({
        top: this.pendingEventStart,
        bottom: dayHeight - this.pendingEventEnd
      });

      this.pendingEvent.data('starttime', ((this.pendingEventStart / hourHeight) || 0) + this.startTime);
      this.pendingEvent.data('endtime', ((this.pendingEventEnd / hourHeight) || 1) + this.startTime);
    },

    modifyEvent: function(event) {
      var target = $(event.currentTarget);
      var targetOffset = target.parent().offset();
      var mouseOffsetTop = event.pageY - targetOffset.top;
      var dayHeight = $(event.currentTarget).height();
      var hourHeight = Math.round(dayHeight / this.timeDifference);

      var tempStart = Math.floor(mouseOffsetTop / hourHeight) * hourHeight;
      var tempEnd = Math.ceil(mouseOffsetTop / hourHeight) * hourHeight;

      console.log(tempStart, tempEnd);
    },

    getWeekSpan: function(date, offset) {
      var first = this.getFirstDayOfWeek(date, offset);
      var last = this.getLastDayOfWeek(date, offset);

      var span = this.timef('%M %d', first) + ' - ';
      if (first.getMonth() == last.getMonth()) {
        span += this.timef('%d', last);
      } else {
        span += this.timef('%M %d', last);
      }
      return span;
    },

    getFirstDayOfWeek: function(date, offset) {
      offset = offset || 0;
      var first = date.getDate() - date.getDay();
      var newDate = new Date(date.getTime());
      newDate.setDate(first + (offset * 7));
      return newDate;
    },

    getLastDayOfWeek: function(date, weekOffset) {
      weekOffset = weekOffset || 0;
      var first = date.getDate() - date.getDay();
      var newDate = new Date(date.getTime());
      newDate.setDate(first + 6 + (weekOffset * 7));
      return newDate;
    },

    getDates: function() {
      var curr = this.currentDate;

      var daysInWeek = 7;

      var days = [];
      var sunday = this.getFirstDayOfWeek(curr, this.weekOffset);

      for (var i = 0, c = daysInWeek; i < c; i++) {
        var d = new Date(sunday.getTime());
        d.setDate(d.getDate() - d.getDay() + i);
        days.push(d);
      }
      return days;
    },

    getTimes: function() {
      var end = this.endTime + 12;

      var times = [];
      for (var i = this.startTime; i <= end; i++) {
        var hour = (i > 12) ? i - 12 : i;
        var timeString = hour+':00 ';

        timeString += (i > 11) ? 'PM' : 'AM';

        times.push(timeString);
      }

      return times;
    },

    nextWeek: function() {
      this.changeDate(1);
    },

    prevWeek: function() {
      this.changeDate(-1);
    },

    jumpToday: function() {
      this.changeDate(0);
    },

    changeDate: function(offsetWeek) {
      if(!offsetWeek) {
        this.weekOffset = 0;
      } else {
        this.weekOffset += offsetWeek;
      }

      this.update();
    },

    renderEvent: function(event) {
      var startDate = event.start.getFullYear() + "-" + event.start.getMonth() + "-" + event.start.getDate();
      var startTime = event.start.toTimeString().slice(0,5);
      var endDate = event.end.getFullYear() + "-" + event.end.getMonth() + "-" + event.end.getDate();
      var endTime = event.end.toTimeString().slice(0,5);

      var topOffset = 100 - this.getTimeOffsetPercent(startTime);
      var bottomOffset = this.getTimeOffsetPercent(endTime);

      var eventTemplate = $('

');

      eventTemplate.data(event);

      eventTemplate.css({
        top: topOffset + '%',
        bottom: bottomOffset + '%'
      }).append([
        '<button data-action="removeEvent" class="weekly-delete">×</button>',
        '
' + this.timef('%g:%i %a', event.start) + ' -
' + this.timef('%g:%i %a', event.end) + '
',
        '
' + event.name + '
',
        '
' + event.description + '
',
        '

'
      ].join(''));

      var selectedDay = this.el.find('.weekly-grid .weekly-day[data-date="' + startDate + '"]');

      selectedDay.append(eventTemplate);

      if (this.fitText) {
        selectedDay.find(".weekly-event").fitText(1, {
          'minFontSize': this.fitTextMin,
          'maxFontSize': this.fitTextMax
        });
      }
    },

    toFraction: function(time) {
      if(time.toString().indexOf(':') === -1) {
        return time;
      }

      var parts = time.toString().split(':');

      return parseFloat(parts[0] + '.' + 100/(60/parts[1]));
    },

    getTimeOffsetPercent: function(time) {
      time = this.toFraction(time) - this.startTime;

      var diff = this.timeDifference - time;

      if(diff < 0) {
        diff = time - this.timeDifference;
      }

      var percent = (diff / this.timeDifference) * 100;

      return percent;
    },

    timef: function(format, time) {
      if(!time instanceof Date) return;

      var months = 'January|February|March|April|May|June|July|August|September|October|November|December'.split('|');
      var days = 'Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday'.split('|');

      // Implements PHP's date format syntax.
      return format.replace(/%d|%D|%j|%l|%S|%w|%F|%m|%M|%n|%Y|%y|%a|%A|%g|%G|%h|%H|%i|%s|%u|%e/g, function(match) {
        switch(match) {
          case '%d':
            return ("0" + time.getDate()).substr(-2,2);
          case '%D':
            return days[time.getDay()].substr(0,3);
          case '%j':
            return time.getDate();
          case '%l':
            return days[time.getDay()];
          case '%S':
            if(time.getDate() === 1) {
              return 'st';
            } else if(time.getDate() === 2) {
              return 'nd';
            } else if(time.getDate() === 3) {
              return 'rd';
            } else {
              return 'th';
            }
            break;
          case '%w':
            return time.getDay();
          case '%F':
            return months[time.getMonth()];
          case '%m':
            return ("0" + time.getMonth()).substr(-2,2);
          case '%M':
            return months[time.getMonth()].substr(0,3);
          case '%n':
            return time.getMonth();
          case '%Y':
            return time.getFullYear();
          case '%y':
            return time.getFullYear().toString().substr(-2,2);
          case '%a':
            return time.getHours() > 11 ? 'pm' : 'am';
          case '%A':
            return time.getHours() > 11 ? 'PM' : 'AM';
          case '%g':
            return time.getHours() > 12 ? time.getHours() -12 : time.getHours();
          case '%G':
          return time.getHours();
          case '%h':
            return ("0" + (time.getHours() > 12 ? time.getHours() -12 : time.getHours())).substr(-2,2);
          case '%H':
            return ("0" + time.getHours()).substr(-2,2);
          case '%i':
            return ("0" + time.getMinutes()).substr(-2,2);
          case '%s':
            return ("0" + time.getSeconds()).substr(-2,2);
          case '%u':
            return time.getMilliseconds();
          case '%e':
            return time.getTimezoneOffset();
        }
      });
    },

    addEvent: function(event) {
      if(event instanceof Array) {
        for(var i = 0, c = event.length; i < c; i++) {
          this.addEvent(event[i]);
        }
        return;
      }

      event = $.extend({
        name: '',
        description: '',
        start: null,
        end: null
      }, event);

      event._index = this.events.length;

      this.renderEvent(event);
      this.events.push(event);

      this.el.trigger('addEvent', [event]);
    },

    removeEvent: function(e) {
      var el = $(e.target).parents('.weekly-event');
      var event = el.data();

      this.events.splice(event._index, 1);
      el.remove();

      this.el.trigger('removeEvent', event);

      return false;
    },

    clearEvents: function() {
      this.el.find('.weekly-event').remove();

      this.events = [];

      this.el.trigger('clearEvents');
    }
  });

})(jQuery);
<|endoftext|>


<commit_before>//
// worker.js
//
// A worker process that starts a new forked server instance
// which listens on a unique port

var cluster = require('cluster'),
	Server = require('./server'),
	log = require('./log'),
	lib = require('./lib');

if (cluster.isWorker) {
	// The config is passed through the process environment
	var worker = cluster.worker,
		process = worker.process,
		config = JSON.parse(process.env.config),
		server = new Server(config);

	// Valid command messages that the worker accepts
	// from the managing instance
	var commands = {
		start: function (message) {
			var port = message.port + worker.id;
			server.start(port);
			return { port: port };
		},

		stop: function () {
			// Shutdown all connections
			server.close(true);
			// TODO: consider implementing a timeout to handle long-lived connections
		},

		pause: function () {
			server.pause();
		}
	};

	process.on('message', function (message) {
		if (message.type in commands) {
			var response = commands[message.type](message);
			// Send ack back to server with response
			process.send(lib.apply({ type: 'ack', id: message.id }, response || {}));
		}
	});
}
else {
	// This file should never be called directly
	log.error('Worker.js not started through forked process.')
}
<commit_msg>adding worker support for multiple connection types<commit_after>//
// worker.js
//
// A worker process that starts a new forked server instance
// which listens on a unique port

var cluster = require('cluster'),
	TCPServer = require('servers/TCPServer'),
	UDPServer = require('servers/UDPServer'),
	WSServer = require('servers/WebSocketServer'),
	log = require('./log'),
	lib = require('./lib');

if (cluster.isWorker) {
	// The config is passed through the process environment
	var worker = cluster.worker,
		process = worker.process,
		config = JSON.parse(process.env.config),
		constants = JSON.parse(process.env.constants),
		servers = initializeServers(constants, config);

	// Valid command messages that the worker accepts
	// from the managing instance
	var commands = {
		start: function (message) {
			// var port = message.port + worker.id;
			servers.forEach(function (server) {
				server.start();
			});
			return { port: port };
		},

		stop: function () {
			// Shutdown all connections
			servers.forEach(function (server) {
				server.close(true);
			});
			// TODO: consider implementing a timeout to handle long-lived connections
		},

		pause: function () {
			servers.forEach(function (server) {
				server.pause();
			});
		}
	};

	process.on('message', function (message) {
		if (message.type in commands) {
			var response = commands[message.type](message);
			// Send ack back to server with response
			process.send(lib.apply({ type: 'ack', id: message.id }, response || {}));
		}
	});
}
else {
	// This file should never be called directly
	log.error('Worker.js not started through forked process.')
}

// -----------------------------------------
/**
 * Helper functions
 */

/**
 * Initialize the servers that this worker will manage
 * This is very similar to the code used in tycho.js for the master process
 */
function initializeServers(constants, config) {
	var index = config.serverIndex,
		servers = [];

	if (constants.connectionTypes.TCP in index) {
		servers.push( new TCPServer(index[constants.connectionTypes.TCP]) );
	}
	if (constants.connectionTypes.UDP in index) {
		servers.push( new UDPServer(index[constants.connectionTypes.UDP]) );
	}
	if (constants.connectionTypes.WEBSOCKET in index) {
		servers.push( new WSServer(index[constants.connectionTypes.WEBSOCKET]) );
	}

	return servers;
}

<|endoftext|>