From ac6b09fe50701048ea6238cf169794336f7400b2 Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Mon, 15 Dec 2014 11:15:29 -0800 Subject: [PATCH] Clean up lib for JSHint --- lib/adaptor.js | 7 +- lib/api.js | 48 ++-- lib/api/auth/basic.js | 78 +++--- lib/api/routes.js | 28 ++- lib/basestar.js | 20 +- lib/config.js | 2 +- lib/connection.js | 26 +- lib/cylon.js | 59 +++-- lib/device.js | 25 +- lib/driver.js | 15 +- lib/io/digital-pin.js | 56 +++-- lib/io/utils.js | 10 +- lib/logger.js | 90 +++---- lib/logger/basic_logger.js | 10 +- lib/logger/null_logger.js | 4 +- lib/registry.js | 25 +- lib/robot.js | 122 +++++---- lib/test/loopback.js | 6 +- lib/test/ping.js | 10 +- lib/test/test-adaptor.js | 6 +- lib/test/test-driver.js | 8 +- lib/utils.js | 491 +++++++++++++++++++------------------ 22 files changed, 599 insertions(+), 547 deletions(-) diff --git a/lib/adaptor.js b/lib/adaptor.js index 96e48f0..b0de545 100644 --- a/lib/adaptor.js +++ b/lib/adaptor.js @@ -8,9 +8,8 @@ "use strict"; -var Basestar = require('./basestar'), - Logger = require('./logger'), - Utils = require('./utils'); +var Basestar = require("./basestar"), + Utils = require("./utils"); // Public: Creates a new Adaptor // @@ -35,7 +34,7 @@ var Adaptor = module.exports = function Adaptor(opts) { this.details = {}; for (var opt in opts) { - if (['robot', 'name', 'adaptor'].indexOf(opt) < 0) { + if (["robot", "name", "adaptor"].indexOf(opt) < 0) { this.details[opt] = opts[opt]; } } diff --git a/lib/api.js b/lib/api.js index c8d9fe9..b8d3b6d 100644 --- a/lib/api.js +++ b/lib/api.js @@ -8,13 +8,13 @@ "use strict"; -var fs = require('fs'), - path = require('path'); +var fs = require("fs"), + path = require("path"); -var express = require('express'), - bodyParser = require('body-parser'); +var express = require("express"), + bodyParser = require("body-parser"); -var Logger = require('./logger'); +var Logger = require("./logger"); var API = module.exports = function API(opts) { if (opts == null) { @@ -27,7 +27,7 @@ var API = module.exports = function API(opts) { this.createServer(); - this.express.set('title', 'Cylon API Server'); + this.express.set("title", "Cylon API Server"); this.express.use(this.setupAuth()); @@ -40,7 +40,7 @@ var API = module.exports = function API(opts) { this.express.use(function(req, res, next) { res.set("Access-Control-Allow-Origin", this.CORS || "*"); res.set("Access-Control-Allow-Headers", "Content-Type"); - res.set('Content-Type', 'application/json'); + res.set("Content-Type", "application/json"); return next(); }.bind(this)); @@ -56,19 +56,19 @@ var API = module.exports = function API(opts) { }); // load route definitions - this.express.use('/api', require('./api/routes')); + this.express.use("/api", require("./api/routes")); // error handling - this.express.use(function(err, req, res, next) { - res.status(500).json({ error: err.message || "An error occured."}) + this.express.use(function(err, req, res) { + res.status(500).json({ error: err.message || "An error occured."}); }); }; API.prototype.defaults = { - host: '127.0.0.1', - port: '3000', + host: "127.0.0.1", + port: "3000", auth: false, - CORS: '', + CORS: "", ssl: { key: path.normalize(__dirname + "/api/ssl/server.key"), cert: path.normalize(__dirname + "/api/ssl/server.crt") @@ -79,15 +79,18 @@ API.prototype.createServer = function createServer() { this.express = express(); //configure ssl if requested - if (this.ssl && typeof(this.ssl) === 'object') { - var https = require('https'); + if (this.ssl && typeof(this.ssl) === "object") { + var https = require("https"); this.server = https.createServer({ key: fs.readFileSync(this.ssl.key), cert: fs.readFileSync(this.ssl.cert) }, this.express); } else { - Logger.warn("API using insecure connection. We recommend using an SSL certificate with Cylon."); + var str = "API using insecure connection. "; + str += "We recommend using an SSL certificate with Cylon."; + + Logger.warn(str); this.server = this.express; } }; @@ -95,7 +98,7 @@ API.prototype.createServer = function createServer() { API.prototype.setupAuth = function setupAuth() { var authfn = function auth(req, res, next) { next(); }; - if (!!this.auth && typeof(this.auth) === 'object' && this.auth.type) { + if (!!this.auth && typeof(this.auth) === "object" && this.auth.type) { var type = this.auth.type, module = "./api/auth/" + type, filename = path.normalize(__dirname + "/" + module + ".js"), @@ -104,17 +107,20 @@ API.prototype.setupAuth = function setupAuth() { if (exists) { authfn = require(filename)(this.auth); } - }; + } return authfn; }; API.prototype.listen = function() { this.server.listen(this.port, this.host, null, function() { - var title = this.express.get('title'); - var protocol = this.ssl ? "https" : "http"; + var title = this.express.get("title"); + var protocol = this.ssl ? "https" : "http", + str; + + str = "Listening at " + protocol + "://" + this.host + ":" + this.port; Logger.info(title + " is now online."); - Logger.info("Listening at " + protocol + "://" + this.host + ":" + this.port); + Logger.info(str); }.bind(this)); }; diff --git a/lib/api/auth/basic.js b/lib/api/auth/basic.js index d03bf4e..dfdf438 100644 --- a/lib/api/auth/basic.js +++ b/lib/api/auth/basic.js @@ -6,48 +6,14 @@ * Licensed under the Apache 2.0 license. */ -var http = require('http'); +"use strict"; -module.exports = function(config) { - var user = config.user, - pass = config.pass; - - return function auth(req, res, next) { - var auth = req.headers.authorization; - - if (!auth) { - return unauthorized(res); - } - - // malformed - var parts = auth.split(' '); - - if ('basic' != parts[0].toLowerCase() || !parts[1]) { - return next(error(400)); - } - - auth = parts[1]; - - // credentials - auth = new Buffer(auth, 'base64').toString(); - auth = auth.match(/^([^:]+):(.+)$/); - - if (!auth) { - return unauthorized(res); - } - - if (auth[1] === user && auth[2] === pass) { - return next(); - } - - return unauthorized(res); - }; -}; +var http = require("http"); var unauthorized = function unauthorized(res) { res.statusCode = 401; - res.setHeader('WWW-Authenticate', 'Basic realm="Authorization Required"'); - res.end('Unauthorized'); + res.setHeader("WWW-Authenticate", "Basic realm=\"Authorization Required\""); + res.end("Unauthorized"); }; var error = function error(code, msg){ @@ -55,3 +21,39 @@ var error = function error(code, msg){ err.status = code; return err; }; + +module.exports = function(config) { + var user = config.user, + pass = config.pass; + + return function auth(req, res, next) { + var authorization = req.headers.authorization; + + if (!authorization) { + return unauthorized(res); + } + + // malformed + var parts = authorization.split(" "); + + if ("basic" !== parts[0].toLowerCase() || !parts[1]) { + return next(error(400)); + } + + authorization = parts[1]; + + // credentials + authorization = new Buffer(authorization, "base64").toString(); + authorization = authorization.match(/^([^:]+):(.+)$/); + + if (!authorization) { + return unauthorized(res); + } + + if (authorization[1] === user && authorization[2] === pass) { + return next(); + } + + return unauthorized(res); + }; +}; diff --git a/lib/api/routes.js b/lib/api/routes.js index 7c55bd8..f2bdc31 100644 --- a/lib/api/routes.js +++ b/lib/api/routes.js @@ -1,3 +1,5 @@ +/* jshint maxlen: false */ + /* * Cylon API - Route Definitions * cylonjs.com @@ -6,9 +8,11 @@ * Licensed under the Apache 2.0 license. */ -var Cylon = require('../cylon'); +"use strict"; -var router = module.exports = require('express').Router(); +var Cylon = require("../cylon"); + +var router = module.exports = require("express").Router(); // Loads up the appropriate Robot/Device/Connection instances, if they are // present in the route params. @@ -20,21 +24,27 @@ var load = function load(req, res, next) { if (robot) { req.robot = Cylon.robots[robot]; if (!req.robot) { - return res.status(404).json({ error: "No Robot found with the name " + robot }); + return res.status(404).json({ + error: "No Robot found with the name " + robot + }); } } if (device) { req.device = req.robot.devices[device]; if (!req.device) { - return res.status(404).json({ error: "No device found with the name " + device }); + return res.status(404).json({ + error: "No device found with the name " + device + }); } } if (connection) { req.connection = req.robot.connections[connection]; if (!req.connection) { - return res.status(404).json({ error: "No connection found with the name " + connection }); + return res.status(404).json({ + error: "No connection found with the name " + connection + }); } } @@ -85,9 +95,9 @@ router.get("/robots/:robot/devices/:device/events/:event", load, function(req, r var event = req.params.event; res.writeHead(200, { - 'Content-Type': 'text/event-stream', - 'Connection': 'keep-alive', - 'Cache-Control': 'no-cache' + "Content-Type": "text/event-stream", + "Connection": "keep-alive", + "Cache-Control": "no-cache" }); var writeData = function(data) { @@ -96,7 +106,7 @@ router.get("/robots/:robot/devices/:device/events/:event", load, function(req, r req.device.on(event, writeData); - res.on('close', function() { + res.on("close", function() { req.device.removeListener(event, writeData); }); }); diff --git a/lib/basestar.js b/lib/basestar.js index c25dceb..c4ecdc7 100644 --- a/lib/basestar.js +++ b/lib/basestar.js @@ -8,9 +8,9 @@ "use strict"; -var EventEmitter = require('events').EventEmitter; +var EventEmitter = require("events").EventEmitter; -var Utils = require('./utils'); +var Utils = require("./utils"); // Basestar is a base class to be used when writing external Cylon adaptors and // drivers. It provides some useful base methods and functionality @@ -46,7 +46,7 @@ Basestar.prototype.proxyMethods = function(methods, target, source, force) { // target // - target - object to proxy event to // - source - object to proxy event from -// - sendUpdate - whether or not to send an 'update' event +// - sendUpdate - whether or not to send an "update" event // // Returns the source Basestar.prototype.defineEvent = function(opts) { @@ -59,7 +59,7 @@ Basestar.prototype.defineEvent = function(opts) { opts.target.emit.apply(opts.target, args); if (opts.sendUpdate) { - args.unshift('update'); + args.unshift("update"); opts.target.emit.apply(opts.target, args); } }); @@ -67,8 +67,8 @@ Basestar.prototype.defineEvent = function(opts) { return opts.source; }; -// Public: Creates an event handler that proxies events from an adaptor's -// 'connector' (reference to whatever module is actually talking to the hw) +// Public: Creates an event handler that proxies events from an adaptor"s +// "connector" (reference to whatever module is actually talking to the hw) // to the adaptor // // opts - hash of opts to be passed to defineEvent() @@ -78,7 +78,7 @@ Basestar.prototype.defineAdaptorEvent = function(opts) { return this._proxyEvents(opts, this.connector, this); }; -// Public: Creates an event handler that proxies events from a driver's +// Public: Creates an event handler that proxies events from a driver"s // connection to the driver // // opts - hash of opts to be passed to defineEvent() @@ -89,10 +89,10 @@ Basestar.prototype.defineDriverEvent = function(opts) { }; Basestar.prototype._proxyEvents = function(opts, source, target) { - opts = (typeof opts === 'string') ? { eventName: opts } : opts; + opts = (typeof opts === "string") ? { eventName: opts } : opts; opts.source = source; - opts.target = target + opts.target = target; return this.defineEvent(opts); -} +}; diff --git a/lib/config.js b/lib/config.js index 7d90281..38fd391 100644 --- a/lib/config.js +++ b/lib/config.js @@ -6,7 +6,7 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; module.exports = { logging: {}, diff --git a/lib/connection.js b/lib/connection.js index 4ac3426..dc28b1f 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -6,14 +6,13 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; -var Registry = require('./registry'), - Config = require('./config'), - Logger = require('./logger'); +var Registry = require("./registry"), + Config = require("./config"); var testMode = function() { - return process.env.NODE_ENV === 'test' && Config.testMode; + return process.env.NODE_ENV === "test" && Config.testMode; }; // Public: Creates a new Adaptor and returns it. @@ -26,7 +25,8 @@ var testMode = function() { // // Returns the newly set-up connection module.exports = function Connection(opts) { - var module; + var module, + prop; opts = opts || {}; @@ -37,27 +37,27 @@ module.exports = function Connection(opts) { } if (!module) { - Registry.register('cylon-' + opts.adaptor); + Registry.register("cylon-" + opts.adaptor); module = Registry.findByAdaptor(opts.adaptor); } var adaptor = module.adaptor(opts); - for (var prop in adaptor) { - if (~['constructor'].indexOf(prop)) { + for (prop in adaptor) { + if (~["constructor"].indexOf(prop)) { continue; } - if (typeof adaptor[prop] === 'function') { + if (typeof adaptor[prop] === "function") { adaptor[prop] = adaptor[prop].bind(adaptor); } } if (testMode()) { - var testAdaptor = Registry.findByAdaptor('test').adaptor(opts); + var testAdaptor = Registry.findByAdaptor("test").adaptor(opts); - for (var prop in adaptor) { - if (typeof adaptor[prop] === 'function' && !testAdaptor[prop]) { + for (prop in adaptor) { + if (typeof adaptor[prop] === "function" && !testAdaptor[prop]) { testAdaptor[prop] = function() { return true; }; } } diff --git a/lib/cylon.js b/lib/cylon.js index 7884bb8..b3dcb10 100644 --- a/lib/cylon.js +++ b/lib/cylon.js @@ -8,22 +8,22 @@ "use strict"; -var Async = require('async'); +var Async = require("async"); -var Logger = require('./logger'), - Robot = require('./robot'), - Config = require('./config'), - Utils = require('./utils'); +var Logger = require("./logger"), + Robot = require("./robot"), + Config = require("./config"), + Utils = require("./utils"); var Cylon = module.exports = { Logger: Logger, - Driver: require('./driver'), - Adaptor: require('./adaptor'), + Driver: require("./driver"), + Adaptor: require("./adaptor"), Utils: Utils, IO: { - DigitalPin: require('./io/digital-pin'), - Utils: require('./io/utils') + DigitalPin: require("./io/digital-pin"), + Utils: require("./io/utils") }, api_instance: null, @@ -39,8 +39,8 @@ var Cylon = module.exports = { // Returns a shiny new Robot // Examples: // Cylon.robot -// connection: { name: 'arduino', adaptor: 'firmata' } -// device: { name: 'led', driver: 'led', pin: 13 } +// connection: { name: "arduino", adaptor: "firmata" } +// device: { name: "led", driver: "led", pin: 13 } // // work: (me) -> // me.led.toggle() @@ -51,25 +51,29 @@ Cylon.robot = function robot(opts) { if (opts.name && this.robots[opts.name]) { var original = opts.name; opts.name = Utils.makeUnique(original, Object.keys(this.robots)); - Logger.warn("Robot names must be unique. Renaming '" + original + "' to '" + opts.name + "'"); + + var str = "Robot names must be unique. Renaming '"; + str += original + "' to '" + opts.name + "'"; + + Logger.warn(str); } - var robot = new Robot(opts); - this.robots[robot.name] = robot; - return robot; + var bot = new Robot(opts); + this.robots[bot.name] = bot; + return bot; }; // Public: Creates a new API based on passed options // // Returns nothing Cylon.api = function api(opts) { - if (typeof opts === 'object') { + if (typeof opts === "object") { this.config({ api: opts }); } - var API = require('./api'); + var API = require("./api"); - var config = Utils.fetch(Config, 'api', {}); + var config = Utils.fetch(Config, "api", {}); this.api_instance = new API(config); this.api_instance.listen(); @@ -83,13 +87,14 @@ Cylon.start = function start() { for (var bot in this.robots) { starters.push(this.robots[bot].start); } - Async.parallel(starters, function(err, results) { - var mode = Utils.fetch(Config, 'workMode', 'async'); - if (mode === 'sync') { + Async.parallel(starters, function() { + var mode = Utils.fetch(Config, "workMode", "async"); + + if (mode === "sync") { for (var bot in this.robots) { this.robots[bot].startWork(); } - } + } }.bind(this)); }; @@ -101,7 +106,7 @@ Cylon.start = function start() { Cylon.config = function(opts) { var loggingChanged = (opts.logging && Config.logging !== opts.logging); - if (opts && typeof(opts) === 'object' && !Array.isArray(opts)) { + if (opts && typeof(opts) === "object" && !Array.isArray(opts)) { for (var o in opts) { Config[o] = opts[o]; } @@ -120,9 +125,9 @@ Cylon.config = function(opts) { // // Returns nothing Cylon.halt = function halt(callback) { - callback = callback || function() {} - // if robots can't shut down quickly enough, forcefully self-terminate - var timeout = Config.haltTimeout || 3000 + callback = callback || function() {}; + // if robots can"t shut down quickly enough, forcefully self-terminate + var timeout = Config.haltTimeout || 3000; Utils.after(timeout, callback); var fns = []; @@ -145,7 +150,7 @@ Cylon.toJSON = function() { return { robots: robots, commands: Object.keys(this.commands) - } + }; }; if (process.platform === "win32") { diff --git a/lib/device.js b/lib/device.js index 5f0a95e..b4c6f2d 100644 --- a/lib/device.js +++ b/lib/device.js @@ -6,13 +6,13 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; -var Registry = require('./registry'), - Config = require('./config'); +var Registry = require("./registry"), + Config = require("./config"); var testMode = function() { - return process.env.NODE_ENV === 'test' && Config.testMode; + return process.env.NODE_ENV === "test" && Config.testMode; }; // Public: Creates a new Device @@ -26,7 +26,8 @@ var testMode = function() { // // Returns a new Device module.exports = function Device(opts) { - var module; + var module, + prop; if (opts.module) { module = Registry.register(opts.module); @@ -37,27 +38,27 @@ module.exports = function Device(opts) { opts.device = this; if (!module) { - Registry.register('cylon-' + opts.driver); + Registry.register("cylon-" + opts.driver); module = Registry.findByDriver(opts.driver); } var driver = module.driver(opts); - for (var prop in driver) { - if (~['constructor'].indexOf(prop)) { + for (prop in driver) { + if (~["constructor"].indexOf(prop)) { continue; } - if (typeof driver[prop] === 'function') { + if (typeof driver[prop] === "function") { driver[prop] = driver[prop].bind(driver); } } if (testMode()) { - var testDriver = Registry.findByDriver('test').driver(opts); + var testDriver = Registry.findByDriver("test").driver(opts); - for (var prop in driver) { - if (typeof driver[prop] === 'function' && !testDriver[prop]) { + for (prop in driver) { + if (typeof driver[prop] === "function" && !testDriver[prop]) { testDriver[prop] = function() { return true; }; } } diff --git a/lib/driver.js b/lib/driver.js index c589ac7..9c4ce3c 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -6,11 +6,10 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; -var Basestar = require('./basestar'), - Logger = require('./logger'), - Utils = require('./utils'); +var Basestar = require("./basestar"), + Utils = require("./utils"); // Public: Creates a new Driver // @@ -23,7 +22,7 @@ var Driver = module.exports = function Driver(opts) { opts = opts || {}; this.name = opts.name; - this.robot = opts.robot + this.robot = opts.robot; this.connection = opts.connection; @@ -36,7 +35,7 @@ var Driver = module.exports = function Driver(opts) { this.details = {}; for (var opt in opts) { - if (['robot', 'name', 'connection', 'driver'].indexOf(opt) < 0) { + if (["robot", "name", "connection", "driver"].indexOf(opt) < 0) { this.details[opt] = opts[opt]; } } @@ -62,11 +61,11 @@ Driver.prototype.setupCommands = function(commands, proxy) { } return "_" + match.toLowerCase(); - }).replace(/^_/, ''); + }).replace(/^_/, ""); this.commands[snake_case] = this[command]; } -} +}; Driver.prototype.toJSON = function() { return { diff --git a/lib/io/digital-pin.js b/lib/io/digital-pin.js index 285ebd8..7bb5be7 100644 --- a/lib/io/digital-pin.js +++ b/lib/io/digital-pin.js @@ -8,24 +8,21 @@ "use strict"; -var FS = require('fs'), - EventEmitter = require('events').EventEmitter; +var FS = require("fs"), + EventEmitter = require("events").EventEmitter; -var Utils = require('../utils'); +var Utils = require("../utils"); var GPIO_PATH = "/sys/class/gpio"; var GPIO_READ = "in"; var GPIO_WRITE = "out"; -var HIGH = 1; -var LOW = 0; - // DigitalPin class offers an interface with the Linux GPIO system present in // single-board computers such as a Raspberry Pi, or a BeagleBone var DigitalPin = module.exports = function DigitalPin(opts) { this.pinNum = opts.pin.toString(); - this.status = 'low'; + this.status = "low"; this.ready = false; this.mode = opts.mode; }; @@ -38,7 +35,11 @@ DigitalPin.prototype.connect = function(mode) { } FS.exists(this._pinPath(), function(exists) { - exists ? this._openPin() : this._createGPIOPin(); + if (exists) { + this._openPin(); + } else { + this._createGPIOPin(); + } }.bind(this)); }; @@ -54,40 +55,43 @@ DigitalPin.prototype.closeSync = function() { }; DigitalPin.prototype.digitalWrite = function(value) { - if (this.mode !== 'w') { - this._setMode('w'); + if (this.mode !== "w") { + this._setMode("w"); } - this.status = value === 1 ? 'high' : 'low'; + this.status = value === 1 ? "high" : "low"; FS.writeFile(this._valuePath(), value, function(err) { if (err) { - this.emit('error', "Error occurred while writing value " + value + " to pin " + this.pinNum); + var str = "Error occurred while writing value "; + str += value + " to pin " + this.pinNum; + + this.emit("error", str); } else { - this.emit('digitalWrite', value); + this.emit("digitalWrite", value); } }.bind(this)); return value; }; -// Public: Reads the digial pin's value periodicly on a supplied interval, +// Public: Reads the digial pin"s value periodicly on a supplied interval, // and emits the result or an error // // interval - time (in milliseconds) to read from the pin at // // Returns the defined interval DigitalPin.prototype.digitalRead = function(interval) { - if (this.mode !== 'r') { this._setMode('r'); } + if (this.mode !== "r") { this._setMode("r"); } Utils.every(interval, function() { FS.readFile(this._valuePath(), function(err, data) { if (err) { var error = "Error occurred while reading from pin " + this.pinNum; - this.emit('error', error); + this.emit("error", error); } else { var readData = parseInt(data.toString()); - this.emit('digitalRead', readData); + this.emit("digitalRead", readData); } }.bind(this)); }.bind(this)); @@ -102,14 +106,14 @@ DigitalPin.prototype.setLow = function() { }; DigitalPin.prototype.toggle = function() { - return (this.status === 'low') ? this.setHigh() : this.setLow(); + return (this.status === "low") ? this.setHigh() : this.setLow(); }; // Creates the GPIO file to read/write from DigitalPin.prototype._createGPIOPin = function() { FS.writeFile(this._exportPath(), this.pinNum, function(err) { if (err) { - this.emit('error', 'Error while creating pin files'); + this.emit("error", "Error while creating pin files"); } else { this._openPin(); } @@ -118,24 +122,24 @@ DigitalPin.prototype._createGPIOPin = function() { DigitalPin.prototype._openPin = function() { this._setMode(this.mode, true); - this.emit('open'); + this.emit("open"); }; DigitalPin.prototype._closeCallback = function(err) { if (err) { - this.emit('error', 'Error while closing pin files'); + this.emit("error", "Error while closing pin files"); } else { - this.emit('close', this.pinNum); + this.emit("close", this.pinNum); } }; -// Sets the mode for the GPIO pin by writing the correct values to the pin reference files +// Sets the mode for the pin by writing the values to the pin reference files DigitalPin.prototype._setMode = function(mode, emitConnect) { if (emitConnect == null) { emitConnect = false; } this.mode = mode; - var data = (mode === 'w') ? GPIO_WRITE : GPIO_READ; + var data = (mode === "w") ? GPIO_WRITE : GPIO_READ; FS.writeFile(this._directionPath(), data, function(err) { this._setModeCallback(err, emitConnect); @@ -144,13 +148,13 @@ DigitalPin.prototype._setMode = function(mode, emitConnect) { DigitalPin.prototype._setModeCallback = function(err, emitConnect) { if (err) { - return this.emit('error', "Setting up pin direction failed"); + return this.emit("error", "Setting up pin direction failed"); } this.ready = true; if (emitConnect) { - this.emit('connect', this.mode); + this.emit("connect", this.mode); } }; diff --git a/lib/io/utils.js b/lib/io/utils.js index a2c0c3d..85522cd 100644 --- a/lib/io/utils.js +++ b/lib/io/utils.js @@ -1,11 +1,13 @@ -Utils = { +"use strict"; + +module.exports = { // Returns { period: int, duty: int } // Calculated based on params value, freq, pulseWidth = { min: int, max: int } // pulseWidth min and max need to be specified in microseconds periodAndDuty: function(scaledDuty, freq, pulseWidth, polarity) { var period, duty, maxDuty; - polarity = polarity || 'high'; + polarity = polarity || "high"; period = Math.round(1.0e9 / freq); if (pulseWidth != null) { @@ -18,12 +20,10 @@ Utils = { duty = Math.round(period * scaledDuty); } - if (polarity == 'low') { + if (polarity === "low") { duty = period - duty; } return { period: period, duty: duty }; } }; - -module.exports = Utils; diff --git a/lib/logger.js b/lib/logger.js index 373e0e7..d42ad22 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -10,55 +10,15 @@ var levels = ["debug", "info", "warn", "error", "fatal"]; -var BasicLogger = require('./logger/basic_logger'), - NullLogger = require('./logger/null_logger'), - Config = require('./config'); +var BasicLogger = require("./logger/basic_logger"), + NullLogger = require("./logger/null_logger"), + Config = require("./config"); // The Logger is a global object to facilitate logging stuff to the console (or -// other output) easily and consistently. It's available anywhere in Cylon, as +// other output) easily and consistently. It"s available anywhere in Cylon, as // well as in external modules that are loaded into Cylon var Logger = module.exports = {}; -// Public: Creates a Logger instance and assigns it to @logger -// -// logger - logger object to use. Defaults to a BasicLogger, or a NullLogger if -// false is supplied -// -// level - logging level to use. if supplied, will only log to specified level -// or above -// -// Returns the new logger instance -Logger.setup = function setup(opts) { - var Cylon = require('./cylon'); - - if (typeof opts === 'object') { - Cylon.config({ logging: opts }); - } - - var logger = Config.logging.logger, - level = Config.logging.level || "info"; - - if (logger == null) { logger = BasicLogger; } - - this.logger = logger || NullLogger; - - if (typeof level === 'string') { - setLogLevel(level); - } - - return this.logger; -}; - -Logger.toString = function() { - return this.logger.toString(); -}; - -levels.forEach(function(level) { - Logger[level] = function() { - return this.logger[level].apply(this.logger, arguments); - }; -}); - var setLogLevel = function(level) { var index = levels.indexOf(level), active, @@ -80,7 +40,47 @@ var setLogLevel = function(level) { ignored.forEach(function(level) { Logger[level] = function() {}; }); -} +}; + +// Public: Creates a Logger instance and assigns it to @logger +// +// logger - logger object to use. Defaults to a BasicLogger, or a NullLogger if +// false is supplied +// +// level - logging level to use. if supplied, will only log to specified level +// or above +// +// Returns the new logger instance +Logger.setup = function setup(opts) { + var Cylon = require("./cylon"); + + if (typeof opts === "object") { + Cylon.config({ logging: opts }); + } + + var logger = Config.logging.logger, + level = Config.logging.level || "info"; + + if (logger == null) { logger = BasicLogger; } + + this.logger = logger || NullLogger; + + if (typeof level === "string") { + setLogLevel(level); + } + + return this.logger; +}; + +Logger.toString = function() { + return this.logger.toString(); +}; + +levels.forEach(function(level) { + Logger[level] = function() { + return this.logger[level].apply(this.logger, arguments); + }; +}); Logger.setup(); diff --git a/lib/logger/basic_logger.js b/lib/logger/basic_logger.js index 6acfc22..46a6db5 100644 --- a/lib/logger/basic_logger.js +++ b/lib/logger/basic_logger.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; var getArgs = function(args) { return args.length >= 1 ? [].slice.call(args, 0) : []; @@ -6,10 +6,10 @@ var getArgs = function(args) { var logString = function(type) { var time = new Date().toISOString(), - type = String(type).toUpperCase(), - padded = String(" " + type).slice(-5); + upcase = String(type).toUpperCase(), + padded = String(" " + upcase).slice(-5); - return type[0] + ", [" + time + "] " + padded + " -- :"; + return upcase[0] + ", [" + time + "] " + padded + " -- :"; }; // The BasicLogger logs to console.log @@ -17,7 +17,7 @@ var BasicLogger = module.exports = { toString: function() { return "BasicLogger"; }, }; -['debug', 'info', 'warn', 'error', 'fatal'].forEach(function(type) { +["debug", "info", "warn", "error", "fatal"].forEach(function(type) { BasicLogger[type] = function() { var args = getArgs(arguments); return console.log.apply(console, [].concat(logString(type), args)); diff --git a/lib/logger/null_logger.js b/lib/logger/null_logger.js index 756c82b..0daadc8 100644 --- a/lib/logger/null_logger.js +++ b/lib/logger/null_logger.js @@ -1,9 +1,11 @@ +"use strict"; + // The NullLogger is designed for cases where you want absolutely nothing to // print to anywhere. Every proxied method from the Logger returns a noop. var NullLogger = module.exports = { toString: function() { return "NullLogger"; } }; -['debug', 'info', 'warn', 'error', 'fatal'].forEach(function(type) { +["debug", "info", "warn", "error", "fatal"].forEach(function(type) { NullLogger[type] = function() {}; }); diff --git a/lib/registry.js b/lib/registry.js index 0f00d56..f603c02 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -12,21 +12,22 @@ "use strict"; -var Logger = require('./logger'); +var Logger = require("./logger"); // Explicitly these modules here, so Browserify can grab them later -require('./test/loopback'); -require('./test/test-adaptor'); -require('./test/test-driver'); -require('./test/ping'); +require("./test/loopback"); +require("./test/test-adaptor"); +require("./test/test-driver"); +require("./test/ping"); var missingModuleError = function(module) { - var string = "Cannot find the '" + module + "' module.\n"; - string += "This problem might be fixed by installing it with 'npm install " + module + "' and trying again."; + var str = "Cannot find the '" + module + "' module.\n"; + str += "This problem might be fixed by installing it with "; + str +="'npm install " + module + "' and trying again."; - console.log(string); + console.log(str); - process.emit('SIGINT'); + process.emit("SIGINT"); }; var Registry = module.exports = { @@ -86,7 +87,7 @@ var Registry = module.exports = { Logger.debug("Registering module " + name); - ['adaptors', 'drivers', 'dependencies'].forEach(function(field) { + ["adaptors", "drivers", "dependencies"].forEach(function(field) { if (module[field].length) { Logger.debug(" " + field + ":"); module[field].forEach(function(item) { @@ -110,6 +111,6 @@ var Registry = module.exports = { }; // Default drivers/adaptors: -['loopback', 'ping', 'test-adaptor', 'test-driver'].forEach(function(module) { - Registry.register('./test/' + module); +["loopback", "ping", "test-adaptor", "test-driver"].forEach(function(module) { + Registry.register("./test/" + module); }); diff --git a/lib/robot.js b/lib/robot.js index c99c347..7c79671 100644 --- a/lib/robot.js +++ b/lib/robot.js @@ -6,25 +6,16 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; -var Connection = require("./connection"), - Device = require("./device"), - Logger = require('./logger'), - Utils = require('./utils'), - Config = require('./config'); +var initConnection = require("./connection"), + initDevice = require("./device"), + Logger = require("./logger"), + Utils = require("./utils"), + Config = require("./config"); var Async = require("async"), - EventEmitter = require('events').EventEmitter; - -var missingModuleError = function(module) { - var string = "Cannot find the '" + module + "' module. "; - string += "Please install it with 'npm install " + module + "' and try again."; - - console.log(string); - - process.emit('SIGINT'); -}; + EventEmitter = require("events").EventEmitter; // Public: Creates a new Robot // @@ -40,10 +31,10 @@ var missingModuleError = function(module) { // name: "Spherobot!" // // connection: -// name: 'sphero', adaptor: 'sphero', port: '/dev/rfcomm0' +// name: "sphero", adaptor: "sphero", port: "/dev/rfcomm0" // // device: -// name: 'sphero', driver: 'sphero' +// name: "sphero", driver: "sphero" // // work: (me) -> // Utils.every 1.second(), -> @@ -90,7 +81,7 @@ var Robot = module.exports = function Robot(opts) { this[n] = opt; - if (typeof opt === 'function' && opts.commands == null) { + if (typeof opt === "function" && opts.commands == null) { this.commands[n] = opt; } } @@ -98,14 +89,14 @@ var Robot = module.exports = function Robot(opts) { if (opts.commands) { var cmds = opts.commands; - if (typeof cmds === 'object') { + if (typeof cmds === "object") { this.commands = cmds; } - if (typeof cmds === 'function') { + if (typeof cmds === "function") { var result = cmds.call(this, this); - if (typeof result === 'object' && !Array.isArray(result)) { + if (typeof result === "object" && !Array.isArray(result)) { this.commands = result; } else { throw new Error("#commands function must return an object"); @@ -113,10 +104,10 @@ var Robot = module.exports = function Robot(opts) { } } - var mode = Utils.fetch(Config, 'mode', 'manual'); + var mode = Utils.fetch(Config, "mode", "manual"); - if (mode === 'auto') { - // run on the next tick, to allow for 'work' event handlers to be set up + if (mode === "auto") { + // run on the next tick, to allow for "work" event handlers to be set up setTimeout(this.start, 0); } }; @@ -159,12 +150,16 @@ Robot.prototype.connection = function(name, conn) { conn.name = name; if (this.connections[conn.name]) { - var original = conn.name; + var original = conn.name, + str; conn.name = Utils.makeUnique(original, Object.keys(this.connections)); - Logger.warn("Connection names must be unique. Renaming '" + original + "' to '" + conn.name + "'"); + + str = "Connection names must be unique."; + str += "Renaming '" + original + "' to '" + conn.name + "'"; + Logger.warn(str); } - this.connections[conn.name] = Connection(conn); + this.connections[conn.name] = initConnection(conn); return this; }; @@ -177,6 +172,8 @@ Robot.prototype.connection = function(name, conn) { Robot.prototype.initConnections = function(opts) { Logger.info("Initializing connections."); + var str; + var isArray = Array.isArray; if (opts.connection == null && opts.connections == null) { @@ -184,19 +181,27 @@ Robot.prototype.initConnections = function(opts) { } if (opts.connection) { - Logger.warn("Specifying a single connection with the 'connection' key is deprecated, and will be removed in 1.0.0."); + str = "Specifying a single connection with the 'connection' key "; + str += "is deprecated. It will be removed in 1.0.0."; + + Logger.warn(str); + this.connection(opts.connection.name, opts.connection); return this.connections; } - if (typeof opts.connections == 'object' && !isArray(opts.connections)) { + if (typeof opts.connections === "object" && !isArray(opts.connections)) { for (var name in opts.connections) { this.connection(name, opts.connections[name]); } } if (isArray(opts.connections)) { - Logger.warn("Specifying connections as an array is deprecated, and will be removed in 1.0.0."); + str = "Specifying connections as an array is deprecated. "; + str += "It will be removed in 1.0.0."; + + Logger.warn(str); + opts.connections.forEach(function(conn) { this.connection(conn.name, conn); }, this); @@ -206,20 +211,25 @@ Robot.prototype.initConnections = function(opts) { }; Robot.prototype.device = function(name, device) { + var str; + device.robot = this; device.name = name; if (this.devices[device.name]) { var original = device.name; device.name = Utils.makeUnique(original, Object.keys(this.devices)); - Logger.warn("Device names must be unique. Renaming '" + original + "' to '" + device.name + "'"); + + str = "Device names must be unique."; + str += "Renaming '" + original + "' to '" + device.name + "'"; + Logger.warn(str); } - if (typeof device.connection === 'string') { + if (typeof device.connection === "string") { if (this.connections[device.connection] == null) { - var str = "No connection found with the name " + device.connection + ".\n"; + str = "No connection found with the name " + device.connection + ".\n"; Logger.fatal(str); - process.emit('SIGINT'); + process.emit("SIGINT"); } device.connection = this.connections[device.connection]; @@ -230,10 +240,10 @@ Robot.prototype.device = function(name, device) { } } - this.devices[device.name] = Device(device); + this.devices[device.name] = initDevice(device); return this; -} +}; // Public: Initializes all devices for the robot // @@ -241,9 +251,10 @@ Robot.prototype.device = function(name, device) { // // Returns initialized devices Robot.prototype.initDevices = function(opts) { - Logger.info("Initializing devices."); + var isArray = Array.isArray, + str; - var isArray = Array.isArray; + Logger.info("Initializing devices."); if (opts.device == null && opts.devices == null) { return this.devices; @@ -251,23 +262,28 @@ Robot.prototype.initDevices = function(opts) { // check that there are connections to use if (!Object.keys(this.connections).length) { - throw new Error("No connections specified") + throw new Error("No connections specified"); } if (opts.device) { - Logger.warn("Specifying a single device with the 'device' key is deprecated, and will be removed in 1.0.0."); + str = "Specifying a single device with the 'device' key is deprecated. "; + str += "It will be removed in 1.0.0."; + + Logger.warn(str); this.device(opts.device.name, opts.device); return this.devices; } - if (typeof opts.devices == 'object' && !isArray(opts.devices)) { + if (typeof opts.devices === "object" && !isArray(opts.devices)) { for (var name in opts.devices) { this.device(name, opts.devices[name]); } } if (isArray(opts.devices)) { - Logger.warn("Specifying devices as an array is deprecated, and will be removed in 1.0.0."); + str = "Specifying devices as an array is deprecated."; + str += "It will be removed in 1.0.0."; + Logger.warn(str); opts.devices.forEach(function(device) { this.device(device.name, device); }, this); @@ -286,13 +302,13 @@ Robot.prototype.start = function(callback) { return this; } - var mode = Utils.fetch(Config, 'workMode', 'async'); + var mode = Utils.fetch(Config, "workMode", "async"); Async.series([ this.startConnections, this.startDevices, function(callback) { - if (mode === 'async') { + if (mode === "async") { this.startWork(); } callback(null, true); @@ -301,12 +317,12 @@ Robot.prototype.start = function(callback) { if (!!err) { Logger.fatal("An error occured while trying to start the robot:"); Logger.fatal(err); - if (typeof(this.error) === 'function') { + if (typeof(this.error) === "function") { this.error.call(this, err); } - this.emit('error', err); + this.emit("error", err); } - if (typeof(callback) === 'function') { + if (typeof(callback) === "function") { callback(err, results); } }.bind(this)); @@ -314,20 +330,20 @@ Robot.prototype.start = function(callback) { return this; }; -// Public: Starts the Robot's work and triggers a callback +// Public: Starts the Robot"s work and triggers a callback // // callback - callback function to be triggered // // Returns nothing Robot.prototype.startWork = function() { - Logger.info('Working.'); + Logger.info("Working."); - this.emit('ready', this); + this.emit("ready", this); this.work.call(this, this); this.running = true; }; -// Public: Starts the Robot's connections and triggers a callback +// Public: Starts the Robot"s connections and triggers a callback // // callback - callback function to be triggered // @@ -355,7 +371,7 @@ Robot.prototype.startConnections = function(callback) { return Async.parallel(starters, callback); }; -// Public: Starts the Robot's devices and triggers a callback +// Public: Starts the Robot"s devices and triggers a callback // // callback - callback function to be triggered // diff --git a/lib/test/loopback.js b/lib/test/loopback.js index 9a39d0d..54874fa 100644 --- a/lib/test/loopback.js +++ b/lib/test/loopback.js @@ -8,8 +8,8 @@ "use strict"; -var Adaptor = require('../adaptor'), - Utils = require('../utils'); +var Adaptor = require("../adaptor"), + Utils = require("../utils"); var Loopback; @@ -27,5 +27,5 @@ Loopback.prototype.disconnect = function(callback) { callback(); }; -Loopback.adaptors = ['loopback']; +Loopback.adaptors = ["loopback"]; Loopback.adaptor = function(opts) { return new Loopback(opts); }; diff --git a/lib/test/ping.js b/lib/test/ping.js index c9348a9..d88cd70 100644 --- a/lib/test/ping.js +++ b/lib/test/ping.js @@ -6,10 +6,10 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; -var Driver = require('../driver'), - Utils = require('../utils'); +var Driver = require("../driver"), + Utils = require("../utils"); var Ping = module.exports = function Ping() { Ping.__super__.constructor.apply(this, arguments); @@ -22,7 +22,7 @@ var Ping = module.exports = function Ping() { Utils.subclass(Ping, Driver); Ping.prototype.ping = function() { - this.emit('ping', 'ping'); + this.emit("ping", "ping"); return "pong"; }; @@ -34,5 +34,5 @@ Ping.prototype.halt = function(callback) { callback(); }; -Ping.drivers = ['ping']; +Ping.drivers = ["ping"]; Ping.driver = function(opts) { return new Ping(opts); }; diff --git a/lib/test/test-adaptor.js b/lib/test/test-adaptor.js index 3918a6d..490e6e7 100644 --- a/lib/test/test-adaptor.js +++ b/lib/test/test-adaptor.js @@ -8,8 +8,8 @@ "use strict"; -var Adaptor = require('../adaptor'), - Utils = require('../utils'); +var Adaptor = require("../adaptor"), + Utils = require("../utils"); var TestAdaptor; @@ -19,5 +19,5 @@ module.exports = TestAdaptor = function TestAdaptor() { Utils.subclass(TestAdaptor, Adaptor); -TestAdaptor.adaptors = ['test']; +TestAdaptor.adaptors = ["test"]; TestAdaptor.adaptor = function(opts) { return new TestAdaptor(opts); }; diff --git a/lib/test/test-driver.js b/lib/test/test-driver.js index 640c5ae..57dfefb 100644 --- a/lib/test/test-driver.js +++ b/lib/test/test-driver.js @@ -6,10 +6,10 @@ * Licensed under the Apache 2.0 license. */ -'use strict'; +"use strict"; -var Driver = require('../driver'), - Utils = require('../utils'); +var Driver = require("../driver"), + Utils = require("../utils"); var TestDriver; @@ -19,5 +19,5 @@ module.exports = TestDriver = function TestDriver() { Utils.subclass(TestDriver, Driver); -TestDriver.drivers = ['test']; +TestDriver.drivers = ["test"]; TestDriver.driver = function(opts) { return new TestDriver(opts); }; diff --git a/lib/utils.js b/lib/utils.js index 9b5942d..61ada99 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -6,248 +6,12 @@ * Licensed under the Apache 2.0 license. */ -var Utils = module.exports = { - // Public: Alias to setInterval, combined with Number monkeypatches below to - // create an artoo-like syntax. - // - // interval - interval to run action on - // action - action to perform at interval - // - // Examples - // - // every((5).seconds(), function() { - // console.log('Hello world (and again in 5 seconds)!'); - // }); - // - // Returns an interval - every: function every(interval, action) { - return setInterval(action, interval); - }, - - // Public: Alias to setTimeout, combined with Number monkeypatches below to - // create an artoo-like syntax. - // - // interval - interval to run action on - // action - action to perform at interval - // - // Examples - // - // after((10).seconds(), function() { - // console.log('Hello world from ten seconds ago!'); - // }); - // - // Returns an interval - after: function after(delay, action) { - return setTimeout(action, delay); - }, - - // Public: Alias to the `every` function, but passing 0 - // Examples - // - // constantly(function() { - // console.log('hello world (and again and again)!'); - // }); - // - // Returns an interval - constantly: function constantly(action) { - return every(0, action); - }, - - // Public: Sleep - do nothing for some duration of time. - // - // ms - number of ms to sleep for - // - // Examples - // - // sleep((1).second()); - // - // Returns a function - sleep: function sleep(ms) { - var start = Date.now(); - - while(Date.now() < start + ms) { - var i = 0; - } - }, - - // Public: Function to use for class inheritance. Copy of a CoffeeScript helper - // function. - // - // Example - // - // var Sphero = function Sphero() {}; - // - // subclass(Sphero, ParentClass); - // - // // Sphero is now a subclass of Parent, and can access parent methods - // // through Sphero.__super__ - // - // Returns subclass - subclass: function subclass(child, parent) { - var ctor = function() { - this.constructor = child; - }; - - for (var key in parent) { - if (Object.hasOwnProperty.call(parent, key)) { - child[key] = parent[key]; - } - } - - ctor.prototype = parent.prototype; - child.prototype = new ctor(); - child.__super__ = parent.prototype; - return child; - }, - - proxyFunctions: function proxyFunctions(source, target) { - for (var opt in source) { - if (!target[opt] && typeof source[opt] === 'function') { - target[opt] = source[opt].bind(source); - } - } - }, - - // Public: Proxies a list of methods from one object to another. It will not - // overwrite existing methods unless told to. - // - // methods - array of functions to proxy - // target - object to proxy the functions to - // base - (optional) object that proxied functions will be declared on. Defaults - // to this - // force - (optional) boolean - whether or not to force method assignment - // - // Returns base - proxyFunctionsToObject: function proxyFunctionsToObject(methods, target, base, force) { - if (base == null) { - base = this; - } - - if (force == null) { - force = false; - } - - var proxy = function(method) { - return base[method] = function() { - return target[method].apply(target, arguments); - }; - }; - - for (var i = 0; i < methods.length; i++) { - var method = methods[i]; - - if (!force && typeof(base[method]) === 'function') { - continue; - } - - proxy(method); - } - return base; - }, - - // Public: Analogue to Ruby's Hash#fetch method for looking up object - // properties. - // - // obj - object to do property lookup on - // property - string property name to attempt to look up - // fallback - either: - // - a fallback value to return if `property` can't be found - // - a function to be executed if `property` can't be found. The function - // will be passed `property` as an argument. - // - // Examples - // - // var object = { property: "hello world" }; - // fetch(object, "property"); - // //=> "hello world" - // - // fetch(object, "notaproperty", "default value"); - // //=> "default value" - // - // var notFound = function(prop) { return prop + " not found!" }; - // fetch(object, "notaproperty", notFound) - // // "notaproperty not found!" - // - // var badFallback = function(prop) { prop + " not found!" }; - // fetch(object, "notaproperty", badFallback) - // // Error: no return value from provided callback function - // - // fetch(object, "notaproperty"); - // // Error: key not found: "notaproperty" - // - // Returns the value of obj[property], a fallback value, or the results of - // running 'fallback'. If the property isn't found, and 'fallback' hasn't been - // provided, will throw an error. - fetch: function(obj, property, fallback) { - if (obj.hasOwnProperty(property)) { - return obj[property]; - } - - if (fallback === void 0) { - throw new Error('key not found: "' + property + '"'); - } - - if (typeof(fallback) === 'function') { - var value = fallback(property); - - if (value === void 0) { - throw new Error('no return value from provided fallback function'); - } - - return value; - } - - return fallback; - }, - - // Public: Given a name, and an array of existing names, returns a unique - // name. - // - // name - name that's colliding with existing names - // arr - array of existing names - // - // Returns the new name as a string - 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; - } - } - }, - - // Public: Adds necessary utils to global namespace, along with base class - // extensions. - // - // Examples - // - // Number.prototype.seconds // undefined - // after // undefined - // - // Utils.bootstrap(); - // - // Number.prototype.seconds // [function] - // (after === Utils.after) // true - // - // Returns Cylon.Utils - bootstrap: function bootstrap() { - global.every = this.every; - global.after = this.after; - global.constantly = this.constantly; - - addCoreExtensions(); - - return this; - } -}; +"use strict"; var addCoreExtensions = function addCoreExtensions() { + var max = Math.max, + min = Math.min; + // Public: Monkey-patches Number to have Rails-like //seconds() function. // Warning, due to the way the Javascript parser works, applying functions on // numbers is kind of weird. See examples for details. @@ -294,7 +58,7 @@ var addCoreExtensions = function addCoreExtensions() { // // Returns an integer representing the scaled value Number.prototype.fromScale = function(start, end) { - var val = (this - Math.min(start, end)) / (Math.max(start, end) - Math.min(start, end)); + var val = (this - min(start, end)) / (max(start, end) - min(start, end)); if (val > 1) { return 1; @@ -319,7 +83,7 @@ var addCoreExtensions = function addCoreExtensions() { // // Returns an integer representing the scaled value Number.prototype.toScale = function(start, end) { - var i = this * (Math.max(start, end) - Math.min(start, end)) + Math.min(start, end); + var i = this * (max(start, end) - min(start, end)) + min(start, end); if (i < start) { return start; @@ -333,4 +97,247 @@ var addCoreExtensions = function addCoreExtensions() { }; }; + +var Utils = module.exports = { + // Public: Alias to setInterval, combined with Number monkeypatches below to + // create an artoo-like syntax. + // + // interval - interval to run action on + // action - action to perform at interval + // + // Examples + // + // every((5).seconds(), function() { + // console.log("Hello world (and again in 5 seconds)!"); + // }); + // + // Returns an interval + every: function every(interval, action) { + return setInterval(action, interval); + }, + + // Public: Alias to setTimeout, combined with Number monkeypatches below to + // create an artoo-like syntax. + // + // interval - interval to run action on + // action - action to perform at interval + // + // Examples + // + // after((10).seconds(), function() { + // console.log("Hello world from ten seconds ago!"); + // }); + // + // Returns an interval + after: function after(delay, action) { + return setTimeout(action, delay); + }, + + // Public: Alias to the `every` function, but passing 0 + // Examples + // + // constantly(function() { + // console.log("hello world (and again and again)!"); + // }); + // + // Returns an interval + constantly: function constantly(action) { + return every(0, action); + }, + + // Public: Sleep - do nothing for some duration of time. + // + // ms - number of ms to sleep for + // + // Examples + // + // sleep((1).second()); + // + // Returns a function + sleep: function sleep(ms) { + var start = Date.now(), + i; + + while(Date.now() < start + ms) { + i = 0; + } + }, + + // Public: Function to use for class inheritance. + // Based on CoffeeScript's implementation. + // + // Example + // + // var Sphero = function Sphero() {}; + // + // subclass(Sphero, ParentClass); + // + // // Sphero is now a subclass of Parent, and can access parent methods + // // through Sphero.__super__ + // + // Returns subclass + subclass: function subclass(child, parent) { + var Ctor = function() { + this.constructor = child; + }; + + for (var key in parent) { + if (Object.hasOwnProperty.call(parent, key)) { + child[key] = parent[key]; + } + } + + Ctor.prototype = parent.prototype; + child.prototype = new Ctor(); + child.__super__ = parent.prototype; + return child; + }, + + proxyFunctions: function proxyFunctions(source, target) { + for (var opt in source) { + if (!target[opt] && typeof source[opt] === "function") { + target[opt] = source[opt].bind(source); + } + } + }, + + // Public: Proxies a list of methods from one object to another. It will not + // overwrite existing methods unless told to. + // + // methods - array of functions to proxy + // target - object to proxy the functions to + // base - (optional) object that proxied functions will be declared on. + // Defaults to 'this'. + // force - (optional) boolean - whether or not to force method assignment + // + // Returns base + proxyFunctionsToObject: function(methods, target, base, force) { + if (base == null) { + base = this; + } + + if (force == null) { + force = false; + } + + var proxy = function(method) { + return base[method] = function() { + return target[method].apply(target, arguments); + }; + }; + + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + + if (!force && typeof(base[method]) === "function") { + continue; + } + + proxy(method); + } + return base; + }, + + // Public: Analogue to Ruby"s Hash#fetch method for looking up object + // properties. + // + // obj - object to do property lookup on + // property - string property name to attempt to look up + // fallback - either: + // - a fallback value to return if `property` can"t be found + // - a function to be executed if `property` can"t be found. The function + // will be passed `property` as an argument. + // + // Examples + // + // var object = { property: "hello world" }; + // fetch(object, "property"); + // //=> "hello world" + // + // fetch(object, "notaproperty", "default value"); + // //=> "default value" + // + // var notFound = function(prop) { return prop + " not found!" }; + // fetch(object, "notaproperty", notFound) + // // "notaproperty not found!" + // + // var badFallback = function(prop) { prop + " not found!" }; + // fetch(object, "notaproperty", badFallback) + // // Error: no return value from provided callback function + // + // fetch(object, "notaproperty"); + // // Error: key not found: "notaproperty" + // + // Returns the value of obj[property], a fallback value, or the results of + // running "fallback". If the property isn"t found, and "fallback" hasn"t been + // provided, will throw an error. + fetch: function(obj, property, fallback) { + if (obj.hasOwnProperty(property)) { + return obj[property]; + } + + if (fallback === void 0) { + throw new Error("key not found: \"" + property + "\""); + } + + if (typeof(fallback) === "function") { + var value = fallback(property); + + if (value === void 0) { + throw new Error("no return value from provided fallback function"); + } + + return value; + } + + return fallback; + }, + + // Public: Given a name, and an array of existing names, returns a unique + // name. + // + // name - name that"s colliding with existing names + // arr - array of existing names + // + // Returns the new name as a string + 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; + } + } + }, + + // Public: Adds necessary utils to global namespace, along with base class + // extensions. + // + // Examples + // + // Number.prototype.seconds // undefined + // after // undefined + // + // Utils.bootstrap(); + // + // Number.prototype.seconds // [function] + // (after === Utils.after) // true + // + // Returns Cylon.Utils + bootstrap: function bootstrap() { + global.every = this.every; + global.after = this.after; + global.constantly = this.constantly; + + addCoreExtensions(); + + return this; + } +}; + Utils.bootstrap();