<commit_before>(function (window) {

    // save all methods in this object
    var OldConsole = {};

    // each method
    for (var method in console) {

        (function (m) {
            // save the current method in the OldConsole object
            OldConsole[m] = console[m]

            // replace the old method with a new function
            console[m] = function () {

                // one of these methods
                if (["log", "info", "warn", "error" ].indexOf(m) !== -1) {

                    // each DOM reference where the message must be appended
                    for (var i = 0; i < ConsoleJs._els.length; ++i) {

                        // current element
                        var cEl = ConsoleJs._els[i]

                            // get the element HTML
                          , htmlToSet = cEl.innerHTML

                            // line separator
                          , lineSeparator = "
";

                        // pre element
                        if (cEl.tagName === "PRE") {

                            // new line character
                            lineSeparator = "\n";
                        }

                        // add spans with method classes
                        htmlToSet += ""

                        // each argument
                        for (var i = 0; i < arguments.length; ++i) {
                            htmlToSet += JSON.stringify(arguments[i]) + " ";
                        }

                        // close span element
                        htmlToSet += ""

                        // append the line separator
                        htmlToSet += lineSeparator;

                        // set the new html
                        cEl.innerHTML = htmlToSet;
                    }
                }

                // call the old method
                // OldConsole[m].apply(window, arguments);
            }
        })(method);
    }

    // the library object
    var ConsoleJs = {

        /*
         *  ConsoleJs#init
         *  Adds a new element where the console will show the output
         *
         *
         *  Arugments
         *      @options: object containing the following fields:
         *          - selector: the selector of the DOM element
         *
         * */
        init: function (options) {

            // set options
            if (!options || options.constructor !== Object) {
                options = {};
            }

            // selector was not provided
            if (!options.selector) {
                throw new Error ("options.selector is required");
            }

            // get all elements
            var newEls = document.querySelectorAll(options.selector);

            // each element
            for (var i = 0; i < newEls.length; ++i) {

                // push the current element
                ConsoleJs._els.push(newEls[i]);
            }
        }

        // here we will save the DOM where console output is shown
      , _els: []
    };

    // export it
    window.ConsoleJs = ConsoleJs;
})(window);
<commit_msg>Added dir in the list of console messages<commit_after>(function (window) {

    // save all methods in this object
    var OldConsole = {};

    // each method
    for (var method in console) {

        (function (m) {
            // save the current method in the OldConsole object
            OldConsole[m] = console[m]

            // replace the old method with a new function
            console[m] = function () {

                // one of these methods
                if (["log", "info", "warn", "error", "dir"].indexOf(m) !== -1) {

                    // each DOM reference where the message must be appended
                    for (var i = 0; i < ConsoleJs._els.length; ++i) {

                        // current element
                        var cEl = ConsoleJs._els[i]

                            // get the element HTML
                          , htmlToSet = cEl.innerHTML

                            // line separator
                          , lineSeparator = "
";

                        // pre element
                        if (cEl.tagName === "PRE") {

                            // new line character
                            lineSeparator = "\n";
                        }

                        // add spans with method classes
                        htmlToSet += ""

                        // each argument
                        for (var i = 0; i < arguments.length; ++i) {
                            htmlToSet += JSON.stringify(arguments[i]) + " ";
                        }

                        // close span element
                        htmlToSet += ""

                        // append the line separator
                        htmlToSet += lineSeparator;

                        // set the new html
                        cEl.innerHTML = htmlToSet;
                    }
                }

                // call the old method
                // OldConsole[m].apply(window, arguments);
            }
        })(method);
    }

    // the library object
    var ConsoleJs = {

        /*
         *  ConsoleJs#init
         *  Adds a new element where the console will show the output
         *
         *
         *  Arugments
         *      @options: object containing the following fields:
         *          - selector: the selector of the DOM element
         *
         * */
        init: function (options) {

            // set options
            if (!options || options.constructor !== Object) {
                options = {};
            }

            // selector was not provided
            if (!options.selector) {
                throw new Error ("options.selector is required");
            }

            // get all elements
            var newEls = document.querySelectorAll(options.selector);

            // each element
            for (var i = 0; i < newEls.length; ++i) {

                // push the current element
                ConsoleJs._els.push(newEls[i]);
            }
        }

        // here we will save the DOM where console output is shown
      , _els: []
    };

    // export it
    window.ConsoleJs = ConsoleJs;
})(window);
<|endoftext|>


<commit_before><commit_msg>clean context .dependent vars<commit_after><|endoftext|>


<commit_before>'use strict';

var util = require('util'),
	EventEmitter = require('events').EventEmitter;

var request = require('request'),
	async = require('async'),
	_ = require('lodash');

/**
 * constructor of Crawler
 */
function Crawler(opts) {
	this._init(opts);
}

util.inherits(Crawler, EventEmitter);

/**
 * initialize Crawler
 */
Crawler.prototype._init = function _init(opts) {
	var self = this;

	self._tasks = [];
	self._rules = {};
	self._taskCache = '';

	self.taskCounter = 0;
	self.failCounter = 0;
	self.settings = {
		interval: 0,
		retry: 3,
		concurrency: 1,
		skipRepetitiveTask: true
	};
	self.requestOpts = {
		timeout: 10000
	};
	self.errLog = '';
	if (opts) {
		if (opts.requestOpts) {
			self.requestOpts = _.assign(self.requestOpts, opts.requestOpts);
			delete opts.requestOpts;
		}
		self.settings = _.assign(self.settings, opts);
	}

	self.on('pollTask', function () {
		var size = self.settings.concurrency;
		size = size <= self._tasks.length ? size : self._tasks.length;
		if (self.q.length() < size) {
			var idle = size - self.q.length();
			var ts = self._tasks.splice(0, idle);
			while (idle != 0) {
				self.q.push(ts[ts.length - idle], function (err) {
					if (err) {
						return console.error(err);
					}
					if (self._tasks.length > 0) {
						self.emit('pollTask');
					}
				});
				idle--;
			}
		}
	});

	// processing of task pool
	self.q = async.queue(function (task, done) {
		var crawl = function crawl() {
			if (self.settings.skipRepetitiveTask) {
				self._taskCache = self._taskCache.concat(task + '|');
			}
			console.log(util.format('Current No.%s: %s', self.taskCounter + 1, task));
			self._crawl(task, function (result) {
				var flag = false;
				Object.keys(self._rules).forEach(function (rule) {
					if (flag) {
						return;
					}
					if (new RegExp(rule).test(result.url)) {
						try {
							self._rules[rule](result);
						} catch (err) {
							self.errLog = self.errLog.concat(util.format('[Processing Error][No.%s]%s\n[%s]\n', result.id, result.url, err));
						} finally {
							flag = true;
							done();
						}
					}
				});
				if (!flag) {
					self.errLog = self.errLog.concat(util.format('[No Rule Handle][No.%s]%s\n', result.id, result.url));
					done();
				}
			});
		};

		// deferred execution for crawl's interval
		if (self.settings.interval != 0 && self.taskCounter != 0) {
			console.log('Crawler is sleeping...');
			setTimeout(crawl, self.settings.interval);
		} else {
			crawl();
		}
	}, self.settings.concurrency);

	self.q.drain = function () {
		console.log(util.format('[Crawler Finished] Total: %s.Success: %s.Fail: %s.',
			self.taskCounter, self.taskCounter - self.failCounter, self.failCounter));
		if (self.errLog.length > 0) {
			console.log(self.errLog);
		}
		if (self.onTaskPoolDrain) {
			self.onTaskPoolDrain();
		}
	};
};

/**
 * twist crawler's settings
 * @param <object> setting
 */
Crawler.prototype.twist = function twist(opts) {
	var self = this;
	if (opts) {
		if (opts.requestOpts) {
			self.requestOpts = _.assign(self.requestOpts, opts.requestOpts);
			delete opts.requestOpts;
		}
		self.settings = _.assign(self.settings, opts);
	}
	return self;
}

/**
 * add urls to task pool
 * @param <array[string]> urls : array of url
 */
Crawler.prototype.addTasks = function addTasks(urls) {
	var self = this;
	if (typeof (urls) == 'string') {
		if (self.settings.skipRepetitiveTask && new RegExp(urls).test(self._taskCache)) {
			return self;
		}
		self._tasks.push(urls);
	} else {
		urls = _.uniq(urls);
		if (self.settings.skipRepetitiveTask) {
			urls = urls.filter(function (url) {
				return !new RegExp(url).test(self._taskCache);
			});
		}
		self._tasks = self._tasks.concat(urls);
	}
	return self;
}

/**
 * add crawl's rule
 * @param <string> reg : reg string
 * @param <function> func : processing Data
 */
Crawler.prototype.addRule = function addRule(reg, func) {
	if (typeof (reg) == 'string') {
		reg = reg.replace(/\./g, '\\.').replace(/\*\*/g, '.*');
		this._rules[reg] = func;
	} else {
		this._rules['.*'] = reg;
	}
	return this;
}

/**
 * fetch/crawl a single page for task
 * @param <string> url : url of crawling page
 * @param <function> callback
 */
Crawler.prototype._crawl = function _crawl(url, callback) {
	var self = this;

	var opts = _.assign(self.requestOpts, { url: url });
	if (opts.proxy == '') {
		delete opts.proxy;
	}

	var retry = 0;

	var id = ++self.taskCounter;

	var func = function func(cb) {
		request(opts, function (e, r, body) {
			if (!e && r.statusCode == 200) {
				cb(null, { id: id, url: url, body: body });
			} else {
				var info = e ? e.code : r.statusCode;
				console.log(util.format('[Retry][%s][%s] : %s', info, ++retry, url));
				if (retry == self.settings.retry) {
					self.failCounter++;
					self.errLog = self.errLog.concat(util.format('[Retry Failed][No.%s][%s]%s\n', id, info, url));
				}
				cb(info);
			}
		});
	};

	async.retry({
		times: self.settings.retry,
		interval: self.settings.interval
	}, func, function (e, result) {
		if (e) {
			callback({ id: id, url: url });
		} else {
			callback(result);
		}
	});
}

/**
 * start the Crawler
 * @param <function> callback
 */
Crawler.prototype.start = function start(callback) {
	var self = this;

	if (self._tasks.length == 0) {
		console.error('Task pool has nothing!');
		if (callback) {
			return callback();
		}
		return;
	}

	if (callback) {
		self.onTaskPoolDrain = callback;
	}

	console.log('[Crawler Started]');

	self.emit('pollTask');
}

