<commit_before>'use strict';

var Promise = require('bluebird'),
	ShareObject = require('./sharedObject'),
	RedisConnector = require('./redisConnector'),
	uuid = require('uuid');

module.exports = function(options) {
	return new RedisSharedObject(options);
};

function RedisSharedObject(arg) {
	if(arg.options){
		this.client = arg;
	}
	else{
		this.options = arg;
	}
	this._initialize();
};

var _ = RedisSharedObject.prototype;

_._initialize = function(){
	var that = this;
	that.prefixSemaphore = 'sema:';
	that.prefixMutex = 'mutex:';
	that.sharedObjects = {};
	if(that.client === undefined){
		that.client = (new RedisConnector(that.options)).client;
		that.subscriber = (new RedisConnector(that.options)).client;
		that._internal_connection_used = true;
	}
	else{
		that.subscriber = (new RedisConnector(that.options)).client;
	}
};

_.end = function(){
	for(var key in this.sharedObjects){
		this.client.del(key);
	}
	this.subscriber.quit();
	if(this._internal_connection_used){
		this.client.quit();
	}
}

_.createSemaphoreClient = function(object, count, cb){
	var that = this;
	if(!that.sharedObjects.hasOwnProperty(that.prefixSemaphore+object)){
		that.sharedObjects[that.prefixSemaphore+object] = new Semaphore(object, that.prefixSemaphore, that.client, that.subscriber);
	}
	if(cb && typeof cb === "function")
		that.sharedObjects[that.prefixSemaphore+object].reset(count, function(err, result){
			if(err)
				cb(err, null);
			else
				cb(null, that.sharedObjects[that.prefixSemaphore+object]);
		});
	else{
		return that.sharedObjects[that.prefixSemaphore+object].reset(count).then(function(result){
			if(result)
				return Promise.resolve(that.sharedObjects[that.prefixSemaphore+object]);
		});
	}
};

_.getSemaphoreClient = function(object){
	return this.sharedObjects[this.prefixSemaphore+object];
};

_.createMutexClient = function(object, timeout, cb){
	var that = this;
	if(!that.sharedObjects.hasOwnProperty(that.prefixMutex+object)){
		that.sharedObjects[that.prefixMutex+object] = new Mutex(object, that.prefixMutex, that.client, that.subscriber, timeout);
	}
	that.sharedObjects[that.prefixMutex+object].timeout = timeout;
	if(cb && typeof cb === "function")
		that.sharedObjects[that.prefixMutex+object].reset(function(err, result){
			if(err)
				cb(err, null);
			else
				cb(null, that.sharedObjects[that.prefixMutex+object]);
		});
	else
		return that.sharedObjects[that.prefixMutex+object].reset().then(function(result){
			if(result)
				return Promise.resolve(that.sharedObjects[that.prefixMutex+object]);
		});
};

_.getMutexClient = function(object){
	return this.sharedObjects[this.prefixMutex+object];
};

function Semaphore(object, prefix, client, subscriber) {
	ShareObject.call(this, object, prefix, client, subscriber);
};

Semaphore.prototype = Object.create(ShareObject.prototype);
Semaphore.prototype.constructor = Semaphore;

Semaphore.prototype.reset = function(count, cb){
	this.count = count;
	ShareObject.prototype.reset.call(this);
	var p = this.client.setAsync(this.key, count || 1);
	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(result == "OK")
				cb(null, true);
			else
				cb(null, false);
		}).catch(function(err){
			cb(err, false);
		});
	}
	else{
		return p.then(function(result){
			return Promise.resolve(result == "OK");
		});
	}
};

Semaphore.prototype._get_not_tested = function(){
	var that = this;

	return new Promise(function(resolve, reject){
		that.client.decr(that.key, function(err, reply) {
			if(err) {
				reject(err);
			} else if(reply < 0) {
				that.client.incr(that.key, function(err, reply){
					resolve(0);
				});
			} else {
				resolve(1);
			}
		});
	});
};

Semaphore.prototype._get = function(client){
	var that = this;

	return new Promise(function(resolve, reject){
		client.watch(that.key);
		client.get(that.key, function(err, reply) {
			if(err) {
				client.unwatch();
				reject(err);
			} else if(reply > 0 || reply === null) {
				var multi = client.multi();
				if(reply === null){
					multi.set(that.key, that.count - 1);
				}
				else{
					multi.decr(that.key);
				}
				multi.exec(function(err, result){
					if(err){
						reject(err);
					}
					else if(!result){
						resolve(-1);
					}
					else{
						resolve(1);
					}
				});
			} else {
				client.unwatch();
				resolve(0);
			}
		});
	});
};

Semaphore.prototype.get = function(cb){ // timeout : sec.
	var that = this,
		redisClient = undefined;
	if(!that._internal_connection_used){
		redisClient = (new RedisConnector(that.client.options)).client;
	}

	var p = that._get(redisClient || that.client);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(redisClient){
				redisClient.quit();
			}
			cb(null, result);
		}).catch(function(err){
			if(redisClient && redisClient.connected){
				redisClient.quit();
			}
			cb(err, false);
		});
	}
	else{
		return p.then(function(result){
			if(redisClient){
				redisClient.quit();
			}
			return Promise.resolve(result);
		}).catch(function(err){
			if(redisClient && redisClient.connected)
				redisClient.quit();
			return Promise.reject(err);
		});
	}
};

Semaphore.prototype.get_not_tested = function(cb){ // timeout : sec.
	var that = this;

	var p = that._get();

	if(cb && typeof cb === "function"){
		p.then(function(result){
			cb(null, result);
		}).catch(function(err){
			cb(err, null);
		});
	}
	else{
		return p;
	}
};

Semaphore.prototype.check = function(cb){
	return ShareObject.prototype.check.call(this, cb, function(reply){
		return reply > 0;
	});
};

Semaphore.prototype.rel = function(cb){
	var that = this,
		p = that.client.incrAsync(that.key);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			var err = null;
			if(result <= that.count){
				that.client.publish(that.key, result);
			}
			else if(result > that.count){
				that.client.decr(that.key);
				err = new Error('the number of semaphores exceeds initial count: ' + result);
			}
			cb(err, result);
		}).catch(function(err){
			cb(err, null);
		});
	}
	else{
		return p.then(function(result){
			var err = null;
			if(result <= that.count){
				that.client.publish(that.key, result);
				return Promise.resolve(result);
			}
			else if(result > that.count){
				that.client.decr(that.key);
				err = new Error('the number of semaphores exceeds initial count : ' + result);
				return Promise.reject(err);
			}
		});
	}
};

Semaphore.prototype.observing = function(timeout, cb, _cb){
	return ShareObject.prototype.observing.call(this, timeout, cb, _cb, function(result){
		return !result;
	});
};

Semaphore.prototype.waitingFor = function(timeout, cb, _cb){
	var that = this;
	return ShareObject.prototype.waitingFor.call(this, timeout, cb, _cb, function(result){
		if(result === 1){
			if(_cb){
				cb(result);
			}
			else{
				cb(null, result);
			}
			return true;
		}
		else if(result === -1){
			that.waitingFor(timeout, cb, _cb);
			return true;
		}
		else {
			return false;
		}
	});
};

Semaphore.prototype.getStatus = function(cb){
	var that = this,
		p = that.client.getAsync(that.key);

	if(cb && typeof cb === "function"){
		ShareObject.prototype.getStatus.call(that, function(err, result){
			if(err){
				cb(err, null);
			}
			else{
				result.value = parseInt(result.value);
				cb(null, result);
			}
		});
	}
	else{
		return ShareObject.prototype.getStatus.call(that).then(function(result){
			result.value = parseInt(result.value);
			return Promise.resolve(result);
		});
	}
};

function Mutex(object, prefix, client, subscriber, timeout){
	this.timeout = timeout || 60;
	ShareObject.call(this, object, prefix, client, subscriber);
};

Mutex.prototype = Object.create(ShareObject.prototype);
Mutex.prototype.constructor = Mutex;

Mutex.prototype.reset = function(cb){
	if(this.timer)
		clearTimeout(this.timer);
	this.timer = null;
	ShareObject.prototype.reset.call(this);
	var p = this.client.delAsync(this.key);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(result > 0)
				cb(null, true);
			else
				cb(null, false);
		}).catch(function(err){
			cb(err, false);
		});
	}
	else{
		return p.then(function(result){
			return Promise.resolve(1);
		});
	}
};

Mutex.prototype.get = function(cb){
	var that = this,
		muid = uuid.v4(),
		p = that.client.setnxAsync(that.key, muid);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(result == 1){
				that.client.expire(that.key, that.timeout);
				that.timer = setTimeout(function(){
						var _muid = muid;
						that.emit('expired', _muid);
					}, that.timeout * 1000);
				cb(null, muid);
			}
			else{
				cb(null, null);
			}
		}).catch(function(err){
			cb(err, null);
		});
	}
	else{
		return p.then(function(result){
			if(result == 1){
				that.client.expire(that.key, that.timeout);
				that.timer = setTimeout(function(){
					var _muid = muid;
					that.emit('expired', _muid);
				}, that.timeout * 1000);
				return Promise.resolve(muid);
			}
			else{
				return Promise.resolve(null);
			}
		});
	}
};

Mutex.prototype.rel = function(muid, cb){
	var that = this,
		p = that.client.getAsync(that.key);

	if(cb && typeof cb === "function"){
		p.then(function(reply){
			if(muid != reply){
				cb(new Error('Access denied'), false);
			}
			else if (muid == reply){
				that.client.del(that.key, function(err, result){
					if(!err){
						clearTimeout(that.timer);
						that.timer = null;
						that.client.publish(that.key, result);
					}
					cb(err, result > 0);
				})
			}
		}).catch(function(err){
			cb(err, false);
		});
	}
	else{
		return p.then(function(reply){
			if(muid != reply){
				return Promise.reject(new Error('Access denied'));
			}
			else if(muid == reply){
				return that.client.delAsync(that.key);
			}
		}).then(function(result){
			clearTimeout(that.timer);
			that.timer = null;
			that.client.publish(that.key, result);
			return Promise.resolve(result > 0);
		});
	}
};

Mutex.prototype.check = function(cb){
	return ShareObject.prototype.check.call(this, cb, function(reply){
		return reply != 0;
	});
};

Mutex.prototype.observing = function(timeout, cb, _cb){
	return ShareObject.prototype.observing.call(this, timeout, cb, _cb, function(result){
		return result;
	});
};

Mutex.prototype.waitingFor = function(timeout, cb, _cb){
	return ShareObject.prototype.waitingFor.call(this, timeout, cb, _cb, function(result){
		if(result){
			if(_cb){
				cb(result);
			}
			else{
				cb(null, result);
			}
			return true;
		}
		else
			return false;
	});
};
<commit_msg>cleaned unused codes<commit_after>'use strict';

var Promise = require('bluebird'),
	ShareObject = require('./sharedObject'),
	RedisConnector = require('./redisConnector'),
	uuid = require('uuid');

module.exports = function(options) {
	return new RedisSharedObject(options);
};

function RedisSharedObject(arg) {
	if(arg.options){
		this.client = arg;
	}
	else{
		this.options = arg;
	}
	this._initialize();
};

var _ = RedisSharedObject.prototype;

