<commit_before>// ==UserScript==
// @name        ArcSoft Jenkins
// @version     5
// @author      maxint <NOT_SPAM_lnychina@gmail.com>
// @namespace   http://maxint.github.io
// @description Save forms etc.
// @include     http://abs02/jenkins/job/*/build*
// @updateURL   https://raw.githubusercontent.com/maxint/userjs/master/ArcSoft_Jenkins/ArcSoft_Jenkins.user.js
// @downloadURL https://raw.githubusercontent.com/maxint/userjs/master/ArcSoft_Jenkins/ArcSoft_Jenkins.user.js
// @grant       none
// @Note
// ==/UserScript==
//

// a function that loads jQuery and calls a callback function when jQuery has finished loading
; (function (callback, safe) {
	"use strict";
	var callback2 = function (jQuery_old, jQuery) {
		//Firefox supports
		console.log('Using jquery ' + jQuery().jquery);
		console.log('Runing custom script');
		callback(jQuery_old, jQuery, typeof(unsafeWindow) === "undefined" ? window : unsafeWindow);
	};
	if (typeof(jQuery) === "undefined" || jQuery.jquery !== '2.1.4') {
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src = "//apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js";
		if (safe) {
			var cb = document.createElement("script");
			cb.type = "text/javascript";
			cb.textContent = "var jq = jQuery.noConflict();(" + callback.toString() + ")($, jq, window);";
			script.addEventListener('load', function() {
				document.head.appendChild(cb);
			});
		} else {
			script.addEventListener('load', function () {
				var jq = jQuery.noConflict();
				callback2($, jq);
			});
		}
		document.head.appendChild(script);
	} else {
		setTimeout(function () {
			callback2(jQuery, jQuery);
		}, 30);
	}
})(function (jq, $, window) {
	"use strict";
	// helper functions
	// local storage
	var IStorage = function (prefix) {
		var db = window.localStorage;
		var pref = prefix + '.';
		var addpref = function (key) { return pref + key; };
		var nameRegExp = new RegExp('^' + prefix + '\\.' + '(.+)$');
		this.prefix = prefix;
		this.get = function (key, def) {
			var val = db.getItem(addpref(key));
			if (val !== null) {
				return val;
			} else {
				return def || null;
			}
		};
		this.set = function (key, val) {
			db.setItem(addpref(key), val);
		};
		this.remove = function (key) {
			db.removeItem(addpref(key));
		};
		this.clear = function () {
			var names = this.getNames();
			console.log('[W] Remove all objects in "' + this.prefix + '" storage: ' + names);
			for (var i = 0; i < names.length; ++i) {
				this.remove(names[i]);
			}
		};
		this.flush = function () {
			db.clear();
		};
		this.getObject = function (key, def) {
			var val = this.get(key);
			return val ? JSON.parse(val) : (def || null);
		};
		this.setObject = function (key, val) {
			this.set(key, JSON.stringify(val));
		};
		this.getNames = function () {
			var names = [];
			var n = db.length;
			//console.log(nameRegExp);
			for (var i = 0; i < n; ++i) {
				var m = nameRegExp.exec(db.key(i));
				if (m === null || m.length === 1) {
					//console.log(' ~ ' + db.key(i) + '|');
					continue;
				}
				names.push(m[1]);
			}
			return names;
		};
		this.copyFrom = function (istore) {
			var names = this.getNames();
			console.log('[I] Object names in "' + this.prefix + '" storage: ' + names);
			if (names.length > 0) return;
			names = istore.getNames();
			console.log('[W] Copy data from "' + istore.prefix + '" storage: ' + names);
			for (var i=0; i < names.length; ++i) {
				var n = names[i];
				var v = istore.get(n);
				console.log('[W] - ' + n + ': ' + v);
				this.set(n, v);
			}
			return true;
		};
	};

	// assert function
	function assert(condition, message) {
		if (!condition) {
			throw message || "Assertion failed";
		}
	}
	// fill input values
	function fillValues(key_id_pairs, istore) {
		console.log('[I] Fill field values');
		for (var key in key_id_pairs) {
			if (key_id_pairs.hasOwnProperty(key)) {
				var elem = key_id_pairs[key];
				var elemType = elem.type;
				if (elemType == undefined)
					console.log('[W] Undefined element type of field:' + elem);
				// debug
				console.log('[D] - ' + key + ' [' + elemType + ']: ' + istore.get(key, ''));
				var newValue = istore.get(key);
				if (newValue == null)
					continue
				if (elemType === 'checkbox' || elemType === 'radio') {
					elem.checked = newValue == 'true';
				} else if (elemType === 'select-one' || elemType === 'select-multiple') {
					$(elem).val(newValue);
				} else {
					$(elem).val(newValue);
				}
			}
		}
	}
	// save input values
	function storeValues(key_id_pairs, istore) {
		console.log('[I] Save field values');
		for (var key in key_id_pairs) {
			if (key_id_pairs.hasOwnProperty(key)) {
				var elem = key_id_pairs[key];
				if (elem.type === 'checkbox' || elem.type === 'radio') {
					istore.set(key, elem.checked);
				} else {
					istore.set(key, elem.value);
				}
			}
		}
	}
	// operate w.r.t. sub path
	var subpath = window.location.pathname;
	console.log('Subpath: ' + subpath);
	if (/jenkins\/job\/[\w_]*\/build\b/.test(subpath)) {
		var projName = $('h1').get(0).innerHTML;
		projName = projName.substr(8, projName.length-8);
		var istore = new IStorage('project/build/' + projName);
		var pairs = {};
		$("table.parameters :input[name$='value']").each(function() {
			var name = this.name;
			if (name.endsWith('.value')) {
				name = name.substr(0, name.length-6) + '.' + this.value;
			} else {
				name = $(this).prev().val();
			}
			//console.log(this); console.log(name);
			pairs[name] = this;
		});
    // save / restore
		fillValues(pairs, istore);
		var saveWhenUnload = true;
		$(window).unload(function () {
			console.log('[I] window unload');
			if (saveWhenUnload) {
				storeValues(pairs, istore);
			}
		});
		// add reset button
		$("table.parameters tbody:last input:last-child").after(
				'' +
				'' +
				'<button title="Reset all saved data!" type="button">Reset</button>' +
				'');
		$('button:last-child').click(function() {
			saveWhenUnload = false;
			istore.clear();
			window.location.reload();
		});
	}
}, true);

// vim: et ts=2 sts=2 sw=2
<commit_msg>Fix bug in chrome<commit_after>// ==UserScript==
// @name        ArcSoft Jenkins
// @version     6
// @author      maxint <NOT_SPAM_lnychina@gmail.com>
// @namespace   http://maxint.github.io
// @description Save forms etc.
// @include     http://abs02/jenkins/job/*/build*
// @updateURL   https://raw.githubusercontent.com/maxint/userjs/master/ArcSoft_Jenkins/ArcSoft_Jenkins.user.js
// @downloadURL https://raw.githubusercontent.com/maxint/userjs/master/ArcSoft_Jenkins/ArcSoft_Jenkins.user.js
// @grant       none
// @Note
// ==/UserScript==
//

// a function that loads jQuery and calls a callback function when jQuery has finished loading
; (function (callback, safe) {
	"use strict";
	var callback2 = function (jQuery_old, jQuery) {
		//Firefox supports
		console.log('[I] Using jquery ' + jQuery().jquery);
		console.log('[I] Runing custom script');
		setTimeout(function () {
			callback(jQuery_old, jQuery, typeof(unsafeWindow) === "undefined" ? window : unsafeWindow);
		}, 30);
	};
	if (typeof(jQuery) === "undefined" || jQuery.jquery !== '2.1.4') {
		console.log('[I] Load jquery from CDN');
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src = "//apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js";
		if (safe) {
			var cb = document.createElement("script");
			cb.type = "text/javascript";
			cb.textContent = "var jq = jQuery.noConflict();(" + callback.toString() + ")($, jq, window);";
			script.addEventListener('load', function() {
				document.head.appendChild(cb);
			});
		} else {
			script.addEventListener('load', function () {
				var jq = jQuery.noConflict();
				callback2($, jq);
			});
		}
		document.head.appendChild(script);
	} else {
		callback2(jQuery, jQuery);
	}
})(function (jq, $, window) {
	"use strict";
	// helper functions
	// local storage
	var IStorage = function (prefix) {
		var db = window.localStorage;
		var pref = prefix + '.';
		var addpref = function (key) { return pref + key; };
		var nameRegExp = new RegExp('^' + prefix + '\\.' + '(.+)$');
		this.prefix = prefix;
		this.get = function (key, def) {
			var val = db.getItem(addpref(key));
			if (val !== null) {
				return val;
			} else {
				return def || null;
			}
		};
		this.set = function (key, val) {
			db.setItem(addpref(key), val);
		};
		this.remove = function (key) {
			db.removeItem(addpref(key));
		};
		this.clear = function () {
			var names = this.getNames();
			console.log('[W] Remove all objects in "' + this.prefix + '" storage: ' + names);
			for (var i = 0; i < names.length; ++i) {
				this.remove(names[i]);
			}
		};
		this.flush = function () {
			db.clear();
		};
		this.getObject = function (key, def) {
			var val = this.get(key);
			return val ? JSON.parse(val) : (def || null);
		};
		this.setObject = function (key, val) {
			this.set(key, JSON.stringify(val));
		};
		this.getNames = function () {
			var names = [];
			var n = db.length;
			//console.log(nameRegExp);
			for (var i = 0; i < n; ++i) {
				var m = nameRegExp.exec(db.key(i));
				if (m === null || m.length === 1) {
					//console.log(' ~ ' + db.key(i) + '|');
					continue;
				}
				names.push(m[1]);
			}
			return names;
		};
		this.copyFrom = function (istore) {
			var names = this.getNames();
			console.log('[I] Object names in "' + this.prefix + '" storage: ' + names);
			if (names.length > 0) return;
			names = istore.getNames();
			console.log('[W] Copy data from "' + istore.prefix + '" storage: ' + names);
			for (var i=0; i < names.length; ++i) {
				var n = names[i];
				var v = istore.get(n);
				console.log('[W] - ' + n + ': ' + v);
				this.set(n, v);
			}
			return true;
		};
	};

	// assert function
	function assert(condition, message) {
		if (!condition) {
			throw message || "Assertion failed";
		}
	}
	// fill input values
	function fillValues(key_id_pairs, istore) {
		console.log('[I] Fill field values');
		for (var key in key_id_pairs) {
			if (key_id_pairs.hasOwnProperty(key)) {
				var elem = key_id_pairs[key];
				var elemType = elem.type;
				if (elemType === undefined)
					console.log('[W] Undefined element type of field:' + elem);
				// debug
				console.log('[D] - ' + key + ' [' + elemType + ']: ' + istore.get(key, ''));
				var newValue = istore.get(key);
				if (newValue === null)
					continue;
				if (elemType === 'checkbox' || elemType === 'radio') {
					elem.checked = newValue == 'true';
				} else if (elemType === 'select-one' || elemType === 'select-multiple') {
					$(elem).val(newValue);
				} else {
					$(elem).val(newValue);
				}
			}
		}
	}
	// save input values
	function storeValues(key_id_pairs, istore) {
		console.log('[I] Save field values');
		for (var key in key_id_pairs) {
			if (key_id_pairs.hasOwnProperty(key)) {
				var elem = key_id_pairs[key];
				if (elem.type === 'checkbox' || elem.type === 'radio') {
					istore.set(key, elem.checked);
				} else {
					istore.set(key, elem.value);
				}
			}
		}
	}
	// operate w.r.t. sub path
	var subpath = window.location.pathname;
	console.log('[I] Subpath: ' + subpath);
	if (/jenkins\/job\/[\w_]*\/build\b/.test(subpath)) {
		var projName = $('h1').get(0).innerHTML;
		projName = projName.substr(8, projName.length-8);
		var istore = new IStorage('project/build/' + projName);
		var pairs = {};
		$("table.parameters :input[name$='value']").each(function() {
			var name = this.name;
			if (name.endsWith('.value')) {
				name = name.substr(0, name.length-6) + '.' + this.value;
			} else {
				name = $(this).prev().val();
			}
			//console.log(this); console.log(name);
			pairs[name] = this;
		});
		// save / restore
		fillValues(pairs, istore);
		var saveWhenUnload = true;
		$(window).unload(function () {
			console.log('[I] window unload');
			if (saveWhenUnload) {
				storeValues(pairs, istore);
			}
		});
		// add reset button
		$("table.parameters tbody:last tr td").append(
				'' +
				'' +
				'<button title="Reset all saved data!" type="button" id="reset_btn">Reset</button>' +
				'');
		console.log($('button:last-child'));
		$('button#reset_btn').click(function() {
			saveWhenUnload = false;
			istore.clear();
			window.location.reload();
		});
	}
}, false);