module.exports = Crawler;
<commit_msg>Update addTasks().<commit_after>'use strict';

var util = require('util'),
	EventEmitter = require('events').EventEmitter;

var request = require('request'),
	async = require('async'),
	_ = require('lodash');

/**
 * constructor of Crawler
 */
function Crawler(opts) {
	this._init(opts);
}

util.inherits(Crawler, EventEmitter);

/**
 * initialize Crawler
 */
Crawler.prototype._init = function _init(opts) {
	var self = this;

	self._tasks = [];
	self._rules = {};
	self._taskCache = '';

	self.taskCounter = 0;
	self.failCounter = 0;
	self.settings = {
		interval: 0,
		retry: 3,
		concurrency: 1,
		skipRepetitiveTask: true
	};
	self.requestOpts = {
		timeout: 10000
	};
	self.errLog = '';
	if (opts) {
		if (opts.requestOpts) {
			self.requestOpts = _.assign(self.requestOpts, opts.requestOpts);
			delete opts.requestOpts;
		}
		self.settings = _.assign(self.settings, opts);
	}

	self.on('pollTask', function () {
		var size = self.settings.concurrency;
		size = size <= self._tasks.length ? size : self._tasks.length;
		if (self.q.length() < size) {
			var idle = size - self.q.length();
			var ts = self._tasks.splice(0, idle);
			while (idle != 0) {
				self.q.push(ts[ts.length - idle], function (err) {
					if (err) {
						return console.error(err);
					}
					if (self._tasks.length > 0) {
						self.emit('pollTask');
					}
				});
				idle--;
			}
		}
	});

	// processing of task pool
	self.q = async.queue(function (task, done) {
		var crawl = function crawl() {
			if (self.settings.skipRepetitiveTask) {
				self._taskCache = self._taskCache.concat(task + '|');
			}
			console.log(util.format('Current No.%s: %s', self.taskCounter + 1, task));
			self._crawl(task, function (result) {
				var flag = false;
				Object.keys(self._rules).forEach(function (rule) {
					if (flag) {
						return;
					}
					if (new RegExp(rule).test(result.url)) {
						try {
							self._rules[rule](result);
						} catch (err) {
							self.errLog = self.errLog.concat(util.format('[Processing Error][No.%s]%s\n[%s]\n', result.id, result.url, err));
						} finally {
							flag = true;
							done();
						}
					}
				});
				if (!flag) {
					self.errLog = self.errLog.concat(util.format('[No Rule Handle][No.%s]%s\n', result.id, result.url));
					done();
				}
			});
		};

		// deferred execution for crawl's interval
		if (self.settings.interval != 0 && self.taskCounter != 0) {
			console.log('Crawler is sleeping...');
			setTimeout(crawl, self.settings.interval);
		} else {
			crawl();
		}
	}, self.settings.concurrency);

	self.q.drain = function () {
		console.log(util.format('[Crawler Finished] Total: %s.Success: %s.Fail: %s.',
			self.taskCounter, self.taskCounter - self.failCounter, self.failCounter));
		if (self.errLog.length > 0) {
			console.log(self.errLog);
		}
		if (self.onTaskPoolDrain) {
			self.onTaskPoolDrain();
		}
	};
};

/**
 * twist crawler's settings
 * @param <object> setting
 */
Crawler.prototype.twist = function twist(opts) {
	var self = this;
	if (opts) {
		if (opts.requestOpts) {
			self.requestOpts = _.assign(self.requestOpts, opts.requestOpts);
			delete opts.requestOpts;
		}
		self.settings = _.assign(self.settings, opts);
	}
	return self;
}

/**
 * add tasks to task pool
 * @param <array[string]> urls : array of url
 */
Crawler.prototype.addTasks = function addTask(taskUrls, props) {
	var self = this;
	var taskProps = props ? props : {};
	if(Array.isArray(taskUrls)) {
		taskUrls = _.uniq(taskUrls);
		if (self.settings.skipRepetitiveTask) {
			taskUrls = taskUrls.filter(function (url) {
				return !new RegExp(url).test(self._taskCache);
			});
		}
		for (var i in taskUrls) {
			 self._tasks.push(_.assign({url:taskUrls[i]}, taskProps));
		}
	} else if (typeof (taskUrls) == 'string') {
		if (self.settings.skipRepetitiveTask && new RegExp(taskUrls).test(self._taskCache)) {
			return self;
		}
		self._tasks.push(_.assign({url:taskUrls}, taskProps));
	}
	return self;
}

/**
 * add crawl's rule
 * @param <string> reg : reg string
 * @param <function> func : processing Data
 */
Crawler.prototype.addRule = function addRule(reg, func) {
	if (typeof (reg) == 'string') {
		reg = reg.replace(/\./g, '\\.').replace(/\*\*/g, '.*');
		this._rules[reg] = func;
	} else {
		this._rules['.*'] = reg;
	}
	return this;
}

/**
 * fetch/crawl a single page for task
 * @param <string> url : url of crawling page
 * @param <function> callback
 */
Crawler.prototype._crawl = function _crawl(url, callback) {
	var self = this;

	var opts = _.assign(self.requestOpts, { url: url });
	if (opts.proxy == '') {
		delete opts.proxy;
	}

	var retry = 0;

	var id = ++self.taskCounter;

	var func = function func(cb) {
		request(opts, function (e, r, body) {
			if (!e && r.statusCode == 200) {
				cb(null, { id: id, url: url, body: body });
			} else {
				var info = e ? e.code : r.statusCode;
				console.log(util.format('[Retry][%s][%s] : %s', info, ++retry, url));
				if (retry == self.settings.retry) {
					self.failCounter++;
					self.errLog = self.errLog.concat(util.format('[Retry Failed][No.%s][%s]%s\n', id, info, url));
				}
				cb(info);
			}
		});
	};

	async.retry({
		times: self.settings.retry,
		interval: self.settings.interval
	}, func, function (e, result) {
		if (e) {
			callback({ id: id, url: url });
		} else {
			callback(result);
		}
	});
}

/**
 * start the Crawler
 * @param <function> callback
 */
Crawler.prototype.start = function start(callback) {
	var self = this;

	if (self._tasks.length == 0) {
		console.error('Task pool has nothing!');
		if (callback) {
			return callback();
		}
		return;
	}

	if (callback) {
		self.onTaskPoolDrain = callback;
	}

	console.log('[Crawler Started]');

	self.emit('pollTask');
}

module.exports = Crawler;
<|endoftext|>


<commit_before>/**
 * Constructs a new Gesture object.
 *
 * An uninitialized Gesture object is considered invalid. Get valid instances
 * of the Gesture class, which will be one of the Gesture subclasses, from a
 * Frame object.
 *
 * @class Gesture
 * @classdesc
 * The Gesture class represents a recognized movement by the user.
 *
 * The Leap watches the activity within its field of view for certain movement
 * patterns typical of a user gesture or command. For example, a movement from side to
 * side with the hand can indicate a swipe gesture, while a finger poking forward
 * can indicate a screen tap gesture.
 *
 * When the Leap recognizes a gesture, it assigns an ID and adds a
 * Gesture object to the frame gesture list. For continuous gestures, which
 * occur over many frames, the Leap updates the gesture by adding
 * a Gesture object having the same ID and updated properties in each
 * subsequent frame.
 *
 * **Important:** Recognition for each type of gesture must be enabled;
 * otherwise **no gestures are recognized or reported**.
 *
 * Subclasses of Gesture define the properties for the specific movement patterns
 * recognized by the Leap.
 *
 * The Gesture subclasses for include:
 *
 * * CircleGesture -- A circular movement by a finger.
 * * SwipeGesture -- A straight line movement by the hand with fingers extended.
 * * ScreenTapGesture -- A forward tapping movement by a finger.
 * * KeyTapGesture -- A downward tapping movement by a finger.
 *
 * Circle and swipe gestures are continuous and these objects can have a
 * state of start, update, and stop.
 *
 * The screen tap gesture is a discrete gesture. The Leap only creates a single
 * ScreenTapGesture object appears for each tap and it always has a stop state.
 *
 * Get valid Gesture instances from a Frame object. You can get a list of gestures
 * from the Frame gestures array. You can also use the Frame gesture() method
 * to find a gesture in the current frame using an ID value obtained in a
 * previous frame.
 *
 * Gesture objects can be invalid. For example, when you get a gesture by ID
 * using Frame.gesture(), and there is no gesture with that ID in the current
 * frame, then gesture() returns an Invalid Gesture object (rather than a null
 * value). Always check object validity in situations where a gesture might be
 * invalid.
 */
var Gesture = exports.Gesture = function(data) {
  var gesture = undefined
  switch (data.type) {
    case 'circle':
      gesture = new CircleGesture(data)
      break
    case 'swipe':
      gesture = new SwipeGesture(data)
      break
    case 'screenTap':
      gesture = new ScreenTapGesture(data)
      break
    case 'keyTap':
      gesture = new KeyTapGesture(data)
      break
    default:
      throw "unkown gesture type"
  }
 /**
  * The gesture ID.
  *
  * All Gesture objects belonging to the same recognized movement share the
  * same ID value. Use the ID value with the Frame::gesture() method to
  * find updates related to this Gesture object in subsequent frames.
  *
  * @member Gesture.prototype.id
  * @type {Number}
  */
  gesture.id = data.id
 /**
  * The list of hands associated with this Gesture, if any.
  *
  * If no hands are related to this gesture, the list is empty.
  *
  * @member Gesture.prototype.handIds
  * @type {Array}
  */
  gesture.handIds = data.handIds
 /**
  * The list of fingers and tools associated with this Gesture, if any.
  *
  * If no Pointable objects are related to this gesture, the list is empty.
  *
  * @member Gesture.prototype.pointableIds
  * @type {Array}
  */
  gesture.pointableIds = data.pointableIds
 /**
  * The elapsed duration of the recognized movement up to the
  * frame containing this Gesture object, in microseconds.
  *
  * The duration reported for the first Gesture in the sequence (with the
  * start state) will typically be a small positive number since
  * the movement must progress far enough for the Leap to recognize it as
  * an intentional gesture.
  *
  * @member Gesture.prototype.duration
  * @type {Number}
  */
  gesture.duration = data.duration
 /**
  * The gesture ID.
  *
  * Recognized movements occur over time and have a beginning, a middle,
  * and an end. The 'state()' attribute reports where in that sequence this
  * Gesture object falls.
  *
  * Possible values for the state field are:
  *
  * * start
  * * update
  * * stop
  *
  * @member Gesture.prototype.state
  * @type {String}
  */
  gesture.state = data.state
 /**
  * The gesture type.
  *
  * Possible values for the type field are:
  *
  * * circle
  * * swipe
  * * screentap
  * * keyTap
  *
  * @member Gesture.prototype.type
  * @type {String}
  */
  gesture.type = data.type
  return gesture
}