_._initialize = function(){
	var that = this;
	that.prefixSemaphore = 'sema:';
	that.prefixMutex = 'mutex:';
	that.sharedObjects = {};
	if(that.client === undefined){
		that.client = (new RedisConnector(that.options)).client;
		that.subscriber = (new RedisConnector(that.options)).client;
		that._internal_connection_used = true;
	}
	else{
		that.subscriber = (new RedisConnector(that.options)).client;
	}
};

_.end = function(){
	for(var key in this.sharedObjects){
		this.client.del(key);
	}
	this.subscriber.quit();
	if(this._internal_connection_used){
		this.client.quit();
	}
}

_.createSemaphoreClient = function(object, count, cb){
	var that = this;
	if(!that.sharedObjects.hasOwnProperty(that.prefixSemaphore+object)){
		that.sharedObjects[that.prefixSemaphore+object] = new Semaphore(object, that.prefixSemaphore, that.client, that.subscriber);
	}
	if(cb && typeof cb === "function")
		that.sharedObjects[that.prefixSemaphore+object].reset(count, function(err, result){
			if(err)
				cb(err, null);
			else
				cb(null, that.sharedObjects[that.prefixSemaphore+object]);
		});
	else{
		return that.sharedObjects[that.prefixSemaphore+object].reset(count).then(function(result){
			if(result)
				return Promise.resolve(that.sharedObjects[that.prefixSemaphore+object]);
		});
	}
};

_.getSemaphoreClient = function(object){
	return this.sharedObjects[this.prefixSemaphore+object];
};

_.createMutexClient = function(object, timeout, cb){
	var that = this;
	if(!that.sharedObjects.hasOwnProperty(that.prefixMutex+object)){
		that.sharedObjects[that.prefixMutex+object] = new Mutex(object, that.prefixMutex, that.client, that.subscriber, timeout);
	}
	that.sharedObjects[that.prefixMutex+object].timeout = timeout;
	if(cb && typeof cb === "function")
		that.sharedObjects[that.prefixMutex+object].reset(function(err, result){
			if(err)
				cb(err, null);
			else
				cb(null, that.sharedObjects[that.prefixMutex+object]);
		});
	else
		return that.sharedObjects[that.prefixMutex+object].reset().then(function(result){
			if(result)
				return Promise.resolve(that.sharedObjects[that.prefixMutex+object]);
		});
};

_.getMutexClient = function(object){
	return this.sharedObjects[this.prefixMutex+object];
};

function Semaphore(object, prefix, client, subscriber) {
	ShareObject.call(this, object, prefix, client, subscriber);
};

Semaphore.prototype = Object.create(ShareObject.prototype);
Semaphore.prototype.constructor = Semaphore;

Semaphore.prototype.reset = function(count, cb){
	this.count = count;
	ShareObject.prototype.reset.call(this);
	var p = this.client.setAsync(this.key, count || 1);
	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(result == "OK")
				cb(null, true);
			else
				cb(null, false);
		}).catch(function(err){
			cb(err, false);
		});
	}
	else{
		return p.then(function(result){
			return Promise.resolve(result == "OK");
		});
	}
};

Semaphore.prototype._get = function(client){
	var that = this;

	return new Promise(function(resolve, reject){
		client.watch(that.key);
		client.get(that.key, function(err, reply) {
			if(err) {
				client.unwatch();
				reject(err);
			} else if(reply > 0 || reply === null) {
				var multi = client.multi();
				if(reply === null){
					multi.set(that.key, that.count - 1);
				}
				else{
					multi.decr(that.key);
				}
				multi.exec(function(err, result){
					if(err){
						reject(err);
					}
					else if(!result){
						resolve(-1);
					}
					else{
						resolve(1);
					}
				});
			} else {
				client.unwatch();
				resolve(0);
			}
		});
	});
};

Semaphore.prototype.get = function(cb){ // timeout : sec.
	var that = this,
		redisClient = undefined;
	if(!that._internal_connection_used){
		redisClient = (new RedisConnector(that.client.options)).client;
	}

	var p = that._get(redisClient || that.client);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(redisClient){
				redisClient.quit();
			}
			cb(null, result);
		}).catch(function(err){
			if(redisClient && redisClient.connected){
				redisClient.quit();
			}
			cb(err, false);
		});
	}
	else{
		return p.then(function(result){
			if(redisClient){
				redisClient.quit();
			}
			return Promise.resolve(result);
		}).catch(function(err){
			if(redisClient && redisClient.connected)
				redisClient.quit();
			return Promise.reject(err);
		});
	}
};

Semaphore.prototype.check = function(cb){
	return ShareObject.prototype.check.call(this, cb, function(reply){
		return reply > 0;
	});
};

Semaphore.prototype.rel = function(cb){
	var that = this,
		p = that.client.incrAsync(that.key);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			var err = null;
			if(result <= that.count){
				that.client.publish(that.key, result);
			}
			else if(result > that.count){
				that.client.decr(that.key);
				err = new Error('the number of semaphores exceeds initial count: ' + result);
			}
			cb(err, result);
		}).catch(function(err){
			cb(err, null);
		});
	}
	else{
		return p.then(function(result){
			var err = null;
			if(result <= that.count){
				that.client.publish(that.key, result);
				return Promise.resolve(result);
			}
			else if(result > that.count){
				that.client.decr(that.key);
				err = new Error('the number of semaphores exceeds initial count : ' + result);
				return Promise.reject(err);
			}
		});
	}
};

Semaphore.prototype.observing = function(timeout, cb, _cb){
	return ShareObject.prototype.observing.call(this, timeout, cb, _cb, function(result){
		return !result;
	});
};

Semaphore.prototype.waitingFor = function(timeout, cb, _cb){
	var that = this;
	return ShareObject.prototype.waitingFor.call(this, timeout, cb, _cb, function(result){
		if(result === 1){
			if(_cb){
				cb(result);
			}
			else{
				cb(null, result);
			}
			return true;
		}
		else if(result === -1){
			that.waitingFor(timeout, cb, _cb);
			return true;
		}
		else {
			return false;
		}
	});
};

Semaphore.prototype.getStatus = function(cb){
	var that = this,
		p = that.client.getAsync(that.key);

	if(cb && typeof cb === "function"){
		ShareObject.prototype.getStatus.call(that, function(err, result){
			if(err){
				cb(err, null);
			}
			else{
				result.value = parseInt(result.value);
				cb(null, result);
			}
		});
	}
	else{
		return ShareObject.prototype.getStatus.call(that).then(function(result){
			result.value = parseInt(result.value);
			return Promise.resolve(result);
		});
	}
};

function Mutex(object, prefix, client, subscriber, timeout){
	this.timeout = timeout || 60;
	ShareObject.call(this, object, prefix, client, subscriber);
};

Mutex.prototype = Object.create(ShareObject.prototype);
Mutex.prototype.constructor = Mutex;

Mutex.prototype.reset = function(cb){
	if(this.timer)
		clearTimeout(this.timer);
	this.timer = null;
	ShareObject.prototype.reset.call(this);
	var p = this.client.delAsync(this.key);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(result > 0)
				cb(null, true);
			else
				cb(null, false);
		}).catch(function(err){
			cb(err, false);
		});
	}
	else{
		return p.then(function(result){
			return Promise.resolve(1);
		});
	}
};

Mutex.prototype.get = function(cb){
	var that = this,
		muid = uuid.v4(),
		p = that.client.setnxAsync(that.key, muid);

	if(cb && typeof cb === "function"){
		p.then(function(result){
			if(result == 1){
				that.client.expire(that.key, that.timeout);
				that.timer = setTimeout(function(){
						var _muid = muid;
						that.emit('expired', _muid);
					}, that.timeout * 1000);
				cb(null, muid);
			}
			else{
				cb(null, null);
			}
		}).catch(function(err){
			cb(err, null);
		});
	}
	else{
		return p.then(function(result){
			if(result == 1){
				that.client.expire(that.key, that.timeout);
				that.timer = setTimeout(function(){
					var _muid = muid;
					that.emit('expired', _muid);
				}, that.timeout * 1000);
				return Promise.resolve(muid);
			}
			else{
				return Promise.resolve(null);
			}
		});
	}
};

Mutex.prototype.rel = function(muid, cb){
	var that = this,
		p = that.client.getAsync(that.key);

	if(cb && typeof cb === "function"){
		p.then(function(reply){
			if(muid != reply){
				cb(new Error('Access denied'), false);
			}
			else if (muid == reply){
				that.client.del(that.key, function(err, result){
					if(!err){
						clearTimeout(that.timer);
						that.timer = null;
						that.client.publish(that.key, result);
					}
					cb(err, result > 0);
				})
			}
		}).catch(function(err){
			cb(err, false);
		});
	}
	else{
		return p.then(function(reply){
			if(muid != reply){
				return Promise.reject(new Error('Access denied'));
			}
			else if(muid == reply){
				return that.client.delAsync(that.key);
			}
		}).then(function(result){
			clearTimeout(that.timer);
			that.timer = null;
			that.client.publish(that.key, result);
			return Promise.resolve(result > 0);
		});
	}
};

Mutex.prototype.check = function(cb){
	return ShareObject.prototype.check.call(this, cb, function(reply){
		return reply != 0;
	});
};

Mutex.prototype.observing = function(timeout, cb, _cb){
	return ShareObject.prototype.observing.call(this, timeout, cb, _cb, function(result){
		return result;
	});
};

Mutex.prototype.waitingFor = function(timeout, cb, _cb){
	return ShareObject.prototype.waitingFor.call(this, timeout, cb, _cb, function(result){
		if(result){
			if(_cb){
				cb(result);
			}
			else{
				cb(null, result);
			}
			return true;
		}
		else
			return false;
	});
};
<|endoftext|>


<commit_before>var _ = require('underscore'),
    uglifyJs = require('uglify-js'),
    uglifyAst = {};

// JSON subset supported
uglifyAst.objToAst = function (obj) {
    if (obj === null || obj === true || obj === false) {
        return ['name', "" + obj];
    } else if (_.isArray(obj)) {
        return ['array', obj.map(uglifyAst.objToAst)];
    } else if (typeof obj === 'object') {
        return ['object', Object.keys(obj).sort().map(function (key) {
            return [key, uglifyAst.objToAst(obj[key])];
        })];
    } else if (_.isNumber(obj)) {
        return ['num', obj];
    } else if (_.isString(obj)) {
        return ['string', obj];
    } else if (_.isFunction(obj)) {
        return uglifyJs.parser.parse('!' + obj.toString())[1][0][1][2];
    } else {
        throw new Error("uglifyAst.objToAst: Cannot convert " + JSON.stringify(obj));
    }
};

// JSON subset supported
uglifyAst.astToObj = function (ast) {
    if (ast[0] === 'string' || ast[0] === 'num') {
        return ast[1];
    } else if (ast[0] === 'name') {
        if (ast[1] === 'false') {
            return false;
        } else if (ast[1] === 'true') {
            return true;
        } else if (ast[1] === 'null') {
            return null;
        } else {
            throw new Error('uglifyAst.astToObj: Unsupported ["name", ...] node');
        }
    } else if (ast[0] === 'object') {
        var obj = {};
        ast[1].forEach(function (keyAndValueArr) {
            obj[keyAndValueArr[0]] = uglifyAst.astToObj(keyAndValueArr[1]);
        });
        return obj;
    } else if (ast[0] === 'array') {
        return ast[1].map(uglifyAst.astToObj);
    } else if (ast[0] === 'function') {
        return new Function(ast[2].join(","), uglifyJs.uglify.gen_code(['toplevel', ast[3]]));
    } else {
        throw new Error("uglifyAst.astToObj: Cannot convert " + JSON.stringify(ast));
    }
};