// vim: noet ts=2 sts=2 sw=2
<|endoftext|>


<commit_before>/* @flow */
'use strict'

const objectMerge = require('object-merge')

const configHelper = require('./utils/config-helper')

// const CLIENT_CLI_SERVICE_DOMAIN = 'auth-api.blinkm.io'
// const CLIENT_CLI_SERVICE_ORIGIN = `https://${CLIENT_CLI_SERVICE_DOMAIN}`
const getTenantData = require('./getTenantData')

function read(cwd /* : string */) /* : Promise<Object> */ {
  return configHelper
    .read(cwd)
    .then(cfg => (cfg.cdn ? cfg.cdn : {}))
    .then(cfg => {
      // Set defaults for service
      cfg.service = cfg.service || {}
      if (!cfg.service.origin && !!cfg.tenant) {
        cfg.service.origin = getTenantData.getOrigin(cfg.tenant)
      }
      cfg.service.origin = cfg.service.origin || getTenantData.TENANTS.ONEBLINK

      const matches = cfg.service.origin.match(
        /https:\/\/client-cli-service(-test)?\.blinkm\.io/
      )

      if (matches) {
        const subdomains = cfg.service.origin.split('.')
        if (matches[1]) {
          subdomains[0] = `${subdomains[0]}${matches[1] || ''}`
        }
        cfg.service.origin = `https://${subdomains.join('.')}`
      }

      return cfg
    })
}

function write(
  cwd /* : string */,
  options /* : Object */
) /* : Promise<Object> */ {
  if (!options || !options.scope) {
    return Promise.reject(new Error('Options.scope was not defined.'))
  }

  let values = { cdn: { scope: options.scope, region: options.region } }
  return configHelper.write(cwd, config => objectMerge(config, values))
}

function show(cwd /* : string */) /* : Promise<void> */ {
  return read(cwd).then(cfg =>
    cfg.scope ? log(cfg) : console.log('s3 Bucket scope has not been set yet.')
  )
}

function log({ scope, region }) {
  console.log(`s3 Bucket name: ${scope}`)
  console.log(`s3 Bucket region: ${region}`)
}

module.exports = { read, write, show }
<commit_msg>ON-4988 # prettier<commit_after>/* @flow */
'use strict'

const objectMerge = require('object-merge')

const configHelper = require('./utils/config-helper')

// const CLIENT_CLI_SERVICE_DOMAIN = 'auth-api.blinkm.io'
// const CLIENT_CLI_SERVICE_ORIGIN = `https://${CLIENT_CLI_SERVICE_DOMAIN}`
const getTenantData = require('./getTenantData')

function read (cwd /* : string */) /* : Promise<Object> */ {
  return configHelper
    .read(cwd)
    .then(cfg => (cfg.cdn ? cfg.cdn : {}))
    .then(cfg => {
      // Set defaults for service
      cfg.service = cfg.service || {}
      if (!cfg.service.origin && !!cfg.tenant) {
        cfg.service.origin = getTenantData.getOrigin(cfg.tenant)
      }
      cfg.service.origin = cfg.service.origin || getTenantData.TENANTS.ONEBLINK

      const matches = cfg.service.origin.match(
        /https:\/\/client-cli-service(-test)?\.blinkm\.io/
      )

      if (matches) {
        const subdomains = cfg.service.origin.split('.')
        if (matches[1]) {
          subdomains[0] = `${subdomains[0]}${matches[1] || ''}`
        }
        cfg.service.origin = `https://${subdomains.join('.')}`
      }

      return cfg
    })
}

function write (
  cwd /* : string */,
  options /* : Object */
) /* : Promise<Object> */ {
  if (!options || !options.scope) {
    return Promise.reject(new Error('Options.scope was not defined.'))
  }

  let values = { cdn: { scope: options.scope, region: options.region } }
  return configHelper.write(cwd, config => objectMerge(config, values))
}

function show (cwd /* : string */) /* : Promise<void> */ {
  return read(cwd).then(cfg =>
    cfg.scope ? log(cfg) : console.log('s3 Bucket scope has not been set yet.')
  )
}

function log ({ scope, region }) {
  console.log(`s3 Bucket name: ${scope}`)
  console.log(`s3 Bucket region: ${region}`)
}

module.exports = { read, write, show }
<|endoftext|>


<commit_before>var $ = require('jquery');
function Score (options) {
  this.options = options || {};
  this.type = "text";
  this.width = this.options.width;
  this.height = this.options.height;
  this.x = this.options.x;
  this.y = this.options.y;
}

module.exports = Score;
<commit_msg>added update function to score attributes<commit_after>var $ = require('jquery');
function Score (options) {
  this.options = options || {};
  this.type = "text";
  this.width = this.options.width;
  this.height = this.options.height;
  this.x = this.options.x;
  this.y = this.options.y;
  this.update = function () {
    var ctx = canvas.getContext("2d");
    ctx.font = this.width + " " + this.height;
    ctx.fillStyle = color;
    ctx.fillText(this.text, this.x, this.y);
  };
}

module.exports = Score;
<|endoftext|>


<commit_before>var async = require('async');
var moment = require('moment');
var Rest = require('./rest.js').Rest;
var _ = require('underscore');

function Sensu(config) {
  this.host = config.host;
  this.ssl = config.ssl;
  this.port = config.port;
  this.path = config.path;
  this.method = 'GET';
  this.timeout = config.timeout;
  this.headers = { 'Content-Type': 'application/json' };
  this.rest = new Rest();
  this.events = {};
  this.clients = {};
  this.client = {};
  this.checks = {};
  this.config = config;
}

Sensu.prototype.getRest = function(options, callback){
  this.rest.get(options, this.config, function(err, result){
    if(err){
      console.log("Fatal error, Sensu API responded with: " + err);
      callback("Fatal error while talking with the Sensu API!");
    }
    else {
      callback(null, result);
    }
  });
}

Sensu.prototype.postRest = function(options, data, callback){
  this.rest.post(options, data, this.config, function(err, result){
    if(err){
      console.log("Fatal error, Sensu API responded with: " + err);
      callback(err);
    }
    else {
      callback(null, result);
    }
  });
}

Sensu.prototype.deleteRest = function(options, callback){
  this.rest.delete(options, this.config, function(err){
    if(err){
      console.log("Fatal error, Sensu API responded with: " + err);
      callback(err);
    }
    else {
      callback(null);
    }
  });
}

Sensu.prototype.getClient = function(name, callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/clients/'+name+'/history',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getClients = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/clients',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getEvents = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/events',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getChecks = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/checks',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getStashes = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/stashes',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);

}

Sensu.prototype.postStash = function(data, callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/stashes',
    method: 'POST',
    timeout: this.timeout,
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': data.length
    }
  }
  this.postRest(options, data, callback);
}

Sensu.prototype.deleteStash = function(data, callback){
  var obj = JSON.parse(data)
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/stashes/'+obj.path,
    method: 'DELETE',
    timeout: this.timeout,
    headers: this.headers
  }
  this.deleteRest(options, callback);
}

Sensu.prototype.resolveEvent = function(data, callback){
  var obj = JSON.parse(data)
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: '/resolve',
    method: 'POST',
    timeout: this.timeout,
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': data.length
    }
  }
  this.postRest(options, data, callback);
}

Sensu.prototype.getTimestamp = function(resultSet, unit, attribute, callback){
  async.each(resultSet, function(item, next){
    if(item[unit] == 0){
      item[attribute] = "Never";
    }
    else {
      var timestamp = (_.isUndefined(item.content)) ? new Date(item[unit]*1000) : new Date(item.content[unit]*1000);
      item[attribute] = moment(timestamp).format('YYYY[-]MM[-]DD HH[:]mm');
    }
    next();
  }, function(err){
    callback();
  });
}

Sensu.prototype.sortClients = function(clients, events, callback){
  async.each(clients, function(client, next){
    var hasEvent = events.filter(function(event){ return event.client == client.name });
    if (hasEvent.length > 0){
      client.events = hasEvent;
      var isCritical = hasEvent.filter(function(event){ return event.status == "2" });
      if (isCritical.length > 0){
        client.status = 2;
      }
      else {
        client.status = 1;
      }
    }
    else {
      client.status = 0;
    }
    next();
  }, function(err){
    clients.sort(function(a,b){
      if(a.status > b.status) return -1;
      if(a.status < b.status) return 1;
      return (a.name.toUpperCase() > b.name.toUpperCase()) ? 1 : (a.name.toUpperCase() < b.name.toUpperCase()) ? -1 : 0;
    });
    callback();
  });
}

Sensu.prototype.sortEvents = function (resultSet, id, status, callback){
  resultSet.sort(function(a,b){
    if(a[status] > b[status]) return -1;
    if(a[status] < b[status]) return 1;
    return (a[id].toUpperCase() > b[id].toUpperCase()) ? 1 : (a[id].toUpperCase() < b[id].toUpperCase()) ? -1 : 0;
  });
  callback();
};

Sensu.prototype.sortByKey = function (resultSet, key, callback){
  resultSet.sort(function(a,b){
    if(a[key] > b[key]) return 1;
    if(a[key] < b[key]) return -1;
    return 0;
  });
  callback();
};