/**
 * Constructs a new CircleGesture object.
 *
 * An uninitialized CircleGesture object is considered invalid. Get valid instances
 * of the CircleGesture class from a Frame object.
 *
 * @class CircleGesture
 * @classdesc
 * The CircleGesture classes represents a circular finger movement.
 *
 * A circle movement is recognized when the tip of a finger draws a circle
 * within the Leap field of view.
 *
 *
 *
 * **Important:** To use circle gestures in your application, you must enable
 * recognition of the circle gesture. You can enable recognition with:
 *
 *    TODO
 *
 * Circle gestures are continuous. The CircleGesture objects for the gesture have
 * three possible states:
 *
 * * start -- The circle gesture has just started. The movement has
 *  progressed far enough for the recognizer to classify it as a circle.
 * * update -- The circle gesture is continuing.
 * * stop -- The circle gesture is finished.
 */
var CircleGesture = function(data) {
 /**
  * The center point of the circle within the Leap frame of reference.
  *
  * @member CircleGesture.prototype.center
  * @type {Array: [x,y,z]}
  */
  this.center = data.center
 /**
  * The normal vector for the circle being traced.
  *
  * If you draw the circle clockwise, the normal vector points in the same
  * general direction as the pointable object drawing the circle. If you draw
  * the circle counterclockwise, the normal points back toward the
  * pointable. If the angle between the normal and the pointable object
  * drawing the circle is less than 90 degrees, then the circle is clockwise.
  *
  *    var clockwiseness;
  *    if (circle.pointable.direction.angleTo(circle.normal) <= PI/4) {
  *        clockwiseness = "clockwise";
  *    }
  *    else
  *    {
  *        clockwiseness = "counterclockwise";
  *    }
  *
  *
  * @member CircleGesture.prototype.normal
  * @type {Array: [x,y,z]}
  */
  this.normal = data.direction
 /**
  * The number of times the finger tip has traversed the circle.
  *
  * Progress is reported as a positive number of the number. For example,
  * a progress value of .5 indicates that the finger has gone halfway
  * around, while a value of 3 indicates that the finger has gone around
  * the the circle three times.
  *
  * Progress starts where the circle gesture began. Since it the circle
  * must be partially formed before the Leap can recognize it, progress
  * will be greater than zero when a circle gesture first appears in the
  * frame.
  *
  * @member CircleGesture.prototype.progress
  * @type {Number}
  */
  this.progress = data.progress
 /**
  * The radius of the circle in mm.
  *
  * @member CircleGesture.prototype.radius
  * @type {Number}
  */
  this.radius = data.radius
}

/**
 * Constructs a new SwipeGesture object.
 *
 * An uninitialized SwipeGesture object is considered invalid. Get valid instances
 * of the SwipeGesture class from a Frame object.
 *
 * @class SwipeGesture
 * @classdesc
 * The SwipeGesture class represents a swiping motion of a finger or tool.
 *
 *
 *
 * **Important:** To use swipe gestures in your application, you must enable
 * recognition of the swipe gesture. You can enable recognition with:
 *
 *     TODO
 *
 * Swipe gestures are continuous.
 */
var SwipeGesture = function(data) {
 /**
  * The starting position within the Leap frame of
  * reference, in mm.
  *
  * @member SwipeGesture.prototype.startPosition
  * @type {Array: [x,y,z]}
  */
  this.startPosition = data.startPosition
 /**
  * The current swipe position within the Leap frame of
  * reference, in mm.
  *
  * @member SwipeGesture.prototype.position
  * @type {Array: [x,y,z]}
  */
  this.position = data.position
 /**
  * The unit direction vector parallel to the swipe motion.
  *
  * You can compare the components of the vector to classify the swipe as
  * appropriate for your application. For example, if you are using swipes
  * for two dimensional scrolling, you can compare the x and y values to
  * determine if the swipe is primarily horizontal or vertical.
  *
  * @member SwipeGesture.prototype.direction
  * @type {Array: [x,y,z]}
  */
  this.direction = data.direction
 /**
  * The speed of the finger performing the swipe gesture in
  * millimeters per second.
  *
  * @member SwipeGesture.prototype.speed
  * @type {Number}
  */
  this.speed = data.speed
}

/**
 * Constructs a new ScreenTapGesture object.
 *
 * An uninitialized ScreenTapGesture object is considered invalid. Get valid instances
 * of the ScreenTapGesture class from a Frame object.
 *
 * @class ScreenTapGesture
 * @classdesc
 * The ScreenTapGesture class represents a tapping gesture by a finger or tool.
 *
 * A screen tap gesture is recognized when the tip of a finger pokes forward
 * and then springs back to approximately the original postion, as if
 * tapping a vertical screen. The tapping finger must pause briefly before beginning the tap.
 *
 *
 *
 * **Important:** To use screen tap gestures in your application, you must enable
 * recognition of the screen tap gesture. You can enable recognition with:
 *
 *     TODO
 *
 * ScreenTap gestures are discrete. The ScreenTapGesture object representing a tap always
 * has the state, STATE_STOP. Only one ScreenTapGesture object is created for each
 * screen tap gesture recognized.
 */
var ScreenTapGesture = function(data) {
 /**
  * The position where the screen tap is registered.
  *
  * @member ScreenTapGesture.prototype.position
  * @type {Array: [x,y,z]}
  */
  this.position = data.position
 /**
  * The direction of finger tip motion.
  *
  * @member ScreenTapGesture.prototype.direction
  * @type {Array: [x,y,z]}
  */
  this.direction = data.direction
 /**
  * The progess value is always 1.0 for a screen tap gesture.
  *
  * @member ScreenTapGesture.prototype.progress
  * @type {Number}
  */
  this.progress = data.progress
}

/**
 * Constructs a new KeyTapGesture object.
 *
 * An uninitialized KeyTapGesture object is considered invalid. Get valid instances
 * of the KeyTapGesture class from a Frame object.
 *
 * @class KeyTapGesture
 * @classdesc
 * The KeyTapGesture class represents a tapping gesture by a finger or tool.
 *
 * A key tap gesture is recognized when the tip of a finger rotates down toward the
 * palm and then springs back to approximately the original postion, as if
 * tapping. The tapping finger must pause briefly before beginning the tap.
 *
 *
 *
 * **Important:** To use key tap gestures in your application, you must enable
 * recognition of the key tap gesture. You can enable recognition with:
 *
 *     TODO
 *
 * Key tap gestures are discrete. The KeyTapGesture object representing a tap always
 * has the state, STATE_STOP. Only one KeyTapGesture object is created for each
 * key tap gesture recognized.
 */
var KeyTapGesture = function(data) {
    /**
     * The position where the key tap is registered.
     *
     * @member KeyTapGesture.prototype.position
     * @type {Array: [x,y,z]}
     */
    this.position = data.position
    /**
     * The direction of finger tip motion.
     *
     * @member KeyTapGesture.prototype.direction
     * @type {Array: [x,y,z]}
     */
    this.direction = data.direction
    /**
     * The progess value is always 1.0 for a key tap gesture.
     *
     * @member KeyTapGesture.prototype.progress
     * @type {Number}
     */
    this.progress = data.progress
}
<commit_msg>Fixed constructor error in CircleGesture normal property<commit_after>/**
 * Constructs a new Gesture object.
 *
 * An uninitialized Gesture object is considered invalid. Get valid instances
 * of the Gesture class, which will be one of the Gesture subclasses, from a
 * Frame object.
 *
 * @class Gesture
 * @classdesc
 * The Gesture class represents a recognized movement by the user.
 *
 * The Leap watches the activity within its field of view for certain movement
 * patterns typical of a user gesture or command. For example, a movement from side to
 * side with the hand can indicate a swipe gesture, while a finger poking forward
 * can indicate a screen tap gesture.
 *
 * When the Leap recognizes a gesture, it assigns an ID and adds a
 * Gesture object to the frame gesture list. For continuous gestures, which
 * occur over many frames, the Leap updates the gesture by adding
 * a Gesture object having the same ID and updated properties in each
 * subsequent frame.
 *
 * **Important:** Recognition for each type of gesture must be enabled;
 * otherwise **no gestures are recognized or reported**.
 *
 * Subclasses of Gesture define the properties for the specific movement patterns
 * recognized by the Leap.
 *
 * The Gesture subclasses for include:
 *
 * * CircleGesture -- A circular movement by a finger.
 * * SwipeGesture -- A straight line movement by the hand with fingers extended.
 * * ScreenTapGesture -- A forward tapping movement by a finger.
 * * KeyTapGesture -- A downward tapping movement by a finger.
 *
 * Circle and swipe gestures are continuous and these objects can have a
 * state of start, update, and stop.
 *
 * The screen tap gesture is a discrete gesture. The Leap only creates a single
 * ScreenTapGesture object appears for each tap and it always has a stop state.
 *
 * Get valid Gesture instances from a Frame object. You can get a list of gestures
 * from the Frame gestures array. You can also use the Frame gesture() method
 * to find a gesture in the current frame using an ID value obtained in a
 * previous frame.
 *
 * Gesture objects can be invalid. For example, when you get a gesture by ID
 * using Frame.gesture(), and there is no gesture with that ID in the current
 * frame, then gesture() returns an Invalid Gesture object (rather than a null
 * value). Always check object validity in situations where a gesture might be
 * invalid.
 */