uglifyAst.getFunctionBodyAst = function (lambda) {
    return uglifyJs.parser.parse("(" + lambda.toString() + ")")[1][0][1][3];
};

_.extend(exports, uglifyAst);
<commit_msg>Added support for undefined.<commit_after>var _ = require('underscore'),
    uglifyJs = require('uglify-js'),
    uglifyAst = {};

// JSON subset supported
uglifyAst.objToAst = function (obj) {
    if (obj === null || obj === true || obj === false) {
        return ['name', "" + obj];
    } else if (_.isArray(obj)) {
        return ['array', obj.map(uglifyAst.objToAst)];
    } else if (typeof obj === 'object') {
        return ['object', Object.keys(obj).sort().map(function (key) {
            return [key, uglifyAst.objToAst(obj[key])];
        })];
    } else if (_.isNumber(obj)) {
        return ['num', obj];
    } else if (_.isString(obj)) {
        return ['string', obj];
    } else if (_.isFunction(obj)) {
        return uglifyJs.parser.parse('!' + obj.toString())[1][0][1][2];
    } else if (typeof obj === 'undefined') {
        return ['name', 'undefined'];
    } else {
        throw new Error("uglifyAst.objToAst: Cannot convert " + JSON.stringify(obj));
    }
};

// JSON subset supported
uglifyAst.astToObj = function (ast) {
    if (ast[0] === 'string' || ast[0] === 'num') {
        return ast[1];
    } else if (ast[0] === 'name') {
        if (ast[1] === 'false') {
            return false;
        } else if (ast[1] === 'true') {
            return true;
        } else if (ast[1] === 'null') {
            return null;
        } else if (ast[1] === 'undefined') {
            return undefined;
        } else {
            throw new Error('uglifyAst.astToObj: Unsupported ["name", ...] node');
        }
    } else if (ast[0] === 'object') {
        var obj = {};
        ast[1].forEach(function (keyAndValueArr) {
            obj[keyAndValueArr[0]] = uglifyAst.astToObj(keyAndValueArr[1]);
        });
        return obj;
    } else if (ast[0] === 'array') {
        return ast[1].map(uglifyAst.astToObj);
    } else if (ast[0] === 'function') {
        return new Function(ast[2].join(","), uglifyJs.uglify.gen_code(['toplevel', ast[3]]));
    } else {
        throw new Error("uglifyAst.astToObj: Cannot convert " + JSON.stringify(ast));
    }
};

uglifyAst.getFunctionBodyAst = function (lambda) {
    return uglifyJs.parser.parse("(" + lambda.toString() + ")")[1][0][1][3];
};

_.extend(exports, uglifyAst);
<|endoftext|>


<commit_before>import Request      from '../HTTP/Request.js'
import Vue          from 'vue'
import * as _       from 'lodash'
import { autobind } from '../utils.js'

/**
 * Base class for all things common between Model and Collection.
 */
class Base {

    constructor(options) {
        autobind(this);

        // Define an automatic unique ID. This is primarily to distinguish
        // between multiple instances of the same name and data.
        Object.defineProperty(this, '_uid', {
            value:        _.uniqueId(),
            enumerable:   false,
            configurable: false,
            writable:     false,
        });

        Vue.set(this, '_listeners', {});  // Event listeners
        Vue.set(this, '_options',   {});  // Internal option store

        this.setOptions(options);
        this.boot();
    }

    /**
     * @returns {string} The class name of this instance.
     */
    get $class() {
        return (Object.getPrototypeOf(this)).constructor.name;
    }

    /**
     * Called after construction, this hook allows you to add some extra setup
     * logic without having to override the constructor.
     */
    boot() {

    }

    /**
     * Returns a route configuration in the form {key: name}, where key may be
     * 'save', 'fetch', 'delete' or any other custom key, and the name is what
     * will be passed to the route resolver to generate the URL. See @getURL
     *
     * @returns {Object}
     */
    routes() {
        return {};
    }

    /**
     * Returns the default context for all events emitted by this instance.
     *
     * @returns {Object}
     */
    getDefaultEventContext() {
        return {target: this}
    }

    /**
     * @returns {string} Default string representation.
     */
    toString() {
        return `<${this.$class} #${this._uid}>`;
    }

    /**
     * Emits an event by name to all registered listeners on that event.

     * Listeners will be called in the order that they were added. If a listener
     * returns `false`, no other listeners will be called.
     *
     * @param {string} event    The name of the event to emit.
     * @param {Object} context  The context of the event, passed to listeners.
     */
    emit(event, context = {}) {
        let listeners = _.get(this._listeners, event);

        if ( ! listeners) {
            return;
        }

        // Create the context for the event.
        context = _.defaults({}, context, this.getDefaultEventContext());

        // Run through each listener. If any of them return false, stop the
        // iteration and mark that the event wasn't handled by all listeners.
        _.each(listeners, (listener) => listener(context));
    }

    /**
     * Registers an event listener for a given event.
     *
     * Event names can be comma-separated to register multiple events.
     *
     * @param {string}   event      The name of the event to listen for.
     * @param {function} listener   The event listener, accepts context.
     */
    on(event, listener) {
        let events = _.map(_.split(event, ','), _.trim);

        _.each(events, (event) => {
            this._listeners[event] = this._listeners[event] || [];
            this._listeners[event].push(listener);
        });
    }

    /**
     * @returns {Object} Parameters to use for replacement in route patterns.
     */
    getRouteParameters() {
        return {}
    }

    /**
     * @returns {RegExp|string} Pattern to match and group route parameters.
     */
    getRouteParameterPattern() {
        return this.getOption('routeParameterPattern');
    }

    /**
     * @returns {RegExp} The default route parameter pattern.
     */
    getDefaultRouteParameterPattern() {
        return /\{([^}]+)\}/;
    }

    /**
     * @returns {Object} This class' default options.
     */
    getDefaultOptions() {
        return {

            // Default HTTP methods for requests.
            methods: this.getDefaultMethods(),

            // Default route parameter interpolation pattern.
            routeParameterPattern: this.getDefaultRouteParameterPattern(),

            // The HTTP status code to use for indicating a validation error.
            validationErrorStatus: 422,
        }
    }

    /**
     * @param {Array|string} path     Option path resolved by `_.get`
     * @param {*}            fallback Fallback value if the option is not set.
     *
     * @returns {*} The value of the given option path.
     */
    getOption(path, fallback = null) {
        return _.get(this._options, path, fallback);
    }

    /**
     * @returns {Object} This instance's default options.
     */
    options() {
        return {}
    }

    /**
     * Sets an option.
     *
     * @param {string} path
     * @param {*}      value
     */
    setOption(path, value) {
        _.set(this._options, path, value);
    }

    /**
     * Sets all given options. Successive values for the same option won't be
     * overwritten, so this follows the 'defaults' behaviour, and not 'merge'.
     *
     * @param {...Object} options One or more objects of options.
     */
    setOptions(...options) {
        Vue.set(this, '_options', _.defaultsDeep(
            {},
            ...options,                 // Given options
            this.options(),             // Instance defaults
            this.getDefaultOptions()    // Class defaults
        ));
    }

    /**
     * Returns all the options that are currently set on this instance.
     *
     * @return {Object}
     */
    getOptions() {
        return _.defaultTo(this._options, {});
    }

    /**
     * Returns a function that translates a route key and parameters to a URL.
     *
     * @returns {Function} Will be passed `route` and `parameters`
     */
    getRouteResolver() {
        return this.getDefaultRouteResolver();
    }

    /**
     * @returns {Object} An object consisting of all route string replacements.
     */
    getRouteReplacements(route, parameters = {}) {
        let replace = {};
        let pattern = new RegExp(this.getRouteParameterPattern(), 'g');

        for (let parameter; (parameter = pattern.exec(route)) !== null; ) {
            replace[parameter[0]] = parameters[parameter[1]];
        }

        return replace;
    }

    /**
     * Returns the default URL provider, which assumes that route keys are URL's,
     * and parameter replacement syntax is in the form "{param}".
     *
     * @returns {Function}
     */
    getDefaultRouteResolver() {
        return (route, parameters = {}) => {
            let replacements = this.getRouteReplacements(route, parameters);

            // Replace all route parameters with their replacement values.
            return _.reduce(replacements, (result, value, parameter) => {
                return _.replace(result, parameter, value);
            }, route);
        }
    }

    /**
     * @returns {Object} The data to send to the server when saving this model.
     */
    getDeleteBody() {
        return {};
    }

    /**
     * @returns {Object} Query parameters that will be appended to the `fetch` URL.
     */
    getFetchQuery() {
        return {};
    }

    /**
     * @returns {Object} Query parameters that will be appended to the `save` URL.
     */
    getSaveQuery() {
        return {};
    }

    /**
     * @returns {Object} Query parameters that will be appended to the `delete` URL.
     */
    getDeleteQuery() {
        return {};
    }

    /**
     * @returns {string} The key to use when generating the `fetch` URL.
     */
    getFetchRoute() {
        return this.getRoute('fetch');
    }

    /**
     * @returns {string} The key to use when generating the `save` URL.
     */
    getSaveRoute() {
        return this.getRoute('save');
    }

    /**
     * @returns {string} The key to use when generating the `delete` URL.
     */
    getDeleteRoute() {
        return this.getRoute('delete');
    }

    /**
     * @returns {Object} Headers to use when making a save request.
     */
    getSaveHeaders() {
        return {};
    }

    /**
     * @returns {Object} Headers to use when making any request.
     */
    getDefaultHeaders() {
        return {};
    }

    /**
     * @returns {Object} Headers to use when making a fetch request.
     */
    getFetchHeaders() {
        return {};
    }

    /**
     * @returns {Object} Headers to use when making a delete request.
     */
    getDeleteHeaders() {
        return {};
    }

    /**
     * @returns {Object} Default HTTP methods.
     */
    getDefaultMethods() {
        return {
            fetch:  'GET',
            save:   'POST',
            update: 'POST',
            create: 'POST',
            patch:  'PATCH',
            delete: 'DELETE',
        }
    }

    /**
     * @returns {string} HTTP method to use when making a save request.
     */
    getSaveMethod() {
        return this.getOption('methods.save');
    }

    /**
     * @returns {string} HTTP method to use when making a fetch request.
     */
    getFetchMethod() {
        return this.getOption('methods.fetch');
    }

    /**
     * @returns {string} HTTP method to use when updating a resource.
     */
    getUpdateMethod() {
        return this.getOption('methods.update');
    }

    /**
     * @returns {string} HTTP method to use when patching a resource.
     */
    getPatchMethod() {
        return this.getOption('methods.patch');
    }

    /**
     * @returns {string} HTTP method to use when creating a resource.
     */
    getCreateMethod() {
        return this.getOption('methods.create');
    }

    /**
     * @returns {string} HTTP method to use when deleting a resource.
     */
    getDeleteMethod() {
        return this.getOption('methods.delete');
    }

    /**
     * @returns {number} The HTTP status code that indicates a validation error.
     */
    getValidationErrorStatus() {
        return _.defaultTo(this.getOption('validationErrorStatus'), 422);
    }

    /**
     * @returns {boolean} `true` if the response indicates a validation error.
     */
    isBackendValidationError(error) {

        // The error must have a response for it to be a validation error.
        if ( ! _.invoke(error, 'getResponse', false)) {
            return false;
        }

        let status  = error.getResponse().getStatus();
        let invalid = this.getValidationErrorStatus();

        return status == invalid;
    }

    /**
     * @return {string|undefined} Route value by key.
     */
    getRoute(key, fallback) {
        let route = _.get(this.routes(), key, _.get(this.routes(), fallback));

        if ( ! route) {
            throw new Error(`Invalid or missing route`);
        }

        return route;
    }

    /**
     * @returns {string} The full URL to use when making a fetch request.
     */
    getFetchURL() {
        return this.getURL(this.getFetchRoute(), this.getRouteParameters());
    }

    /**
     * @returns {string} The full URL to use when making a save request.
     */
    getSaveURL() {
        return this.getURL(this.getSaveRoute(), this.getRouteParameters());
    }

    /**
     * @returns {string} The full URL to use when making a delete request.
     */
    getDeleteURL() {
        return this.getURL(this.getDeleteRoute(), this.getRouteParameters());
    }

    /**
     * @param {string} route      The route key to use to generate the URL.
     * @param {Object} parameters Route parameters.
     *
     * @returns {string} A URL that was generated using the given route key.
     */
    getURL(route, parameters = {}) {
        return this.getRouteResolver()(route, parameters);
    }

    /**
     * @returns {Request} A new `Request` using the given configuration.
     */
    getRequest(config) {
        return new Request(config);
    }

    /**
     * This is the central component for all HTTP requests and handling.
     *
     * @param  {Object}     config      Request configuration
     * @param  {function}   onRequest   Called before the request is made.
     * @param  {function}   onSuccess   Called when the request was successful.
     * @param  {function}   onFailure   Called when the request failed.
     */
    request(config, onRequest, onSuccess, onFailure) {
        return new Promise((resolve, reject) => {

            let check = onRequest();

            // Request should be skipped but the promise should not be resolved.
            if (check === false) {
                return;
            }

            // Request should be skipped but should be considered successful.
            if (check === true) {
                onSuccess(null);
                return resolve(null);
            }

            // Support passing the request configuration as a function, to allow
            // for deferred resolution of certain values that may have changed
            // during the call to "onRequest".
            if (_.isFunction(config)) {
                config = config();
            }

            // Apply the default headers.
            _.defaults(config.headers, this.getDefaultHeaders());

            // Make the request.
            return this.getRequest(config).send()

                // Success
                .then((response) => {
                    onSuccess(response);
                    return resolve(response);
                })

                // Failure
                .catch((error) => {
                    onFailure(error);
                    return reject(error);
                })

                // Failure fallback, for errors that occur in `onFailure`.
                .catch((error) => {
                    return reject(error);
                });
        });
    }

    /**
     * Fetches data from the database/API.
     *
     * @param {options}             Fetch options
     * @param {options.params}      Query params
     * @param {options.headers}     Query headers
     * @returns {Promise}
     */
    fetch(options = {}) {
        let config = () => _.defaults(options, {
            url:     this.getFetchURL(),
            method:  this.getFetchMethod(),
            params:  this.getFetchQuery(),
            headers: this.getFetchHeaders(),
        });

        return this.request(
            config,
            this.onFetch,
            this.onFetchSuccess,
            this.onFetchFailure
        );
    }

    /**
     * Persists data to the database/API.
     * @returns {Promise}
     */
    save() {
        let config = () => ({
            url:     this.getSaveURL(),
            method:  this.getSaveMethod(),
            data:    this.getSaveData(),
            params:  this.getSaveQuery(),
            headers: this.getSaveHeaders(),
        });

        return this.request(
            config,
            this.onSave,
            this.onSaveSuccess,
            this.onSaveFailure
        );
    }

    /**
     * Removes model or collection data from the database/API.
     * @returns {Promise}
     */
    delete() {
        let config = () => ({
            url:     this.getDeleteURL(),
            method:  this.getDeleteMethod(),
            data:    this.getDeleteBody(),
            params:  this.getDeleteQuery(),
            headers: this.getDeleteHeaders(),
        });

        return this.request(
            config,
            this.onDelete,
            this.onDeleteSuccess,
            this.onDeleteFailure
        );
    }
}

