2014-12-16 03:15:29 +08:00
|
|
|
"use strict";
|
|
|
|
|
2015-02-21 10:49:31 +08:00
|
|
|
var _ = require("./utils/helpers");
|
|
|
|
|
2015-02-20 09:23:41 +08:00
|
|
|
var monkeyPatches = require("./utils/monkey-patches");
|
2014-12-16 03:15:29 +08:00
|
|
|
|
2014-06-06 03:11:37 +08:00
|
|
|
var Utils = module.exports = {
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* A wrapper around setInterval to provide a more english-like syntax
|
|
|
|
* (e.g. "every 5 seconds, do this thing")
|
|
|
|
*
|
|
|
|
* @param {Number} interval delay between action invocations
|
|
|
|
* @param {Function} action function to trigger every time interval elapses
|
|
|
|
* @example every((5).seconds(), function() {});
|
|
|
|
* @return {intervalID} setInterval ID to pass to clearInterval()
|
|
|
|
*/
|
2014-05-14 09:24:41 +08:00
|
|
|
every: function every(interval, action) {
|
|
|
|
return setInterval(action, interval);
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* A wrapper around setInterval to provide a more english-like syntax
|
|
|
|
* (e.g. "after 5 seconds, do this thing")
|
|
|
|
*
|
|
|
|
* @param {Number} delay how long to wait
|
|
|
|
* @param {Function} action action to perform after delay
|
|
|
|
* @example after((5).seconds(), function() {});
|
|
|
|
* @return {timeoutId} setTimeout ID to pass to clearTimeout()
|
|
|
|
*/
|
2014-05-14 09:24:41 +08:00
|
|
|
after: function after(delay, action) {
|
|
|
|
return setTimeout(action, delay);
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* A wrapper around setInterval, with a delay of 0ms
|
|
|
|
*
|
|
|
|
* @param {Function} action function to invoke constantly
|
|
|
|
* @example constantly(function() {});
|
|
|
|
* @return {intervalID} setInterval ID to pass to clearInterval()
|
|
|
|
*/
|
2014-05-14 09:24:41 +08:00
|
|
|
constantly: function constantly(action) {
|
|
|
|
return every(0, action);
|
|
|
|
},
|
|
|
|
|
2015-09-02 02:30:55 +08:00
|
|
|
/**
|
|
|
|
* A wrapper around clearInterval
|
|
|
|
*
|
2015-09-02 02:42:10 +08:00
|
|
|
* @param {intervalID} intervalID ID of every/after/constantly
|
|
|
|
* @example finish(blinking);
|
2015-09-02 02:30:55 +08:00
|
|
|
* @return {void}
|
|
|
|
*/
|
|
|
|
finish: function finish(intervalID) {
|
|
|
|
clearInterval(intervalID);
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* Sleeps the program for a period of time.
|
|
|
|
*
|
|
|
|
* Use of this is not advised, as your program can't do anything else while
|
|
|
|
* it's running.
|
|
|
|
*
|
|
|
|
* @param {Number} ms number of milliseconds to sleep
|
|
|
|
* @return {void}
|
|
|
|
*/
|
2014-05-14 09:24:41 +08:00
|
|
|
sleep: function sleep(ms) {
|
2014-12-16 03:15:29 +08:00
|
|
|
var start = Date.now(),
|
2015-04-15 12:49:12 +08:00
|
|
|
i = 0;
|
2014-05-14 09:24:41 +08:00
|
|
|
|
2015-04-15 12:49:12 +08:00
|
|
|
while (Date.now() < start + ms) {
|
|
|
|
i = i.toString();
|
2014-05-14 09:24:41 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* Utility for providing class inheritance.
|
|
|
|
*
|
|
|
|
* Based on CoffeeScript's implementation of inheritance.
|
|
|
|
*
|
|
|
|
* Parent class methods/properites are available on Child.__super__.
|
|
|
|
*
|
|
|
|
* @param {Function} child the descendent class
|
|
|
|
* @param {Function} parent the parent class
|
|
|
|
* @return {Function} the child class
|
|
|
|
*/
|
2014-05-14 09:24:41 +08:00
|
|
|
subclass: function subclass(child, parent) {
|
2014-12-16 03:15:29 +08:00
|
|
|
var Ctor = function() {
|
2014-06-06 07:30:05 +08:00
|
|
|
this.constructor = child;
|
|
|
|
};
|
2014-05-14 09:24:41 +08:00
|
|
|
|
2015-02-20 09:23:41 +08:00
|
|
|
for (var key in parent) {
|
|
|
|
if (parent.hasOwnProperty(key)) {
|
|
|
|
child[key] = parent[key];
|
|
|
|
}
|
|
|
|
}
|
2014-04-02 03:31:13 +08:00
|
|
|
|
2014-12-16 03:15:29 +08:00
|
|
|
Ctor.prototype = parent.prototype;
|
|
|
|
child.prototype = new Ctor();
|
2014-05-14 09:24:41 +08:00
|
|
|
child.__super__ = parent.prototype;
|
|
|
|
return child;
|
|
|
|
},
|
|
|
|
|
2014-10-03 02:00:39 +08:00
|
|
|
proxyFunctions: function proxyFunctions(source, target) {
|
2015-02-21 10:49:31 +08:00
|
|
|
_.each(source, function(prop, key) {
|
|
|
|
if (_.isFunction(prop) && !target[key]) {
|
2014-12-18 06:42:34 +08:00
|
|
|
target[key] = prop.bind(source);
|
2014-10-03 02:00:39 +08:00
|
|
|
}
|
2015-02-21 10:49:31 +08:00
|
|
|
});
|
2014-10-03 02:00:39 +08:00
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* Proxies calls from all methods in the source to a target object
|
|
|
|
*
|
|
|
|
* @param {String[]} methods methods to proxy
|
|
|
|
* @param {Object} target object to proxy methods to
|
|
|
|
* @param {Object} [base=this] object to proxy methods from
|
|
|
|
* @param {Boolean} [force=false] whether to overwrite existing methods
|
|
|
|
* @return {Object} the base
|
|
|
|
*/
|
2014-12-16 03:15:29 +08:00
|
|
|
proxyFunctionsToObject: function(methods, target, base, force) {
|
2014-06-06 07:30:05 +08:00
|
|
|
if (base == null) {
|
|
|
|
base = this;
|
|
|
|
}
|
|
|
|
|
2014-12-18 06:42:34 +08:00
|
|
|
force = force || false;
|
2014-03-11 03:00:48 +08:00
|
|
|
|
2015-02-20 09:23:41 +08:00
|
|
|
methods.forEach(function(method) {
|
2015-02-21 10:49:31 +08:00
|
|
|
if (_.isFunction(base[method]) && !force) {
|
2014-12-18 06:42:34 +08:00
|
|
|
return;
|
2014-03-11 03:00:48 +08:00
|
|
|
}
|
2014-04-02 03:31:13 +08:00
|
|
|
|
2014-12-19 05:15:30 +08:00
|
|
|
base[method] = function() {
|
|
|
|
return target[method].apply(target, arguments);
|
|
|
|
};
|
2014-12-18 06:42:34 +08:00
|
|
|
});
|
|
|
|
|
2014-05-14 09:24:41 +08:00
|
|
|
return base;
|
|
|
|
},
|
|
|
|
|
2015-05-13 09:47:27 +08:00
|
|
|
classCallCheck: function(instance, Constructor) {
|
|
|
|
if (!(instance instanceof Constructor)) {
|
|
|
|
throw new TypeError("Cannot call a class as a function");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* Approximation of Ruby's Hash#fetch method for object property lookup
|
|
|
|
*
|
|
|
|
* @param {Object} obj object to do lookup on
|
|
|
|
* @param {String} property property name to attempt to access
|
|
|
|
* @param {*} fallback a fallback value if property can't be found. if
|
|
|
|
* a function, will be invoked with the string property name.
|
|
|
|
* @throws Error if fallback needed but not provided, or fallback fn doesn't
|
|
|
|
* return anything
|
|
|
|
* @example
|
|
|
|
* fetch({ property: "hello world" }, "property"); //=> "hello world"
|
|
|
|
* @example
|
|
|
|
* fetch({}, "notaproperty", "default value"); //=> "default value"
|
|
|
|
* @example
|
|
|
|
* var notFound = function(prop) { return prop + " not found!" };
|
|
|
|
* fetch({}, "notaproperty", notFound); //=> "notaproperty not found!"
|
|
|
|
* @example
|
|
|
|
* var badFallback = function(prop) { prop + " not found!" };
|
|
|
|
* fetch({}, "notaproperty", badFallback);
|
|
|
|
* // Error: no return value from provided callback function
|
|
|
|
* @example
|
|
|
|
* fetch(object, "notaproperty");
|
|
|
|
* // Error: key not found: "notaproperty"
|
|
|
|
* @return {*} fetched property, fallback, or fallback function return value
|
|
|
|
*/
|
2014-06-18 07:00:36 +08:00
|
|
|
fetch: function(obj, property, fallback) {
|
|
|
|
if (obj.hasOwnProperty(property)) {
|
|
|
|
return obj[property];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fallback === void 0) {
|
2014-12-16 03:15:29 +08:00
|
|
|
throw new Error("key not found: \"" + property + "\"");
|
2014-06-18 07:00:36 +08:00
|
|
|
}
|
|
|
|
|
2015-02-21 10:49:31 +08:00
|
|
|
if (_.isFunction(fallback)) {
|
2014-06-18 07:00:36 +08:00
|
|
|
var value = fallback(property);
|
|
|
|
|
|
|
|
if (value === void 0) {
|
2014-12-16 03:15:29 +08:00
|
|
|
throw new Error("no return value from provided fallback function");
|
2014-06-18 07:00:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fallback;
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* Given a name, and an array of existing names, returns a unique new name
|
|
|
|
*
|
|
|
|
* @param {String} name the name that's colliding with existing names
|
|
|
|
* @param {String[]} arr array of existing names
|
|
|
|
* @example
|
|
|
|
* makeUnique("hello", ["hello", "hello-1", "hello-2"]); //=> "hello3"
|
|
|
|
* @return {String} the new name
|
|
|
|
*/
|
2014-09-05 05:51:44 +08:00
|
|
|
makeUnique: function(name, arr) {
|
|
|
|
var newName;
|
|
|
|
|
|
|
|
if (!~arr.indexOf(name)) {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var n = 1; ; n++) {
|
|
|
|
newName = name + "-" + n;
|
|
|
|
if (!~arr.indexOf(newName)) {
|
|
|
|
return newName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-06-30 01:21:25 +08:00
|
|
|
/**
|
|
|
|
* Adds every/after/constantly to the global namespace, and installs
|
|
|
|
* monkey-patches.
|
|
|
|
*
|
|
|
|
* @return {Object} utils object
|
|
|
|
*/
|
2014-05-14 09:24:41 +08:00
|
|
|
bootstrap: function bootstrap() {
|
2014-06-06 03:11:37 +08:00
|
|
|
global.every = this.every;
|
|
|
|
global.after = this.after;
|
|
|
|
global.constantly = this.constantly;
|
|
|
|
|
2015-02-21 02:52:01 +08:00
|
|
|
monkeyPatches.install();
|
2014-03-11 03:00:48 +08:00
|
|
|
|
2014-05-14 09:24:41 +08:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-06-06 03:11:37 +08:00
|
|
|
Utils.bootstrap();
|