var Gesture = exports.Gesture = function(data) {
  var gesture = undefined
  switch (data.type) {
    case 'circle':
      gesture = new CircleGesture(data)
      break
    case 'swipe':
      gesture = new SwipeGesture(data)
      break
    case 'screenTap':
      gesture = new ScreenTapGesture(data)
      break
    case 'keyTap':
      gesture = new KeyTapGesture(data)
      break
    default:
      throw "unkown gesture type"
  }
 /**
  * The gesture ID.
  *
  * All Gesture objects belonging to the same recognized movement share the
  * same ID value. Use the ID value with the Frame::gesture() method to
  * find updates related to this Gesture object in subsequent frames.
  *
  * @member Gesture.prototype.id
  * @type {Number}
  */
  gesture.id = data.id
 /**
  * The list of hands associated with this Gesture, if any.
  *
  * If no hands are related to this gesture, the list is empty.
  *
  * @member Gesture.prototype.handIds
  * @type {Array}
  */
  gesture.handIds = data.handIds
 /**
  * The list of fingers and tools associated with this Gesture, if any.
  *
  * If no Pointable objects are related to this gesture, the list is empty.
  *
  * @member Gesture.prototype.pointableIds
  * @type {Array}
  */
  gesture.pointableIds = data.pointableIds
 /**
  * The elapsed duration of the recognized movement up to the
  * frame containing this Gesture object, in microseconds.
  *
  * The duration reported for the first Gesture in the sequence (with the
  * start state) will typically be a small positive number since
  * the movement must progress far enough for the Leap to recognize it as
  * an intentional gesture.
  *
  * @member Gesture.prototype.duration
  * @type {Number}
  */
  gesture.duration = data.duration
 /**
  * The gesture ID.
  *
  * Recognized movements occur over time and have a beginning, a middle,
  * and an end. The 'state()' attribute reports where in that sequence this
  * Gesture object falls.
  *
  * Possible values for the state field are:
  *
  * * start
  * * update
  * * stop
  *
  * @member Gesture.prototype.state
  * @type {String}
  */
  gesture.state = data.state
 /**
  * The gesture type.
  *
  * Possible values for the type field are:
  *
  * * circle
  * * swipe
  * * screentap
  * * keyTap
  *
  * @member Gesture.prototype.type
  * @type {String}
  */
  gesture.type = data.type
  return gesture
}

/**
 * Constructs a new CircleGesture object.
 *
 * An uninitialized CircleGesture object is considered invalid. Get valid instances
 * of the CircleGesture class from a Frame object.
 *
 * @class CircleGesture
 * @classdesc
 * The CircleGesture classes represents a circular finger movement.
 *
 * A circle movement is recognized when the tip of a finger draws a circle
 * within the Leap field of view.
 *
 *
 *
 * **Important:** To use circle gestures in your application, you must enable
 * recognition of the circle gesture. You can enable recognition with:
 *
 *    TODO
 *
 * Circle gestures are continuous. The CircleGesture objects for the gesture have
 * three possible states:
 *
 * * start -- The circle gesture has just started. The movement has
 *  progressed far enough for the recognizer to classify it as a circle.
 * * update -- The circle gesture is continuing.
 * * stop -- The circle gesture is finished.
 */
var CircleGesture = function(data) {
 /**
  * The center point of the circle within the Leap frame of reference.
  *
  * @member CircleGesture.prototype.center
  * @type {Array: [x,y,z]}
  */
  this.center = data.center
 /**
  * The normal vector for the circle being traced.
  *
  * If you draw the circle clockwise, the normal vector points in the same
  * general direction as the pointable object drawing the circle. If you draw
  * the circle counterclockwise, the normal points back toward the
  * pointable. If the angle between the normal and the pointable object
  * drawing the circle is less than 90 degrees, then the circle is clockwise.
  *
  *    var clockwiseness;
  *    if (circle.pointable.direction.angleTo(circle.normal) <= PI/4) {
  *        clockwiseness = "clockwise";
  *    }
  *    else
  *    {
  *        clockwiseness = "counterclockwise";
  *    }
  *
  *
  * @member CircleGesture.prototype.normal
  * @type {Array: [x,y,z]}
  */
  this.normal = data.normal
 /**
  * The number of times the finger tip has traversed the circle.
  *
  * Progress is reported as a positive number of the number. For example,
  * a progress value of .5 indicates that the finger has gone halfway
  * around, while a value of 3 indicates that the finger has gone around
  * the the circle three times.
  *
  * Progress starts where the circle gesture began. Since the circle
  * must be partially formed before the Leap can recognize it, progress
  * will be greater than zero when a circle gesture first appears in the
  * frame.
  *
  * @member CircleGesture.prototype.progress
  * @type {Number}
  */
  this.progress = data.progress
 /**
  * The radius of the circle in mm.
  *
  * @member CircleGesture.prototype.radius
  * @type {Number}
  */
  this.radius = data.radius
}

/**
 * Constructs a new SwipeGesture object.
 *
 * An uninitialized SwipeGesture object is considered invalid. Get valid instances
 * of the SwipeGesture class from a Frame object.
 *
 * @class SwipeGesture
 * @classdesc
 * The SwipeGesture class represents a swiping motion of a finger or tool.
 *
 *
 *
 * **Important:** To use swipe gestures in your application, you must enable
 * recognition of the swipe gesture. You can enable recognition with:
 *
 *     TODO
 *
 * Swipe gestures are continuous.
 */
var SwipeGesture = function(data) {
 /**
  * The starting position within the Leap frame of
  * reference, in mm.
  *
  * @member SwipeGesture.prototype.startPosition
  * @type {Array: [x,y,z]}
  */
  this.startPosition = data.startPosition
 /**
  * The current swipe position within the Leap frame of
  * reference, in mm.
  *
  * @member SwipeGesture.prototype.position
  * @type {Array: [x,y,z]}
  */
  this.position = data.position
 /**
  * The unit direction vector parallel to the swipe motion.
  *
  * You can compare the components of the vector to classify the swipe as
  * appropriate for your application. For example, if you are using swipes
  * for two dimensional scrolling, you can compare the x and y values to
  * determine if the swipe is primarily horizontal or vertical.
  *
  * @member SwipeGesture.prototype.direction
  * @type {Array: [x,y,z]}
  */
  this.direction = data.direction
 /**
  * The speed of the finger performing the swipe gesture in
  * millimeters per second.
  *
  * @member SwipeGesture.prototype.speed
  * @type {Number}
  */
  this.speed = data.speed
}

/**
 * Constructs a new ScreenTapGesture object.
 *
 * An uninitialized ScreenTapGesture object is considered invalid. Get valid instances
 * of the ScreenTapGesture class from a Frame object.
 *
 * @class ScreenTapGesture
 * @classdesc
 * The ScreenTapGesture class represents a tapping gesture by a finger or tool.
 *
 * A screen tap gesture is recognized when the tip of a finger pokes forward
 * and then springs back to approximately the original postion, as if
 * tapping a vertical screen. The tapping finger must pause briefly before beginning the tap.
 *
 *
 *
 * **Important:** To use screen tap gestures in your application, you must enable
 * recognition of the screen tap gesture. You can enable recognition with:
 *
 *     TODO
 *
 * ScreenTap gestures are discrete. The ScreenTapGesture object representing a tap always
 * has the state, STATE_STOP. Only one ScreenTapGesture object is created for each
 * screen tap gesture recognized.
 */
var ScreenTapGesture = function(data) {
 /**
  * The position where the screen tap is registered.
  *
  * @member ScreenTapGesture.prototype.position
  * @type {Array: [x,y,z]}
  */
  this.position = data.position
 /**
  * The direction of finger tip motion.
  *
  * @member ScreenTapGesture.prototype.direction
  * @type {Array: [x,y,z]}
  */
  this.direction = data.direction
 /**
  * The progess value is always 1.0 for a screen tap gesture.
  *
  * @member ScreenTapGesture.prototype.progress
  * @type {Number}
  */
  this.progress = data.progress
}

/**
 * Constructs a new KeyTapGesture object.
 *
 * An uninitialized KeyTapGesture object is considered invalid. Get valid instances
 * of the KeyTapGesture class from a Frame object.
 *
 * @class KeyTapGesture
 * @classdesc
 * The KeyTapGesture class represents a tapping gesture by a finger or tool.
 *
 * A key tap gesture is recognized when the tip of a finger rotates down toward the
 * palm and then springs back to approximately the original postion, as if
 * tapping. The tapping finger must pause briefly before beginning the tap.
 *
 *
 *
 * **Important:** To use key tap gestures in your application, you must enable
 * recognition of the key tap gesture. You can enable recognition with:
 *
 *     TODO
 *
 * Key tap gestures are discrete. The KeyTapGesture object representing a tap always
 * has the state, STATE_STOP. Only one KeyTapGesture object is created for each
 * key tap gesture recognized.
 */