exports.Sensu = Sensu;
<commit_msg>Use provided path for the API<commit_after>var async = require('async');
var moment = require('moment');
var Rest = require('./rest.js').Rest;
var _ = require('underscore');

function Sensu(config) {
  this.host = config.host;
  this.ssl = config.ssl;
  this.port = config.port;
  this.path = config.path;
  this.method = 'GET';
  this.timeout = config.timeout;
  this.headers = { 'Content-Type': 'application/json' };
  this.rest = new Rest();
  this.events = {};
  this.clients = {};
  this.client = {};
  this.checks = {};
  this.config = config;
}

Sensu.prototype.getRest = function(options, callback){
  this.rest.get(options, this.config, function(err, result){
    if(err){
      console.log("Fatal error, Sensu API responded with: " + err);
      callback("Fatal error while talking with the Sensu API!");
    }
    else {
      callback(null, result);
    }
  });
}

Sensu.prototype.postRest = function(options, data, callback){
  this.rest.post(options, data, this.config, function(err, result){
    if(err){
      console.log("Fatal error, Sensu API responded with: " + err);
      callback(err);
    }
    else {
      callback(null, result);
    }
  });
}

Sensu.prototype.deleteRest = function(options, callback){
  this.rest.delete(options, this.config, function(err){
    if(err){
      console.log("Fatal error, Sensu API responded with: " + err);
      callback(err);
    }
    else {
      callback(null);
    }
  });
}

Sensu.prototype.getClient = function(name, callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/clients/'+name+'/history',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getClients = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/clients',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getEvents = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/events',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getChecks = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/checks',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);
}

Sensu.prototype.getStashes = function(callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/stashes',
    method: 'GET',
    timeout: this.timeout,
    headers: this.headers
  }
  this.getRest(options, callback);

}

Sensu.prototype.postStash = function(data, callback){
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/stashes',
    method: 'POST',
    timeout: this.timeout,
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': data.length
    }
  }
  this.postRest(options, data, callback);
}

Sensu.prototype.deleteStash = function(data, callback){
  var obj = JSON.parse(data)
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/stashes/'+obj.path,
    method: 'DELETE',
    timeout: this.timeout,
    headers: this.headers
  }
  this.deleteRest(options, callback);
}

Sensu.prototype.resolveEvent = function(data, callback){
  var obj = JSON.parse(data)
  var options = {
    host: this.host,
    ssl: this.ssl,
    port: this.port,
    path: this.path+'/resolve',
    method: 'POST',
    timeout: this.timeout,
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': data.length
    }
  }
  this.postRest(options, data, callback);
}

Sensu.prototype.getTimestamp = function(resultSet, unit, attribute, callback){
  async.each(resultSet, function(item, next){
    if(item[unit] == 0){
      item[attribute] = "Never";
    }
    else {
      var timestamp = (_.isUndefined(item.content)) ? new Date(item[unit]*1000) : new Date(item.content[unit]*1000);
      item[attribute] = moment(timestamp).format('YYYY[-]MM[-]DD HH[:]mm');
    }
    next();
  }, function(err){
    callback();
  });
}

Sensu.prototype.sortClients = function(clients, events, callback){
  async.each(clients, function(client, next){
    var hasEvent = events.filter(function(event){ return event.client == client.name });
    if (hasEvent.length > 0){
      client.events = hasEvent;
      var isCritical = hasEvent.filter(function(event){ return event.status == "2" });
      if (isCritical.length > 0){
        client.status = 2;
      }
      else {
        client.status = 1;
      }
    }
    else {
      client.status = 0;
    }
    next();
  }, function(err){
    clients.sort(function(a,b){
      if(a.status > b.status) return -1;
      if(a.status < b.status) return 1;
      return (a.name.toUpperCase() > b.name.toUpperCase()) ? 1 : (a.name.toUpperCase() < b.name.toUpperCase()) ? -1 : 0;
    });
    callback();
  });
}

Sensu.prototype.sortEvents = function (resultSet, id, status, callback){
  resultSet.sort(function(a,b){
    if(a[status] > b[status]) return -1;
    if(a[status] < b[status]) return 1;
    return (a[id].toUpperCase() > b[id].toUpperCase()) ? 1 : (a[id].toUpperCase() < b[id].toUpperCase()) ? -1 : 0;
  });
  callback();
};

Sensu.prototype.sortByKey = function (resultSet, key, callback){
  resultSet.sort(function(a,b){
    if(a[key] > b[key]) return 1;
    if(a[key] < b[key]) return -1;
    return 0;
  });
  callback();
};

exports.Sensu = Sensu;
<|endoftext|>


<commit_before>var Board = require("../lib/board.js"),
  events = require("events"),
  util = require("util"),
  __ = require("../lib/fn.js");

// Sensor instance private data
var priv = new WeakMap(),
  SERVOS = [];

/**
 * Servo
 * @constructor
 *
 * @param {Object} opts Options: pin, type, id, range
 */

function Servo(opts) {
  if (!(this instanceof Servo)) {
    return new Servo(opts);
  }

  var history = [];

  // Initialize a Device instance on a Board
  Board.Device.call(
    this, opts = Board.Options(opts)
  );

  // Set the pin to SERVO (OUTPUT) mode
  this.mode = this.firmata.MODES.SERVO;
  this.firmata.pinMode(this.pin, this.mode);

  // When in debug mode, if pin is not a PWM pin, emit an error
  if (opts.debug && !this.board.pins.isServo(this.pin)) {
    Board.Pins.Error({
      pin: this.pin,
      type: "PWM",
      via: "Servo",
    });
  }

  // Servo instance properties
  this.range = opts.range || [0, 180];

  if (opts.pwmRange) {
    this.range = [
      __.scale(opts.pwmRange[0], 600, 2400, 0, 180),
      __.scale(opts.pwmRange[1], 600, 2400, 0, 180)
    ];
  }

  // Allow user defined ids, defaults to system ID
  this.id = opts.id || Board.uid();

  // Invert the value of all servoWrite operations
  // eg. 80 => 100, 90 => 90, 0 => 180
  this.isInverted = opts.isInverted || false;

  // The type of servo determines certain alternate
  // behaviours in the API
  this.type = opts.type || "standard";

  // Specification config
  this.specs = opts.specs || {
    speed: Servo.Continuous.speeds["@5.0V"]
  };

  // Collect all movement history for this servo
  // history = [
  //   {
  //     timestamp: Date.now(),
  //     degrees: degrees
  //   }
  // ];

  // Interval/Sweep pointer
  this.interval = null;

  // Create a non-writable "last" property
  // shortcut to access the last servo movement
  Object.defineProperties(this, {
    history: {
      get: function() {
        return history.slice(-5);
      }
    },
    last: {
      get: function() {
        return history[history.length - 1];
      }
    },
    position: {
      get: function() {
        return history[history.length - 1].degrees;
      }
    }
  });

  // Allow "setup"instructions to come from
  // constructor options properties

  // If "startAt" is defined and center is falsy
  // set servo to min or max degrees
  if (opts.startAt !== undefined && !opts.center) {
    this.move(opts.startAt);
  }

  // If "center" true set servo to 90deg
  if (opts.center) {
    this.center();
  }

  // Push this servo into the private
  // servo instance array.
  SERVOS.push(this);

  priv.set(this, {
    history: history
  });
}

util.inherits(Servo, events.EventEmitter);

/**
 * move Move the servo horn N degrees
 *
 * @param  {Number} degrees   Degrees to turn servo to
 * @return {Object} instance
 */

Servo.prototype.move = function(degrees, time) {

  // Enforce limited range of motion
  degrees = Board.constrain(degrees, this.range[0], this.range[1]);

  if (this.isInverted) {
    degrees = Board.map(
      degrees, this.range[0], this.range[1], this.range[1], this.range[0]
    );
  }

  // If same degrees, do nothing
  if (this.last && this.last.degrees === degrees) {
    return this;
  }

  var last, distance, percent;
  var isReverse = false;
  var history = priv.get(this).history;

  if (typeof time !== "undefined") {

    last = this.last.degrees;
    distance = Math.abs(last - degrees);
    percent = 0;

    if (this.interval) {
      clearImmediate(this.interval);
    }

    if (degrees < last) {
      isReverse = true;
    }

    this.interval = setInterval(function() {
      var delta = ++percent * (distance / 100);

      if (isReverse) {
        delta *= -1;
      }

      this.firmata.servoWrite(this.pin, last + delta);

      history.push({
        timestamp: Date.now(),
        degrees: last + delta
      });

      if (percent === 100) {
        this.emit("move:complete");
        clearInterval(this.interval);
      }
    }.bind(this), time / 100);

  } else {

    this.firmata.servoWrite(this.pin, degrees);

    history.push({
      timestamp: Date.now(),
      degrees: degrees
    });
  }

  // return this instance
  return this;
};

/**
 * to Alias for Servo.prototype.move
 */
Servo.prototype.to = Servo.prototype.move;

/**
 * min Set Servo to minimum degrees, defaults to 0deg
 * @return {Object} instance
 */

Servo.prototype.min = function() {
  return this.move(this.range[0], true);
};

/**
 * max Set Servo to maximum degrees, defaults to 180deg
 * @return {[type]} [description]
 */
Servo.prototype.max = function() {
  return this.move(this.range[1], true);
};

/**
 * center Set Servo to centerpoint, defaults to 90deg
 * @return {[type]} [description]
 */
Servo.prototype.center = function() {
  return this.move(Math.abs((this.range[0] + this.range[1]) / 2));
};

/**
 * sweep Sweep the servo between min and max or provided range
 * @param  {Array} range constrain sweep to range
 *
 * @param {Object} options Set range or interval.
 *
 * @return {[type]} [description]
 */
Servo.prototype.sweep = function(opts) {
  var degrees,
    range = this.range,
    interval = 100,
    step = 10;

  opts = opts || {};

  // If opts is an array, then assume a range was passed
  //
  //  - This implies:
  //    - an interval of 100ms.
  //    - a step of 10 degrees
  //
  if (Array.isArray(opts)) {
    range = opts;
  } else {

    // Otherwise, opts is an object.
    //
    //  - Check for:
    //    - a range, if present use it, otherwise
    //      use the servo's range property.
    //    - an interval, if present use it, otherwise
    //      use the default interval.
    //    - a step, if present use it, otherwise
    //      use the default step
    //
    range = opts.range || range;
    interval = opts.interval || interval;
    step = opts.step || step;
  }

  degrees = range[0];

  // If the last recorded movement was not range[0]deg
  // move the servo to range[0]deg
  if (this.last && this.last.degrees !== degrees) {
    this.move(degrees);
  }

  if (this.interval) {
    clearInterval(this.interval);
  }

  this.interval = setInterval(function() {
    var abs;

    if (degrees >= range[1] || degrees < range[0]) {
      if (degrees >= range[1]) {
        this.emit("sweep:half");
      }

      step *= -1;
    }

    if (degrees === range[0]) {
      if (step !== (abs = Math.abs(step))) {
        this.emit("sweep:full");
        step = abs;
      }
    }

    degrees += step;

    this.move(degrees);
  }.bind(this), interval);

  return this;
};