export default Base;
<commit_msg>Fix RegExp flag assignment issues on IE<commit_after>import Request      from '../HTTP/Request.js'
import Vue          from 'vue'
import * as _       from 'lodash'
import { autobind } from '../utils.js'

/**
 * Base class for all things common between Model and Collection.
 */
class Base {

    constructor(options) {
        autobind(this);

        // Define an automatic unique ID. This is primarily to distinguish
        // between multiple instances of the same name and data.
        Object.defineProperty(this, '_uid', {
            value:        _.uniqueId(),
            enumerable:   false,
            configurable: false,
            writable:     false,
        });

        Vue.set(this, '_listeners', {});  // Event listeners
        Vue.set(this, '_options',   {});  // Internal option store

        this.setOptions(options);
        this.boot();
    }

    /**
     * @returns {string} The class name of this instance.
     */
    get $class() {
        return (Object.getPrototypeOf(this)).constructor.name;
    }

    /**
     * Called after construction, this hook allows you to add some extra setup
     * logic without having to override the constructor.
     */
    boot() {

    }

    /**
     * Returns a route configuration in the form {key: name}, where key may be
     * 'save', 'fetch', 'delete' or any other custom key, and the name is what
     * will be passed to the route resolver to generate the URL. See @getURL
     *
     * @returns {Object}
     */
    routes() {
        return {};
    }

    /**
     * Returns the default context for all events emitted by this instance.
     *
     * @returns {Object}
     */
    getDefaultEventContext() {
        return {target: this}
    }

    /**
     * @returns {string} Default string representation.
     */
    toString() {
        return `<${this.$class} #${this._uid}>`;
    }

    /**
     * Emits an event by name to all registered listeners on that event.

     * Listeners will be called in the order that they were added. If a listener
     * returns `false`, no other listeners will be called.
     *
     * @param {string} event    The name of the event to emit.
     * @param {Object} context  The context of the event, passed to listeners.
     */
    emit(event, context = {}) {
        let listeners = _.get(this._listeners, event);

        if ( ! listeners) {
            return;
        }

        // Create the context for the event.
        context = _.defaults({}, context, this.getDefaultEventContext());

        // Run through each listener. If any of them return false, stop the
        // iteration and mark that the event wasn't handled by all listeners.
        _.each(listeners, (listener) => listener(context));
    }

    /**
     * Registers an event listener for a given event.
     *
     * Event names can be comma-separated to register multiple events.
     *
     * @param {string}   event      The name of the event to listen for.
     * @param {function} listener   The event listener, accepts context.
     */
    on(event, listener) {
        let events = _.map(_.split(event, ','), _.trim);

        _.each(events, (event) => {
            this._listeners[event] = this._listeners[event] || [];
            this._listeners[event].push(listener);
        });
    }

    /**
     * @returns {Object} Parameters to use for replacement in route patterns.
     */
    getRouteParameters() {
        return {}
    }

    /**
     * @returns {RegExp|string} Pattern to match and group route parameters.
     */
    getRouteParameterPattern() {
        return this.getOption('routeParameterPattern');
    }