var KeyTapGesture = function(data) {
    /**
     * The position where the key tap is registered.
     *
     * @member KeyTapGesture.prototype.position
     * @type {Array: [x,y,z]}
     */
    this.position = data.position
    /**
     * The direction of finger tip motion.
     *
     * @member KeyTapGesture.prototype.direction
     * @type {Array: [x,y,z]}
     */
    this.direction = data.direction
    /**
     * The progess value is always 1.0 for a key tap gesture.
     *
     * @member KeyTapGesture.prototype.progress
     * @type {Number}
     */
    this.progress = data.progress
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014, Gregg Tavares.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Gregg Tavares. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
"use strict";

/**
 * @module
 */

/**
 * Gets the ip address of this machine.
 * @returns {String[]} array of ip addresses.
 */
var getIpAddress = (function() {

  var addresses;

  return function() {
    if (!addresses) {
      var os = require('os')

      var interfaces = os.networkInterfaces();
      var addresses = [];
      for (var k in interfaces) {
        for (var k2 in interfaces[k]) {
          var address = interfaces[k][k2];
          if (address.family == 'IPv4' && !address.internal) {
            addresses.push(address.address)
          }
        }
      }
    }

    return addresses;
  };
}());

exports.getIpAddress = getIpAddress;

<commit_msg>make getIPaddress get new address at most once a second<commit_after>/*
 * Copyright 2014, Gregg Tavares.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Gregg Tavares. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
"use strict";

/**
 * @module
 */

/**
 * Gets the ip address of this machine.
 * @returns {String[]} array of ip addresses.
 */
var getIpAddress = (function() {
  // Premature optimization. Don't read this more than once a second
  var cacheTime = 1 * 1000;  // 1 second
  var lastRead = Date.now();
  var addresses;

  var os = require('os');

  return function() {

    var now = Date.now();
    if (!addresses || now - lastRead > cacheTime) {
      lastRead = now;
      var interfaces = os.networkInterfaces();
      addresses = [];
      for (var k in interfaces) {
        for (var k2 in interfaces[k]) {
          var address = interfaces[k][k2];
          if (address.family == 'IPv4' && !address.internal) {
            addresses.push(address.address)
          }
        }
      }
    }
    return addresses;
  };
}());

var getOneIpAddress = function() {
  var address;
  var addresses = getIpAddress();

  if (addresses.length < 1) {
    console.error("No IP address found!!!");
  } else {
    address = addresses[0];
    if (addresses.length > 1) {
      console.log("more than 1 IP address found: " + addresses);
    }
  }
  return address;
};

exports.getIpAddress = getIpAddress;
exports.getOneIpAddress = getOneIpAddress;

<|endoftext|>


<commit_before>;
(function(mag) {

  "use strict";

  var render = {
    roots: [],
    contexts: [],
    templates: {},
    cache: {}
  }

  var unloaders = [],
    cached = [],
    pendingRequests = 0

    function callView(elementClone, module, i) {
      var args = module.getArgs(i)

      var mod = module.modules[i]
      var controller = module.controllers[i]

      // var controllerConstructor = mod.controller.$original || mod.controller
      // var controller = controllerConstructor === cached.controllerConstructor ? cached.controller : new(mod.controller || function() {})
      // give it unfrozen context ?

      var context = render.contexts[i] = render.contexts[i] || {}

      try {
        mod.view(controller, elementClone, context)
      } catch (e) {
        //THROW ?
        console.log(elementClone.id, i, e)
        //throw new Error(e)
      }
      // if (controller.onunload) unloaders.push({
      //   controller: controller,
      //   handler: controller.onunload
      // })
    }

  render.callOnload = function(module) {
    for (var i = 0, controller; controller = module.controllers[i]; i++) {
      // call once
      if (controller.onload && !controller.called) {
        controller.onload.call(null, module.elements[i])
        controller.called = 1
      }
    }
  }
  render.callConfigs = function(configs) {
    for (var i = 0, len = configs.length; i < len; i++) configs[i]()
  }
  var cache = []
  render.redraw = function(module, fill, WatchJS) {

    module = module || render.module || {}

    this.fun = (this.fun || debounce(function(id) {
      // clear existing configs
      fill.configs.splice(0, fill.configs.length)

      render.doLoop(module, fill, WatchJS)
    }))
    this.fun()
  }

  render.doLoop = function(module, fill, WatchJS) {
    for (var i = 0, root; root = render.roots[i]; i++) {
      mag.running = true

      if (module.controllers[i]) {
        if (!render.innerLoop(module, fill, i, WatchJS)) {
          //cached
        } else {
          module.deferreds[i][2]({
            _html: module.elements[i].innerHTML
          })
          //TODO: remove clones
          if (module.deferreds[i][0]) {
            var index = module.deferreds[i][1]
            delete module.elements[index],
              module.modules[index], module.controllers[index], module.promises[index], module.deferreds[index]
          }
        }
      }
    }
    fill.unclear()
  }

  render.clear = function(index, elementId, fill) {
    if (index !== -1 && cache[index]) {
      // clear events too
      fill.clear()
      //console.log('clear called on reload', elementId)
      // delete cache[index]
    }
  }
  render.innerLoop = function(module, fill, i, WatchJS) {
    var elementClone = module.elements[i]
    var args = module.getArgs(i)

    if (cache[i] && cache[i] === JSON.stringify(args[0])) {
      return false
    }
    callView(elementClone, module, i)
    // circular references will throw an exception
    // such as setting to a dom element
    cache[i] = JSON.stringify(args[0])

    render.setupWatch(WatchJS, args, fill, elementClone, i, module)

    fill.fill(elementClone, args[0])
    render.callConfigs(fill.configs)

    // call onload if present in all controllers
    render.callOnload(module)
    return true
  }
  var prevId
  //render.doWatch = function(fill, ele, i, module, frameId, prop, action, difference, oldvalue) {
  render.doWatch = function(fill, ele, i, module, changes, frameId) {

    if (frameId == prevId) return
    prevId = frameId
    mag.running = true

    var args = module.getArgs(i)

    // check if data changed
    if (cache[i] && cache[i] === JSON.stringify(args[0])) {
      return
    }
    callView(ele, module, i)
    cache[i] = JSON.stringify(args[0])

    fill.fill(ele, args[0])

    render.callConfigs(fill.configs)

    mag.running = false
  }

  render.setupWatch = function(WatchJS, args, fill, elementClone, i, module) {
    // WatchJS.watch(args[0], debounce(render.doWatch.bind(null, fill, elementClone, i, module)), 6, true)
    // return
    //this.fun = (this.fun || debounce(render.doWatch.bind(null, fill, elementClone, i, module)))

    // Which we then observe
    observeNested(args[0], function(changes) {
      // changes.forEach(function(change) {
      //console.log(change.type, change.name, change.oldValue);
      //});
      debounce(render.doWatch.bind(null, fill, elementClone, i, module, changes))()
    });
  }
  var $cancelAnimationFrame = window.cancelAnimationFrame || window.clearTimeout;
  var $requestAnimationFrame = window.requestAnimationFrame || window.setTimeout;

  var debounce = function(fn, threshhold) {
    var lastRedrawCallTime, FRAME_BUDGET = threshhold || 16,
      deferTimer
    return function() {
      if (+new Date - lastRedrawCallTime > FRAME_BUDGET || $requestAnimationFrame === window.requestAnimationFrame) {
        // hold on to it
        if (deferTimer > 0) $cancelAnimationFrame(deferTimer)
        var args = arguments
        deferTimer = $requestAnimationFrame(function() {
          lastRedrawCallTime = +new Date
          var nargs = [].slice.call(arguments).concat([].slice.call(args))
          fn.apply(this, nargs);
        }, FRAME_BUDGET)
      } else {
        lastRedrawCallTime = +new Date
        var nargs = [].slice.call(arguments).concat([].slice.call(args))
        fn.apply(this, nargs)
        deferTimer = $requestAnimationFrame(function() {
          deferTimer = null
        }, FRAME_BUDGET)
      }
    }
  }

  mag.render = render

  function observeNested(obj, callback) {
    if (obj && typeof Object.observe !== 'undefined') {
      Object.observe(obj, function(changes) {
        changes.forEach(function(change) {
          if (typeof obj[change.name] == 'object') {
            observeNested(obj[change.name], callback);
          }
        });
        callback.apply(this, arguments);
      });
    } else if(obj) {
      observeNested2(obj,callback)
    }
  }
  if (typeof Object.observe === 'undefined') {
    Object.defineProperty(Object.prototype, "__watch", {
      enumerable: false,
      configurable: true,
      writable: false,
      value: function(prop, handler) {
        var
        oldval = this[prop],
          getter = function() {
            return oldval;
          },
          setter = function(newval) {
            if (oldval !== newval) {
              handler.call(this, prop, oldval, newval);
              oldval = newval;
            }
            return newval;
          };

        if (delete this[prop]) { // can't watch constants
          Object.defineProperty(this, prop, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
          });
        }
      }
    });
    function observeNested2(obj, callback) {
        if (obj) {
          for (var k in obj) {
            if (typeof obj[k] == 'object') {
              observeNested2(obj[k], callback);
            }
            obj.__watch(k, function() {
              callback.apply(this, arguments);
            });
          }
        }
      }
  }

})(window.mag || {})
<commit_msg>Update render.js<commit_after>;
(function(mag) {

  "use strict";

  var render = {
    roots: [],
    contexts: [],
    templates: {},
    cache: {}
  }

  var unloaders = [],
    cached = [],
    pendingRequests = 0

    function callView(elementClone, module, i) {
      var args = module.getArgs(i)

      var mod = module.modules[i]
      var controller = module.controllers[i]

      // var controllerConstructor = mod.controller.$original || mod.controller
      // var controller = controllerConstructor === cached.controllerConstructor ? cached.controller : new(mod.controller || function() {})
      // give it unfrozen context ?

      var context = render.contexts[i] = render.contexts[i] || {}

      try {
        mod.view(controller, elementClone, context)
      } catch (e) {
        //THROW ?
        console.log(elementClone.id, i, e)
        //throw new Error(e)
      }
      // if (controller.onunload) unloaders.push({
      //   controller: controller,
      //   handler: controller.onunload
      // })
    }

  render.callOnload = function(module) {
    for (var i = 0, controller; controller = module.controllers[i]; i++) {
      // call once
      if (controller.onload && !controller.called) {
        controller.onload.call(null, module.elements[i])
        controller.called = 1
      }
    }
  }
  render.callConfigs = function(configs) {
    for (var i = 0, len = configs.length; i < len; i++) configs[i]()
  }
  var cache = []
  render.redraw = function(module, fill, WatchJS) {

    module = module || render.module || {}

    this.fun = (this.fun || debounce(function(id) {
      // clear existing configs
      fill.configs.splice(0, fill.configs.length)

      render.doLoop(module, fill, WatchJS)
    }))
    this.fun()
  }

  render.doLoop = function(module, fill, WatchJS) {
    for (var i = 0, root; root = render.roots[i]; i++) {
      mag.running = true

      if (module.controllers[i]) {
        if (!render.innerLoop(module, fill, i, WatchJS)) {
          //cached
        } else {
          module.deferreds[i][2]({
            _html: module.elements[i].innerHTML
          })
          //TODO: remove clones
          if (module.deferreds[i][0]) {
            var index = module.deferreds[i][1]
            delete module.elements[index],
              module.modules[index], module.controllers[index], module.promises[index], module.deferreds[index]
          }
        }
      }
    }
    fill.unclear()
  }

  render.clear = function(index, elementId, fill) {
    if (index !== -1 && cache[index]) {
      // clear events too
      fill.clear()
      //console.log('clear called on reload', elementId)
      // delete cache[index]
    }
  }
  render.innerLoop = function(module, fill, i, WatchJS) {
    var elementClone = module.elements[i]
    var args = module.getArgs(i)

    if (cache[i] && cache[i] === JSON.stringify(args[0])) {
      return false
    }
    callView(elementClone, module, i)
    // circular references will throw an exception
    // such as setting to a dom element

    cache[i] = JSON.stringify(args[0])

    render.setupWatch(WatchJS, args, fill, elementClone, i, module)

    fill.fill(elementClone, args[0])
    render.callConfigs(fill.configs)

    // call onload if present in all controllers
    render.callOnload(module)
    return true
  }
  var prevId
  //render.doWatch = function(fill, ele, i, module, frameId, prop, action, difference, oldvalue) {
  render.doWatch = function(fill, ele, i, module, changes, frameId) {

    if (frameId == prevId) return
    prevId = frameId
    mag.running = true

    var args = module.getArgs(i)

    // check if data changed
    if (cache[i] && cache[i] === JSON.stringify(args[0])) {
      return
    }
    callView(ele, module, i)
    cache[i] = JSON.stringify(args[0])

    fill.fill(ele, args[0])

    render.callConfigs(fill.configs)

    mag.running = false
  }

  render.setupWatch = function(WatchJS, args, fill, elementClone, i, module) {
    // WatchJS.watch(args[0], debounce(render.doWatch.bind(null, fill, elementClone, i, module)), 6, true)
    // return
    //this.fun = (this.fun || debounce(render.doWatch.bind(null, fill, elementClone, i, module)))

    // Which we then observe
    observeNested(args[0], function(changes) {
      // changes.forEach(function(change) {
      //console.log(change.type, change.name, change.oldValue);
      //});
      debounce(render.doWatch.bind({}, fill, elementClone, i, module, changes))()
    });
  }
  var $cancelAnimationFrame = window.cancelAnimationFrame || window.clearTimeout;
  var $requestAnimationFrame = window.requestAnimationFrame || window.setTimeout;

  var debounce = function(fn, threshhold) {
    var lastRedrawCallTime, FRAME_BUDGET = threshhold || 16,
      deferTimer
    return function() {
      if (+new Date - lastRedrawCallTime > FRAME_BUDGET || $requestAnimationFrame === window.requestAnimationFrame) {
        // hold on to it
        if (deferTimer > 0) $cancelAnimationFrame(deferTimer)
        var args = arguments
        deferTimer = $requestAnimationFrame(function() {
          lastRedrawCallTime = +new Date
          var nargs = [].slice.call(arguments).concat([].slice.call(args))
          fn.apply(this, nargs);
        }, FRAME_BUDGET)
      } else {
        lastRedrawCallTime = +new Date
        var nargs = [].slice.call(arguments).concat([].slice.call(args))
        fn.apply(this, nargs)
        deferTimer = $requestAnimationFrame(function() {
          deferTimer = null
        }, FRAME_BUDGET)
      }
    }
  }

  mag.render = render

  function observeNested(obj, callback) {
    if (obj && typeof Object.observe !== 'undefined') {
      Object.observe(obj, function(changes) {
        changes.forEach(function(change) {
          if (typeof obj[change.name] == 'object') {
            observeNested(obj[change.name], callback);
          }
        });
        callback.apply(this, arguments);
      });
    }
  }

})(window.mag || {})
<|endoftext|>