/**
 * stop Stop a moving servo
 * @return {[type]} [description]
 */
Servo.prototype.stop = function() {
  if (this.type === "continuous") {
    this.move(90);
  } else {
    clearInterval(this.interval);
  }

  return this;
};

/** Speeds for continuous rotation
 *
 * Mock directions: A, B
 *
 * 0 Full speed A
 * 89 Slowest speed A
 * 90 Stopped
 * 91 Slowest speed B
 * 180 Fastest speed B
 *
 *
 **/

/**
 * Degrees to Pulse lengths in ms
 * Servo.pulse = {
 *   lengths: {
 *     0: 1,
 *     90: 1,
 *     180: 2
 *   },
 *   width: 2 / 180
 * };
 *
 **/

[{
  apis: ["clockWise", "cw"],
  args: [0, 1, 91, 180]
}, {
  apis: ["counterClockwise", "ccw"],
  args: [0, 1, 89, 0]
}].forEach(function(setup) {
  var args = setup.args.slice();

  setup.apis.forEach(function(api) {
    Servo.prototype[api] = function(rate) {
      var copy = args.slice();
      rate = rate === undefined ? 1 : rate;

      if (this.type !== "continuous") {
        this.board.error(
          "Servo",
          "Servo.prototype." + api + " is only available for continuous servos"
        );
      }
      copy.unshift(rate);
      return this.to(__.scale.apply(null, copy));
    };
  });
});

/**
 *
 * Static API
 *
 *
 */

Servo.Continuous = {
  speeds: {
    // seconds to travel 60 degrees
    "@4.8V": 0.23,
    "@5.0V": 0.17,
    "@6.0V": 0.18
  }
};

/**
 * Servo.Array()
 * new Servo.Array()
 *
 * Constructs an Array-like instance of all servos
 */
Servo.Array = function(pins) {
  if (!(this instanceof Servo.Array)) {
    return new Servo.Array(pins);
  }

  var servos = [];
  var pinOrServo;

  if (pins) {
    while (pins.length) {
      pinOrServo = pins.shift();
      servos.push(
        typeof pinOrServo === "number" ?
        new Servo(pinOrServo) : pinOrServo
      );
    }
  } else {
    servos = SERVOS.slice();
  }

  servos.forEach(function(servo, index) {
    this[index] = servo;
  }, this);

  this.length = servos.length;
};

/**
 * each Execute callbackFn for each active servo instance
 *
 * eg.
 * array.each(function( servo, index ) {
 *  `this` refers to the current servo instance
 * });
 *
 * @param  {[type]} callbackFn [description]
 * @return {[type]}            [description]
 */
Servo.Array.prototype.each = function(callbackFn) {
  var servo, i, length;

  length = this.length;

  for (i = 0; i < length; i++) {
    servo = this[i];
    callbackFn.call(servo, servo, i);
  }

  return this;
};

/**
 * Servo.Array, center()
 *
 * centers all servos to 90deg
 *
 * eg. array.center();

 * Servo.Array, min()
 *
 * set all servos to the minimum degrees
 * defaults to 0
 *
 * eg. array.min();

 * Servo.Array, max()
 *
 * set all servos to the maximum degrees
 * defaults to 180
 *
 * eg. array.max();

 * Servo.Array, stop()
 *
 * stop all servos
 *
 * eg. array.stop();
 */

[
  "center", "min", "max", "move", "stop", "sweep", "to"
].forEach(function(method) {
  // Create Servo.Array wrappers for each method listed.
  // This will allow us control over all Servo instances
  // simultaneously.
  Servo.Array.prototype[method] = function() {
    var args = [].slice.call(arguments);

    this.each(function(servo) {
      Servo.prototype[method].apply(servo, args);
    });

    return this;
  };
});

// Alias
// TODO: Deprecate and REMOVE
Servo.prototype.write = Servo.prototype.move;

module.exports = Servo;

// References
//
// http://www.societyofrobots.com/actuators_servos.shtml
// http://www.parallax.com/Portals/0/Downloads/docs/prod/motors/900-00008-CRServo-v2.2.pdf
// http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
// http://servocity.com/html/hs-7980th_servo.html
// http://mbed.org/cookbook/Servo

// Further API info:
// http://www.tinkerforge.com/doc/Software/Bricks/Servo_Brick_Python.html#servo-brick-python-api
// http://www.tinkerforge.com/doc/Software/Bricks/Servo_Brick_Java.html#servo-brick-java-api
<commit_msg>Servo cleanup<commit_after>var Board = require("../lib/board.js"),
  events = require("events"),
  util = require("util"),
  __ = require("../lib/fn.js");

// Sensor instance private data
var priv = new WeakMap(),
  SERVOS = [];

/**
 * Servo
 * @constructor
 *
 * @param {Object} opts Options: pin, type, id, range
 */

function Servo(opts) {
  if (!(this instanceof Servo)) {
    return new Servo(opts);
  }

  var history = [];

  // Initialize a Device instance on a Board
  Board.Device.call(
    this, opts = Board.Options(opts)
  );

  // Set the pin to SERVO (OUTPUT) mode
  this.mode = this.firmata.MODES.SERVO;
  this.firmata.pinMode(this.pin, this.mode);

  // When in debug mode, if pin is not a PWM pin, emit an error
  if (opts.debug && !this.board.pins.isServo(this.pin)) {
    Board.Pins.Error({
      pin: this.pin,
      type: "PWM",
      via: "Servo",
    });
  }

  // Servo instance properties
  this.range = opts.range || [0, 180];

  if (opts.pwmRange) {
    this.range = [
      __.scale(opts.pwmRange[0], 600, 2400, 0, 180),
      __.scale(opts.pwmRange[1], 600, 2400, 0, 180)
    ];
  }

  priv.set(this, {
    history: history
  });

  // Allow user defined ids, defaults to system ID
  this.id = opts.id || Board.uid();

  // Invert the value of all servoWrite operations
  // eg. 80 => 100, 90 => 90, 0 => 180
  this.isInverted = opts.isInverted || false;

  // The type of servo determines certain alternate
  // behaviours in the API
  this.type = opts.type || "standard";

  // Specification config
  this.specs = opts.specs || {
    speed: Servo.Continuous.speeds["@5.0V"]
  };

  // Collect all movement history for this servo
  // history = [
  //   {
  //     timestamp: Date.now(),
  //     degrees: degrees
  //   }
  // ];

  // Interval/Sweep pointer
  this.interval = null;

  // Create a non-writable "last" property
  // shortcut to access the last servo movement
  Object.defineProperties(this, {
    history: {
      get: function() {
        return history.slice(-5);
      }
    },
    last: {
      get: function() {
        return history[history.length - 1];
      }
    },
    position: {
      get: function() {
        return history[history.length - 1].degrees;
      }
    }
  });

  // Allow "setup"instructions to come from
  // constructor options properties

  // If "startAt" is defined and center is falsy
  // set servo to min or max degrees
  if (opts.startAt !== undefined && !opts.center) {
    this.to(opts.startAt);
  }

  // If "center" true set servo to 90deg
  if (opts.center) {
    this.center();
  }

  // Push this servo into the private
  // servo instance array.
  SERVOS.push(this);
}

util.inherits(Servo, events.EventEmitter);

/**
 * to
 *
 * Set the servo horn's position to given degree over time.
 *
 * @param  {Number} degrees   Degrees to turn servo to.
 * @param  {Number} time      Time to spend in motion.
 *
 * @return {Servo} instance
 */

Servo.prototype.to = function(degrees, time) {

  // Enforce limited range of motion
  degrees = Board.constrain(degrees, this.range[0], this.range[1]);

  if (this.isInverted) {
    degrees = Board.map(
      degrees,
      this.range[0], this.range[1],
      this.range[1], this.range[0]
    );
  }

  // If same degrees, do nothing
  if (this.last && this.last.degrees === degrees) {
    return this;
  }

  var last, distance, percent;
  var isReverse = false;
  var history = priv.get(this).history;

  if (typeof time !== "undefined") {

    last = this.last && this.last.degrees || 0;
    distance = Math.abs(last - degrees);
    percent = 0;

    if (this.interval) {
      clearImmediate(this.interval);
    }

    if (degrees < last) {
      isReverse = true;
    }

    this.interval = setInterval(function() {
      var delta = ++percent * (distance / 100);

      if (isReverse) {
        delta *= -1;
      }

      this.firmata.servoWrite(this.pin, last + delta);

      history.push({
        timestamp: Date.now(),
        degrees: last + delta
      });

      if (percent === 100) {
        this.emit("move:complete");
        clearInterval(this.interval);
      }
    }.bind(this), time / 100);

  } else {

    this.firmata.servoWrite(this.pin, degrees);

    history.push({
      timestamp: Date.now(),
      degrees: degrees
    });
  }

  // return this instance
  return this;
};

/**
 * step
 *
 * Update the servo horn's position by specified degrees (over time)
 *
 * @param  {Number} degrees   Degrees to turn servo to.
 * @param  {Number} time      Time to spend in motion.
 *
 * @return {Servo} instance
 */

Servo.prototype.step = function(degrees, time) {
  return this.to(this.last.degrees + degrees, time);
};

/**
 * move Alias for Servo.prototype.to
 */
Servo.prototype.move = function(degrees, time) {
  console.warn("Servo.prototype.move has been renamed to Servo.prototype.to");

  return this.to(degrees, time);
};

/**
 * min Set Servo to minimum degrees, defaults to 0deg
 * @return {Object} instance
 */

Servo.prototype.min = function() {
  return this.to(this.range[0]);
};

/**
 * max Set Servo to maximum degrees, defaults to 180deg
 * @return {[type]} [description]
 */
Servo.prototype.max = function() {
  return this.to(this.range[1]);
};

/**
 * center Set Servo to centerpoint, defaults to 90deg
 * @return {[type]} [description]
 */
Servo.prototype.center = function() {
  return this.to(Math.abs((this.range[0] + this.range[1]) / 2));
};

/**
 * sweep Sweep the servo between min and max or provided range
 * @param  {Array} range constrain sweep to range
 *
 * @param {Object} options Set range or interval.
 *
 * @return {[type]} [description]
 */
Servo.prototype.sweep = function(opts) {
  var degrees,
    range = this.range,
    interval = 100,
    step = 10;

  opts = opts || {};

  // If opts is an array, then assume a range was passed
  //
  //  - This implies:
  //    - an interval of 100ms.
  //    - a step of 10 degrees
  //
  if (Array.isArray(opts)) {
    range = opts;
  } else {

    // Otherwise, opts is an object.
    //
    //  - Check for:
    //    - a range, if present use it, otherwise
    //      use the servo's range property.
    //    - an interval, if present use it, otherwise
    //      use the default interval.
    //    - a step, if present use it, otherwise
    //      use the default step
    //
    range = opts.range || range;
    interval = opts.interval || interval;
    step = opts.step || step;
  }

  degrees = range[0];

  // If the last recorded movement was not range[0]deg
  // move the servo to range[0]deg
  if (this.last && this.last.degrees !== degrees) {
    this.to(degrees);
  }

  if (this.interval) {
    clearInterval(this.interval);
  }

  this.interval = setInterval(function() {
    var abs;

    if (degrees >= range[1] || degrees < range[0]) {
      if (degrees >= range[1]) {
        this.emit("sweep:half");
      }

      step *= -1;
    }

    if (degrees === range[0]) {
      if (step !== (abs = Math.abs(step))) {
        this.emit("sweep:full");
        step = abs;
      }
    }

    degrees += step;

    this.to(degrees);
  }.bind(this), interval);

  return this;
};