    /**
     * @returns {RegExp} The default route parameter pattern.
     */
    getDefaultRouteParameterPattern() {
        return /\{([^}]+)\}/;
    }

    /**
     * @returns {Object} This class' default options.
     */
    getDefaultOptions() {
        return {

            // Default HTTP methods for requests.
            methods: this.getDefaultMethods(),

            // Default route parameter interpolation pattern.
            routeParameterPattern: this.getDefaultRouteParameterPattern(),

            // The HTTP status code to use for indicating a validation error.
            validationErrorStatus: 422,
        }
    }

    /**
     * @param {Array|string} path     Option path resolved by `_.get`
     * @param {*}            fallback Fallback value if the option is not set.
     *
     * @returns {*} The value of the given option path.
     */
    getOption(path, fallback = null) {
        return _.get(this._options, path, fallback);
    }

    /**
     * @returns {Object} This instance's default options.
     */
    options() {
        return {}
    }

    /**
     * Sets an option.
     *
     * @param {string} path
     * @param {*}      value
     */
    setOption(path, value) {
        _.set(this._options, path, value);
    }

    /**
     * Sets all given options. Successive values for the same option won't be
     * overwritten, so this follows the 'defaults' behaviour, and not 'merge'.
     *
     * @param {...Object} options One or more objects of options.
     */
    setOptions(...options) {
        Vue.set(this, '_options', _.defaultsDeep(
            {},
            ...options,                 // Given options
            this.options(),             // Instance defaults
            this.getDefaultOptions()    // Class defaults
        ));
    }

    /**
     * Returns all the options that are currently set on this instance.
     *
     * @return {Object}
     */
    getOptions() {
        return _.defaultTo(this._options, {});
    }

    /**
     * Returns a function that translates a route key and parameters to a URL.
     *
     * @returns {Function} Will be passed `route` and `parameters`
     */
    getRouteResolver() {
        return this.getDefaultRouteResolver();
    }

    /**
     * @returns {Object} An object consisting of all route string replacements.
     */
    getRouteReplacements(route, parameters = {}) {
        const replace = {};
        let pattern = this.getRouteParameterPattern();
        pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g');

        for (let parameter; (parameter = pattern.exec(route)) !== null; ) {
            replace[parameter[0]] = parameters[parameter[1]];
        }

        return replace;
    }

    /**
     * Returns the default URL provider, which assumes that route keys are URL's,
     * and parameter replacement syntax is in the form "{param}".
     *
     * @returns {Function}
     */
    getDefaultRouteResolver() {
        return (route, parameters = {}) => {
            let replacements = this.getRouteReplacements(route, parameters);

            // Replace all route parameters with their replacement values.
            return _.reduce(replacements, (result, value, parameter) => {
                return _.replace(result, parameter, value);
            }, route);
        }
    }

    /**
     * @returns {Object} The data to send to the server when saving this model.
     */
    getDeleteBody() {
        return {};
    }

    /**
     * @returns {Object} Query parameters that will be appended to the `fetch` URL.
     */
    getFetchQuery() {
        return {};
    }

    /**
     * @returns {Object} Query parameters that will be appended to the `save` URL.
     */
    getSaveQuery() {
        return {};
    }

    /**
     * @returns {Object} Query parameters that will be appended to the `delete` URL.
     */
    getDeleteQuery() {
        return {};
    }

    /**
     * @returns {string} The key to use when generating the `fetch` URL.
     */
    getFetchRoute() {
        return this.getRoute('fetch');
    }

    /**
     * @returns {string} The key to use when generating the `save` URL.
     */
    getSaveRoute() {
        return this.getRoute('save');
    }

    /**
     * @returns {string} The key to use when generating the `delete` URL.
     */
    getDeleteRoute() {
        return this.getRoute('delete');
    }

    /**
     * @returns {Object} Headers to use when making a save request.
     */
    getSaveHeaders() {
        return {};
    }

    /**
     * @returns {Object} Headers to use when making any request.
     */
    getDefaultHeaders() {
        return {};
    }

    /**
     * @returns {Object} Headers to use when making a fetch request.
     */
    getFetchHeaders() {
        return {};
    }

    /**
     * @returns {Object} Headers to use when making a delete request.
     */
    getDeleteHeaders() {
        return {};
    }

    /**
     * @returns {Object} Default HTTP methods.
     */
    getDefaultMethods() {
        return {
            fetch:  'GET',
            save:   'POST',
            update: 'POST',
            create: 'POST',
            patch:  'PATCH',
            delete: 'DELETE',
        }
    }

    /**
     * @returns {string} HTTP method to use when making a save request.
     */
    getSaveMethod() {
        return this.getOption('methods.save');
    }

    /**
     * @returns {string} HTTP method to use when making a fetch request.
     */
    getFetchMethod() {
        return this.getOption('methods.fetch');
    }

    /**
     * @returns {string} HTTP method to use when updating a resource.
     */
    getUpdateMethod() {
        return this.getOption('methods.update');
    }

    /**
     * @returns {string} HTTP method to use when patching a resource.
     */
    getPatchMethod() {
        return this.getOption('methods.patch');
    }

    /**
     * @returns {string} HTTP method to use when creating a resource.
     */
    getCreateMethod() {
        return this.getOption('methods.create');
    }

    /**
     * @returns {string} HTTP method to use when deleting a resource.
     */
    getDeleteMethod() {
        return this.getOption('methods.delete');
    }

    /**
     * @returns {number} The HTTP status code that indicates a validation error.
     */
    getValidationErrorStatus() {
        return _.defaultTo(this.getOption('validationErrorStatus'), 422);
    }

    /**
     * @returns {boolean} `true` if the response indicates a validation error.
     */
    isBackendValidationError(error) {

        // The error must have a response for it to be a validation error.
        if ( ! _.invoke(error, 'getResponse', false)) {
            return false;
        }

        let status  = error.getResponse().getStatus();
        let invalid = this.getValidationErrorStatus();

        return status == invalid;
    }

    /**
     * @return {string|undefined} Route value by key.
     */
    getRoute(key, fallback) {
        let route = _.get(this.routes(), key, _.get(this.routes(), fallback));

        if ( ! route) {
            throw new Error(`Invalid or missing route`);
        }

        return route;
    }

    /**
     * @returns {string} The full URL to use when making a fetch request.
     */
    getFetchURL() {
        return this.getURL(this.getFetchRoute(), this.getRouteParameters());
    }

    /**
     * @returns {string} The full URL to use when making a save request.
     */
    getSaveURL() {
        return this.getURL(this.getSaveRoute(), this.getRouteParameters());
    }

    /**
     * @returns {string} The full URL to use when making a delete request.
     */
    getDeleteURL() {
        return this.getURL(this.getDeleteRoute(), this.getRouteParameters());
    }

    /**
     * @param {string} route      The route key to use to generate the URL.
     * @param {Object} parameters Route parameters.
     *
     * @returns {string} A URL that was generated using the given route key.
     */
    getURL(route, parameters = {}) {
        return this.getRouteResolver()(route, parameters);
    }

    /**
     * @returns {Request} A new `Request` using the given configuration.
     */
    getRequest(config) {
        return new Request(config);
    }

    /**
     * This is the central component for all HTTP requests and handling.
     *
     * @param  {Object}     config      Request configuration
     * @param  {function}   onRequest   Called before the request is made.
     * @param  {function}   onSuccess   Called when the request was successful.
     * @param  {function}   onFailure   Called when the request failed.
     */
    request(config, onRequest, onSuccess, onFailure) {
        return new Promise((resolve, reject) => {

            let check = onRequest();

            // Request should be skipped but the promise should not be resolved.
            if (check === false) {
                return;
            }

            // Request should be skipped but should be considered successful.
            if (check === true) {
                onSuccess(null);
                return resolve(null);
            }

            // Support passing the request configuration as a function, to allow
            // for deferred resolution of certain values that may have changed
            // during the call to "onRequest".
            if (_.isFunction(config)) {
                config = config();
            }

            // Apply the default headers.
            _.defaults(config.headers, this.getDefaultHeaders());

            // Make the request.
            return this.getRequest(config).send()

                // Success
                .then((response) => {
                    onSuccess(response);
                    return resolve(response);
                })

                // Failure
                .catch((error) => {
                    onFailure(error);
                    return reject(error);
                })

                // Failure fallback, for errors that occur in `onFailure`.
                .catch((error) => {
                    return reject(error);
                });
        });
    }

    /**
     * Fetches data from the database/API.
     *
     * @param {options}             Fetch options
     * @param {options.params}      Query params
     * @param {options.headers}     Query headers
     * @returns {Promise}
     */
    fetch(options = {}) {
        let config = () => _.defaults(options, {
            url:     this.getFetchURL(),
            method:  this.getFetchMethod(),
            params:  this.getFetchQuery(),
            headers: this.getFetchHeaders(),
        });

        return this.request(
            config,
            this.onFetch,
            this.onFetchSuccess,
            this.onFetchFailure
        );
    }

    /**
     * Persists data to the database/API.
     * @returns {Promise}
     */
    save() {
        let config = () => ({
            url:     this.getSaveURL(),
            method:  this.getSaveMethod(),
            data:    this.getSaveData(),
            params:  this.getSaveQuery(),
            headers: this.getSaveHeaders(),
        });

        return this.request(
            config,
            this.onSave,
            this.onSaveSuccess,
            this.onSaveFailure
        );
    }

    /**
     * Removes model or collection data from the database/API.
     * @returns {Promise}
     */
    delete() {
        let config = () => ({
            url:     this.getDeleteURL(),
            method:  this.getDeleteMethod(),
            data:    this.getDeleteBody(),
            params:  this.getDeleteQuery(),
            headers: this.getDeleteHeaders(),
        });

        return this.request(
            config,
            this.onDelete,
            this.onDeleteSuccess,
            this.onDeleteFailure
        );
    }
}

export default Base;
<|endoftext|>


<commit_before>var assert = require('assert')
var sugar  = require('sugar')

var captureRE = /\[([A-Za-z_][A-Za-z_0-9]*)\]/g

function mergeCaptures(current, next, noOverwrite) {
    for (var key in next) {
        if (!next.hasOwnProperty(key)) continue
        if (current.hasOwnProperty(key) && noOverwrite)
            assert.deepEqual(current[key], next[key], "Got mismatching results for capture [" + key + "]")

        current[key] = next[key]
    }
    return current
}

var Hooks = {
  date: function(input) {
    return Date.create(input).format('{yyyy}-{MM}-{dd}T{hh}:{mm}:{ss}{zzzz}');
  }
}

function processHooks(input, hooks) {
  var keys = Object.keys(hooks)

  return keys.reduce(function(str, key) {
    var regex = new RegExp("<" + key + ": ?([^>]+)>")
    return str.replace(regex, function(_, match) {
      return '"' + hooks[key](match) + '"'
    })
  }, input)
}

function JSONExp(src, options) {
    var self = function() {
      return self.exec.apply(self, arguments)
    }

    self.src     = src
    self.pattern = JSONExp.parse(src)
    self.options = options || {}

    self.__proto__ = JSONExp.prototype
    self.constructor = JSONExp

    return self
}

var specials = function(obj) {
    var opts = {}

    if (obj.hasOwnProperty(specials.glob))    opts.glob    = obj[specials.glob]
    if (obj.hasOwnProperty(specials.capture)) opts.capture = obj[specials.capture]
    if (obj.hasOwnProperty(specials.any))     opts.any     = obj[specials.any]
    if (obj.hasOwnProperty(specials.many))    opts.many    = obj[specials.many]

    if (obj instanceof Array) {
        var last = obj[obj.length - 1]
          , first = obj[0]
        if (first && first.hasOwnProperty(specials.many))
            opts.suffix = first[specials.many]
        if (last && last.hasOwnProperty(specials.many))
            opts.prefix = last[specials.many]
    }

    return opts
}

specials.glob    = "$JSONExp GLOB"
specials.capture = "$JSONExp CAPTURE"
specials.any     = "$JSONExp ANY"
specials.many    = "$JSONExp MANY"

function unicodeEscape(str) {
    var output = '', code
    for (var i = 0; i < str.length; i++) {
        code = str.charCodeAt(i).toString(16)
        while (code.length < 4) code = '0' + code
        output += '\\u' + code
    }
    return output
}

function escapeString(match, contents) {
    return '"' + unicodeEscape(contents) + '"'
}

JSONExp.preprocess = function(src) {
  var output = src
        .replace(/"(.*?[^\\])"/g, escapeString)
        .replace(/\.\.\.\s*\]/g,   '{ "' + specials.many    + '": true }]')
        .replace(/\[\s*\.\.\./g,  '[{ "' + specials.many    + '": true }')
        .replace(/\*/g,     '{ "' + specials.any     + '": true }')
        .replace(/\.\.\./g,   '"' + specials.glob    + '": true')
        .replace(captureRE, '{ "' + specials.capture + '": "$1" }')
        .replace(/(^(\s)*|\s)\/\/.*\n/g, '')

  return processHooks(output, Hooks);
}

JSONExp.parse = function(src) {
    return JSON.parse(JSONExp.preprocess(src))
}

JSONExp.prototype = new Function

JSONExp.prototype.exec = function(obj, options) {
    try {
        var match = this.assert(obj, options)
        return match
    } catch (e) {
        if (!(e instanceof assert.AssertionError)) throw e
        return null
    }
}

JSONExp.prototype.assert = function(obj, options) {
    // tests obj for matching, throws assertion errors if mismatched
    options = options || {}
    if (!options.namespace) options.namespace = {}

    var copy = JSON.parse(JSON.stringify(this.pattern))

    var match = this._match(copy, obj, options, '/')

    if (options.merge) mergeCaptures(options.namespace, match, true)

    return match
}

JSONExp.prototype.test = function(obj, options) {
    return !!this.exec(obj, options)
}

JSONExp.prototype._match = function(pattern, given, options, path) {
    var match = {}

    function test(a, b, message) {
        assert.equal(a, b, "At key '" + path + "': expected " + a + ", got " + b)
    }

    if ('object' == typeof pattern && pattern) {
        if (specials(pattern).capture) {
            match[specials(pattern).capture] = given
        } else if (specials(pattern).any) {
            // anything will do
        } else if (given instanceof Array) {
            // ignore arrays
            assert.ok(pattern, "Expected an Array at " + path + ", got null")
            assert.ok(pattern instanceof Array, "Expected an Array at " + path + ", got " + pattern.constructor)
            var length = pattern.length, o = 0, i = 0
            if (specials(pattern).prefix) {
                length--
            } else if (specials(pattern).suffix) {
                o = given.length - pattern.length
                i = 1
            } else {
                assert.equal(given.length, pattern.length, "Arrays are not the same length at " + path)
            }
            for (; i < length; i++) {
                var m = this._match(pattern[i], given[i + o], options, path + '/' + i)
                mergeCaptures(match, m)
            }
        } else {
            test(typeof pattern, typeof given)

            if (specials(pattern).glob) {
                delete pattern[specials.glob]
            } else {
                for (var key in given) {
                    if (!given.hasOwnProperty(key)) continue
                    assert.ok(pattern.hasOwnProperty(key), "At " + path + ", unexpected key \"" + key + "\": " + JSON.stringify(given[key]) )
                }
            }

            assert.ok(given, "Got null at " + path)
            assert.equal(given.constructor, pattern.constructor, "Not the same type")

            for (var key in pattern) {
                if (!pattern.hasOwnProperty(key)) continue
                assert.ok(given.hasOwnProperty(key), "At " + path + ", key '" + key + "' not found")
                var m = this._match(pattern[key], given[key], options, path + '/' + key)
                mergeCaptures(match, m)
            }
        }
    } else { // non-objects
        test(typeof pattern, typeof given)
        test(pattern, given)
    }
    return match
}