<commit_before>import React from 'react';
import {Route} from 'react-router';
import App from './components/App';

export default (
  <Route path="/" component={App}>
  </Route>
);
<commit_msg>Added Button to routes.<commit_after>import React from 'react';
import {Route} from 'react-router';
import App from './components/App';
import Button from './components/Button/Button';

export default (
  <Route path="/" component={App}>
    <Route path="button" component={Button} key="1" />
  </Route>
);
<|endoftext|>


<commit_before>import React from 'react';
import {IndexRoute, Route} from 'react-router';
import { isLoaded as isAuthLoaded, load as loadAuth } from 'redux/modules/auth';
import {
    App,
    Chat,
    Home,
    Widgets,
    About,
    Login,
    LoginSuccess,
    Survey,
    NotFound,
  } from 'containers';

export default (store) => {
  const requireLogin = (nextState, replace, cb) => {
    function checkAuth() {
      const { auth: { user }} = store.getState();
      if (!user) {
        // oops, not logged in, so can't be here!
        replace('/');
      }
      cb();
    }

    if (!isAuthLoaded(store.getState())) {
      store.dispatch(loadAuth()).then(checkAuth);
    } else {
      checkAuth();
    }
  };

  /**
   * Please keep routes in alphabetical order
   */
  return (
    <Route path="/" component={App}>
      { /* Home (main) route */ }
      <IndexRoute component={Home}/>

      { /* Routes requiring login */ }
      <Route onEnter={requireLogin}>
        <Route path="chat" component={Chat}/>
        <Route path="loginSuccess" component={LoginSuccess}/>
      </Route>

      { /* Routes */ }
      <Route path="about" component={About}/>
      <Route path="login" component={Login}/>
      <Route path="survey" component={Survey}/>
      <Route path="widgets" component={Widgets}/>

      { /* Catch all route */ }
      <Route path="*" component={NotFound} status={404} />
    </Route>
  );
};
<commit_msg>working again<commit_after>import React from 'react';
import {IndexRoute, Route} from 'react-router';
import { isLoaded as isAuthLoaded, load as loadAuth } from 'redux/modules/auth';
import {
    App,
    Chat,
    Home,
    Widgets,
    About,
    Login,
    LoginSuccess,
    Survey,
    NotFound,
  } from 'containers';

export default (store) => {
  const requireLogin = (nextState, replace, cb) => {
    function checkAuth() {
      const { auth: { user }} = store.getState();
      if (!user) {
        // oops, not logged in, so can't be here!
        replace('/');
      }
      cb();
    }

    if (!isAuthLoaded(store.getState())) {
      store.dispatch(loadAuth()).then(checkAuth);
    } else {
      checkAuth();
    }
  };

  /**
   * Please keep routes in alphabetical order
   */
  return (
    <Route path="/" component={App}>
      { /* Home (main) route */ }
      <IndexRoute component={Home}/>
    </Route>
  );
};
<|endoftext|>


<commit_before>import { Router } from 'express'
import {
  dataForEnrolmentTrend,
  dataForOverallInstructor,
  dataForCoursePerformance,
  dataForUMIVSDispersion,
  dataForUMIInstructor,
  dataForFaculyAndDept,
  dataForStats,
  dataForOverview
} from './service/dataService'

require('dotenv').config()

const routes = Router()

const removeIDs = (arr) => arr.map(x => {
  delete x['_id']
  return x
})

routes.get('/', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('index', { append: '/courseval' })
  } else res.render('index')
})

routes.get('/course', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('course', { append: '/courseval' })
  } else res.render('course')
})

routes.get('/instructor', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('instructor', { append: '/courseval' })
  } else res.render('instructor')
})

routes.get('/export', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('export', { append: '/courseval' })
  } else res.render('export')
})

routes.get('/overview/UMIDispersion', (req, res) => {
  dataForUMIVSDispersion().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/UMIInstructor', (req, res) => {
  dataForUMIInstructor().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/OverallInstructor', (req, res) => {
  dataForOverallInstructor().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/CoursePerformance', (req, res) => {
  dataForCoursePerformance().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/EnrolmentTrend', (req, res) => {
  dataForEnrolmentTrend().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/FacultyDept', (req, res) => {
  dataForFaculyAndDept().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/Overview/:year', (req, res) => {
  console.log('hit overview')
  dataForOverview(req.params.year).then(data => res.send(data))
})

routes.get('/export/:fromYear/:toYear/:dept', (req, res) => {
  dataForStats(req.params.fromYear, req.params.toYear, req.params.dept).then(data => res.send(data))
})

routes.get('/list', (req, res, next) => {
  const { title } = req.query

  if (title == null || title === '') {
    // You probably want to set the response HTTP status to 400 Bad Request
    // or 422 Unprocessable Entity instead of the default 500 of
    // the global error handler (e.g check out https://github.com/kbariotis/throw.js).
    // This is just for demo purposes.
    next(new Error('The "title" parameter is required'))
    return
  }

  res.render('index', { title })
})

export default routes
<commit_msg>removed console<commit_after>import { Router } from 'express'
import {
  dataForEnrolmentTrend,
  dataForOverallInstructor,
  dataForCoursePerformance,
  dataForUMIVSDispersion,
  dataForUMIInstructor,
  dataForFaculyAndDept,
  dataForStats,
  dataForOverview
} from './service/dataService'

require('dotenv').config()

const routes = Router()

const removeIDs = (arr) => arr.map(x => {
  delete x['_id']
  return x
})

routes.get('/', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('index', { append: '/courseval' })
  } else res.render('index')
})

routes.get('/course', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('course', { append: '/courseval' })
  } else res.render('course')
})

routes.get('/instructor', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('instructor', { append: '/courseval' })
  } else res.render('instructor')
})

routes.get('/export', (req, res) => {
  if (process.env.BUILD === 'PRODUCTION') {
    res.render('export', { append: '/courseval' })
  } else res.render('export')
})

routes.get('/overview/UMIDispersion', (req, res) => {
  dataForUMIVSDispersion().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/UMIInstructor', (req, res) => {
  dataForUMIInstructor().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/OverallInstructor', (req, res) => {
  dataForOverallInstructor().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/CoursePerformance', (req, res) => {
  dataForCoursePerformance().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/EnrolmentTrend', (req, res) => {
  dataForEnrolmentTrend().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/FacultyDept', (req, res) => {
  dataForFaculyAndDept().then(data => res.send(removeIDs(data)))
})

routes.get('/overview/Overview/:year', (req, res) => {
  dataForOverview(req.params.year).then(data => res.send(data))
})

routes.get('/export/:fromYear/:toYear/:dept', (req, res) => {
  dataForStats(req.params.fromYear, req.params.toYear, req.params.dept).then(data => res.send(data))
})

routes.get('/list', (req, res, next) => {
  const { title } = req.query

  if (title == null || title === '') {
    // You probably want to set the response HTTP status to 400 Bad Request
    // or 422 Unprocessable Entity instead of the default 500 of
    // the global error handler (e.g check out https://github.com/kbariotis/throw.js).
    // This is just for demo purposes.
    next(new Error('The "title" parameter is required'))
    return
  }

  res.render('index', { title })
})

export default routes
<|endoftext|>