/**
 * stop Stop a moving servo
 * @return {[type]} [description]
 */
Servo.prototype.stop = function() {
  if (this.type === "continuous") {
    this.to(90);
  } else {
    clearInterval(this.interval);
  }

  return this;
};

/** Speeds for continuous rotation
 *
 * Mock directions: A, B
 *
 * 0 Full speed A
 * 89 Slowest speed A
 * 90 Stopped
 * 91 Slowest speed B
 * 180 Fastest speed B
 *
 *
 **/

/**
 * Degrees to Pulse lengths in ms
 * Servo.pulse = {
 *   lengths: {
 *     0: 1,
 *     90: 1,
 *     180: 2
 *   },
 *   width: 2 / 180
 * };
 *
 **/

[{
  apis: ["clockWise", "cw"],
  args: [0, 1, 91, 180]
}, {
  apis: ["counterClockwise", "ccw"],
  args: [0, 1, 89, 0]
}].forEach(function(setup) {
  var args = setup.args.slice();

  setup.apis.forEach(function(api) {
    Servo.prototype[api] = function(rate) {
      var copy = args.slice();
      rate = rate === undefined ? 1 : rate;

      if (this.type !== "continuous") {
        this.board.error(
          "Servo",
          "Servo.prototype." + api + " is only available for continuous servos"
        );
      }
      copy.unshift(rate);
      return this.to(__.scale.apply(null, copy));
    };
  });
});

/**
 *
 * Static API
 *
 *
 */

Servo.Continuous = {
  speeds: {
    // seconds to travel 60 degrees
    "@4.8V": 0.23,
    "@5.0V": 0.17,
    "@6.0V": 0.18
  }
};

/**
 * Servo.Array()
 * new Servo.Array()
 *
 * Constructs an Array-like instance of all servos
 */
Servo.Array = function(pins) {
  if (!(this instanceof Servo.Array)) {
    return new Servo.Array(pins);
  }

  var servos = [];
  var pinOrServo;

  if (pins) {
    while (pins.length) {
      pinOrServo = pins.shift();
      servos.push(
        typeof pinOrServo === "number" ?
        new Servo(pinOrServo) : pinOrServo
      );
    }
  } else {
    servos = SERVOS.slice();
  }

  servos.forEach(function(servo, index) {
    this[index] = servo;
  }, this);

  this.length = servos.length;
};

/**
 * each Execute callbackFn for each active servo instance
 *
 * eg.
 * array.each(function( servo, index ) {
 *  `this` refers to the current servo instance
 * });
 *
 * @param  {[type]} callbackFn [description]
 * @return {[type]}            [description]
 */
Servo.Array.prototype.each = function(callbackFn) {
  var servo, i, length;

  length = this.length;

  for (i = 0; i < length; i++) {
    servo = this[i];
    callbackFn.call(servo, servo, i);
  }

  return this;
};

/**
 * Servo.Array, center()
 *
 * centers all servos to 90deg
 *
 * eg. array.center();

 * Servo.Array, min()
 *
 * set all servos to the minimum degrees
 * defaults to 0
 *
 * eg. array.min();

 * Servo.Array, max()
 *
 * set all servos to the maximum degrees
 * defaults to 180
 *
 * eg. array.max();

 * Servo.Array, stop()
 *
 * stop all servos
 *
 * eg. array.stop();
 */

Object.keys(Servo.prototype).forEach(function(method) {
  // Create Servo.Array wrappers for each method listed.
  // This will allow us control over all Servo instances
  // simultaneously.
  Servo.Array.prototype[method] = function() {
    var args = [].slice.call(arguments);

    this.each(function(servo) {
      Servo.prototype[method].apply(servo, args);
    });

    return this;
  };
});

// Alias
// TODO: Deprecate and REMOVE
Servo.prototype.write = Servo.prototype.move;

module.exports = Servo;

// References
//
// http://www.societyofrobots.com/actuators_servos.shtml
// http://www.parallax.com/Portals/0/Downloads/docs/prod/motors/900-00008-CRServo-v2.2.pdf
// http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
// http://servocity.com/html/hs-7980th_servo.html
// http://mbed.org/cookbook/Servo

// Further API info:
// http://www.tinkerforge.com/doc/Software/Bricks/Servo_Brick_Python.html#servo-brick-python-api
// http://www.tinkerforge.com/doc/Software/Bricks/Servo_Brick_Java.html#servo-brick-java-api
<|endoftext|>


<commit_before>module.exports = (basePath, authorize) => {
  const superagent = require('superagent');
  const req = require('../util/req')(authorize);
  const util = require('../util/util');

  /**
   * Get site(s)
   *
   * Possible options:
   * -----------------
   * `siteId` (optional) - ID of the site to be fetched
   * `siteName` (optional) - name of the site to be fetched
   *
   * Get all sites in the account if no options provided,
   * `siteId` has priority over the `siteName` otherwise.
   *
   * @param  {Object} options
   * @return {Promise}
   */
  const get = ({siteId, siteName} = {}) => {
    var path = `${basePath}/sites`;

    if (siteId) {
      path = `${basePath}/sites/${siteId}`
    }

    if (!siteId && siteName) {
      return req.get(path).then(sites => {
        return util.getByName(sites, siteName);
      });
    } else {
      return req.get(path);
    }
  };

  /**
   * Site Scripts
   * @type {Object}
   */
  const scripts = {
    /**
     * Get Site Script(s)
     *
     * Possible options:
     * -----------------
     * `siteId` (optional if `siteName` provided) - ID of the site to be fetched
     * `siteName` (optional if `siteId` provided) - name of the site to be fetched
     *
     * Get all Site Scripts of the given site;
     * `siteId` has priority over the `siteName`.
     *
     * @param  {Object} options
     * @return {Promise}
     */
    get({siteId, siteName}) {
      if (!siteId && !siteName) {
        return Promise.resolve({ error: '`siteId` or `siteName` must be provided!' });
      }

      if (!siteId && siteName) {
        return req.get(`${basePath}/sites`).then(sites => {
          var site = util.getByName(sites, siteName);
          return req.get(util.getScriptPath(basePath, site.id, ''));
        });
      } else {
        return req.get(util.getScriptPath(basePath, siteId, ''));
      }
    },

    /**
     * Update Site Script
     *
     * Possible options:
     * -----------------
     * `siteId` (optional if `siteName` provided) - ID of the site to be fetched
     * `siteName` (optional if `siteId` provided) - name of the site to be fetched
     * `scriptId` (optional if `scriptName` provided) - ID of the script to be fetched
     * `scriptName` (optional if `scriptId` provided) - name of the script to be fetched
     *
     * Update Site Script for the given site and of the given ID/Name;
     * `siteId` has priority over the `siteName`.
     * `scriptId` has priority over the `scriptName`.
     *
     * @param  {Object} options
     * @return {Promise}
     */
    update(options) {
      let {siteId, siteName, scriptId, scriptName, name, description, content} = options;

      if (!siteId && !siteName) {
        return Promise.reject({ error: '`siteId` or `siteName` must be provided!' });
      }

      if (!scriptId && !scriptName) {
        return Promise.reject({ error: '`scriptId` or `scriptName` must be provided!' });
      }

      let updatedData = util.getUpdatedData(options);
      let promise;
      let path;

      if (!siteId && siteName) {
        promise = get().then(sites => getByName(sites, siteName))

        return promise
          .then(site => {
            if (!scriptId && scriptName) {
              return this.get({siteId: site.id})
                .then(scripts => getByName(scripts, scriptName))
                .then(script => {
                  return req.put(util.getScriptPath(basePath, site.id, script.id));
                });
            } else {
              return req.put(util.getScriptPath(basePath, site.id, scriptId));
            }
          });
      } else {
        if (!scriptId && scriptName) {
          return this.get({siteId: siteId})
            .then(scripts => getByName(scripts, scriptName))
            .then(script => {
              return req.put(util.getScriptPath(basePath, siteId, script.id));
            });
        } else {
          return req.put(util.getScriptPath(basePath, siteId, scriptId));
        }
      }
    }
  };

  /**
   * Site Actions
   * @type {Object}
   */
  const actions = {
    /**
     * Get Site Action(s)
     *
     * Possible options:
     * -----------------
     * `siteId` (optional if `siteName` provided) - ID of the site to be fetched
     * `siteName` (optional if `siteId` provided) - name of the site to be fetched
     *
     * Get all Site Scripts of the given site;
     * `siteId` has priority over the `siteName`.
     *
     * @param  {Object} options
     * @return {Promise}
     */
    get({siteId, siteName}) {
      var path;

      if (!siteId && !siteName) {
        return Promise.resolve({ error: '`siteId` or `siteName` must be provided!' });
      }

      if (!siteId && siteName) {
        return req.get(`${basePath}/sites`).then(sites => {
          var site = util.getByName(sites, siteName);
          return req.get(`${basePath}/sites/${site.id}/sandbox/actions`);
        });
      } else {
        return req.get(`${basePath}/sites/${siteId}/sandbox/actions`);
      }
    }
  };

  // exports
  return {
    get,
    scripts,
    actions
  };
};
<commit_msg>! site.js<commit_after>module.exports = (basePath, authorize) => {
  const superagent = require('superagent');
  const req = require('../util/req')(authorize);
  const util = require('../util/util');

  /**
   * Get site(s)
   *
   * Possible options:
   * -----------------
   * `siteId` (optional) - ID of the site to be fetched
   * `siteName` (optional) - name of the site to be fetched
   *
   * Get all sites in the account if no options provided,
   * `siteId` has priority over the `siteName` otherwise.
   *
   * @param  {Object} options
   * @return {Promise}
   */
  const get = ({siteId, siteName} = {}) => {
    var path = `${basePath}/sites`;

    if (siteId) {
      path = `${basePath}/sites/${siteId}`
    }

    if (!siteId && siteName) {
      return req.get(path).then(sites => {
        return util.getByName(sites, siteName);
      });
    } else {
      return req.get(path);
    }
  };

  /**
   * Site Scripts
   * @type {Object}
   */
  const scripts = {
    /**
     * Get Site Script(s)
     *
     * Possible options:
     * -----------------
     * `siteId` (optional if `siteName` provided) - ID of the site to be fetched
     * `siteName` (optional if `siteId` provided) - name of the site to be fetched
     *
     * Get all Site Scripts of the given site;
     * `siteId` has priority over the `siteName`.
     *
     * @param  {Object} options
     * @return {Promise}
     */
    get({siteId, siteName}) {
      if (!siteId && !siteName) {
        return Promise.resolve({ error: '`siteId` or `siteName` must be provided!' });
      }

      if (!siteId && siteName) {
        return req.get(`${basePath}/sites`).then(sites => {
          var site = util.getByName(sites, siteName);
          return req.get(util.getScriptPath(basePath, site.id, ''));
        });
      } else {
        return req.get(util.getScriptPath(basePath, siteId, ''));
      }
    },

    /**
     * Update Site Script
     *
     * Possible options:
     * -----------------
     * `siteId` (optional if `siteName` provided) - ID of the site to be fetched
     * `siteName` (optional if `siteId` provided) - name of the site to be fetched
     * `scriptId` (optional if `scriptName` provided) - ID of the script to be fetched
     * `scriptName` (optional if `scriptId` provided) - name of the script to be fetched
     *
     * Update Site Script for the given site and of the given ID/Name;
     * `siteId` has priority over the `siteName`.
     * `scriptId` has priority over the `scriptName`.
     *
     * @param  {Object} options
     * @return {Promise}
     */
    update(options) {
      let {siteId, siteName, scriptId, scriptName, name, description, content} = options;

      if (!siteId && !siteName) {
        return Promise.reject({ error: '`siteId` or `siteName` must be provided!' });
      }

      if (!scriptId && !scriptName) {
        return Promise.reject({ error: '`scriptId` or `scriptName` must be provided!' });
      }

      let updatedData = util.getUpdatedData(options);
      let promise;
      let path;

      if (!siteId && siteName) {
        promise = get().then(sites => util.getByName(sites, siteName))

        return promise
          .then(site => {
            if (!scriptId && scriptName) {
              return this.get({siteId: site.id})
                .then(scripts => util.getByName(scripts, scriptName))
                .then(script => {
                  return req.put(util.getScriptPath(basePath, site.id, script.id), updatedData);
                });
            } else {
              return req.put(util.getScriptPath(basePath, site.id, scriptId), updatedData);
            }
          });
      } else {
        if (!scriptId && scriptName) {
          return this.get({siteId: siteId})
            .then(scripts => util.getByName(scripts, scriptName))
            .then(script => {
              return req.put(util.getScriptPath(basePath, siteId, script.id), updatedData);
            });
        } else {
          return req.put(util.getScriptPath(basePath, siteId, scriptId), updatedData);
        }
      }
    }
  };

  /**
   * Site Actions
   * @type {Object}
   */
  const actions = {
    /**
     * Get Site Action(s)
     *
     * Possible options:
     * -----------------
     * `siteId` (optional if `siteName` provided) - ID of the site to be fetched
     * `siteName` (optional if `siteId` provided) - name of the site to be fetched
     *
     * Get all Site Scripts of the given site;
     * `siteId` has priority over the `siteName`.
     *
     * @param  {Object} options
     * @return {Promise}
     */
    get({siteId, siteName}) {
      var path;

      if (!siteId && !siteName) {
        return Promise.resolve({ error: '`siteId` or `siteName` must be provided!' });
      }

      if (!siteId && siteName) {
        return req.get(`${basePath}/sites`).then(sites => {
          var site = util.getByName(sites, siteName);
          return req.get(`${basePath}/sites/${site.id}/sandbox/actions`);
        });
      } else {
        return req.get(`${basePath}/sites/${siteId}/sandbox/actions`);
      }
    }
  };

  // exports
  return {
    get,
    scripts,
    actions
  };
};
<|endoftext|>