JSONExp.prototype.toString = function() {
    return '' + this.src
}

JSONExp.prototype.compile = function() {
    // precompile for better performance
    console.log("precompiling not yet implemented")

    /* Someday:
    this.exec = function() {
        // precompiled version
    }
    */
    return this
}

JSONExp.compile = function(pattern, options) {
    var exp = new JSONExp(pattern, options)
    return exp.compile()
}

module.exports = JSONExp
<commit_msg>Expose JSONExp.hooks so users can add new ones<commit_after>var assert = require('assert')
var sugar  = require('sugar')

var captureRE = /\[([A-Za-z_][A-Za-z_0-9]*)\]/g

function mergeCaptures(current, next, noOverwrite) {
    for (var key in next) {
        if (!next.hasOwnProperty(key)) continue
        if (current.hasOwnProperty(key) && noOverwrite)
            assert.deepEqual(current[key], next[key], "Got mismatching results for capture [" + key + "]")

        current[key] = next[key]
    }
    return current
}

function processHooks(input, hooks) {
  var keys = Object.keys(hooks)

  return keys.reduce(function(str, key) {
    var regex = new RegExp("<" + key + ": ?([^>]+)>")
    return str.replace(regex, function(_, match) {
      return '"' + hooks[key](match) + '"'
    })
  }, input)
}

function JSONExp(src, options) {
    var self = function() {
      return self.exec.apply(self, arguments)
    }

    self.src     = src
    self.pattern = JSONExp.parse(src)
    self.options = options || {}

    self.__proto__ = JSONExp.prototype
    self.constructor = JSONExp

    return self
}

var specials = function(obj) {
    var opts = {}

    if (obj.hasOwnProperty(specials.glob))    opts.glob    = obj[specials.glob]
    if (obj.hasOwnProperty(specials.capture)) opts.capture = obj[specials.capture]
    if (obj.hasOwnProperty(specials.any))     opts.any     = obj[specials.any]
    if (obj.hasOwnProperty(specials.many))    opts.many    = obj[specials.many]

    if (obj instanceof Array) {
        var last = obj[obj.length - 1]
          , first = obj[0]
        if (first && first.hasOwnProperty(specials.many))
            opts.suffix = first[specials.many]
        if (last && last.hasOwnProperty(specials.many))
            opts.prefix = last[specials.many]
    }

    return opts
}

specials.glob    = "$JSONExp GLOB"
specials.capture = "$JSONExp CAPTURE"
specials.any     = "$JSONExp ANY"
specials.many    = "$JSONExp MANY"

function unicodeEscape(str) {
    var output = '', code
    for (var i = 0; i < str.length; i++) {
        code = str.charCodeAt(i).toString(16)
        while (code.length < 4) code = '0' + code
        output += '\\u' + code
    }
    return output
}

function escapeString(match, contents) {
    return '"' + unicodeEscape(contents) + '"'
}

JSONExp.preprocess = function(src) {
  var output = src
        .replace(/"(.*?[^\\])"/g, escapeString)
        .replace(/\.\.\.\s*\]/g,   '{ "' + specials.many    + '": true }]')
        .replace(/\[\s*\.\.\./g,  '[{ "' + specials.many    + '": true }')
        .replace(/\*/g,     '{ "' + specials.any     + '": true }')
        .replace(/\.\.\./g,   '"' + specials.glob    + '": true')
        .replace(captureRE, '{ "' + specials.capture + '": "$1" }')
        .replace(/(^(\s)*|\s)\/\/.*\n/g, '')

  return processHooks(output, JSONExp.hooks);
}

JSONExp.parse = function(src) {
    return JSON.parse(JSONExp.preprocess(src))
}

JSONExp.prototype = new Function

JSONExp.prototype.exec = function(obj, options) {
    try {
        var match = this.assert(obj, options)
        return match
    } catch (e) {
        if (!(e instanceof assert.AssertionError)) throw e
        return null
    }
}

JSONExp.prototype.assert = function(obj, options) {
    // tests obj for matching, throws assertion errors if mismatched
    options = options || {}
    if (!options.namespace) options.namespace = {}

    var copy = JSON.parse(JSON.stringify(this.pattern))

    var match = this._match(copy, obj, options, '/')

    if (options.merge) mergeCaptures(options.namespace, match, true)

    return match
}

JSONExp.prototype.test = function(obj, options) {
    return !!this.exec(obj, options)
}

JSONExp.prototype._match = function(pattern, given, options, path) {
    var match = {}

    function test(a, b, message) {
        assert.equal(a, b, "At key '" + path + "': expected " + a + ", got " + b)
    }

    if ('object' == typeof pattern && pattern) {
        if (specials(pattern).capture) {
            match[specials(pattern).capture] = given
        } else if (specials(pattern).any) {
            // anything will do
        } else if (given instanceof Array) {
            // ignore arrays
            assert.ok(pattern, "Expected an Array at " + path + ", got null")
            assert.ok(pattern instanceof Array, "Expected an Array at " + path + ", got " + pattern.constructor)
            var length = pattern.length, o = 0, i = 0
            if (specials(pattern).prefix) {
                length--
            } else if (specials(pattern).suffix) {
                o = given.length - pattern.length
                i = 1
            } else {
                assert.equal(given.length, pattern.length, "Arrays are not the same length at " + path)
            }
            for (; i < length; i++) {
                var m = this._match(pattern[i], given[i + o], options, path + '/' + i)
                mergeCaptures(match, m)
            }
        } else {
            test(typeof pattern, typeof given)

            if (specials(pattern).glob) {
                delete pattern[specials.glob]
            } else {
                for (var key in given) {
                    if (!given.hasOwnProperty(key)) continue
                    assert.ok(pattern.hasOwnProperty(key), "At " + path + ", unexpected key \"" + key + "\": " + JSON.stringify(given[key]) )
                }
            }

            assert.ok(given, "Got null at " + path)
            assert.equal(given.constructor, pattern.constructor, "Not the same type")

            for (var key in pattern) {
                if (!pattern.hasOwnProperty(key)) continue
                assert.ok(given.hasOwnProperty(key), "At " + path + ", key '" + key + "' not found")
                var m = this._match(pattern[key], given[key], options, path + '/' + key)
                mergeCaptures(match, m)
            }
        }
    } else { // non-objects
        test(typeof pattern, typeof given)
        test(pattern, given)
    }
    return match
}

JSONExp.prototype.toString = function() {
    return '' + this.src
}

JSONExp.prototype.compile = function() {
    // precompile for better performance
    console.log("precompiling not yet implemented")

    /* Someday:
    this.exec = function() {
        // precompiled version
    }
    */
    return this
}

JSONExp.compile = function(pattern, options) {
    var exp = new JSONExp(pattern, options)
    return exp.compile()
}

JSONExp.hooks = {
  date: function(input) {
    return Date.create(input).format('{yyyy}-{MM}-{dd}T{hh}:{mm}:{ss}{zzzz}');
  }
}

module.exports = JSONExp
<|endoftext|>


<commit_before>var _ = require('underscore'),
    child_process = require('child_process'),
    seq = require('seq'),
    Canvas = require('canvas'),
    passError = require('./util/passError'),
    packers = require('./packers');

require('bufferjs');

function getCanvasImageFromBuffer(buffer, cb) {
    var canvasImage = new Canvas.Image();
    canvasImage.onerror = function (err) {
        process.nextTick(function () {
            cb(err);
        });
    };
    canvasImage.onload = function () {
        process.nextTick(function () {
            cb(null, canvasImage);
        });
    };
    canvasImage.src = buffer;
}

function getCanvasImageFromImageAsset(imageAsset, cb) {
    imageAsset.getSerializedSrc(passError(cb, function (src) {
        if (imageAsset.type === 'Png' || imageAsset.type === 'Jpeg') {
            getCanvasImageFromBuffer(src, cb);
        } else {
            // Use GraphicsMagick while waiting for gif support: https://github.com/LearnBoost/node-canvas/issues/78
            var buffers = [],
                convertProcess = child_process.spawn('gm', ['convert', imageAsset.type.toLowerCase() + ':-', 'png:-']);

            convertProcess.stdout.on('data', function (buffer) {
                buffers.push(buffer);
            });
            convertProcess.on('exit', function () {
                getCanvasImageFromBuffer(Buffer.concat(buffers), cb);
            }).on('error', cb);

            convertProcess.stdin.write(src);
            convertProcess.stdin.end();
        }
    }));
}

function calculateSpritePadding(paddingStr) {
    if (paddingStr) {
        // Strip units ('px' assumed)
        var tokens = [];
        paddingStr.split(/\s+/).forEach(function (token) {
            var num = parseInt(token.replace(/[a-z]+$/, ''), 10);
            if (!isNaN(num)) {
                tokens.push(num);
            }
        });
        if (tokens.length === 4) {
            return tokens;
        } else if (tokens.length === 3) {
            return [tokens[0], tokens[1], tokens[2], tokens[1]]; // T, L+R, B
        } else if (tokens.length === 2) {
            return [tokens[0], tokens[1], tokens[0], tokens[1]]; // T+B, L+R
        } else if (tokens.length === 1) {
            return [tokens[0], tokens[0], tokens[0], tokens[0]];
        }
    }
    return [0, 0, 0, 0];
}