<commit_before>import React from 'react';
import { Route, IndexRoute, browserHistory, Router, Redirect } from 'react-router';
import AUTH0 from './../config';
import App from './components/app';
import AuthService from './utils/AuthService';
import ProfileCreate from './components/profile_create';
import Profile from './components/profile';
import Calendar from './components/calendar';
import SummerCamp from './components/summer-camp';
import Advancement from './components/advancement';
import Roster from './containers/roster';
import About from './components/about';
import Activities from './components/activities';
import Login from './components/login';
import Blog from './components/blog';
import Contact from './components/contact';
import Handbook from './components/handbook';
import Dinner from './components/dinner';
import NewBlogPost from './components/blog_new_post';
import Home from './components/home';
import config from './../config';

const { clientId, domain } = config();

const auth = new AuthService(clientId, domain);
// onEnter callback to validate authentication in private routes
const requireAuth = (nextState, replace) => {
  if (!auth.loggedIn()) {
    replace({ pathname: '/login' })
  }
}

export default (
   <Route path="/" component={App} >
      <IndexRoute component={Home} />
      <Route path="/login" component={Login} onEnter={requireAuth} />
      <Route path="/home" component={Home} />
      <Route path="/profile_create" component={ProfileCreate} />
      <Route path="/profile/:profileid" component={Profile} />
      <Route path="/calendar" component={Calendar} />
      <Route path="/summer-camp" component={SummerCamp} />
      <Route path="/advancement" component={Advancement} />

      <Route path="/roster" component={Roster} />
      <Route path="/about" component={About} />
      <Route path="/activities" component={Activities} />
      <Route path="/blog" component={Blog} />
      <Route path="/newpost" component={NewBlogPost} />
      <Route path="/contact" component={Contact} />
      <Route path="/handbook" component={Handbook} />
      <Route path="/dinner" component={Dinner} />

   </Route>
);
      // <Route path="/roster" component={Roster} onEnter={requireAuth} />
<commit_msg>clearing routes from login auth0<commit_after>import React from 'react';
import { Route, IndexRoute, browserHistory, Router, Redirect } from 'react-router';
import AUTH0 from './../config';
import App from './components/app';
import AuthService from './utils/AuthService';
import ProfileCreate from './components/profile_create';
import Profile from './components/profile';
import Calendar from './components/calendar';
import SummerCamp from './components/summer-camp';
import Advancement from './components/advancement';
import Roster from './containers/roster';
import About from './components/about';
import Activities from './components/activities';
import Login from './components/login';
import Blog from './components/blog';
import Contact from './components/contact';
import Handbook from './components/handbook';
import Dinner from './components/dinner';
import NewBlogPost from './components/blog_new_post';
import Home from './components/home';
import config from './../config';

const { clientId, domain } = config();

const auth = new AuthService(clientId, domain);
// onEnter callback to validate authentication in private routes
const requireAuth = (nextState, replace) => {
  if (!auth.loggedIn()) {
    replace({ pathname: '/login' })
  }
}

export default (
   <Route path="/" component={App} >
      <IndexRoute component={Home} />

      <Route path="/login" component={Login} />
      <Route path="/home" component={Home} />
      <Route path="/profile_create" component={ProfileCreate} />
      <Route path="/profile/:profileid" component={Profile} />
      <Route path="/calendar" component={Calendar} />
      <Route path="/summer-camp" component={SummerCamp} />
      <Route path="/advancement" component={Advancement} />

      <Route path="/roster" component={Roster} />
      <Route path="/about" component={About} />
      <Route path="/activities" component={Activities} />
      <Route path="/blog" component={Blog} />
      <Route path="/newpost" component={NewBlogPost} />
      <Route path="/contact" component={Contact} />
      <Route path="/handbook" component={Handbook} />
      <Route path="/dinner" component={Dinner} />

   </Route>
);
      // <Route path="/login" component={Login} onEnter={requireAuth} />
      // <Route path="/roster" component={Roster} onEnter={requireAuth} />
<|endoftext|>


<commit_before>const typeDefinitions = `

enum LeagueName {
  ELITE
  LOC
  PARALIGA
}

type League {
  name: String,
  players: [Player]
}

type Player {
  name: String
  stats: Stats
  matches: [Match]
}

type Stats {
  points: Int
  gamesPlayed: Int
  gamesWon: Int
  gamesDrawed: Int
  gamesLost: Int
  goalsInFavor: Int
  goalsAgains: Int
  goalDifference: Int
}

type Match {
  homePlayer: Player
  homeScore: Int
  awayPlayer: Player
  awayScore: Int
}

type Query {
  league(name: LeagueName): League
  leagues: [League]
}

schema {
  query: Query
}
`

export default [typeDefinitions]
<commit_msg>make LeagueName a required value<commit_after>const typeDefinitions = `

enum LeagueName {
  ELITE
  LOC
  PARALIGA
}

type League {
  name: String,
  players: [Player]
}

type Player {
  name: String
  stats: Stats
  matches: [Match]
}

type Stats {
  points: Int
  gamesPlayed: Int
  gamesWon: Int
  gamesDrawed: Int
  gamesLost: Int
  goalsInFavor: Int
  goalsAgains: Int
  goalDifference: Int
}

type Match {
  homePlayer: Player
  homeScore: Int
  awayPlayer: Player
  awayScore: Int
}

type Query {
  league(name: LeagueName!): League
  leagues: [League]
}

schema {
  query: Query
}
`

export default [typeDefinitions]
<|endoftext|>


<commit_before>var co = require('co');
var extend = require('extend-merge').extend;
var merge = require('extend-merge').merge;
var BaseSchema = require('chaos-orm').Schema;
var Collection = require('chaos-orm').Collection;
var Relationship = require('chaos-orm').Relationship;
var BelongsTo = require('chaos-orm').BelongsTo;
var HasOne = require('chaos-orm').HasOne;
var HasMany = require('chaos-orm').HasMany;
var HasManyThrough = require('chaos-orm').HasManyThrough;
var Query = require('./query');
var Payload = require('./payload');

function arrayDiff(a, b) {
  var len = a.length;
  var arr = [];

  for (var i = 0; i < len; i++) {
    if (b.indexOf(a[i]) === -1) {
      arr.push(a[i]);
    }
  }
  return arr;
}

function path() {
  var result = '';
  for(var i = 0, length = arguments.length; i < length; i++) {
    if (result.substr(-1) === '/' && arguments[i][0] === '/') {
      result = result.substr(0, result.length - 1);
    }
    result += arguments[i];
  }
  return result;
}

class Schema extends BaseSchema {

    /**
   * Configures the meta for use.
   *
   * @param Object config Possible options are:
   *                      - `'connection'`  _Function_ : The connection instance (defaults to `undefined`).
   */
  constructor(config) {
    var defaults = {
      connection: undefined,
    };

    config = merge({}, defaults, config);
    super(config);

    /**
     * The connection instance.
     *
     * @var Object
     */
    this._connection = undefined;

    if (config.connection) {
      this.connection(config.connection);
    }
  }

  /**
   * Return a query to retrieve data from the connected data source.
   *
   * @param  Object options Query options.
   * @return Object         An instance of `Query`.
   */
  query(options) {
    var defaults = {
      connection: this.connection(),
      model:      this.reference(),
      path:       '/' + this.source()
    };
    options = extend({}, defaults, options);

    var query = this.constructor.classes().query;

    if (!options.model) {
      throw new Error("Missing model for this schema, can't create a query.");
    }
    return new query(options);
  }

  /**
   * Gets/sets the connection object to which this class is bound.
   *
   * @param  Object connection The connection instance to set or `null` to get the current one.
   * @return mixed             Returns the connection instance on get or `this` on set.
   */
  connection(connection) {
    if (arguments.length) {
      this._connection = connection;
      merge(this._formatters, this._connection.formatters());
      return this;
    }
    if (!this._connection) {
      throw new Error("Error, missing connection for this schema.");
    }
    return this._connection;
  }

  /**
   * Create the schema.
   *
   * @param  Object  options An object of options.
   * @return Boolean
   */
  create(options) {
    throw new Error("Creating schemas are not supported by JSON API.");
  }

  /**
   * Drop the schema
   *
   * @param  array   options An array of options.
   * @return boolean
   * @throws DatabaseException If no connection is defined or the schema name is missing.
   */
  drop(options) {
    throw new Error("Dropping schemas are not supported by JSON API.");
  }

  /**
   * Save data related to relations.
   *
   * @param  Object  entity  The entity instance.
   * @param  Array   types   Type of relations to save.
   * @param  Object  options Options array.
   * @return Promise         Returns a promise.
   */
  saveRelation(instance, types, options) {
    return co(function*() {
      return true;
    }.bind(this));
  }

  /**
   * Bulk inserts
   *
   * @param  Array    inserts An array of entities to insert.
   * @param  Function filter  The filter handler for which extract entities values for the insertion.
   * @return Promise          Returns `true` if insert operations succeeded, `false` otherwise.
   */
  bulkInsert(inserts, filter, options) {
    return co(function*() {
      if (!inserts || !inserts.length) {
        return true;
      }
      var payload = new Payload();
      payload.set(new Collection({ data: inserts }), options);
      try {
        var json = yield this.connection().post('/' + this.source(), payload.serialize());
        this._amendCollection(inserts, Payload.parse(extend({ data:[] }, json)).export(), { exists: true });
      } catch (exception) {
        this._manageErrors(inserts, exception);
      }
    }.bind(this));
  }

  /**
   * Bulk updates
   *
   * @param  Array    updates An array of entities to update.
   * @param  Function filter  The filter handler for which extract entities values to update.
   * @return Promise          Returns `true` if update operations succeeded, `false` otherwise.
   */
  bulkUpdate(updates, filter, options) {
    return co(function*() {
      if (!updates || !updates.length) {
        return true;
      }
      var payload = new Payload();
      payload.set(new Collection({ data: updates }), options);
      try {
        var json = yield this.connection().patch('/' + this.source(), payload.serialize());
        this._amendCollection(updates, Payload.parse(extend({ data:[] }, json)).export(), { exists: true });
      } catch (exception) {
        this._manageErrors(updates, exception);
      }
    }.bind(this));
  }

  /**
   * Sync data from the response payload.
   *
   * @param Array  collection The sent collection
   * @param Object response   The JSON-API response payload
   * @param Object options    Some additionnal amend options
   */
  _amendCollection(collection, data, options) {
    options = options || {};
    if (collection.length !== data.length) {
      throw new Error('Error, received data must have the same length as sent data.');
    }
    for (var i = 0, len = data.length; i < len; i++) {
      var entity = collection[i];
      entity.amend(data[i], options);
    }
  }