<commit_before>var buster = require("buster-core");
var uuid = require("node-uuid");
var when = require("when");
var ejs = require("ejs");
var fs = require("fs");
var rampResources = require("ramp-resources");
var bCapServPubsubClient = require("./pubsub-client");
var prisonTemplate = __dirname + "/templates/slave_prison.html";
var PRISON_TEMPLATE = fs.readFileSync(prisonTemplate, "utf8");

var httpServReqListenerProxy = require("./http-server-request-listener-proxy");

var libraries = [
    require.resolve("./../vendor/json/json2"),
    require.resolve("buster-core"),
    require.resolve("buster-core/lib/buster-event-emitter"),
    require.resolve("faye/browser/faye-browser-min"),
    require.resolve("when"),
    require.resolve("node-uuid"),
    require.resolve("./pubsub-client"),
    require.resolve("./session-client"),
    require.resolve("./prison-util"),
    require.resolve("./prison"),
    require.resolve("./prison-init")
];

var basePrisonResourceSet = rampResources.resourceSet.create();
basePrisonResourceSet.addResources(libraries.map(function (path) {
    return {path: path, content: fs.readFileSync(path)};
}).concat([{path: "/prison.js", combine: libraries}]))
    .then(function () {
        basePrisonResourceSet.loadPath.append("/prison.js");
    });

module.exports = buster.extend(buster.eventEmitter.create(), {
    create: function (resourceMiddleware, pubsubServer) {
        var instance = buster.create(this);
        instance._id = uuid();
        instance.isReady = false;
        instance.prisonPath = "/slaves/" + instance._id + "/browser";
        instance.headerPath = "/slaves/" + instance._id + "/header";

        instance._resourceMiddleware = resourceMiddleware;
        instance._attachResourceSet();

        instance._pubsubServer = pubsubServer;
        instance._attachPubsubServer();

        return instance;
    },

    setHeader: function (height, resourceSet) {
        this.hasHeader = true;
        this.headerHeight = height;
        this._resourceMiddleware.mount(this.headerPath, resourceSet);
    },

    teardown: function () {
        this._resourceMiddleware.unmount(this.prisonPath);
        this._resourceMiddleware.unmount(this.headerPath);
        this._pubsubClient.teardown();
    },

    prepare: function () {
        var deferred = when.defer();

        if (this.isReady) {
            deferred.resolve();
        } else {
            this._imprisonmentDeferred = deferred;
        }

        return deferred.promise;
    },

    serialize: function () {
        return {
            prisonPath: this.prisonPath,
            id: this._id,
            userAgent: this.userAgent
        };
    },

    loadSession: function (session) {
        this._loadSessionDeferred = when.defer();
        var event = "slave:" + this._id + ":session:load";
        this._pubsubClient.emit(event, session.serialize());
        return this._loadSessionDeferred;
    },

    unloadSession: function () {
        this._unloadSessionDeferred = when.defer();
        this._pubsubClient.emit("slave:" + this._id + ":session:unload");
        return this._unloadSessionDeferred;
    },

    _onSessionLoaded: function () {
        this._loadSessionDeferred.resolve();
    },

    _onSessionUnloaded: function () {
        this._unloadSessionDeferred.resolve();
    },

    _onImprisoned: function (e) {
        var self = this;
        this.userAgent = e.userAgent;
        this._pubsubServer.onDisconnect(e.pubsubClientId).then(function () {
            self.emit("end");
        });
        this.isReady = true;
        this._imprisonmentDeferred.resolve();
    },

    _attachResourceSet: function () {
        var self = this;

        var prisonResourceSet = basePrisonResourceSet.concat();
        prisonResourceSet.addResource({
            path: "/",
            content: function () {
                var locals = {};
                locals.hasHeader = self.hasHeader;
                if (locals.hasHeader) {
                    locals.headerHeight = self.headerHeight;
                    locals.headerPath = self.headerPath + "/";
                }

                return ejs.render(PRISON_TEMPLATE, {locals: locals});
            }
        });

        this._resourceMiddleware.mount(this.prisonPath, prisonResourceSet);
    },

    _attachPubsubServer: function () {
        this._pubsubClient = this._pubsubServer.createClient();
        this._pubsubClient.on(
            "slave:" + this._id + ":session:loaded",
            this._onSessionLoaded.bind(this)
        );
        this._pubsubClient.on(
            "slave:" + this._id + ":session:unloaded",
            this._onSessionUnloaded.bind(this)
        );

        this._pubsubClient.on(
            "slave:" + this._id + ":imprisoned",
            this._onImprisoned.bind(this)
        );
    }
});
<commit_msg>Slight look improvement.<commit_after>var buster = require("buster-core");
var uuid = require("node-uuid");
var when = require("when");
var ejs = require("ejs");
var fs = require("fs");
var rampResources = require("ramp-resources");
var bCapServPubsubClient = require("./pubsub-client");
var prisonTemplate = __dirname + "/templates/slave_prison.html";
var PRISON_TEMPLATE = fs.readFileSync(prisonTemplate, "utf8");

var httpServReqListenerProxy = require("./http-server-request-listener-proxy");

var libraries = [
    require.resolve("./../vendor/json/json2"),
    require.resolve("buster-core"),
    require.resolve("buster-core/lib/buster-event-emitter"),
    require.resolve("faye/browser/faye-browser-min"),
    require.resolve("when"),
    require.resolve("node-uuid"),
    require.resolve("./pubsub-client"),
    require.resolve("./session-client"),
    require.resolve("./prison-util"),
    require.resolve("./prison"),
    require.resolve("./prison-init")
];

var basePrisonResourceSet = rampResources.resourceSet.create();
basePrisonResourceSet.addResources(libraries.map(function (path) {
    return {path: path, content: fs.readFileSync(path)};
}).concat([{path: "/prison.js", combine: libraries}]))
    .then(function () {
        basePrisonResourceSet.loadPath.append("/prison.js");
    });

module.exports = buster.extend(buster.eventEmitter.create(), {
    create: function (resourceMiddleware, pubsubServer) {
        var instance = buster.create(this);
        instance._id = uuid();
        instance.isReady = false;
        instance.prisonPath = "/slaves/" + instance._id + "/browser";
        instance.headerPath = "/slaves/" + instance._id + "/header";
        instance._resourceMiddleware = resourceMiddleware;
        instance._pubsubServer = pubsubServer;

        instance._attachResourceSet();
        instance._attachPubsubServer();

        return instance;
    },

    setHeader: function (height, resourceSet) {
        this.hasHeader = true;
        this.headerHeight = height;
        this._resourceMiddleware.mount(this.headerPath, resourceSet);
    },

    teardown: function () {
        this._resourceMiddleware.unmount(this.prisonPath);
        this._resourceMiddleware.unmount(this.headerPath);
        this._pubsubClient.teardown();
    },

    prepare: function () {
        var deferred = when.defer();

        if (this.isReady) {
            deferred.resolve();
        } else {
            this._imprisonmentDeferred = deferred;
        }

        return deferred.promise;
    },

    serialize: function () {
        return {
            prisonPath: this.prisonPath,
            id: this._id,
            userAgent: this.userAgent
        };
    },

    loadSession: function (session) {
        this._loadSessionDeferred = when.defer();
        var event = "slave:" + this._id + ":session:load";
        this._pubsubClient.emit(event, session.serialize());
        return this._loadSessionDeferred;
    },

    unloadSession: function () {
        this._unloadSessionDeferred = when.defer();
        this._pubsubClient.emit("slave:" + this._id + ":session:unload");
        return this._unloadSessionDeferred;
    },

    _onSessionLoaded: function () {
        this._loadSessionDeferred.resolve();
    },

    _onSessionUnloaded: function () {
        this._unloadSessionDeferred.resolve();
    },

    _onImprisoned: function (e) {
        var self = this;
        this.userAgent = e.userAgent;
        this._pubsubServer.onDisconnect(e.pubsubClientId).then(function () {
            self.emit("end");
        });
        this.isReady = true;
        this._imprisonmentDeferred.resolve();
    },

    _attachResourceSet: function () {
        var self = this;

        var prisonResourceSet = basePrisonResourceSet.concat();
        prisonResourceSet.addResource({
            path: "/",
            content: function () {
                var locals = {};
                locals.hasHeader = self.hasHeader;
                if (locals.hasHeader) {
                    locals.headerHeight = self.headerHeight;
                    locals.headerPath = self.headerPath + "/";
                }

                return ejs.render(PRISON_TEMPLATE, {locals: locals});
            }
        });

        this._resourceMiddleware.mount(this.prisonPath, prisonResourceSet);
    },

    _attachPubsubServer: function () {
        this._pubsubClient = this._pubsubServer.createClient();
        this._pubsubClient.on(
            "slave:" + this._id + ":session:loaded",
            this._onSessionLoaded.bind(this)
        );
        this._pubsubClient.on(
            "slave:" + this._id + ":session:unloaded",
            this._onSessionUnloaded.bind(this)
        );

        this._pubsubClient.on(
            "slave:" + this._id + ":imprisoned",
            this._onImprisoned.bind(this)
        );
    }
});
<|endoftext|>