module.exports = function () {
    return function spriteBackgroundImages(assetGraph, cb) {
        // Waiting for https://github.com/LearnBoost/node-canvas/issues/52
        var cairoVersion = Canvas.cairoVersion.split(".").map(function (str) {return parseInt(str, 10);});
        if (cairoVersion[0] < 1 || cairoVersion[1] < 10) {
            console.warn("assetgraph-sprite: Cannot create sprites due to missing canvas.getContext('2d').drawImage() support. Please compile node-canvas with Cairo version 1.10.0 or above.");
            return cb();
        }

        var assets = assetGraph.constructor.assets, // Hack
            relations = assetGraph.constructor.relations, // Hack
            spriteGroups = {};
        assetGraph.findRelations({type: 'CssImage'}).forEach(function (relation) {
            var spriteInfo = assets.Css.extractInfoFromRule(relation.cssRule, assets.Css.vendorPrefix + '-sprite-'),
                asset = relation.to;
            if (spriteInfo.group) {
                var spriteGroup = spriteGroups[spriteInfo.group];
                if (!spriteGroup) {
                    spriteGroup = spriteGroups[spriteInfo.group] = {
                        imageInfosById: {}
                    };
                }
                var imageInfo = spriteGroup[asset.id],
                    padding = calculateSpritePadding(spriteInfo.padding);
                if (!imageInfo) {
                    imageInfo = spriteGroup.imageInfosById[asset.id] = {
                        padding: padding,
                        asset: asset,
                        incomingRelations: [relation]
                    };
                } else {
                    imageInfo.incomingRelations.push(relation);
                    for (var i = 0 ; i < 4 ; i += 1) {
                        imageInfo.padding[i] = Math.max(padding[i], imageInfo.padding[i]);
                    }
                }
            }
        });

        seq(assetGraph.findAssets({type: 'Css'}))
            .parEach(function (cssAsset) {
                cssAsset.getParseTree(this);
            })
            .parEach(function (cssAsset) {
                assets.Css.eachRuleInParseTree(cssAsset.parseTree, function (cssRule) {
                    if ((assets.Css.vendorPrefix + '-sprite-selector-for-group') in cssRule.style) {
                        var spriteInfo = assets.Css.extractInfoFromRule(cssRule, assets.Css.vendorPrefix + '-sprite-'),
                            spriteGroupName = spriteInfo.selectorForGroup;
                        if (spriteGroupName in spriteGroups) {
                            if (spriteGroups[spriteGroupName].placeHolder) {
                                console.warn("assetgraph-sprite: Multiple definitions of " + spriteGroupName + " sprite");
                            }
                            spriteGroups[spriteGroupName].placeHolder = _.extend(spriteInfo, {
                                asset: cssAsset,
                                cssRule: cssRule
                            });
                        }
                    }
                });
                this();
            })
            .set(_.keys(spriteGroups))
            .seqEach(function (spriteGroupName) {
                var callback = this,
                    spriteGroup = spriteGroups[spriteGroupName],
                    imageInfos = _.values(spriteGroup.imageInfosById),
                    spriteInfo = spriteGroup.placeHolder ? spriteGroup.placeHolder : {};

                seq(imageInfos)
                    .parMap(function (imageInfo) {
                        getCanvasImageFromImageAsset(imageInfo.asset, this);
                    })
                    .seqEach(function (canvasImage, i) {
                        _.extend(imageInfos[i], {
                            canvasImage: canvasImage,
                            width: canvasImage.width,
                            height: canvasImage.height
                        });
                        process.nextTick(this);
                    })
                    .seq(function () {
                        var packerName = {
                            'jim-scott': 'jimScott',
                            horizontal: 'horizontal',
                            vertical: 'vertical'
                        }[spriteInfo.packer] || 'tryAll';
                        var packingData = packers[packerName].pack(imageInfos),
                            canvas = new Canvas(packingData.width, packingData.height),
                            ctx = canvas.getContext('2d');
                        imageInfos = packingData.imageInfos;
                        if ('backgroundColor' in spriteInfo) {
                            ctx.fillStyle = spriteInfo.imageBackgroundColor;
                            ctx.fillRect(0, 0, canvas.width, canvas.height);
                        }
                        imageInfos.forEach(function (imageInfo) {
                            ctx.drawImage(imageInfo.canvasImage, imageInfo.x, imageInfo.y, imageInfo.width, imageInfo.height);
                        });
                        canvas.toBuffer(this);
                    })
                    .seq(function (spriteBuffer) {
                        var spriteAsset = new assets.Png({
                            rawSrc: spriteBuffer
                        });
                        spriteAsset.url = assetGraph.root + spriteAsset.id + spriteAsset.defaultExtension; // FIXME
                        assetGraph.addAsset(spriteAsset);
                        if (spriteGroup.placeHolder) {
                            var cssRule = spriteGroup.placeHolder.cssRule,
                                relation = new relations.CssImage({
                                    cssRule: cssRule,
                                    propertyName: 'background-image',
                                    from: spriteGroup.placeHolder.asset,
                                    to: spriteAsset
                                });
                            cssRule.style.setProperty('background-image', 'url(...)',
                                                      spriteGroup.placeHolder.important && 'important');
                            // I can't see why the ordering of CssImage relations should be significant...
                            assetGraph.addRelation(relation, 'last');
                            assetGraph.refreshRelationUrl(relation);
                            ['selector-for-group', 'packer', 'image-format', 'background-color'].forEach(function (propertyName) {
                                spriteGroup.placeHolder.cssRule.style.removeProperty(assets.Css.vendorPrefix + '-sprite-' + propertyName);
                            });
                        }
                        imageInfos.forEach(function (imageInfo) {
                            imageInfo.incomingRelations.forEach(function (incomingRelation) {
                                assetGraph.markAssetDirty(incomingRelation.from);
                                var relationSpriteInfo = assets.Css.extractInfoFromRule(incomingRelation.cssRule, assets.Css.vendorPrefix + '-sprite-');
                                incomingRelation.cssRule.style.setProperty('background-position',
                                                                           (imageInfo.x ? (-imageInfo.x) + "px " : "0 ") +
                                                                           (imageInfo.y ? -imageInfo.y + "px" : "0"),
                                                                           relationSpriteInfo.important && 'important');
                                ['group', 'padding', 'no-group-selector', 'important'].forEach(function (propertyName) {
                                    incomingRelation.cssRule.style.removeProperty(assets.Css.vendorPrefix + '-sprite-' + propertyName);
                                }, this);
                                if (relationSpriteInfo.noGroupSelector) {
                                    // The user specified that this selector needs its own background-image/background
                                    // property pointing at the sprite rather than relying on the Html elements also being
                                    // matched by the sprite group's "main" selector, which would have been preferable.
                                    var relation = new relations.CssImage({
                                        cssRule: incomingRelation.cssRule,
                                        propertyName: incomingRelation.propertyName,
                                        from: incomingRelation.from,
                                        to: spriteAsset
                                    });
                                    assetGraph.addRelation(relation, 'before', incomingRelation);
                                    assetGraph.refreshRelationUrl(relation);
                                    assetGraph.removeRelation(incomingRelation);
                                } else {
                                    assetGraph.detachAndRemoveRelation(incomingRelation);
                                }

                                // Remove the original image if it has become an orphan:
                                if (!assetGraph.findRelations({to: incomingRelation.to}).length) {
                                    assetGraph.removeAsset(incomingRelation.to);
                                }
                            });
                        });
                        callback();
                    })
                    ['catch'](callback);
            })
            .seq(function () {
                cb();
            })
            ['catch'](cb);
    };
};
<commit_msg>Use urlTools.resolveUrl(assetGraph.root, foo) instead of assetGraph.root + foo.<commit_after>var _ = require('underscore'),
    child_process = require('child_process'),
    seq = require('seq'),
    Canvas = require('canvas'),
    passError = require('./util/passError'),
    urlTools = require('assetgraph/lib/util/urlTools'),
    packers = require('./packers');

require('bufferjs');

function getCanvasImageFromBuffer(buffer, cb) {
    var canvasImage = new Canvas.Image();
    canvasImage.onerror = function (err) {
        process.nextTick(function () {
            cb(err);
        });
    };
    canvasImage.onload = function () {
        process.nextTick(function () {
            cb(null, canvasImage);
        });
    };
    canvasImage.src = buffer;
}

function getCanvasImageFromImageAsset(imageAsset, cb) {
    imageAsset.getSerializedSrc(passError(cb, function (src) {
        if (imageAsset.type === 'Png' || imageAsset.type === 'Jpeg') {
            getCanvasImageFromBuffer(src, cb);
        } else {
            // Use GraphicsMagick while waiting for gif support: https://github.com/LearnBoost/node-canvas/issues/78
            var buffers = [],
                convertProcess = child_process.spawn('gm', ['convert', imageAsset.type.toLowerCase() + ':-', 'png:-']);

            convertProcess.stdout.on('data', function (buffer) {
                buffers.push(buffer);
            });
            convertProcess.on('exit', function () {
                getCanvasImageFromBuffer(Buffer.concat(buffers), cb);
            }).on('error', cb);

            convertProcess.stdin.write(src);
            convertProcess.stdin.end();
        }
    }));
}

function calculateSpritePadding(paddingStr) {
    if (paddingStr) {
        // Strip units ('px' assumed)
        var tokens = [];
        paddingStr.split(/\s+/).forEach(function (token) {
            var num = parseInt(token.replace(/[a-z]+$/, ''), 10);
            if (!isNaN(num)) {
                tokens.push(num);
            }
        });
        if (tokens.length === 4) {
            return tokens;
        } else if (tokens.length === 3) {
            return [tokens[0], tokens[1], tokens[2], tokens[1]]; // T, L+R, B
        } else if (tokens.length === 2) {
            return [tokens[0], tokens[1], tokens[0], tokens[1]]; // T+B, L+R
        } else if (tokens.length === 1) {
            return [tokens[0], tokens[0], tokens[0], tokens[0]];
        }
    }
    return [0, 0, 0, 0];
}