  /**
   * Manage errors as well as validation errors.
   *
   * @param Array  collection The sent collection
   * @param Object e          The response exception
   */
  _manageErrors(collection, e) {
    if (e.stack) {
      throw e;
    }
    var exception = new Error();
    var response = e.response;
    exception.response = response;

    if (response.data && response.data.errors) {
      var errors = response.data.errors;
      for (var error of errors) {
        if (error.code !== 422) {
          exception.message = error.title;
          exception.data = error.data || {};
          break;
        }
        exception.message = 'Error, please check invalid input.';

        var data = error.data;
        if (collection.length !== data.length) {
          exception.message = 'Error, received errors must have the same length as sent data.';
          break;
        }
        for (var i = 0, len = data.length; i < len; i++) {
          collection[i].invalidate(data[i]);
        }
      }
    } else {
      exception.message = 'An unknown error has occurred.';
    }

    throw exception;
  }

  /**
   * Delete a record or document.
   *
   * @param  mixed    conditions The conditions with key/value pairs representing the ID of the records or
   *                             documents to be deleted.
   * @return Promise             Returns a promise.
   */
  delete(instance) {
    return co(function*() {
      var payload = new Payload();
      if ((Array.isArray(instance) || instance instanceof Collection) && !instance.length) {
        return;
      }
      payload.delete(instance);
      try {
        yield this.connection().delete('/' + this.source(), payload.serialize());
      } catch (exception) {
        var response = exception.response;
        if (response.data && response.data.errors) {
          var errors = response.data.errors;
          for (var error of errors) {
            throw new Error(error.title);
          }
        }
        throw new Error('Unexpected error');
      }
    }.bind(this));
  }

  /**
   * Return the last insert id from the database.
   *
   * @return mixed Returns the last insert id.
   */
  lastInsertId() {
    var key = this.key();
    var data = this.connection().lastInsert();
    return data ? data[key] : undefined;
  }

  /**
   * Return the last request.
   *
   * @return Object Returns the last request.
   */
  lastRequest() {
    return this.connection().lastRequest();
  }

  /**
   * Return the last response.
   *
   * @return Object Returns the last response.
   */
  lastResponse() {
    return this.connection().lastResponse();
  }
}

/**
 * Class dependencies.
 *
 * @var array
 */
Schema._classes = {
  relationship: Relationship,
  belongsTo: BelongsTo,
  hasOne: HasOne,
  hasMany: HasMany,
  hasManyThrough: HasManyThrough,
  query: Query
};

module.exports = Schema;
<commit_msg>Fix error managment when a validation response is received.<commit_after>var co = require('co');
var extend = require('extend-merge').extend;
var merge = require('extend-merge').merge;
var BaseSchema = require('chaos-orm').Schema;
var Collection = require('chaos-orm').Collection;
var Relationship = require('chaos-orm').Relationship;
var BelongsTo = require('chaos-orm').BelongsTo;
var HasOne = require('chaos-orm').HasOne;
var HasMany = require('chaos-orm').HasMany;
var HasManyThrough = require('chaos-orm').HasManyThrough;
var Query = require('./query');
var Payload = require('./payload');

function arrayDiff(a, b) {
  var len = a.length;
  var arr = [];

  for (var i = 0; i < len; i++) {
    if (b.indexOf(a[i]) === -1) {
      arr.push(a[i]);
    }
  }
  return arr;
}

function path() {
  var result = '';
  for(var i = 0, length = arguments.length; i < length; i++) {
    if (result.substr(-1) === '/' && arguments[i][0] === '/') {
      result = result.substr(0, result.length - 1);
    }
    result += arguments[i];
  }
  return result;
}

class Schema extends BaseSchema {

    /**
   * Configures the meta for use.
   *
   * @param Object config Possible options are:
   *                      - `'connection'`  _Function_ : The connection instance (defaults to `undefined`).
   */
  constructor(config) {
    var defaults = {
      connection: undefined,
    };

    config = merge({}, defaults, config);
    super(config);

    /**
     * The connection instance.
     *
     * @var Object
     */
    this._connection = undefined;

    if (config.connection) {
      this.connection(config.connection);
    }
  }

  /**
   * Return a query to retrieve data from the connected data source.
   *
   * @param  Object options Query options.
   * @return Object         An instance of `Query`.
   */
  query(options) {
    var defaults = {
      connection: this.connection(),
      model:      this.reference(),
      path:       '/' + this.source()
    };
    options = extend({}, defaults, options);

    var query = this.constructor.classes().query;

    if (!options.model) {
      throw new Error("Missing model for this schema, can't create a query.");
    }
    return new query(options);
  }

  /**
   * Gets/sets the connection object to which this class is bound.
   *
   * @param  Object connection The connection instance to set or `null` to get the current one.
   * @return mixed             Returns the connection instance on get or `this` on set.
   */
  connection(connection) {
    if (arguments.length) {
      this._connection = connection;
      merge(this._formatters, this._connection.formatters());
      return this;
    }
    if (!this._connection) {
      throw new Error("Error, missing connection for this schema.");
    }
    return this._connection;
  }

  /**
   * Create the schema.
   *
   * @param  Object  options An object of options.
   * @return Boolean
   */
  create(options) {
    throw new Error("Creating schemas are not supported by JSON API.");
  }

  /**
   * Drop the schema
   *
   * @param  array   options An array of options.
   * @return boolean
   * @throws DatabaseException If no connection is defined or the schema name is missing.
   */
  drop(options) {
    throw new Error("Dropping schemas are not supported by JSON API.");
  }

  /**
   * Save data related to relations.
   *
   * @param  Object  entity  The entity instance.
   * @param  Array   types   Type of relations to save.
   * @param  Object  options Options array.
   * @return Promise         Returns a promise.
   */
  saveRelation(instance, types, options) {
    return co(function*() {
      return true;
    }.bind(this));
  }

  /**
   * Bulk inserts
   *
   * @param  Array    inserts An array of entities to insert.
   * @param  Function filter  The filter handler for which extract entities values for the insertion.
   * @return Promise          Returns `true` if insert operations succeeded, `false` otherwise.
   */
  bulkInsert(inserts, filter, options) {
    return co(function*() {
      if (!inserts || !inserts.length) {
        return true;
      }
      var payload = new Payload();
      payload.set(new Collection({ data: inserts }), options);
      try {
        var json = yield this.connection().post('/' + this.source(), payload.serialize());
        this._amendCollection(inserts, Payload.parse(extend({ data:[] }, json)).export(), { exists: true });
      } catch (exception) {
        this._manageErrors(inserts, exception);
      }
    }.bind(this));
  }

  /**
   * Bulk updates
   *
   * @param  Array    updates An array of entities to update.
   * @param  Function filter  The filter handler for which extract entities values to update.
   * @return Promise          Returns `true` if update operations succeeded, `false` otherwise.
   */
  bulkUpdate(updates, filter, options) {
    return co(function*() {
      if (!updates || !updates.length) {
        return true;
      }
      var payload = new Payload();
      payload.set(new Collection({ data: updates }), options);
      try {
        var json = yield this.connection().patch('/' + this.source(), payload.serialize());
        this._amendCollection(updates, Payload.parse(extend({ data:[] }, json)).export(), { exists: true });
      } catch (exception) {
        this._manageErrors(updates, exception);
      }
    }.bind(this));
  }

  /**
   * Sync data from the response payload.
   *
   * @param Array  collection The sent collection
   * @param Object response   The JSON-API response payload
   * @param Object options    Some additionnal amend options
   */
  _amendCollection(collection, data, options) {
    options = options || {};
    for (var i = 0, len = data.length; i < len; i++) {
      var entity = collection[i];
      entity.amend(data[i], options);
    }
  }

  /**
   * Manage errors as well as validation errors.
   *
   * @param Array  collection The sent collection
   * @param Object e          The response exception
   */
  _manageErrors(collection, e) {
    if (!e.response) {
      throw e;
    }
    var exception = new Error();
    var response = e.response;
    exception.response = response;

    if (response.data && response.data.errors) {
      var errors = response.data.errors;
      for (var error of errors) {
        if (error.code !== 422) {
          exception.message = error.title;
          exception.data = error.data || {};
          break;
        }
        exception.message = 'Error, please check invalid input.';

        var data = error.data;
        if (collection.length !== data.length) {
          exception.message = 'Error, received errors must have the same length as sent data.';
          break;
        }
        for (var i = 0, len = data.length; i < len; i++) {
          collection[i].invalidate(data[i]);
        }
      }
    } else {
      exception.message = 'An unknown error has occurred.';
    }

    throw exception;
  }

  /**
   * Delete a record or document.
   *
   * @param  mixed    conditions The conditions with key/value pairs representing the ID of the records or
   *                             documents to be deleted.
   * @return Promise             Returns a promise.
   */
  delete(instance) {
    return co(function*() {
      var payload = new Payload();
      if ((Array.isArray(instance) || instance instanceof Collection) && !instance.length) {
        return;
      }
      payload.delete(instance);
      try {
        yield this.connection().delete('/' + this.source(), payload.serialize());
      } catch (exception) {
        var response = exception.response;
        if (response.data && response.data.errors) {
          var errors = response.data.errors;
          for (var error of errors) {
            throw new Error(error.title);
          }
        }
        throw new Error('Unexpected error');
      }
    }.bind(this));
  }

  /**
   * Return the last insert id from the database.
   *
   * @return mixed Returns the last insert id.
   */
  lastInsertId() {
    var key = this.key();
    var data = this.connection().lastInsert();
    return data ? data[key] : undefined;
  }

  /**
   * Return the last request.
   *
   * @return Object Returns the last request.
   */
  lastRequest() {
    return this.connection().lastRequest();
  }

  /**
   * Return the last response.
   *
   * @return Object Returns the last response.
   */
  lastResponse() {
    return this.connection().lastResponse();
  }
}

/**
 * Class dependencies.
 *
 * @var array
 */
Schema._classes = {
  relationship: Relationship,
  belongsTo: BelongsTo,
  hasOne: HasOne,
  hasMany: HasMany,
  hasManyThrough: HasManyThrough,
  query: Query
};

module.exports = Schema;
<|endoftext|>