<commit_before>var extend        = require('extend');
var Broadcast     = require('./broadcast');
var Warlock       = require('node-redis-warlock');
var debug         = require('debug')('stash');
var helpers       = require('./helpers');
var debug         = require('debug')('stash');
var memoize       = require('memoizee');
var createMetrics = require('./Metrics');

var timed = helpers.timed;
var slice = Array.prototype.slice;

var Stash = function(createRedisClient, opts) {
  var self = this;

  var conf = {
    redis: {
      wait: true,           // when false, errors if redis connection is down, otherwise queues commands
      ttl: {
        cache: 600000,      // redis cache key ttl
        lock: 10000         // in-case unlock does not work, ttl of redis-based fetchFn lock
      }
    },
    timeout: {
      retry: 1000,          // optimistic lock retry delay
      del: 1000,            // time limit for deletes
      fetch: 10000          // time limit for total cache & db fetch trip
    },
    memoize: {              // in-memory cache options
      result: {
        ttl: 600000,        // ttl of results cache
        max: 100000         // max number of items in results cache
      },
      error: {
        ttl: 5000,          // ttl of errors cache
        max: 100000         // max number of items in error cache
      }
    },
    retryLimit: 5,          // max retry times for optimistic locking
    log: debug,             // set to your own logging function
    metrics: createMetrics()// set to your own metrics function
  };

  self.conf = extend(true, conf, opts || {});
  self.log = conf.log;
  self.metrics = conf.metrics;
  self.cacheRedis = createRedisClient();
  self.broadcastRedis = createRedisClient();
  self.warlock = Warlock(self.cacheRedis);

  self.memoize = {};

  // memoization = cache result for given input
  self.memoize.result = memoize(self.getError.bind(self), {
    async: true,
    primitive: true,
    maxAge: self.conf.memoize.result.ttl,
    max: self.conf.memoize.result.max,
    resolvers: [String],
    length: 1
  });

  self.memoize.err = memoize(self.cacheFetch.bind(self), {
    async: true,
    primitive: true,
    maxAge: self.conf.memoize.error.ttl,
    max: self.conf.memoize.error.max,
    resolvers: [String],
    length: 1
  });

  self.broadcast = Broadcast(self, {
    redis: self.cacheRedis,
    subscriberRedis: self.broadcastRedis,
    log: self.log,
    metrics: self.metrics
  });
};

Stash.prototype.get = function(key, dbFetch, cb) {
  var self = this;

  if (typeof cb !== 'function') {
    // no callback given so not sure exactly what arguments have been
    // supplied. Safest to no-op than to assume dbFetch is actually
    // a fetch function.
    return self.log('stash.get requires callback');
  }

  if (typeof dbFetch !== 'function') {
    var err = new Error('no fetch function given');
    setImmediate(function() {
      cb(err);
    });
    return;
  }

  self.metrics.increment('get');
  self.memoize.result(key, dbFetch, cb);
};

/**
 * Result not cached in memory, try to get a cached error
 */
Stash.prototype.getError = function(key, dbFetch, cb) {
  var self = this;
  var retries = 0;

  var done = function unswap(result, err) {
    // flip args back around so err bypasses result cache
    // result gets cached if no error
    result = result || [];
    result.unshift(err);
    cb.apply(cb, result);
  };

  self.memoize.err(key, dbFetch, retries, done);
};

/** Neither result nor error cached in memory, so fetch from cache */
Stash.prototype.cacheFetch = function(key, dbFetch, retries, cb) {
  var self = this;

  var done = function swap(err){
    // masquerade result as an error so it bypasses error cache
    var result = slice.call(arguments, 1);
    // err or result must be null otherwise we'd bypass both caches
    if (err) result = null;
    else err = null;
    cb(result, err);
  };

  self.metrics.increment('fetch');

  if (!self.cacheRedis.connected && !self.conf.redis.wait) {
    return setImmediate(function() {
      done(new Error('redis unavailable'));
    });
  }

  done = timed(self.conf.timeout.fetch, done);

  self.cacheRedis.get(key, function(err, content) {
    if (err) return done(self.reportCacheErr(err));
    var result;

    if (content) {
      self.log('cache:hit');
      self.metrics.increment('hit');
      try {
        result = self.decodeContent(content);
      } catch (e) {
        self.log('cache:decode exception');
        err = e;
      }

      result.unshift(err);
      return done.apply(done, result);
    }

    self.log('cache:miss');
    self.metrics.increment('miss');

    var lock = function(done) {
      self.log('cache:locking');
      self.warlock.lock(
        key,
        self.conf.redis.ttl.lock,
        done
      );
    };

    // Before invoking the db fetch, set a lock
    lock(function(err, unlock) {
      if (err) return done(self.reportCacheErr(err));

      if (typeof unlock !== 'function') {
        // No unlock function given so we didn't obtain a lock
        self.log('cache:locked');

        if (retries++ >= self.conf.retryLimit) {
          self.log('cache:retry limit reached');
          return done(new Error('retry limit reached'));
        }
        self.log('cache:retry');

        // Try fetching from redis cache again after a short pause
        // Hopefully the data will be cached by the time we retry
        setTimeout(function() {
          self.cacheFetch(key, dbFetch, retries, cb);
        }, self.conf.timeout.retry);

        return;
      }

      // Lock in place. We are now responsible for fetching
      // from DB and saving to the cache
      self.log('cache:lockAcquired');

      self.log('db:fetch');
      return dbFetch(function() {
        var content;
        var args = slice.call(arguments, 0);
        var err = args[0];
        var result = args.slice(1);

        // Don't save in redis but will still save to memory
        if (err) {
          self.log('db:error');
          debug(err);
          return unlock(function() {
            done.apply(done, args);
          });
        }

        // Store result args as array in cache
        try {
          content = self.encodeContent(result);
        } catch (e) {
          self.log('cache:encode exception');
          err = new Error('encode exception');
          err.exception = e;
          args[0] = err;
          return unlock(function() {
            done.apply(done, args);
          });
        }

        self.cacheRedis.multi()
          .set(key, content)
          .pexpire(key, self.conf.redis.ttl.cache)
          .exec(function(err) {
            if (err) {
              self.log('cache:save failed');
              debug(err);
            } else {
              self.log('cache:saved');
            }

            self.metrics.increment('stored');
            unlock(function() {
              done.apply(done, args);
            });
          });
      });
    });
  });
};

Stash.prototype.reportCacheErr = function(err) {
  var self = this;
  self.log('cache:error');

  if (err.message === 'timeout') {
    self.metrics.increment('cache.timeout');
    self.log('cache:timeout');
  }

  self.metrics.increment('cache.error');
  return err;
};

Stash.prototype.del = function(key, cb) {
  var self = this;
  cb = cb || function() {};
  self.log('cache:del');
  self.metrics.increment('del');

  if (!self.cacheRedis.connected && !self.conf.redis.wait) {
    self.log('cache:del failed');
    return setImmediate(function() {
      cb(new Error('redis unavailable'));
    });
  }

  self.cacheDel(key, function(err) {
    if (err) return cb(err);

    self.memDel(key);
    cb();
  });
};

Stash.prototype.memDel = function(key) {
  var self = this;
  self.memoize.result.delete(key);
  self.memoize.err.delete(key);
};

Stash.prototype.cacheDel = function(key, cb) {
  var self = this;
  self.cacheRedis.del(key, function(err) {

    if (err) {
      self.log('cache:del failed');
      debug(err);
      return cb(err);
    } else {
      self.log('cache:del success');
      self.metrics.increment('deleted');
      return cb(err);
    }
  });
};

Stash.prototype.invalidate = function(key, cb) {
  var self = this;
  cb = cb || function() {};

  self.metrics.increment('invalidate');
  self.del(key, function(err) {
    if (err) return cb(err);

    self.broadcast.publish('invalidateStash', {
      key: key
    }, cb);
  });
};

Stash.prototype.clear = function() {
  var self = this;

  self.metrics.increment('clear');
  self.memoize.result.clear();
  self.memoize.err.clear();
};

// Override if required
Stash.prototype.encodeContent = JSON.stringify;
Stash.prototype.decodeContent = JSON.parse;

var createStash = function(createRedisClient, opts) {
  return new Stash(createRedisClient, opts);
};

exports.Stash = Stash;
exports.createStash = createStash;
<commit_msg>Results args as array<commit_after>var extend        = require('extend');
var Broadcast     = require('./broadcast');
var Warlock       = require('node-redis-warlock');
var debug         = require('debug')('stash');
var helpers       = require('./helpers');
var debug         = require('debug')('stash');
var memoize       = require('memoizee');
var createMetrics = require('./Metrics');

var timed = helpers.timed;
var slice = Array.prototype.slice;

var Stash = function(createRedisClient, opts) {
  var self = this;

  var conf = {
    redis: {
      wait: true,           // when false, errors if redis connection is down, otherwise queues commands
      ttl: {
        cache: 600000,      // redis cache key ttl
        lock: 10000         // in-case unlock does not work, ttl of redis-based fetchFn lock
      }
    },
    timeout: {
      retry: 1000,          // optimistic lock retry delay
      del: 1000,            // time limit for deletes
      fetch: 10000          // time limit for total cache & db fetch trip
    },
    memoize: {              // in-memory cache options
      result: {
        ttl: 600000,        // ttl of results cache
        max: 100000         // max number of items in results cache
      },
      error: {
        ttl: 5000,          // ttl of errors cache
        max: 100000         // max number of items in error cache
      }
    },
    retryLimit: 5,          // max retry times for optimistic locking
    log: debug,             // set to your own logging function
    metrics: createMetrics()// set to your own metrics function
  };

  self.conf = extend(true, conf, opts || {});
  self.log = conf.log;
  self.metrics = conf.metrics;
  self.cacheRedis = createRedisClient();
  self.broadcastRedis = createRedisClient();
  self.warlock = Warlock(self.cacheRedis);

  self.memoize = {};

  // memoization = cache result for given input
  self.memoize.result = memoize(self.getError.bind(self), {
    async: true,
    primitive: true,
    maxAge: self.conf.memoize.result.ttl,
    max: self.conf.memoize.result.max,
    resolvers: [String],
    length: 1
  });

  self.memoize.err = memoize(self.cacheFetch.bind(self), {
    async: true,
    primitive: true,
    maxAge: self.conf.memoize.error.ttl,
    max: self.conf.memoize.error.max,
    resolvers: [String],
    length: 1
  });

  self.broadcast = Broadcast(self, {
    redis: self.cacheRedis,
    subscriberRedis: self.broadcastRedis,
    log: self.log,
    metrics: self.metrics
  });
};