module.exports = function () {
    return function spriteBackgroundImages(assetGraph, cb) {
        // Waiting for https://github.com/LearnBoost/node-canvas/issues/52
        var cairoVersion = Canvas.cairoVersion.split(".").map(function (str) {return parseInt(str, 10);});
        if (cairoVersion[0] < 1 || cairoVersion[1] < 10) {
            console.warn("assetgraph-sprite: Cannot create sprites due to missing canvas.getContext('2d').drawImage() support. Please compile node-canvas with Cairo version 1.10.0 or above.");
            return cb();
        }

        var assets = assetGraph.constructor.assets, // Hack
            relations = assetGraph.constructor.relations, // Hack
            spriteGroups = {};
        assetGraph.findRelations({type: 'CssImage'}).forEach(function (relation) {
            var spriteInfo = assets.Css.extractInfoFromRule(relation.cssRule, assets.Css.vendorPrefix + '-sprite-'),
                asset = relation.to;
            if (spriteInfo.group) {
                var spriteGroup = spriteGroups[spriteInfo.group];
                if (!spriteGroup) {
                    spriteGroup = spriteGroups[spriteInfo.group] = {
                        imageInfosById: {}
                    };
                }
                var imageInfo = spriteGroup[asset.id],
                    padding = calculateSpritePadding(spriteInfo.padding);
                if (!imageInfo) {
                    imageInfo = spriteGroup.imageInfosById[asset.id] = {
                        padding: padding,
                        asset: asset,
                        incomingRelations: [relation]
                    };
                } else {
                    imageInfo.incomingRelations.push(relation);
                    for (var i = 0 ; i < 4 ; i += 1) {
                        imageInfo.padding[i] = Math.max(padding[i], imageInfo.padding[i]);
                    }
                }
            }
        });

        seq(assetGraph.findAssets({type: 'Css'}))
            .parEach(function (cssAsset) {
                cssAsset.getParseTree(this);
            })
            .parEach(function (cssAsset) {
                assets.Css.eachRuleInParseTree(cssAsset.parseTree, function (cssRule) {
                    if ((assets.Css.vendorPrefix + '-sprite-selector-for-group') in cssRule.style) {
                        var spriteInfo = assets.Css.extractInfoFromRule(cssRule, assets.Css.vendorPrefix + '-sprite-'),
                            spriteGroupName = spriteInfo.selectorForGroup;
                        if (spriteGroupName in spriteGroups) {
                            if (spriteGroups[spriteGroupName].placeHolder) {
                                console.warn("assetgraph-sprite: Multiple definitions of " + spriteGroupName + " sprite");
                            }
                            spriteGroups[spriteGroupName].placeHolder = _.extend(spriteInfo, {
                                asset: cssAsset,
                                cssRule: cssRule
                            });
                        }
                    }
                });
                this();
            })
            .set(_.keys(spriteGroups))
            .seqEach(function (spriteGroupName) {
                var callback = this,
                    spriteGroup = spriteGroups[spriteGroupName],
                    imageInfos = _.values(spriteGroup.imageInfosById),
                    spriteInfo = spriteGroup.placeHolder ? spriteGroup.placeHolder : {};

                seq(imageInfos)
                    .parMap(function (imageInfo) {
                        getCanvasImageFromImageAsset(imageInfo.asset, this);
                    })
                    .seqEach(function (canvasImage, i) {
                        _.extend(imageInfos[i], {
                            canvasImage: canvasImage,
                            width: canvasImage.width,
                            height: canvasImage.height
                        });
                        process.nextTick(this);
                    })
                    .seq(function () {
                        var packerName = {
                            'jim-scott': 'jimScott',
                            horizontal: 'horizontal',
                            vertical: 'vertical'
                        }[spriteInfo.packer] || 'tryAll';
                        var packingData = packers[packerName].pack(imageInfos),
                            canvas = new Canvas(packingData.width, packingData.height),
                            ctx = canvas.getContext('2d');
                        imageInfos = packingData.imageInfos;
                        if ('backgroundColor' in spriteInfo) {
                            ctx.fillStyle = spriteInfo.imageBackgroundColor;
                            ctx.fillRect(0, 0, canvas.width, canvas.height);
                        }
                        imageInfos.forEach(function (imageInfo) {
                            ctx.drawImage(imageInfo.canvasImage, imageInfo.x, imageInfo.y, imageInfo.width, imageInfo.height);
                        });
                        canvas.toBuffer(this);
                    })
                    .seq(function (spriteBuffer) {
                        var spriteAsset = new assets.Png({
                            rawSrc: spriteBuffer
                        });
                        spriteAsset.url = urlTools.resolveUrl(assetGraph.root, spriteAsset.id + spriteAsset.defaultExtension);
                        assetGraph.addAsset(spriteAsset);
                        if (spriteGroup.placeHolder) {
                            var cssRule = spriteGroup.placeHolder.cssRule,
                                relation = new relations.CssImage({
                                    cssRule: cssRule,
                                    propertyName: 'background-image',
                                    from: spriteGroup.placeHolder.asset,
                                    to: spriteAsset
                                });
                            cssRule.style.setProperty('background-image', 'url(...)',
                                                      spriteGroup.placeHolder.important && 'important');
                            // I can't see why the ordering of CssImage relations should be significant...
                            assetGraph.addRelation(relation, 'last');
                            assetGraph.refreshRelationUrl(relation);
                            ['selector-for-group', 'packer', 'image-format', 'background-color'].forEach(function (propertyName) {
                                spriteGroup.placeHolder.cssRule.style.removeProperty(assets.Css.vendorPrefix + '-sprite-' + propertyName);
                            });
                        }
                        imageInfos.forEach(function (imageInfo) {
                            imageInfo.incomingRelations.forEach(function (incomingRelation) {
                                assetGraph.markAssetDirty(incomingRelation.from);
                                var relationSpriteInfo = assets.Css.extractInfoFromRule(incomingRelation.cssRule, assets.Css.vendorPrefix + '-sprite-');
                                incomingRelation.cssRule.style.setProperty('background-position',
                                                                           (imageInfo.x ? (-imageInfo.x) + "px " : "0 ") +
                                                                           (imageInfo.y ? -imageInfo.y + "px" : "0"),
                                                                           relationSpriteInfo.important && 'important');
                                ['group', 'padding', 'no-group-selector', 'important'].forEach(function (propertyName) {
                                    incomingRelation.cssRule.style.removeProperty(assets.Css.vendorPrefix + '-sprite-' + propertyName);
                                }, this);
                                if (relationSpriteInfo.noGroupSelector) {
                                    // The user specified that this selector needs its own background-image/background
                                    // property pointing at the sprite rather than relying on the Html elements also being
                                    // matched by the sprite group's "main" selector, which would have been preferable.
                                    var relation = new relations.CssImage({
                                        cssRule: incomingRelation.cssRule,
                                        propertyName: incomingRelation.propertyName,
                                        from: incomingRelation.from,
                                        to: spriteAsset
                                    });
                                    assetGraph.addRelation(relation, 'before', incomingRelation);
                                    assetGraph.refreshRelationUrl(relation);
                                    assetGraph.removeRelation(incomingRelation);
                                } else {
                                    assetGraph.detachAndRemoveRelation(incomingRelation);
                                }

                                // Remove the original image if it has become an orphan:
                                if (!assetGraph.findRelations({to: incomingRelation.to}).length) {
                                    assetGraph.removeAsset(incomingRelation.to);
                                }
                            });
                        });
                        callback();
                    })
                    ['catch'](callback);
            })
            .seq(function () {
                cb();
            })
            ['catch'](cb);
    };
};
<|endoftext|>


<commit_before>// --------------------
// Sequelize definer
// --------------------

// exports
exports = module.exports = function(Sequelize) {
	return Sequelize;
};
<commit_msg>Code tidy<commit_after>// --------------------
// Sequelize definer
// --------------------

// exports
module.exports = function(Sequelize) {
	return Sequelize;
};
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
var ip = require('ip')
var config = require('./config')
var server = require('./server')
var service = require('./service')
var discovery = require('./discovery')
var Superpipe = require('superpipe')

/**
 * Micromono constructor
 */
var Micromono = function() {
  var micromono = this

  // Store instances of services
  micromono.services = {}
  // Make instance of superpipe
  micromono.superpipe = Superpipe()
  micromono.superpipe.autoBind(true)
  // Assign submodules to the main object
  micromono.register = discovery.register.bind(micromono)
  micromono.Service = service.Service
  micromono.createService = service.createService

  // Expose constructor
  micromono.Micromono = Micromono

  // Apply default configurations
  micromono.defaultConfig()

  if (micromono.get('MICROMONO_DISCOVERY_AGENT')) {
    // Running in discovery agent mode. No service or server will be started.
    // It's also not possible to require services.
    micromono.startService = function() {}
    micromono.startBalancer = function() {}
    micromono.require = function() {}
    // Run prober
    require('./discovery/prober')
    // Ignore uncaught exception for this case.
    process.on('uncaughtException', function(err) {
      console.error('\n\tCaught "uncaughtException" in agent mode. The error might be okay to be ignored:\n\t', err, '\n');
    })
  } else {
    micromono.startService = service.startService.bind(null, micromono)
    micromono.startBalancer = server.startBalancer.bind(micromono)
    micromono.require = discovery.require.bind(micromono)
  }

  return micromono
}

// Add configurable api using superpipe
Micromono.prototype.set = function(name, deps, props) {
  this.superpipe.set(name, deps, props)
  return this
}

Micromono.prototype.get = function(name) {
  return this.superpipe.get(name)
}

Micromono.prototype.config = function(options) {
  var micromono = this
  Object.keys(options).forEach(function(key) {
    if (/^MICROMONO_/.test(key))
      micromono.set(key, options[key])
  })
  return this
}

Micromono.prototype.defaultConfig = function() {
  // Bundle asset for dev?
  if ('development' === process.env.NODE_ENV
    && undefined === this.get('MICROMONO_BUNDLE_DEV'))
    this.set('MICROMONO_BUNDLE_DEV', true)

  this.set('services', this.services)
  // Default configurations
  var host = process.env.HOST || ip.address() || '0.0.0.0'
  this.set({
    MICROMONO_PORT: process.env.PORT || 0,
    MICROMONO_HOST: host,
    MICROMONO_RPC_PORT: 0,
    MICROMONO_RPC_HOST: host,
    MICROMONO_CHN_ENDPOINT: 'tcp://' + host
  })

  // Load configurations from command line and env.
  var options = config(['default', 'discovery', 'service', 'server'])
  this.serviceDir = options.serviceDir
  this.config(options)
}

/**
 * Exports the main MicroMono instance object.
 *
 * @type {Object}
 */
module.exports = new Micromono()

process.on('SIGINT', function() {
  console.log('\n\tShutting down micromono...\n')
  process.exit(0)
})
<commit_msg>Remove all existing `uncaughtException` listeners.<commit_after>/**
 * Module dependencies
 */
var ip = require('ip')
var config = require('./config')
var server = require('./server')
var service = require('./service')
var discovery = require('./discovery')
var Superpipe = require('superpipe')

/**
 * Micromono constructor
 */
var Micromono = function() {
  var micromono = this

  // Store instances of services
  micromono.services = {}
  // Make instance of superpipe
  micromono.superpipe = Superpipe()
  micromono.superpipe.autoBind(true)
  // Assign submodules to the main object
  micromono.register = discovery.register.bind(micromono)
  micromono.Service = service.Service
  micromono.createService = service.createService

  // Expose constructor
  micromono.Micromono = Micromono

  // Apply default configurations
  micromono.defaultConfig()

  if (micromono.get('MICROMONO_DISCOVERY_AGENT')) {
    // Running in discovery agent mode. No service or server will be started.
    // It's also not possible to require services.
    micromono.startService = function() {}
    micromono.startBalancer = function() {}
    micromono.require = function() {}
    // Run prober
    require('./discovery/prober')
    // Ignore uncaught exception for this case.
    process.removeAllListeners('uncaughtException')
    process.on('uncaughtException', function(err) {
      console.error('\n\tCaught "uncaughtException" in agent mode. The error might be okay to be ignored:\n\t', err, '\n');
    })
  } else {
    micromono.startService = service.startService.bind(null, micromono)
    micromono.startBalancer = server.startBalancer.bind(micromono)
    micromono.require = discovery.require.bind(micromono)
  }

  return micromono
}

// Add configurable api using superpipe
Micromono.prototype.set = function(name, deps, props) {
  this.superpipe.set(name, deps, props)
  return this
}

Micromono.prototype.get = function(name) {
  return this.superpipe.get(name)
}

Micromono.prototype.config = function(options) {
  var micromono = this
  Object.keys(options).forEach(function(key) {
    if (/^MICROMONO_/.test(key))
      micromono.set(key, options[key])
  })
  return this
}

Micromono.prototype.defaultConfig = function() {
  // Bundle asset for dev?
  if ('development' === process.env.NODE_ENV
    && undefined === this.get('MICROMONO_BUNDLE_DEV'))
    this.set('MICROMONO_BUNDLE_DEV', true)

  this.set('services', this.services)
  // Default configurations
  var host = process.env.HOST || ip.address() || '0.0.0.0'
  this.set({
    MICROMONO_PORT: process.env.PORT || 0,
    MICROMONO_HOST: host,
    MICROMONO_RPC_PORT: 0,
    MICROMONO_RPC_HOST: host,
    MICROMONO_CHN_ENDPOINT: 'tcp://' + host
  })

  // Load configurations from command line and env.
  var options = config(['default', 'discovery', 'service', 'server'])
  this.serviceDir = options.serviceDir
  this.config(options)
}

/**
 * Exports the main MicroMono instance object.
 *
 * @type {Object}
 */
module.exports = new Micromono()

process.on('SIGINT', function() {
  console.log('\n\tShutting down micromono...\n')
  process.exit(0)
})
<|endoftext|>