UNPKG

johnny-five

Version:

The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!

263 lines (216 loc) 6.03 kB
var IS_TEST_MODE = !!process.env.IS_TEST_MODE; var Emitter = require("events").EventEmitter; var priv = new Map(); /** * Collection * * Make Collections for output classes * * @param {[type]} numsOrObjects */ function Collection(numsOrObjects) { var Type = this.type; var initObjects = []; this.length = 0; if (Array.isArray(numsOrObjects)) { initObjects = numsOrObjects; } else { // Initialize with a Shared Properties object /* istanbul ignore else */ if (Array.isArray(numsOrObjects.pins)) { var keys = Object.keys(numsOrObjects).filter(function(key) { return key !== "pins"; }); initObjects = numsOrObjects.pins.map(function(pin) { var obj = {}; if (Array.isArray(pin)) { obj.pins = pin; } else { obj.pin = pin; } return keys.reduce(function(accum, key) { accum[key] = numsOrObjects[key]; return accum; }, obj); }); } } /* istanbul ignore else */ if (initObjects.length) { while (initObjects.length) { var numOrObject = initObjects.shift(); // When a Type exists, respect it! if (typeof Type === "function") { if (!(numOrObject instanceof Type || numOrObject instanceof this.constructor)) { numOrObject = new Type(numOrObject); } } this.add(numOrObject); } } } Collection.prototype.add = function() { var length = this.length; var aLen = arguments.length; for (var i = 0; i < aLen; i++) { // When a Type exists, respect it! if (this.type) { if (arguments[i] instanceof this.type || arguments[i] instanceof this.constructor) { this[length++] = arguments[i]; } } else { // Otherwise allow user to directly instantiate // Collection or Collection.Emitter to create // a mixed collection this[length++] = arguments[i]; } } return (this.length = length); }; Collection.prototype.each = function(callbackFn) { var length = this.length; for (var i = 0; i < length; i++) { callbackFn.call(this[i], this[i], i); } return this; }; Collection.prototype.forEach = function() { [].forEach.apply(this, arguments); }; Collection.prototype.includes = function() { return [].includes.apply(this, arguments); }; Collection.prototype.indexOf = function() { return [].indexOf.apply(this, arguments); }; Collection.prototype.map = function() { return [].map.apply(this, arguments); }; Collection.prototype.slice = function() { return new this.constructor([].slice.apply(this, arguments)); }; Collection.prototype.byId = function(id) { return [].find.call(this, function(entry) { return entry.id !== undefined && entry.id === id; }); }; /** * Collection.installMethodForwarding * * Copy single method to collection class * * @param {Object} target Target prototype * @param {Object} source Source prototype * @return {Object} target Modified Target prototype */ Collection.installMethodForwarding = function(target, source) { return Object.keys(source).reduce(function(accum, method) { // Create Inputs wrappers for each method listed. // This will allow us control over all Input instances // simultaneously. accum[method] = function() { var length = this.length; for (var i = 0; i < length; i++) { this[i][method].apply(this[i], arguments); } return this; }; return accum; }, target); }; /** * Collection.Emitter * * Make Collections for input classes * * @param {[type]} numsOrObjects * */ Collection.Emitter = function(numsOrObjects) { // Create private state ahead of super call priv.set(this, { timing: { last: Date.now() } }); Collection.call(this, numsOrObjects); // If the Collection.Emitter was created // with a Shared Properties object, then // we should abide by the freq or period // properties... var interval = null; var period = 5; if (!Array.isArray(numsOrObjects) && (typeof numsOrObjects === "object" && numsOrObjects !== null)) { period = numsOrObjects.freq || numsOrObjects.period || period; // _However_, looking to the future, we // need to start thinking about replacing // the garbage named _freq_ (the value is // actually a period), with real _frequency_ // in Hz. // If provided, convert frequency to period /* istanbul ignore else */ if (numsOrObjects.frequency) { period = (1 / numsOrObjects.frequency) * 1000; } } Object.defineProperties(this, { period: { get: function() { return period; }, set: function(value) { if (period !== value) { period = value; } if (interval) { clearInterval(interval); } interval = setInterval(function() { this.emit("data", this); }.bind(this), period); } }, }); this.period = period; this.on("newListener", function(event) { if (event === "change" || event === "data") { return; } this.forEach(function(input) { input.on(event, function(data) { this.emit(event, input, data); }.bind(this)); }, this); }); }; Collection.Emitter.prototype = Object.create(Collection.prototype, { constructor: { value: Collection.Emitter } }); Object.assign(Collection.Emitter.prototype, Emitter.prototype); Collection.Emitter.prototype.add = function() { var inputs = Array.from(arguments); /* istanbul ignore else */ if (inputs.length) { Collection.prototype.add.apply(this, inputs); inputs.forEach(function(input) { if (input) { input.on("change", function() { this.emit("change", input); }.bind(this)); } }, this); } return this.length; // return (this.length = length); }; /* istanbul ignore else */ if (IS_TEST_MODE) { Collection.purge = function() { priv.clear(); }; } module.exports = Collection;