Stash.prototype.get = function(key, dbFetch, cb) {
  var self = this;

  if (typeof cb !== 'function') {
    // no callback given so not sure exactly what arguments have been
    // supplied. Safest to no-op than to assume dbFetch is actually
    // a fetch function.
    return self.log('stash.get requires callback');
  }

  if (typeof dbFetch !== 'function') {
    var err = new Error('no fetch function given');
    setImmediate(function() {
      cb(err);
    });
    return;
  }

  self.metrics.increment('get');
  self.memoize.result(key, dbFetch, cb);
};

/**
 * Result not cached in memory, try to get a cached error
 */
Stash.prototype.getError = function(key, dbFetch, cb) {
  var self = this;
  var retries = 0;

  var done = function unswap(result, err) {
    // flip args back around so err bypasses result cache
    // result gets cached if no error
    result = result || [];
    result.unshift(err);
    cb.apply(cb, result);
  };

  self.memoize.err(key, dbFetch, retries, done);
};

/** Neither result nor error cached in memory, so fetch from cache */
Stash.prototype.cacheFetch = function(key, dbFetch, retries, cb) {
  var self = this;

  var done = function swap(err, result){
    // result is array of dbFetch result args
    // masquerade result as an error so it bypasses error cache
    // err or result must be null otherwise we'd bypass both caches
    if (err) result = null;
    else err = null;
    cb(result, err);
  };

  self.metrics.increment('fetch');

  if (!self.cacheRedis.connected && !self.conf.redis.wait) {
    return setImmediate(function() {
      done(new Error('redis unavailable'));
    });
  }

  done = timed(self.conf.timeout.fetch, done);

  self.cacheRedis.get(key, function(err, content) {
    if (err) return done(self.reportCacheErr(err));
    var result;

    if (content) {
      self.log('cache:hit');
      self.metrics.increment('hit');
      try {
        result = self.decodeContent(content);
      } catch (e) {
        self.log('cache:decode exception');
        err = e;
      }

      return done(err, result);
    }

    self.log('cache:miss');
    self.metrics.increment('miss');

    var lock = function(done) {
      self.log('cache:locking');
      self.warlock.lock(
        key,
        self.conf.redis.ttl.lock,
        done
      );
    };

    // Before invoking the db fetch, set a lock
    lock(function(err, unlock) {
      if (err) return done(self.reportCacheErr(err));

      if (typeof unlock !== 'function') {
        // No unlock function given so we didn't obtain a lock
        self.log('cache:locked');

        if (retries++ >= self.conf.retryLimit) {
          self.log('cache:retry limit reached');
          return done(new Error('retry limit reached'));
        }
        self.log('cache:retry');

        // Try fetching from redis cache again after a short pause
        // Hopefully the data will be cached by the time we retry
        setTimeout(function() {
          self.cacheFetch(key, dbFetch, retries, cb);
        }, self.conf.timeout.retry);

        return;
      }

      // Lock in place. We are now responsible for fetching
      // from DB and saving to the cache
      self.log('cache:lockAcquired');

      self.log('db:fetch');
      return dbFetch(function(err) {
        var content;
        var result = slice.call(arguments, 1);

        // Don't save in redis but will still save to memory
        if (err) {
          self.log('db:error');
          debug(err);
          return unlock(function() {
            done(err, result);
          });
        }

        // Store result args as array in cache
        try {
          content = self.encodeContent(result);
        } catch (e) {
          self.log('cache:encode exception');
          err = new Error('encode exception');
          err.exception = e;
          return unlock(function() {
            // Will not memoize if encode error
            done(err, result);
          });
        }

        self.cacheRedis.multi()
          .set(key, content)
          .pexpire(key, self.conf.redis.ttl.cache)
          .exec(function(err) {
            if (err) {
              self.log('cache:save failed');
              debug(err);
            } else {
              self.log('cache:saved');
            }

            self.metrics.increment('stored');
            unlock(function() {
              // Will memoize result even if failed to save to cache
              done(err, result);
            });
          });
      });
    });
  });
};

Stash.prototype.reportCacheErr = function(err) {
  var self = this;
  self.log('cache:error');

  if (err.message === 'timeout') {
    self.metrics.increment('cache.timeout');
    self.log('cache:timeout');
  }

  self.metrics.increment('cache.error');
  return err;
};

Stash.prototype.del = function(key, cb) {
  var self = this;
  cb = cb || function() {};
  self.log('cache:del');
  self.metrics.increment('del');

  if (!self.cacheRedis.connected && !self.conf.redis.wait) {
    self.log('cache:del failed');
    return setImmediate(function() {
      cb(new Error('redis unavailable'));
    });
  }

  self.cacheDel(key, function(err) {
    if (err) return cb(err);

    self.memDel(key);
    cb();
  });
};

Stash.prototype.memDel = function(key) {
  var self = this;
  self.memoize.result.delete(key);
  self.memoize.err.delete(key);
};

Stash.prototype.cacheDel = function(key, cb) {
  var self = this;
  self.cacheRedis.del(key, function(err) {

    if (err) {
      self.log('cache:del failed');
      debug(err);
      return cb(err);
    } else {
      self.log('cache:del success');
      self.metrics.increment('deleted');
      return cb(err);
    }
  });
};

Stash.prototype.invalidate = function(key, cb) {
  var self = this;
  cb = cb || function() {};

  self.metrics.increment('invalidate');
  self.del(key, function(err) {
    if (err) return cb(err);

    self.broadcast.publish('invalidateStash', {
      key: key
    }, cb);
  });
};

Stash.prototype.clear = function() {
  var self = this;

  self.metrics.increment('clear');
  self.memoize.result.clear();
  self.memoize.err.clear();
};

// Override if required
Stash.prototype.encodeContent = JSON.stringify;
Stash.prototype.decodeContent = JSON.parse;

var createStash = function(createRedisClient, opts) {
  return new Stash(createRedisClient, opts);
};

exports.Stash = Stash;
exports.createStash = createStash;
<|endoftext|>


<commit_before>// Interface to stats
define(["settings"], function(preferences) {
	var stats = {};
	var source = 'Sugarizer';
	var logging = true;

	// Send log to the API
	function sendLog(object, action, label, value) {
		// Not connected
		if (!preferences.isConnected()) {
			return;
		}

		// Compute URL
		var info = preferences.getServer();
		var url = null;
		if (info != null && info.url) {
			url = info.url;
		}
		if (url == null) {
			url = (util.getClientType() == constant.webAppType) ? util.getCurrentServerUrl() : "localhost";
		}
		url += constant.statsURL;
		url = constant.http + url;

		// Compute request
		var req = new XMLHttpRequest();
		req.open('POST', url, true);
		var token = preferences.getToken();
		req.setRequestHeader('Content-Type', 'application/json');
		req.setRequestHeader('x-key', token.x_key);
		req.setRequestHeader('x-access-token', token.access_token);
		var stat = {};
		stat.user_id = preferences.getNetworkId();
		stat.user_agent = navigator.userAgent;
		stat.timestamp = new Date().getTime();
		stat.client_type = util.getClientName();
		stat.event_source = source;
		stat.event_object = object;
		stat.event_action = action;
		stat.event_label = label;
		stat.event_value = value;
		var body = {};
		var content = [];
		content.push(stat);
		body.stats = JSON.stringify(content);

		// Send request
console.log(source+": "+action+"("+object+", "+label+", "+value+")");
		req.onreadystatechange = function() {
			if (this.readyState === 4) {
				if (this.status === 200) {
				} else {
					if (!this.response) {
						console.log("Error sending log");
					} else {
						try {
							var response = JSON.parse(this.response);
							if (response.code != 20) {
								console.log("Error sending log");
							}
						} catch(e) {
							console.log("Error sending log");
						}
					}
				}
			}
		};
		req.send(JSON.stringify(body));
	}

	// Trace an action
	stats.trace = function(object, action, label, value) {
		if (logging) {
			sendLog(object, action, label, value);
		}
	}

	return stats;
});
<commit_msg>Test if stats are active on the server before sending it<commit_after>// Interface to stats
define(["settings"], function(preferences) {
	var stats = {};
	var source = 'Sugarizer';
	var logging = true;

	// Send log to the API
	function sendLog(object, action, label, value) {
		// Not connected
		if (!preferences.isConnected()) {
			return;
		}

		// Statistics not active on server
		var info = preferences.getServer();
		if (info && info.options && !info.options.statistics) {
			return;
		}

		// Compute URL
		var url = null;
		if (info && info.url) {
			url = info.url;
		}
		if (url == null) {
			url = (util.getClientType() == constant.webAppType) ? util.getCurrentServerUrl() : "localhost";
		}
		url += constant.statsURL;
		url = constant.http + url;

		// Compute request
		var req = new XMLHttpRequest();
		req.open('POST', url, true);
		var token = preferences.getToken();
		req.setRequestHeader('Content-Type', 'application/json');
		req.setRequestHeader('x-key', token.x_key);
		req.setRequestHeader('x-access-token', token.access_token);
		var stat = {};
		stat.user_id = preferences.getNetworkId();
		stat.user_agent = navigator.userAgent;
		stat.timestamp = new Date().getTime();
		stat.client_type = util.getClientName();
		stat.event_source = source;
		stat.event_object = object;
		stat.event_action = action;
		stat.event_label = label;
		stat.event_value = value;
		var body = {};
		var content = [];
		content.push(stat);
		body.stats = JSON.stringify(content);

		// Send request
console.log(source+": "+action+"("+object+", "+label+", "+value+")");
		req.onreadystatechange = function() {
			if (this.readyState === 4) {
				if (this.status === 200) {
				} else {
					if (!this.response) {
						console.log("Error sending log");
					} else {
						try {
							var response = JSON.parse(this.response);
							if (response.code != 20) {
								console.log("Error sending log");
							}
						} catch(e) {
							console.log("Error sending log");
						}
					}
				}
			}
		};
		req.send(JSON.stringify(body));
	}

	// Trace an action
	stats.trace = function(object, action, label, value) {
		if (logging) {
			sendLog(object, action, label, value);
		}
	}

	return stats;
});
<|endoftext|>