<commit_before>const exec = require("executive");
const Client = require("node-rest-client").Client;
const util = require("util");
const fs = require("fs");
const path = require("path");
const findRoot = require("find-root");
const rcFilename = ".owditrc";

/**
 * @module owdit
 */

/**
 * @typedef VulnerabilityReport
 * @property {!String} title - Title of the vulnerability
 * @property {!String} description - Short description of the vulnerability
 * @property {!String} versions - Versions of the package that are affected by this vulnerability
 * @property {!String[]} references - A list of URLs containing links to more details about the vulnerability
 */

/**
 * @typedef VulnerablePackage
 * @property {!String} name - Name of the package
 * @property {!String} version - Version of the package in semver format
 * @property {!String[]} path - Top-bottom include list of the package; e.g. ["mypackage@1.0.2", "express@2.5.11", "qs@0.4.2"]
 * @property {!Vulnerability[]} vulnerabilities - List of found vulnerabilities for the package
 */

/**
 * @typedef DependencyReport
 * @property {!Number} vulnerabilityCount - Number of vulnerabilities in the report; can be zero
 * @property {VulnerablePackage[]} vulnerablePackages - A list of vulnerable packages together with their vulnerabilities
 */

/**
 * @callback CheckCallback
 * @param {?Error} error - Not null if an error occurred
 * @param {?DependencyReport} dependencyReport - Not null if no error occurred; contains the found vulnerabilities
 */

/**
 * Read from .owditrc the lists of packages that should be excluded or only generate warnings
 *
 * @private
 */
function readWarnsAndExcludes(cwd) {
  let warns = [];
  let excludes = [];
  const projectRoot = findRoot(cwd);
  const rcFile = path.resolve(projectRoot, rcFilename);

  if (fs.existsSync(rcFile)) {
    try {
      const content = JSON.parse(fs.readFileSync(rcFile));

      warns = content.warns || [];
      excludes = content.excludes || [];
    } catch(err) {
      // Display any error occurred when reading or parsing .owditrc but don't stop the check.
      // At this moment, this file's sole purpose is to make the vulnerability check less strict.
      // Going on when there has been an error at this step means all vulnerabilities will be taken
      // into account and the check can only get stricter.
      console.log(err);
      console.log(`Error reading or parsing ${rcFile}. As a result all dependencies will be scanned.`);
    }
  }

  if (warns.length) {
    console.log(`Vulnerabilities in these packages will only generate warnings: ${warns.join(", ")}.`)
  }
  if (excludes.length) {
    console.log(`Vulnerabilities in these packages will be IGNORED: ${excludes.join(", ")}.`)
  }

  return {warns, excludes};
}

/**
 * Recursively checks the dependencies described in the package.json file in the directory `cwd`
 * for known vulnerabilities.
 * @param  {!String}   cwd            - The current working directory; used to locate the package.json file
 * @param  {!CheckCallback} callback  - Called when the function finishes
 * @returns {null}
 */
function check(cwd, callback)
{
  const {warns, excludes} = readWarnsAndExcludes(cwd);

  const result = exec.sync("npm ls --production=true --json", { cwd: cwd, strict: true, quiet: true });
  if (result.status !== 0) {
    return callback(new Error("Error running npm ls: " + result.stderr));
  }
  let packages = [];

  try {
    packages = JSON.parse(result.stdout);
  } catch (err) {
    return callback(err);
  }

  const registerModules = {};

  /**
   * Makes a flat list of the hierarchical dependency tree generated by npm ls.
   * This method also filters out double packages if there should be any present
   * @param  {String} parentPackage - name of the parent package
   * @param  {Object} dependencies  - Object containing nested key-value pairs for dependencies
   * @return {Object[]} A flat list of packages containing the package manager (i.e. npm), the name of the package and the version of the package
   * @private
   */
  function flattenDependencies(parentPackage, dependencies) {
    const list = [];
    const deps = Object.keys(dependencies);

    deps.forEach((dependency) => {
      if (excludes.includes(dependency)) {
        return;
      }
      const dependent = dependencies[dependency];
      const key = dependency + dependent.version;

      if (!registerModules.hasOwnProperty(key)) {
        const path = [...parentPackage, `${dependency}@${dependent.version}`];

        registerModules[key] = dependent;
        registerModules[key].path = path;
        list.push({
          pm:      "npm",
          name:    dependency,
          version: dependent.version
        });
        if (dependent.dependencies) {
          list.push(...flattenDependencies(path, dependent.dependencies));
        }
      }
    });

    return list;
  }
  if (packages.dependencies) {
    const client = new Client();
    const flatList = flattenDependencies([`${packages.name}@${packages.version}`], packages.dependencies);
    const restOptions = {
      data:    flatList,
      headers: {"Content-Type": "application/json"}
    };
    const request = client.post("https://ossindex.net/v2.0/package", restOptions, (data) => {
      let numVulnerabilitiesFails = 0;   //eslint-disable-line no-magic-numbers
      let numVulnerabilitiesWarns = 0;   //eslint-disable-line no-magic-numbers
      const vulnerablePackages = data.filter((element) => {
        if (warns.includes(element.name)) {
          numVulnerabilitiesWarns += element["vulnerability-matches"];
        } else {
          numVulnerabilitiesFails += element["vulnerability-matches"];
        }
        return element["vulnerability-matches"] > 0;  //eslint-disable-line no-magic-numbers
      }).map((element) => {
        return {
          name: element.name,
          warnOnly:        warns.includes(element.name),
          version:         element.version,
          path:            registerModules[`${element.name}${element.version}`].path,
          vulnerabilities: element.vulnerabilities.map((element) => {
            return {
              title:       element.title,
              description: element.description,
              versions:    element.versions,
              references:  element.references
            };
          })
        };
      });
      const vulnerabilityReport = {
        vulnerabilityFailCount: numVulnerabilitiesFails,
        vulnerabilityWarnCount: numVulnerabilitiesWarns,
        vulnerablePackages: vulnerablePackages
      };

      return callback(null, vulnerabilityReport);
    });

    return request.on("error", callback);
  } else {
    return callback(null, {
      vulnerabilityCount: 0,  //eslint-disable-line no-magic-numbers
      vulnerablePackages: []
    });
  }
}

module.exports.check = check;
<commit_msg>Fix eslint warnings<commit_after>const exec = require("executive");
const Client = require("node-rest-client").Client;
const fs = require("fs");
const path = require("path");
const findRoot = require("find-root");
const rcFilename = ".owditrc";

/**
 * @module owdit
 */

/**
 * @typedef VulnerabilityReport
 * @property {!String} title - Title of the vulnerability
 * @property {!String} description - Short description of the vulnerability
 * @property {!String} versions - Versions of the package that are affected by this vulnerability
 * @property {!String[]} references - A list of URLs containing links to more details about the vulnerability
 */

/**
 * @typedef VulnerablePackage
 * @property {!String} name - Name of the package
 * @property {!String} version - Version of the package in semver format
 * @property {!String[]} path - Top-bottom include list of the package; e.g. ["mypackage@1.0.2", "express@2.5.11", "qs@0.4.2"]
 * @property {!Vulnerability[]} vulnerabilities - List of found vulnerabilities for the package
 */

/**
 * @typedef DependencyReport
 * @property {!Number} vulnerabilityCount - Number of vulnerabilities in the report; can be zero
 * @property {VulnerablePackage[]} vulnerablePackages - A list of vulnerable packages together with their vulnerabilities
 */

/**
 * @callback CheckCallback
 * @param {?Error} error - Not null if an error occurred
 * @param {?DependencyReport} dependencyReport - Not null if no error occurred; contains the found vulnerabilities
 */

/**
 * Read from .owditrc the lists of packages that should be excluded or only generate warnings
 * @param {String} cwd - The current working directory from which to start looking for the .owditrc file
 * @returns {Object} - Returns an object containing a list of modules to only warn about vulnerabilities and a list of modules to ignore
 *
 * @private
 */
function readWarnsAndExcludes(cwd) {
  let warns = [];
  let excludes = [];
  const projectRoot = findRoot(cwd);
  const rcFile = path.resolve(projectRoot, rcFilename);

  if (fs.existsSync(rcFile)) { //eslint-disable-line security/detect-non-literal-fs-filename, no-sync
    try {
      const content = JSON.parse(fs.readFileSync(rcFile)); //eslint-disable-line security/detect-non-literal-fs-filename, no-sync

      warns = content.warns || [];
      excludes = content.excludes || [];
    } catch (err) {
      // Display any error occurred when reading or parsing .owditrc but don't stop the check.
      // At this moment, this file's sole purpose is to make the vulnerability check less strict.
      // Going on when there has been an error at this step means all vulnerabilities will be taken
      // into account and the check can only get stricter.
      console.log(err);
      console.log(`Error reading or parsing ${rcFile}. As a result all dependencies will be scanned.`);
    }
  }

  if (warns.length) {
    console.log(`Vulnerabilities in these packages will only generate warnings: ${warns.join(", ")}.`);
  }
  if (excludes.length) {
    console.log(`Vulnerabilities in these packages will be IGNORED: ${excludes.join(", ")}.`);
  }

  return {
    warns,
    excludes
  };
}

/**
 * Recursively checks the dependencies described in the package.json file in the directory `cwd`
 * for known vulnerabilities.
 * @param  {!String}   cwd            - The current working directory; used to locate the package.json file
 * @param  {!CheckCallback} callback  - Called when the function finishes
 * @returns {null}
 */
function check(cwd, callback) {
  const {warns, excludes} = readWarnsAndExcludes(cwd);

  const result = exec.sync("npm ls --production=true --json", {
    cwd:    cwd,
    strict: true,
    quiet:  true
  });

  if (result.status !== 0) {  //eslint-disable-line no-magic-numbers
    return callback(new Error(`Error running npm ls: ${result.stderr}`));
  }
  let packages = [];

  try {
    packages = JSON.parse(result.stdout);
  } catch (err) {
    return callback(err);
  }

  const registerModules = {};

  /**
   * Makes a flat list of the hierarchical dependency tree generated by npm ls.
   * This method also filters out double packages if there should be any present
   * @param  {String} parentPackage - name of the parent package
   * @param  {Object} dependencies  - Object containing nested key-value pairs for dependencies
   * @return {Object[]} A flat list of packages containing the package manager (i.e. npm), the name of the package and the version of the package
   * @private
   */
  function flattenDependencies(parentPackage, dependencies) {
    const list = [];
    const deps = Object.keys(dependencies);

    deps.forEach((dependency) => {
      if (excludes.includes(dependency)) {
        return;
      }
      const dependent = dependencies[dependency];
      const key = dependency + dependent.version;

      if (!registerModules.hasOwnProperty(key)) {
        const path = [...parentPackage, `${dependency}@${dependent.version}`];

        registerModules[key] = dependent;
        registerModules[key].path = path;
        list.push({
          pm:      "npm",
          name:    dependency,
          version: dependent.version
        });
        if (dependent.dependencies) {
          list.push(...flattenDependencies(path, dependent.dependencies));
        }
      }
    });

    return list;
  }
  if (packages.dependencies) {
    const client = new Client();
    const flatList = flattenDependencies([`${packages.name}@${packages.version}`], packages.dependencies);
    const restOptions = {
      data:    flatList,
      headers: {"Content-Type": "application/json"}
    };
    const request = client.post("https://ossindex.net/v2.0/package", restOptions, (data) => {
      let numVulnerabilitiesFails = 0;   //eslint-disable-line no-magic-numbers
      let numVulnerabilitiesWarns = 0;   //eslint-disable-line no-magic-numbers
      const vulnerablePackages = data.filter((element) => {
        if (warns.includes(element.name)) {
          numVulnerabilitiesWarns += element["vulnerability-matches"];
        } else {
          numVulnerabilitiesFails += element["vulnerability-matches"];
        }

        return element["vulnerability-matches"] > 0;  //eslint-disable-line no-magic-numbers
      }).map((element) => {
        return {
          name:            element.name,
          warnOnly:        warns.includes(element.name),
          version:         element.version,
          path:            registerModules[`${element.name}${element.version}`].path,
          vulnerabilities: element.vulnerabilities.map((element) => {
            return {
              title:       element.title,
              description: element.description,
              versions:    element.versions,
              references:  element.references
            };
          })
        };
      });
      const vulnerabilityReport = {
        vulnerabilityFailCount: numVulnerabilitiesFails,
        vulnerabilityWarnCount: numVulnerabilitiesWarns,
        vulnerablePackages:     vulnerablePackages
      };

      return callback(null, vulnerabilityReport);
    });

    return request.on("error", callback);
  } else {
    return callback(null, {
      vulnerabilityCount: 0,  //eslint-disable-line no-magic-numbers
      vulnerablePackages: []
    });
  }
}

module.exports.check = check;
<|endoftext|>


<commit_before>(function (global) {
  'use strict';

  var re_space = new RegExp('\\s+')
    , timeout = 1000
    , sequences = {}
    , lastDocumentEvent = null;

  function keySequence(str, scope, method) {
    if (!re_space.test(str)) return key.apply(this, arguments);
    if (typeof scope == 'function') method = scope, scope = 'all';

    var keys = str.split(re_space)
      , addedHandlers = []
      , sequence = sequences[str] = {
            keys: keys
          , events: []
          , _gc: null
          , _working: null
        }
      , handler = function() {
          return sequenceHandler.apply([sequence,method], arguments);
        } // sequenceHandler.bind([sequence,method]);

    for (var i=0; i < keys.length; i++) {
      var k = keys[i];
      if (!~addedHandlers.indexOf(k)) {
        // console.log('binding', k);
        key(k, scope, handler);
        addedHandlers.push(k);
      }
    };
  }

  function sequenceHandler(evt, handler) {
    // console.log('handler', handler.shortcut, this[0]);
    var sequence = this[0]
      , method = this[1]
      , curTarget
      , lastEvt;

    if (null === sequence._working) {
      resetSequence(sequence);
    }

    clearTimeout(sequence._gc);
    sequence._gc = setTimeout(function() {
      resetSequence(sequence);
    }, timeout);

    curTarget = sequence._working[0];
    lastEvt = sequence.events[sequence.events.length - 1] || null;

    if (handler.shortcut == curTarget && (lastEvt === null || lastEvt === lastDocumentEvent)) {
      sequence._working.shift()
      sequence.events.push(evt);

      if (sequence._working.length == 0) {
        resetSequence(sequence);
        return method(evt, handler, sequence);
      }
    }
    else {
      resetSequence(sequence);
    }
  }

  function resetSequence(sequence) {
    sequence._working = sequence.keys.slice(0);
    sequence.events = [];
    if (null !== sequence._gc) {
      clearTimeout(sequence._gc);
      sequence._gc = null;
    }
  }

  function addEvent(object, event, method) {
    if (object.addEventListener)
      object.addEventListener(event, method, false);
    else if(object.attachEvent)
      object.attachEvent('on'+event, function(){ method(window.event) });
  };

  var mods = [16, 18, 17, 91, 93, 224 ];
  addEvent(document, 'keydown', function(event) {
    // ignore modifier keys
    if (~mods.indexOf(event.keyCode)) return;
    lastDocumentEvent = event;
  });

  if (!!global.key && typeof global.key.isPressed == 'function') {
    global.key.sequence = keySequence;
  }
  else console.warn('keymaster-sequences.js could not found keymaster.js');
})(this);
<commit_msg>give handler proper this<commit_after>(function (global) {
  'use strict';

  var re_space = new RegExp('\\s+')
    , timeout = 1000
    , sequences = {}
    , lastDocumentEvent = null;

  function keySequence(str, scope, method) {
    if (!re_space.test(str)) return key.apply(this, arguments);
    if (typeof scope == 'function') method = scope, scope = 'all';

    var keys = str.split(re_space)
      , addedHandlers = []
      , sequence = sequences[str] = {
            keys: keys
          , events: []
          , _gc: null
          , _working: null
        }
      , handler = function() {
          var args = Array.prototype.slice.call(arguments, 0);
          args.unshift(sequence, method);
          return sequenceHandler.apply(this, args);
        }

    for (var i=0; i < keys.length; i++) {
      var k = keys[i];
      if (!~addedHandlers.indexOf(k)) {
        // console.log('binding', k);
        key(k, scope, handler);
        addedHandlers.push(k);
      }
    };
  }

  function sequenceHandler(sequence, method, evt, handler) {
    // console.log('handler', handler.shortcut, this[0]);
    var curTarget
      , lastEvt;

    if (null === sequence._working) {
      resetSequence(sequence);
    }

    clearTimeout(sequence._gc);
    sequence._gc = setTimeout(function() {
      resetSequence(sequence);
    }, timeout);

    curTarget = sequence._working[0];
    lastEvt = sequence.events[sequence.events.length - 1] || null;

    if (handler.shortcut == curTarget && (lastEvt === null || lastEvt === lastDocumentEvent)) {
      sequence._working.shift()
      sequence.events.push(evt);

      if (sequence._working.length == 0) {
        resetSequence(sequence);
        return method.call(this, evt, handler, sequence);
      }
    }
    else {
      resetSequence(sequence);
    }
  }

  function resetSequence(sequence) {
    sequence._working = sequence.keys.slice(0);
    sequence.events = [];
    if (null !== sequence._gc) {
      clearTimeout(sequence._gc);
      sequence._gc = null;
    }
  }

  function addEvent(object, event, method) {
    if (object.addEventListener)
      object.addEventListener(event, method, false);
    else if(object.attachEvent)
      object.attachEvent('on'+event, function(){ method(window.event) });
  };

  var mods = [16, 18, 17, 91, 93, 224 ];
  addEvent(document, 'keydown', function(event) {
    // ignore modifier keys
    if (~mods.indexOf(event.keyCode)) return;
    lastDocumentEvent = event;
  });

  if (!!global.key && typeof global.key.isPressed == 'function') {
    global.key.sequence = keySequence;
  }
  else console.warn('keymaster-sequences.js could not found keymaster.js');
})(this);
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

function aspect(type) {
	return function(target, meths, hook1, hook2) {
		if(!Array.isArray(meths)) {
			meths = [meths];
		}

		meths.forEach(function(methodName) {
			var existing = target[methodName];
			if(!existing) return;

			var hookBefore, hookAfter;
			if((type == 'before') && hook1) {
				hookBefore = hook1;
			} else if(type == "around" && hook1 && hook2) {
				hookBefore = hook1;
				hookAfter = hook2;
			} else if((type == 'after') && hook1) {
				hookAfter = hook1;
			}

			var newFunc;
			if(type == 'before') {
				newFunc = function() {
					hookBefore(this, arguments);
					return existing.apply(this, arguments);
				};
			}
			else if(type == "around") {
				newFunc = function() {
					hookBefore(this, arguments);
					var ret = existing.apply(this, arguments);
					if(process.version.split('.')[0] == 'v0' && process.version.split('.')[1] < 8){
						hookAfter(this, arguments, ret);
						return ret;
					}else{
						return hookAfter(this, arguments, ret);
					}
				};
			}
			else if(type == 'after') {
				newFunc = function() {
					var ret = existing.apply(this, arguments);
					if(process.version.split('.')[0] == 'v0' && process.version.split('.')[1] < 8){
						hookAfter(this, arguments, ret);
						return ret;
					} else {
						return hookAfter(this, arguments, ret);
					}
				};
			}
			newFunc.prototype = existing.prototype;

			target[methodName] = newFunc;
		});
	};
}

exports.aroundCallback = function(args, hookBefore, hookAfter) {
	var position = this.findCallbackArg(args);
	if(position == undefined) return;

	var orig = args[position];

	args[position] = function() {
		if(hookBefore) {
			hookBefore(this, arguments);
		}

		var ret = orig.apply(this, arguments);

		if(hookAfter) {
			hookAfter(this, arguments);
		}
		return ret;
	};
}

exports.findCallbackArg = function(args) {
	var position = undefined;
    for (var i = 0; i < args.length; i++) {
        if((typeof args[i] === 'function')) {
            position = i;
            break;
        }
    }
    return position;
}

exports.before = aspect("before");

exports.around = aspect("around");

exports.after = aspect("after");<commit_msg>Expose patched method/function name in aspects API<commit_after>/*******************************************************************************
 * Copyright 2015 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

function aspect(type) {
	return function(target, meths, hook1, hook2) {
		if(!Array.isArray(meths)) {
			meths = [meths];
		}

		meths.forEach(function(methodName) {
			var existing = target[methodName];
			if(!existing) return;

			var hookBefore, hookAfter;
			if((type == 'before') && hook1) {
				hookBefore = hook1;
			} else if(type == "around" && hook1 && hook2) {
				hookBefore = hook1;
				hookAfter = hook2;
			} else if((type == 'after') && hook1) {
				hookAfter = hook1;
			}

			var newFunc;
			if(type == 'before') {
				newFunc = function() {
					hookBefore(this, arguments, methodName);
					return existing.apply(this, arguments);
				};
			}
			else if(type == "around") {
				newFunc = function() {
					hookBefore(this, arguments, methodName);
					var ret = existing.apply(this, arguments);
					if(process.version.split('.')[0] == 'v0' && process.version.split('.')[1] < 8){
						hookAfter(this, arguments, ret, methodName);
						return ret;
					}else{
						return hookAfter(this, arguments, ret, methodName);
					}
				};
			}
			else if(type == 'after') {
				newFunc = function() {
					var ret = existing.apply(this, arguments);
					if(process.version.split('.')[0] == 'v0' && process.version.split('.')[1] < 8){
						hookAfter(this, arguments, ret, methodName);
						return ret;
					} else {
						return hookAfter(this, arguments, ret, methodName);
					}
				};
			}
			newFunc.prototype = existing.prototype;

			target[methodName] = newFunc;
		});
	};
}

exports.aroundCallback = function(args, hookBefore, hookAfter) {
	var position = this.findCallbackArg(args);
	if(position == undefined) return;

	var orig = args[position];

	args[position] = function() {
		if(hookBefore) {
			hookBefore(this, arguments);
		}

		var ret = orig.apply(this, arguments);

		if(hookAfter) {
			hookAfter(this, arguments, ret);
		}
		return ret;
	};
}

exports.findCallbackArg = function(args) {
	var position = undefined;
    for (var i = 0; i < args.length; i++) {
        if((typeof args[i] === 'function')) {
            position = i;
            break;
        }
    }
    return position;
}

exports.before = aspect("before");

exports.around = aspect("around");

exports.after = aspect("after");<|endoftext|>


<commit_before>var request = require('request');

var GoogleAnalytics = function(ua, host) {
  this.ua = ua;
  this.host = host;
  this.debug = false;
};

GoogleAnalytics.prototype._serialize = function(obj) {
  var str = [];
  for(var p in obj)
    str.push(p + "=" + encodeURIComponent(obj[p]));
  return str.join("&");
};

GoogleAnalytics.prototype._makeRequest = function(obj, cb) {
  var self = this;
  var userId = null;
  if (obj.userId) {
    userId = obj.userId;
    delete obj.userId;
  } else {
    userId = Math.round(Math.random()*1000000);
  }
  var today = new Date().getTime().toString();
  obj.utmac = this.ua;
  obj.utmn = today;
  obj.utmv = "1";
  obj.utmr= "-";
  obj.utmcc = "__utma="+userId+"."+Math.round(Math.random()*1000000)+"."+today+"."+today+"."+today+".3; __utmz="+userId+"."+today+".1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none);";
  var url = "http://www.google-analytics.com/__utm.gif?"+this._serialize(obj);

  request({
    url: url
  }, function(err, res, body) {
    if (self.debug) {
      console.log(url);
      if (err) {
        console.log(err);
      }
    }
    if (cb) {
      cb(err);
    }
  });
  return userId;
};

GoogleAnalytics.prototype.trackPage = function(path, userId, cb) {
  if (arguments.length == 2 && (typeof userId === 'function')) {
    cb = userId;
    userId = null;
  }
  var obj = {
    userId: userId,
    utmhn: this.host,
    utmp: path
  };
  return this._makeRequest(obj, cb);
};

// see https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
// event is an object containing {category, action, [label], [value], [nonInteraction]}
GoogleAnalytics.prototype.trackEvent = function(event, userId, cb) {
  var obj,
    payload = [];

  if (arguments.length === 2 && (typeof userId === 'function')) {
    cb = userId;
    userId = null;
  }

  ['category', 'action', 'label'].forEach(function(key) {
    var val = event[key];
    if (typeof val !== 'undefined') {
      payload.push(val);
    }
  });

  obj = {
    userId: userId,
    utmhn: this.host,
    utmt: 'event',
    utme: '5(' + payload.join('*') + ')'
  };

  if (typeof event.value !== 'undefined') {
    obj.utme += '(' + Math.round(event.value) + ')';
  }

  if (event.nonInteraction) {
    obj.utmni = 1;
  }

  return this._makeRequest(obj, cb);
};

module.exports = GoogleAnalytics;
<commit_msg>Remove manual hoisting, keep same style as original<commit_after>var request = require('request');

var GoogleAnalytics = function(ua, host) {
  this.ua = ua;
  this.host = host;
  this.debug = false;
};

GoogleAnalytics.prototype._serialize = function(obj) {
  var str = [];
  for(var p in obj)
    str.push(p + "=" + encodeURIComponent(obj[p]));
  return str.join("&");
};

GoogleAnalytics.prototype._makeRequest = function(obj, cb) {
  var self = this;
  var userId = null;
  if (obj.userId) {
    userId = obj.userId;
    delete obj.userId;
  } else {
    userId = Math.round(Math.random()*1000000);
  }
  var today = new Date().getTime().toString();
  obj.utmac = this.ua;
  obj.utmn = today;
  obj.utmv = "1";
  obj.utmr= "-";
  obj.utmcc = "__utma="+userId+"."+Math.round(Math.random()*1000000)+"."+today+"."+today+"."+today+".3; __utmz="+userId+"."+today+".1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none);";
  var url = "http://www.google-analytics.com/__utm.gif?"+this._serialize(obj);

  request({
    url: url
  }, function(err, res, body) {
    if (self.debug) {
      console.log(url);
      if (err) {
        console.log(err);
      }
    }
    if (cb) {
      cb(err);
    }
  });
  return userId;
};

GoogleAnalytics.prototype.trackPage = function(path, userId, cb) {
  if (arguments.length == 2 && (typeof userId === 'function')) {
    cb = userId;
    userId = null;
  }
  var obj = {
    userId: userId,
    utmhn: this.host,
    utmp: path
  };
  return this._makeRequest(obj, cb);
};

// see https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
// event is an object containing {category, action, [label], [value], [nonInteraction]}
GoogleAnalytics.prototype.trackEvent = function(event, userId, cb) {
  var payload = [];

  if (arguments.length === 2 && (typeof userId === 'function')) {
    cb = userId;
    userId = null;
  }

  ['category', 'action', 'label'].forEach(function(key) {
    var val = event[key];
    if (typeof val !== 'undefined') {
      payload.push(val);
    }
  });

  var obj = {
    userId: userId,
    utmhn: this.host,
    utmt: 'event',
    utme: '5(' + payload.join('*') + ')'
  };

  if (typeof event.value !== 'undefined') {
    obj.utme += '(' + Math.round(event.value) + ')';
  }

  if (event.nonInteraction) {
    obj.utmni = 1;
  }

  return this._makeRequest(obj, cb);
};

module.exports = GoogleAnalytics;
<|endoftext|>


<commit_before>
var extend = require('extend');
var config = require('./config');

function Query (provider, name) {
    this.provider = provider;

    this.method = '';
    this.endpoint = '';
    this._options = {api:name||this.provider.api};
    this.api = this.provider.apis[name||this.provider.api];
}

// http

Query.prototype.get = function (endpoint) {
    this.method = 'get';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.post = function (endpoint) {
    this.method = 'post';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.put = function (endpoint) {
    this.method = 'put';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.del = function (endpoint) {
    this.method = 'del';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.request = function (callback) {
    return this.provider[this.method](this.endpoint, this._options, callback);
}

// http aliases

Query.prototype.select = function (endpoint) {
    return this.get(endpoint);
}

Query.prototype.update = function (endpoint) {
    return this.post(endpoint);
}

Query.prototype.create = function (endpoint) {
    return this.put(endpoint);
}

Query.prototype.insert = function (endpoint) {
    return this.put(endpoint);
}

// options

Query.prototype.headers = function (options) {
    extend(true, this._options, {headers:options});
    return this;
}

Query.prototype.qs = function (options) {
    extend(true, this._options, {qs:options});
    return this;
}

Query.prototype.form = function (options) {
    extend(true, this._options, {form:options});
    return this;
}

Query.prototype.formData = function (options) {
    extend(true, this._options, {headers:{'content-type':'multipart/form-data'}});
    extend(true, this._options, {formData:options});
    return this;
}

Query.prototype.multipart = function (options) {
    extend(true, this._options, {multipart:options});
    return this;
}

Query.prototype.options = function (options) {
    extend(true, this._options, options);
    return this;
}

// options aliases

Query.prototype.where = function (options) {
    return this.qs(options);
}

Query.prototype.set = function (options) {
    return this.form(options);
}

Query.prototype.upload = function (options) {
    return (options instanceof Array)
        ? this.multipart(options)
        : this.formData(options);
}

// obsolete
Query.prototype.files = function (files) {
    extend(true, this._options, {upload:files});
    return this;
}

// authentication

function dcopy (target, values) {
    var copy = {}, index = 0;
    (function read (target, copy) {
        for (var key in target) {
            var obj = target[key];
            if (obj instanceof Object) {
                var value = {},
                    last = add(copy, key, value);
                read(obj, last);
            } else {
                var value = obj;
                add(copy, key, value.replace('['+index+']',values[index]));
                index++;
            }
        }
    }(target, copy));
    return copy;
}
function add (copy, key, value) {
    if (copy instanceof Object) {
        copy[key] = value;
        return copy[key];
    }
}

Query.prototype.auth = function () {
    var auth = config.auth(this.endpoint, this.api.endpoints)||this.api.auth;
    if (!auth) return this;

    var options = (auth instanceof Array)
        ? auth[arguments.length-1]
        : auth;

    var result = dcopy(options, arguments);
    extend(true, this._options, result);
    return this;
}

exports = module.exports = Query;
<commit_msg>Remove headers in formData query method<commit_after>
var extend = require('extend');
var config = require('./config');

function Query (provider, name) {
    this.provider = provider;

    this.method = '';
    this.endpoint = '';
    this._options = {api:name||this.provider.api};
    this.api = this.provider.apis[name||this.provider.api];
}

// http

Query.prototype.get = function (endpoint) {
    this.method = 'get';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.post = function (endpoint) {
    this.method = 'post';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.put = function (endpoint) {
    this.method = 'put';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.del = function (endpoint) {
    this.method = 'del';
    this.endpoint = endpoint;
    return this;
}

Query.prototype.request = function (callback) {
    return this.provider[this.method](this.endpoint, this._options, callback);
}

// http aliases

Query.prototype.select = function (endpoint) {
    return this.get(endpoint);
}

Query.prototype.update = function (endpoint) {
    return this.post(endpoint);
}

Query.prototype.create = function (endpoint) {
    return this.put(endpoint);
}

Query.prototype.insert = function (endpoint) {
    return this.put(endpoint);
}

// options

Query.prototype.headers = function (options) {
    extend(true, this._options, {headers:options});
    return this;
}

Query.prototype.qs = function (options) {
    extend(true, this._options, {qs:options});
    return this;
}

Query.prototype.form = function (options) {
    extend(true, this._options, {form:options});
    return this;
}

Query.prototype.formData = function (options) {
    extend(true, this._options, {formData:options});
    return this;
}

Query.prototype.multipart = function (options) {
    extend(true, this._options, {multipart:options});
    return this;
}

Query.prototype.options = function (options) {
    extend(true, this._options, options);
    return this;
}

// options aliases

Query.prototype.where = function (options) {
    return this.qs(options);
}

Query.prototype.set = function (options) {
    return this.form(options);
}

Query.prototype.upload = function (options) {
    return (options instanceof Array)
        ? this.multipart(options)
        : this.formData(options);
}

// obsolete
Query.prototype.files = function (files) {
    extend(true, this._options, {upload:files});
    return this;
}

// authentication

function dcopy (target, values) {
    var copy = {}, index = 0;
    (function read (target, copy) {
        for (var key in target) {
            var obj = target[key];
            if (obj instanceof Object) {
                var value = {},
                    last = add(copy, key, value);
                read(obj, last);
            } else {
                var value = obj;
                add(copy, key, value.replace('['+index+']',values[index]));
                index++;
            }
        }
    }(target, copy));
    return copy;
}
function add (copy, key, value) {
    if (copy instanceof Object) {
        copy[key] = value;
        return copy[key];
    }
}

Query.prototype.auth = function () {
    var auth = config.auth(this.endpoint, this.api.endpoints)||this.api.auth;
    if (!auth) return this;

    var options = (auth instanceof Array)
        ? auth[arguments.length-1]
        : auth;

    var result = dcopy(options, arguments);
    extend(true, this._options, result);
    return this;
}

exports = module.exports = Query;
<|endoftext|>


<commit_before>'use strict';

const AWS = require('aws-sdk');

module.exports = {};
module.exports.generic = generic;
module.exports.minuteAggregation = minuteAggregation;

function generic(queue, body) {
    const sqs = new AWS.SQS();

    return new Promise((resolve, reject) => {
        sqs.sendMessage({
            QueueUrl: queue,
            MessageBody: JSON.stringify(body)
        }, (err, data) => {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function minuteAggregation(keys, propogate) {
    const promises = [];

    keys.forEach(key => {
        promises.push(generic(process.env.perMinQueue, {
            worker: 'aggregator',
            jobType: 'minute',
            key: key.slice(0, 16)
        }));

        if (propogate) {
            promises.push(hourAggregation(key));
            // promises.push(dayAggregation);
            // promises.push(monthAggregation);
        }
    });

    return Promise.all(promises);
}

function hourAggregation(keys) {
    const promises = [];

    keys.forEach(key => {
        promises.push(generic(process.env.perMinQueue, {
            worker: 'aggregator',
            jobType: 'hour',
            key: key.slice(0, 13)
        }));
    });

    return Promise.all(promises);
}
<commit_msg>Come on aaron<commit_after>'use strict';

const AWS = require('aws-sdk');

module.exports = {};
module.exports.generic = generic;
module.exports.minuteAggregation = minuteAggregation;

function generic(queue, body) {
    const sqs = new AWS.SQS();

    return new Promise((resolve, reject) => {
        sqs.sendMessage({
            QueueUrl: queue,
            MessageBody: JSON.stringify(body)
        }, (err, data) => {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function minuteAggregation(keys, propogate) {
    const promises = [];

    keys.forEach(key => {
        promises.push(generic(process.env.perMinQueue, {
            worker: 'aggregator',
            jobType: 'minute',
            key: key.slice(0, 16)
        }));
    });

    if (propogate) {
        promises.push(hourAggregation(keys));
        // promises.push(dayAggregation);
        // promises.push(monthAggregation);
    }

    return Promise.all(promises);
}

function hourAggregation(keys) {
    const promises = [];

    keys.forEach(key => {
        promises.push(generic(process.env.perMinQueue, {
            worker: 'aggregator',
            jobType: 'hour',
            key: key.slice(0, 13)
        }));
    });

    return Promise.all(promises);
}
<|endoftext|>


<commit_before>/* global THREE */
(function() {
  'use strict';

  var container;

  var scene, camera, controls, renderer;

  var clock = new THREE.Clock();
  var running = true;

  var dt = 1 / 60;
  var accumulatedTime = 0;

  var circles = (function() {
    var geometry = new THREE.BoxGeometry( 1, 1, 1 );
    var material = new THREE.MeshPhongMaterial();

    var mesh = new THREE.Mesh( geometry, material );
    mesh.position.x = 2;

    var group = new THREE.Group();
    group.add( mesh );

    var light = new THREE.DirectionalLight();
    light.position.set( 0, 0, 16 );
    group.add( light );

    group.add( new THREE.AmbientLight( '#222' ) );

    return {
      group: group,
      update: function( dt ) {
        mesh.rotation.x += 5 * dt;
        mesh.rotation.y += 5 * dt;
        group.rotation.y += 5 * dt;
      }
    };
  })();

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
    camera.position.set( 0, 0, 8 );

    controls = new THREE.OrbitControls( camera, renderer.domElement );

    controls.addEventListener( 'change', function() {
      if ( !running ) {
        render();
      }
    });

    scene.add( circles.group );
  }

  function render() {
    renderer.render( scene, camera );
  }

  function animate() {
    if ( !running ) {
      return;
    }

    var delta = Math.min( clock.getDelta(), 0.1 );
    accumulatedTime += delta;

    while ( accumulatedTime >= dt ) {
      circles.update( dt );
      accumulatedTime -= dt;
    }

    render();
    requestAnimationFrame( animate );
  }

  init();
  animate();

  window.addEventListener( 'resize', function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );
  });

  // Double click to restart animation.
  document.addEventListener( 'dblclick', function() {
    running = !running;

    if ( running ) {
      clock.start();
      animate();
    }
  });

  // Scroll to control animation.
  renderer.domElement.addEventListener( 'wheel', function( event ) {
    if ( !event.deltaY ) {
      return;
    }

    event.preventDefault();
    running = false;

    circles.update( event.deltaY * dt );
    render();
  });
})();
<commit_msg>spinners: Add loop spinner.<commit_after>/* global THREE */
(function() {
  'use strict';

  var container;

  var scene, camera, controls, renderer;

  var clock = new THREE.Clock();
  var running = true;

  var dt = 1 / 60;
  var accumulatedTime = 0;

  var spinners = {
    circles: (function() {
      var geometry = new THREE.BoxGeometry( 1, 1, 1 );
      var material = new THREE.MeshPhongMaterial();

      var mesh = new THREE.Mesh( geometry, material );
      mesh.position.x = 2;

      var group = new THREE.Group();
      group.add( mesh );

      var light = new THREE.DirectionalLight();
      light.position.set( 0, 0, 16 );
      group.add( light );

      group.add( new THREE.AmbientLight( '#222' ) );

      return {
        group: group,
        update: function( dt ) {
          mesh.rotation.x += 5 * dt;
          mesh.rotation.y += 5 * dt;
          group.rotation.y += 5 * dt;
        }
      };
    })(),

    loops: (function() {
      var geometry = new THREE.TorusGeometry( 1, 0.1, 16, 64 );
      var material = new THREE.MeshPhongMaterial();

      var group = new THREE.Group();
      var meshes = new THREE.Group();

      [
        { rotation: [ Math.PI / 2, 0, 0 ], scale: 1.4 },
        { rotation: [ 0, Math.PI / 2, 0 ], scale: 1.2 },
        { rotation: [ 0, 0, Math.PI / 2 ] }
      ].map(function( transform ) {
        var scale = transform.scale || 1;
        var transformedGeometry = geometry.clone()
          .scale( scale, scale, scale );

        transformedGeometry.computeFaceNormals();
        transformedGeometry.computeVertexNormals();

        var mesh = new THREE.Mesh( transformedGeometry, material );
        mesh.rotation.fromArray( transform.rotation );
        return mesh;
      }).reduce(function( parent, mesh ) {
        parent.add( mesh );
        return mesh;
      }, meshes );

      group.add( meshes );

      var light = new THREE.DirectionalLight();
      light.position.set( 0, 0, 16 );
      group.add( light );

      group.add( new THREE.AmbientLight( '#222' ) );

      return {
        group: group,
        update: function( dt ) {
          meshes.traverse(function( mesh ) {
            if (!mesh.geometry) {
              return;
            }

            mesh.rotation.x += 2 * dt;
            mesh.rotation.y += 2 * dt;
          });
        }
      };
    })()
  };

  var spinner = spinners.loops;

  function init() {
    container = document.createElement( 'div' );
    document.body.appendChild( container );

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight );
    camera.position.set( 0, 0, 8 );

    controls = new THREE.OrbitControls( camera, renderer.domElement );

    controls.addEventListener( 'change', function() {
      if ( !running ) {
        render();
      }
    });

    scene.add( spinner.group );
  }

  function render() {
    renderer.render( scene, camera );
  }

  function animate() {
    if ( !running ) {
      return;
    }

    var delta = Math.min( clock.getDelta(), 0.1 );
    accumulatedTime += delta;

    while ( accumulatedTime >= dt ) {
      spinner.update( dt );
      accumulatedTime -= dt;
    }

    render();
    requestAnimationFrame( animate );
  }

  init();
  animate();

  window.addEventListener( 'resize', function() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );
  });

  // Double click to restart animation.
  document.addEventListener( 'dblclick', function() {
    running = !running;

    if ( running ) {
      clock.start();
      animate();
    }
  });

  // Scroll to control animation.
  renderer.domElement.addEventListener( 'wheel', function( event ) {
    if ( !event.deltaY ) {
      return;
    }

    event.preventDefault();
    running = false;

    spinner.update( event.deltaY * dt );
    render();
  });
})();
<|endoftext|>


<commit_before>/**
  AuthGateway
  @description Exposes an API to create a user token schema and registers
    the required middleware for verifying and unpacking user data into the req
    object, also exposes an API for generating user tokens with a given payload
  @author tylerFowler
**/

const crypto      = require('crypto');
const _           = require('underscore');
const Promise     = require('bluebird');
const JWT         = Promise.promisifyAll(require('jsonwebtoken'));
const RoleMgr     = require('./role_manager');
const AuthGWError = require('./errors');
const Flavors     = require('./flavors');

const { TokenTransport, TransportMethods } = require('./token_transport');

module.exports = exports = AuthGW;

// secondary components
let middleware = exports.Middleware = {};

/**
  @constructor AuthGW
  @desc Constructor function that creates a new gateway manager instance with
    the configured settings
  @param {string[]} roles list of all possible user roles from lowest
    access level to highest access level
  @param {string} authorityName serves as the JWT 'issuer' name
  @param {object[]} dataSchema defines the keys to r/w to/from tokens
  @param {object} opts
  @param {string} opts.tokenAlgo @default 'HS256'
  @param {string|Buffer} opts.tokenSecret @default 24 random bytes
  @param {number} opts.tokenExpiredCode @default 419 (Token Expired)
  @param {string} opts.flavor
  @param {object} opts.transport
  @param {string} opts.transport.method
  @param {string} opts.transport.key
  @param {object} opts.transport.injectionOptions
**/
function AuthGW(roles, authorityName, dataSchema, opts = {}) {
  if (!Array.isArray(roles)) roles = [roles];

  this.roles = roles;
  this.authorityName = authorityName;

  // if required is omitted default to false
  this.dataSchema = dataSchema.map(s => _.defaults(s, { required: false }));

  this.opts = Object.assign({
    tokenAlgo: 'HS256', // HMAC w/ SHA-256
    tokenSecret: crypto.randomBytes(24).toString('hex'),
    tokenExpiredCode: 419,
    flavor: Flavors.EXPRESSJS
  }, opts, {
    transport: Object.assign({
      method: TransportMethods.HEADER,
      key: 'x-access-token'
    }, opts.transport)
  });

  const { flavor, transport } = this.opts;
  this.tokenTransport = new TokenTransport(
    flavor, transport.method, transport.key, transport.injectionOptions
  );

  this.RoleManager = new RoleMgr(roles);
}

/**
  @name AuthGW#createToken
  @desc Creates a new auth token with the given role & data
  @param {object} data written to the token, needs keys required by schema
  @param {string} role one of the roles given to this authgw instance
  @param {number} expiry time to live of the token in minutes
  @param {object} claims arbitrary claims to be passed to JWT
  @returns {Promise<String>} token containing the raw encrypted token
  @returns {Promise<InvalidRoleError>} if the given role is not listed
**/
AuthGW.prototype.createToken = function createToken(data, role, expiry, claims) {
  return Promise.attempt(() => {
    if (!_.contains(this.roles, role))
      throw new AuthGWError.InvalidRoleError(role);
  })
  .then(() =>
    new Promise((resolve, reject) => {
      const opts = Object.assign({
        algorithm: this.opts.tokenAlgo,
        expiresIn: expiry * 60,
        issuer: this.authorityName
      }, claims || {});

      JWT.sign({data, role}, this.opts.tokenSecret, opts, (err, token) => {
        if (err) reject(err);
        else resolve(token);
      });
    })
  );
};

/**
  @name AuthGW#createTokenSync
  @desc Sync version of create token
  @see AutghGW#createToken
  @returns {string} containing the encrypted token
  @throws InvalidRoleError if the given role is not listed
**/
AuthGW.prototype.createTokenSync = function createToken(data, role, expiry, claims) {
  if (!_.contains(this.roles, role))
    throw new AutghGwError.InvalidRoleError(role);

  const opts = Object.assign({
    algorithm: this.opts.tokenAlgo,
    expiresIn: expiry * 60,
    issuer: this.authorityName
  }, claims || {});

  return JWT.sign({ data, role }, this.opts.tokenSecret, opts);
};

/**
  @name AuthGW#verifyToken
  @desc Verifies that a given token is valid, unexpired, and trusted
  @param {string} token is the encrypted auth token
  @param {object} claims are used in token validation
  @returns {Promise<Object>} object containing the token's payload data if valid
**/
AuthGW.prototype.verifyToken = function verifyToken(token, claims) {
  const opts = Object.assign({ issuer: this.authorityName }, claims || {});
  return JWT.verifyAsync(token, this.opts.tokenSecret, opts);
};

/**
  @name AuthGW#verifyTokenSync
  @desc Sync version of verify token
  @see AuthGW#verifyToken
  @returns {object} contains the token's payload data if valid
**/
AuthGW.prototype.verifyTokenSync = function verifyToken(token, claims) {
  const opts = Object.assign({ issuer: this.authorityName }, claims || {});
  return JWT.verify(token, this.opts.tokenSecret, opts);
};

/**
  @name AuthGW#injectToken
  @desc injects the given token into the response object using the token
    transport associated with the instance
  @param {object} response
  @param {string} token the token to inject
**/
AuthGW.prototype.injectToken = function inejctToken(response, token) {
  this.tokenTransport.injectToken(response, token);
};

/**
  @name AuthGW#middleware
  @desc Factory that creates the two Express middleware functions necessary
    for writing the data we need & verifying the auth token
  @param {object} verifClaims extra claims that will be passed to verification
  @returns [ verifyTokenFn(req,res,next), injectDataFn(req,res,next) ]
**/
AuthGW.prototype.middleware = function createExpressMiddleware(verifClaims) {
  switch (this.opts.flavor) {
  case flavors.EXPRESSJS:
    return [
      middleware.verifyTokenExpress.call(this, verifClaims),
      middleware.injectTokenDataExpress.call(this)
    ];
  default:
    throw new InvalidFlavorError(this.opts.flavor, 'middleware');
  }
};

/**
  @name Middleware#verifyToken
  @desc ExpressJS flavor of the verify token middleware, verifies the auth token
    Note that if no auth token was given we *will* move along, but without
    writing any data to the request object
  @param {object} verifClaims
  @returns {function} verifyToken
  @returns 401 if the given token is invalid or expired
**/
middleware.verifyTokenExpress = function verifyToken(verifClaims) {
  return (req, res, next) => {
    const authToken = this.tokenTransport.extractToken(req);

    // if there's no token just don't inject the user data
    if (!authToken) return next();

    this.verifyToken(authToken, verifClaims || {})
    .then(tokenData => {
      req._tokenData = tokenData.data;
      req.userRole = tokenData.role;
      next();
    })

    // NOTE: it is **vitally** important that the client does *not* send a
    // another request with this token after receiving this error code
    // (say to prompt signin) or else this will be sent again,
    // resulting in an infinite loop
    .catch(
      JWT.TokenExpiredError, () => res.sendStatus(this.opts.tokenExpiredCode)
    )

    // token is malformed or otherwise unreadable
    .catch(JWT.JsonWebTokenError, () =>
      res.status(401).send('Invalid authorization token')
    )

    // general error, send 500
    .catch(err => next(err));
  };
};

/**
  @name Middleware#injectTokenData
  @desc ExpressJS flavor of the injection middleware, injects the decoded
    auth token's data into the req object, returning an error if a required
    field from the schema is missing
  @returns {function} injectTokenData
  @returns 401 if the given token is missing required data
**/
middleware.injectTokenDataExpress = function injectTokenData() {
  return (req, res, next) => {
    var err;
    if (!req._tokenData) return next();

    _.chain(req._tokenData)
    .pick(_.pluck(this.dataSchema, 'name'))
    .tap(filtered => {
      let result = validateSchema(filtered, this.dataSchema);
      if (!result.valid)
        err = new Error(`Required item ${result.missingKey} is missing`);
    })
    .each((v, k) => { if (err) return; req[k] = v; });

    if (err) next(err);
    else next();
  };
};

/** Utility Functions **/
/**
  @name validateSchema
  @desc Validates that the given data fulfills the given schema
  @param {object} data to validate
  @param {object} schema AuthGW schema to validate against
  @returns { valid, missingKey }
**/
function validateSchema(data, schema) {
  let missing = _.find(schema, s => s.required && !_.has(data, s.name));

  if (missing)
    return { valid: false, missingKey: missing.name };

  return { valid: true };
}
<commit_msg>Fix typo<commit_after>/**
  AuthGateway
  @description Exposes an API to create a user token schema and registers
    the required middleware for verifying and unpacking user data into the req
    object, also exposes an API for generating user tokens with a given payload
  @author tylerFowler
**/

const crypto      = require('crypto');
const _           = require('underscore');
const Promise     = require('bluebird');
const JWT         = Promise.promisifyAll(require('jsonwebtoken'));
const RoleMgr     = require('./role_manager');
const AuthGWError = require('./errors');
const Flavors     = require('./flavors');

const { TokenTransport, TransportMethods } = require('./token_transport');

module.exports = exports = AuthGW;

// secondary components
let middleware = exports.Middleware = {};

/**
  @constructor AuthGW
  @desc Constructor function that creates a new gateway manager instance with
    the configured settings
  @param {string[]} roles list of all possible user roles from lowest
    access level to highest access level
  @param {string} authorityName serves as the JWT 'issuer' name
  @param {object[]} dataSchema defines the keys to r/w to/from tokens
  @param {object} opts
  @param {string} opts.tokenAlgo @default 'HS256'
  @param {string|Buffer} opts.tokenSecret @default 24 random bytes
  @param {number} opts.tokenExpiredCode @default 419 (Token Expired)
  @param {string} opts.flavor
  @param {object} opts.transport
  @param {string} opts.transport.method
  @param {string} opts.transport.key
  @param {object} opts.transport.injectionOptions
**/
function AuthGW(roles, authorityName, dataSchema, opts = {}) {
  if (!Array.isArray(roles)) roles = [roles];

  this.roles = roles;
  this.authorityName = authorityName;

  // if required is omitted default to false
  this.dataSchema = dataSchema.map(s => _.defaults(s, { required: false }));

  this.opts = Object.assign({
    tokenAlgo: 'HS256', // HMAC w/ SHA-256
    tokenSecret: crypto.randomBytes(24).toString('hex'),
    tokenExpiredCode: 419,
    flavor: Flavors.EXPRESSJS
  }, opts, {
    transport: Object.assign({
      method: TransportMethods.HEADER,
      key: 'x-access-token'
    }, opts.transport)
  });

  const { flavor, transport } = this.opts;
  this.tokenTransport = new TokenTransport(
    flavor, transport.method, transport.key, transport.injectionOptions
  );

  this.RoleManager = new RoleMgr(roles);
}

/**
  @name AuthGW#createToken
  @desc Creates a new auth token with the given role & data
  @param {object} data written to the token, needs keys required by schema
  @param {string} role one of the roles given to this authgw instance
  @param {number} expiry time to live of the token in minutes
  @param {object} claims arbitrary claims to be passed to JWT
  @returns {Promise<String>} token containing the raw encrypted token
  @returns {Promise<InvalidRoleError>} if the given role is not listed
**/
AuthGW.prototype.createToken = function createToken(data, role, expiry, claims) {
  return Promise.attempt(() => {
    if (!_.contains(this.roles, role))
      throw new AuthGWError.InvalidRoleError(role);
  })
  .then(() =>
    new Promise((resolve, reject) => {
      const opts = Object.assign({
        algorithm: this.opts.tokenAlgo,
        expiresIn: expiry * 60,
        issuer: this.authorityName
      }, claims || {});

      JWT.sign({data, role}, this.opts.tokenSecret, opts, (err, token) => {
        if (err) reject(err);
        else resolve(token);
      });
    })
  );
};

/**
  @name AuthGW#createTokenSync
  @desc Sync version of create token
  @see AutghGW#createToken
  @returns {string} containing the encrypted token
  @throws InvalidRoleError if the given role is not listed
**/
AuthGW.prototype.createTokenSync = function createToken(data, role, expiry, claims) {
  if (!_.contains(this.roles, role))
    throw new AutghGwError.InvalidRoleError(role);

  const opts = Object.assign({
    algorithm: this.opts.tokenAlgo,
    expiresIn: expiry * 60,
    issuer: this.authorityName
  }, claims || {});

  return JWT.sign({ data, role }, this.opts.tokenSecret, opts);
};

/**
  @name AuthGW#verifyToken
  @desc Verifies that a given token is valid, unexpired, and trusted
  @param {string} token is the encrypted auth token
  @param {object} claims are used in token validation
  @returns {Promise<Object>} object containing the token's payload data if valid
**/
AuthGW.prototype.verifyToken = function verifyToken(token, claims) {
  const opts = Object.assign({ issuer: this.authorityName }, claims || {});
  return JWT.verifyAsync(token, this.opts.tokenSecret, opts);
};

/**
  @name AuthGW#verifyTokenSync
  @desc Sync version of verify token
  @see AuthGW#verifyToken
  @returns {object} contains the token's payload data if valid
**/
AuthGW.prototype.verifyTokenSync = function verifyToken(token, claims) {
  const opts = Object.assign({ issuer: this.authorityName }, claims || {});
  return JWT.verify(token, this.opts.tokenSecret, opts);
};

/**
  @name AuthGW#injectToken
  @desc injects the given token into the response object using the token
    transport associated with the instance
  @param {object} response
  @param {string} token the token to inject
**/
AuthGW.prototype.injectToken = function inejctToken(response, token) {
  this.tokenTransport.injectToken(response, token);
};

/**
  @name AuthGW#middleware
  @desc Factory that creates the two Express middleware functions necessary
    for writing the data we need & verifying the auth token
  @param {object} verifClaims extra claims that will be passed to verification
  @returns [ verifyTokenFn(req,res,next), injectDataFn(req,res,next) ]
**/
AuthGW.prototype.middleware = function createExpressMiddleware(verifClaims) {
  switch (this.opts.flavor) {
  case Flavors.EXPRESSJS:
    return [
      middleware.verifyTokenExpress.call(this, verifClaims),
      middleware.injectTokenDataExpress.call(this)
    ];
  default:
    throw new InvalidFlavorError(this.opts.flavor, 'middleware');
  }
};

/**
  @name Middleware#verifyToken
  @desc ExpressJS flavor of the verify token middleware, verifies the auth token
    Note that if no auth token was given we *will* move along, but without
    writing any data to the request object
  @param {object} verifClaims
  @returns {function} verifyToken
  @returns 401 if the given token is invalid or expired
**/
middleware.verifyTokenExpress = function verifyToken(verifClaims) {
  return (req, res, next) => {
    const authToken = this.tokenTransport.extractToken(req);

    // if there's no token just don't inject the user data
    if (!authToken) return next();

    this.verifyToken(authToken, verifClaims || {})
    .then(tokenData => {
      req._tokenData = tokenData.data;
      req.userRole = tokenData.role;
      next();
    })

    // NOTE: it is **vitally** important that the client does *not* send a
    // another request with this token after receiving this error code
    // (say to prompt signin) or else this will be sent again,
    // resulting in an infinite loop
    .catch(
      JWT.TokenExpiredError, () => res.sendStatus(this.opts.tokenExpiredCode)
    )

    // token is malformed or otherwise unreadable
    .catch(JWT.JsonWebTokenError, () =>
      res.status(401).send('Invalid authorization token')
    )

    // general error, send 500
    .catch(err => next(err));
  };
};

/**
  @name Middleware#injectTokenData
  @desc ExpressJS flavor of the injection middleware, injects the decoded
    auth token's data into the req object, returning an error if a required
    field from the schema is missing
  @returns {function} injectTokenData
  @returns 401 if the given token is missing required data
**/
middleware.injectTokenDataExpress = function injectTokenData() {
  return (req, res, next) => {
    var err;
    if (!req._tokenData) return next();

    _.chain(req._tokenData)
    .pick(_.pluck(this.dataSchema, 'name'))
    .tap(filtered => {
      let result = validateSchema(filtered, this.dataSchema);
      if (!result.valid)
        err = new Error(`Required item ${result.missingKey} is missing`);
    })
    .each((v, k) => { if (err) return; req[k] = v; });

    if (err) next(err);
    else next();
  };
};

/** Utility Functions **/
/**
  @name validateSchema
  @desc Validates that the given data fulfills the given schema
  @param {object} data to validate
  @param {object} schema AuthGW schema to validate against
  @returns { valid, missingKey }
**/
function validateSchema(data, schema) {
  let missing = _.find(schema, s => s.required && !_.has(data, s.name));

  if (missing)
    return { valid: false, missingKey: missing.name };

  return { valid: true };
}
<|endoftext|>


<commit_before>var
  async = require('async');

var
  CONCURRENT = 5;

module.exports = function queueCreator (collection, fn, callback) {
  var queue = async.queue(fn, CONCURRENT);

  queue.drain = function () {
    callback && callback();
  };

  (collection || []).forEach(function (item) {
    queue.push(item, function (err) {
      // necessary?
    });
  });
};
<commit_msg>Enable async queue to return aggregate errors<commit_after>var
  async = require('async');

var
  CONCURRENT = 5;

module.exports = function queueCreator (collection, fn, callback) {
  var queue = async.queue(fn, CONCURRENT);
  var errors = [];

  queue.drain = function () {
    callback && callback(errors.length ? errors : null);
  };

  (collection || []).forEach(function (item) {
    queue.push(item, function (err) {
      if (err) {
        errors.push(err);
      }
    });
  });
};
<|endoftext|>


<commit_before>'use strict';

// requires
require('prototypes');
var Canvas = require('canvas');
var testing = require('testing');
var fs = require('fs');
var basicRequest = require('basic-request');
var Log = require('log');
var config = require('../config.js');

// constants
var MAX_PACKAGE_NAME = 19;
var RATING_PARAM = '$rating$';
var COLOR_PARAM = '$color$';
var SHIELDS_IO_URL = 'https://img.shields.io/badge/quality-' + RATING_PARAM + '-' + COLOR_PARAM + '.svg?';

// globals
var log = new Log(config.logLevel);
var badge = fs.readFileSync(__dirname + '/../app/img/badge2.png');
var stars = {};
for (var i = 0.5; i <= 5; i += 0.5) {
	stars[i] = fs.readFileSync(__dirname + '/../app/img/stars' + i + '.png');
}
var ranges = [
	[0, 1],
	[12, 25],
	[37, 50],
	[62, 75],
	[87, 99],
];
var colors = {
	0: 'lightgrey',
	0.5: 'red',
	1: 'red',
	1.5: 'red',
	2: 'orange',
	2.5: 'orange',
	3: 'yellow',
	3.5: 'yellow',
	4: 'lightgreen',
	4.5: 'green',
	5: 'brightgreen',
};

function getStarsCount(score) {
	if (!score)
	{
		return 0;
	}
	var range;
	for (var i = 0, len = ranges.length; i < len; i++) {
		range = ranges[i];
		if (score < range[0]) {
			return i;
		}
		if (score < range[1]) {
			return i + '.5';
		}
	}
	return '5';
}

function getStarsImage(score) {
	return stars[getStarsCount(score)];
}

exports.compileBadge = function (name, quality, callback) {

	if (name.length > MAX_PACKAGE_NAME) {
		name = name.slice(0, MAX_PACKAGE_NAME - 3) + '...';
	}
	var canvas = new Canvas(256, 64);
	var context = canvas.getContext('2d');

	var image = new Canvas.Image();
	image.src = badge;
	context.drawImage(image, 0, 0, 256, 64);

	context.font = '15px monospace';
	context.textAlign = 'right';
	context.fillText(name, 244, 23);

	var starsImage = new Canvas.Image();
	starsImage.src = getStarsImage(quality);
	context.drawImage(starsImage, 155, 27, 92, 17);

	//canvas.toDataURL('image/png', callback);
	canvas.toBuffer(callback);
};

function convertToText(stars)
{
	var text = '';
	for (var i = 0; i < 5; i++)
	{
		if (stars <= i)
		{
			text += '☆';
		}
		else if (stars >= i + 1)
		{
			text += '★';
		}
		else
		{
			text += '½';
		}
	}
	return text;
}

function testStarsText(callback)
{
	testing.assertEquals(convertToText(0), '☆☆☆☆☆', callback);
	testing.assertEquals(convertToText(0.5), '½☆☆☆☆', callback);
	testing.assertEquals(convertToText(1), '★☆☆☆☆', callback);
	testing.assertEquals(convertToText(3.5), '★★★½☆', callback);
	testing.assertEquals(convertToText(5), '★★★★★', callback);
	testing.success(callback);
}

function convertToColor(stars)
{
	return colors[stars];
}

exports.retrieveShield = function(name, quality, queryString, callback)
{
	var stars = getStarsCount(quality);
	var text = convertToText(stars);
	log.info('Shield for %s: quality %s, stars %s, text %s', name, quality, stars, text);
	var color = convertToColor(stars);
	var url = SHIELDS_IO_URL.replace(RATING_PARAM, text).replace(COLOR_PARAM, color) + queryString;
	basicRequest.get(url, function(error, body)
	{
		if (error)
		{
			log.error('Could not get shield for %s from %s: %s', name, url, error);
			return callback('Could not retrieve badge');
		}
		return callback(null, body);
	});
};

function testRetrieveShield(callback)
{
	var shieldsResponse = 'testShieldResponse';
	//stub basicRequest
	var restoreBasicRequest = basicRequest;
	basicRequest = {
		get: function(url, callback) {
			callback (null, shieldsResponse);
		}
	};
	exports.retrieveShield('test', 65, '', function(error, result)
	{
		testing.check(error, 'Could not retrieve shield', callback);
		testing.assertEquals(result, shieldsResponse, 'Did not retrieve shields correctly', callback);
		// restore basicRequest
		basicRequest = restoreBasicRequest;
		testing.success(callback);
	});
}

exports.test = function(callback)
{
	testing.run([
		testStarsText,
		testRetrieveShield,
	], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<commit_msg>Make a better test that checks for stars.<commit_after>'use strict';

// requires
require('prototypes');
var Canvas = require('canvas');
var testing = require('testing');
var fs = require('fs');
var basicRequest = require('basic-request');
var Log = require('log');
var config = require('../config.js');

// constants
var MAX_PACKAGE_NAME = 19;
var RATING_PARAM = '$rating$';
var COLOR_PARAM = '$color$';
var SHIELDS_IO_URL = 'https://img.shields.io/badge/quality-' + RATING_PARAM + '-' + COLOR_PARAM + '.svg?';

// globals
var log = new Log(config.logLevel);
var badge = fs.readFileSync(__dirname + '/../app/img/badge2.png');
var stars = {};
for (var i = 0.5; i <= 5; i += 0.5) {
	stars[i] = fs.readFileSync(__dirname + '/../app/img/stars' + i + '.png');
}
var ranges = [
	[0, 1],
	[12, 25],
	[37, 50],
	[62, 75],
	[87, 99],
];
var colors = {
	0: 'lightgrey',
	0.5: 'red',
	1: 'red',
	1.5: 'red',
	2: 'orange',
	2.5: 'orange',
	3: 'yellow',
	3.5: 'yellow',
	4: 'lightgreen',
	4.5: 'green',
	5: 'brightgreen',
};

function getStarsCount(score) {
	if (!score)
	{
		return 0;
	}
	var range;
	for (var i = 0, len = ranges.length; i < len; i++) {
		range = ranges[i];
		if (score < range[0]) {
			return i;
		}
		if (score < range[1]) {
			return i + '.5';
		}
	}
	return '5';
}

function getStarsImage(score) {
	return stars[getStarsCount(score)];
}

exports.compileBadge = function (name, quality, callback) {

	if (name.length > MAX_PACKAGE_NAME) {
		name = name.slice(0, MAX_PACKAGE_NAME - 3) + '...';
	}
	var canvas = new Canvas(256, 64);
	var context = canvas.getContext('2d');

	var image = new Canvas.Image();
	image.src = badge;
	context.drawImage(image, 0, 0, 256, 64);

	context.font = '15px monospace';
	context.textAlign = 'right';
	context.fillText(name, 244, 23);

	var starsImage = new Canvas.Image();
	starsImage.src = getStarsImage(quality);
	context.drawImage(starsImage, 155, 27, 92, 17);

	//canvas.toDataURL('image/png', callback);
	canvas.toBuffer(callback);
};

function convertToText(stars)
{
	var text = '';
	for (var i = 0; i < 5; i++)
	{
		if (stars <= i)
		{
			text += '☆';
		}
		else if (stars >= i + 1)
		{
			text += '★';
		}
		else
		{
			text += '½';
		}
	}
	return text;
}

function testStarsText(callback)
{
	testing.assertEquals(convertToText(0), '☆☆☆☆☆', callback);
	testing.assertEquals(convertToText(0.5), '½☆☆☆☆', callback);
	testing.assertEquals(convertToText(1), '★☆☆☆☆', callback);
	testing.assertEquals(convertToText(3.5), '★★★½☆', callback);
	testing.assertEquals(convertToText(5), '★★★★★', callback);
	testing.success(callback);
}

function convertToColor(stars)
{
	return colors[stars];
}

exports.retrieveShield = function(name, quality, queryString, callback)
{
	var stars = getStarsCount(quality);
	var text = convertToText(stars);
	log.info('Shield for %s: quality %s, stars %s, text %s', name, quality, stars, text);
	var color = convertToColor(stars);
	var url = SHIELDS_IO_URL.replace(RATING_PARAM, text).replace(COLOR_PARAM, color) + queryString;
	basicRequest.get(url, function(error, body)
	{
		if (error)
		{
			log.error('Could not get shield for %s from %s: %s', name, url, error);
			return callback('Could not retrieve badge');
		}
		return callback(null, body);
	});
};

function testRetrieveShield(callback)
{
	exports.retrieveShield('test', 65, '', function(error, result)
	{
		testing.check(error, 'Could not retrieve shield', callback);
		testing.assert(result.contains('★★★½☆'), 'Result %s should contain stars', result, callback);
		testing.success(callback);
	});
}

exports.test = function(callback)
{
	testing.run([
		testStarsText,
		testRetrieveShield,
	], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<|endoftext|>


<commit_before>

  /***
   * @package Range
   * @dependency core
   * @description Ranges allow creating spans of numbers, strings, or dates. They can enumerate over specific points within that range, and be manipulated and compared.
   *
   ***/

  function Range(start, end) {
    this.start = cloneRangeMember(start);
    this.end   = cloneRangeMember(end);
  };

  function getRangeMemberNumericValue(m) {
    return isString(m) ? m.charCodeAt(0) : m;
  }

  function getRangeMemberPrimitiveValue(m) {
    if(m == null) return m;
    return isDate(m) ? m.getTime() : m.valueOf();
  }

  function cloneRangeMember(m) {
    if(isDate(m)) {
      return new date(m.getTime());
    } else {
      return getRangeMemberPrimitiveValue(m);
    }
  }

  function isValidRangeMember(m) {
    var val = getRangeMemberPrimitiveValue(m);
    return !!val || val === 0;
  }

  function incrementRangeMember(obj, increment) {
    if(isDate(obj)) {
      return advanceDate(obj, increment);
    } else if(isString(obj)) {
      return string.fromCharCode(m.charCodeAt(0) + increment);
    } else if(isNumber(obj)) {
      return obj + increment;
    }
  }

  function advanceDate(current, increment) {
    var unit, amt = increment, tmp, val, d;
    if(isString(increment)) {
      tmp  = extractDurationFromString(increment);
      amt  = tmp[0];
      unit = tmp[1];
      val  = callDateGet(current, unit);
      d    = new date(current.getTime());
      callDateSet(d, unit, val + amt);
      return d;
    } else {
      return new date(current.getTime() + increment);
    }
  }

  /***
   * @method toString()
   * @returns String
   * @short Returns a string representation of the range.
   * @example
   *
   *   Number.range(1, 5).toString()                               -> 1..5
   *   Date.range(new Date(2003, 0), new Date(2005, 0)).toString() -> January 1, 2003..January 1, 2005
   *
   ***/

  // Note: 'toString' doesn't appear in a for..in loop in IE even though
  // hasOwnProperty reports true, so extend() can't be used here.
  // Also tried simply setting the prototype = {} up front for all
  // methods but GCC very oddly started dropping properties in the
  // object randomly (maybe because of the global scope?) hence
  // the need for the split logic here.
  Range.prototype.toString = function() {
    return this.isValid() ? this.start + ".." + this.end : 'Invalid Range';
  };

  extend(Range, true, false, {

    /***
     * @method isValid()
     * @returns Boolean
     * @short Returns true if the range is valid, false otherwise.
     * @example
     *
     *   Date.range(new Date(2003, 0), new Date(2005, 0)).isValid() -> true
     *   Number.range(NaN, NaN).isValid()                           -> false
     *
     ***/
    'isValid': function() {
      return isValidRangeMember(this.start) && isValidRangeMember(this.end) && typeof this.start === typeof this.end;
    },

    /***
     * @method span()
     * @returns Number
     * @short Returns the span of the range. If the range is a date range, the value is in milliseconds.
     * @extra The span includes both the start and the end.
     * @example
     *
     *   Number.range(5, 10).span()                              -> 6
     *   Date.range(new Date(2003, 0), new Date(2005, 0)).span() -> 94694400000
     *
     ***/
    'span': function() {
      return this.isValid() ? math.abs(
        getRangeMemberNumericValue(this.end) - getRangeMemberNumericValue(this.start)
      ) + 1 : NaN;
    },

    /***
     * @method contains(<obj>)
     * @returns Boolean
     * @short Returns true if <obj> is contained inside the range. <obj> may be a value or another range.
     * @example
     *
     *   Number.range(5, 10).contains(7)                                              -> true
     *   Date.range(new Date(2003, 0), new Date(2005, 0)).contains(new Date(2004, 0)) -> true
     *
     ***/
    'contains': function(obj) {
      var self = this, arr;
      if(obj == null) return false;
      arr = obj.start && obj.end ? [obj.start, obj.end] : [obj];
      return arr.every(function(d) {
        return d >= self.start && d <= self.end;
      });
    },

    /***
     * @method every(<increment>, [fn])
     * @returns Array
     * @short Iterates through the range for every <increment>, calling [fn] if it is passed. Returns an array of each increment visited.
     * @extra In the case of date ranges, <increment> can also be a string, in which case it will increment a number of  units. Note that %(2).months()% first resolves to a number, which will be interpreted as milliseconds and is an approximation, so stepping through the actual months by passing %"2 months"% is usually preferable.
     * @example
     *
     *   Number.range(2, 8).every(2)                                       -> [2,4,6,8]
     *   Date.range(new Date(2003, 1), new Date(2003,3)).every("2 months") -> [...]
     *
     ***/
    'every': function(increment, fn) {
      var start   = this.start,
          end     = this.end,
          inverse = end < start,
          current = start,
          index   = 0,
          result  = [];
      if(isFunction(increment)) {
        fn = increment;
        increment = null;
      }
      increment = increment || 1;
      // Avoiding infinite loops
      if(inverse && increment > 0) {
        increment *= -1;
      }
      while(inverse ? current >= end : current <= end) {
        result.push(current);
        if(fn) {
          fn(current, index);
        }
        current = incrementRangeMember(current, increment);
        index++;
      }
      return result;
    },

    /***
     * @method union(<range>)
     * @returns Range
     * @short Returns a new range with the earliest starting point as its start, and the latest ending point as its end. If the two ranges do not intersect this will effectively remove the "gap" between them.
     * @example
     *
     *   Number.range(1, 3).union(Number.range(2, 5)) -> 1..5
     *   Date.range(new Date(2003, 1), new Date(2005, 1)).union(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2003..Jan 1, 2006
     *
     ***/
    'union': function(range) {
      return new Range(
        this.start < range.start ? this.start : range.start,
        this.end   > range.end   ? this.end   : range.end
      );
    },

    /***
     * @method intersect(<range>)
     * @returns Range
     * @short Returns a new range with the latest starting point as its start, and the earliest ending point as its end. If the two ranges do not intersect this will effectively produce an invalid range.
     * @example
     *
     *   Number.range(1, 5).intersect(Number.range(4, 8)) -> 4..5
     *   Date.range(new Date(2003, 1), new Date(2005, 1)).intersect(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2004..Jan 1, 2005
     *
     ***/
    'intersect': function(range) {
      if(range.start > this.end || range.end < this.start) {
        return new Range(NaN, NaN);
      }
      return new Range(
        this.start > range.start ? this.start : range.start,
        this.end   < range.end   ? this.end   : range.end
      );
    },

    /***
     * @method clone()
     * @returns Range
     * @short Clones the range.
     * @extra Members of the range will also be cloned.
     * @example
     *
     *   Number.range(1, 5).clone() -> Returns a copy of the range.
     *
     ***/
    'clone': function(range) {
      return new Range(this.start, this.end);
    },

    /***
     * @method clamp(<obj>)
     * @returns Mixed
     * @short Clamps <obj> to be within the range if it falls outside.
     * @example
     *
     *   Number.range(1, 5).clamp(8) -> 5
     *   Date.range(new Date(2010, 0), new Date(2012, 0)).clamp(new Date(2013, 0)) -> 2012-01
     *
     ***/
    'clamp': function(obj) {
      var clamped,
          start = this.start,
          end = this.end,
          min = end < start ? end : start,
          max = start > end ? start : end;
      if(obj < min) {
        clamped = min;
      } else if(obj > max) {
        clamped = max;
      } else {
        clamped = obj;
      }
      return cloneRangeMember(clamped);
    }

  });

  extend(Range, true, false, {

     /***
     * @method step()
     * @returns Array
     * @short Alias for %every%.
     *
     ***/
    'step': Range.prototype.every

  });

  /***
   * Number module
   ***
   * @method Number.range([start], [end])
   * @returns Range
   * @short Creates a new range between [start] and [end].
   ***
   * String module
   ***
   * @method String.range([start], [end])
   * @returns Range
   * @short Creates a new range between [start] and [end].
   ***
   * Date module
   ***
   * @method Date.range([start], [end])
   * @returns Range
   * @short Creates a new range between [start] and [end].
   * @extra If either [start] or [end] are null, they will default to the current date.
   ***/
  [number, string, date].forEach(function(klass) {
     extend(klass, false, false, {

      'range': function(start, end) {
        if(klass.create) {
          start = klass.create(start);
          end   = klass.create(end);
        }
        return new Range(start, end);
      }

    });

  });

  /***
   * Number module
   *
   ***/

  extend(number, true, false, {

    /***
     * @method upto(<num>, [fn], [step] = 1)
     * @returns Array
     * @short Returns an array containing numbers from the number up to <num>.
     * @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
     * @example
     *
     *   (2).upto(6) -> [2, 3, 4, 5, 6]
     *   (2).upto(6, function(n) {
     *     // This function is called 5 times receiving n as the value.
     *   });
     *   (2).upto(8, null, 2) -> [2, 4, 6, 8]
     *
     ***/
    'upto': function(num, fn, step) {
      return number.range(this, num).step(step, fn);
    },

     /***
     * @method clamp([start] = Infinity, [end] = Infinity)
     * @returns Number
     * @short Constrains the number so that it is between [start] and [end].
     * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method.
     *
     ***/
    'clamp': function(start, end) {
      return new Range(start, end).clamp(this);
    },

     /***
     * @method cap([max] = Infinity)
     * @returns Number
     * @short Constrains the number so that it is between [start] and [end].
     * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method.
     *
     ***/
    'cap': function(max) {
      return this.clamp(Undefined, max);
    }

  });

  extend(number, true, false, {

    /***
     * @method downto(<num>, [fn], [step] = 1)
     * @returns Array
     * @short Returns an array containing numbers from the number down to <num>.
     * @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
     * @example
     *
     *   (8).downto(3) -> [8, 7, 6, 5, 4, 3]
     *   (8).downto(3, function(n) {
     *     // This function is called 6 times receiving n as the value.
     *   });
     *   (8).downto(2, null, 2) -> [8, 6, 4, 2]
     *
     ***/
    'downto': number.prototype.upto

  });

  /***
   * Array module
   *
   ***/

  extend(array, false, function(a) { return a instanceof Range; }, {

    'create': function(range) {
      return range.step();
    }

  });

<commit_msg>more<commit_after>

  /***
   * @package Range
   * @dependency core
   * @description Ranges allow creating spans of numbers, strings, or dates. They can enumerate over specific points within that range, and be manipulated and compared.
   *
   ***/

  function Range(start, end) {
    this.start = cloneRangeMember(start);
    this.end   = cloneRangeMember(end);
  };

  function getRangeMemberNumericValue(m) {
    return isString(m) ? m.charCodeAt(0) : m;
  }

  function getRangeMemberPrimitiveValue(m) {
    if(m == null) return m;
    return isDate(m) ? m.getTime() : m.valueOf();
  }

  function cloneRangeMember(m) {
    if(isDate(m)) {
      return new date(m.getTime());
    } else {
      return getRangeMemberPrimitiveValue(m);
    }
  }

  function isValidRangeMember(m) {
    var val = getRangeMemberPrimitiveValue(m);
    return !!val || val === 0;
  }

  function incrementRangeMember(obj, increment) {
    if(isDate(obj)) {
      return advanceDate(obj, increment);
    } else if(isString(obj)) {
      return string.fromCharCode(m.charCodeAt(0) + increment);
    } else if(isNumber(obj)) {
      return obj + increment;
    }
  }

  function advanceDate(current, increment) {
    var unit, amt = increment, tmp, val, d;
    if(isString(increment)) {
      tmp  = extractDurationFromString(increment);
      amt  = tmp[0];
      unit = tmp[1];
      val  = callDateGet(current, unit);
      d    = new date(current.getTime());
      callDateSet(d, unit, val + amt);
      return d;
    } else {
      return new date(current.getTime() + increment);
    }
  }

  /***
   * @method toString()
   * @returns String
   * @short Returns a string representation of the range.
   * @example
   *
   *   Number.range(1, 5).toString()                               -> 1..5
   *   Date.range(new Date(2003, 0), new Date(2005, 0)).toString() -> January 1, 2003..January 1, 2005
   *
   ***/

  // Note: 'toString' doesn't appear in a for..in loop in IE even though
  // hasOwnProperty reports true, so extend() can't be used here.
  // Also tried simply setting the prototype = {} up front for all
  // methods but GCC very oddly started dropping properties in the
  // object randomly (maybe because of the global scope?) hence
  // the need for the split logic here.
  Range.prototype.toString = function() {
    return this.isValid() ? this.start + ".." + this.end : 'Invalid Range';
  };

  extend(Range, true, false, {

    /***
     * @method isValid()
     * @returns Boolean
     * @short Returns true if the range is valid, false otherwise.
     * @example
     *
     *   Date.range(new Date(2003, 0), new Date(2005, 0)).isValid() -> true
     *   Number.range(NaN, NaN).isValid()                           -> false
     *
     ***/
    'isValid': function() {
      return isValidRangeMember(this.start) && isValidRangeMember(this.end) && typeof this.start === typeof this.end;
    },

    /***
     * @method span()
     * @returns Number
     * @short Returns the span of the range. If the range is a date range, the value is in milliseconds.
     * @extra The span includes both the start and the end.
     * @example
     *
     *   Number.range(5, 10).span()                              -> 6
     *   Date.range(new Date(2003, 0), new Date(2005, 0)).span() -> 94694400000
     *
     ***/
    'span': function() {
      return this.isValid() ? math.abs(
        getRangeMemberNumericValue(this.end) - getRangeMemberNumericValue(this.start)
      ) + 1 : NaN;
    },

    /***
     * @method contains(<obj>)
     * @returns Boolean
     * @short Returns true if <obj> is contained inside the range. <obj> may be a value or another range.
     * @example
     *
     *   Number.range(5, 10).contains(7)                                              -> true
     *   Date.range(new Date(2003, 0), new Date(2005, 0)).contains(new Date(2004, 0)) -> true
     *
     ***/
    'contains': function(obj) {
      var self = this, arr;
      if(obj == null) return false;
      arr = obj.start && obj.end ? [obj.start, obj.end] : [obj];
      return arr.every(function(d) {
        return d >= self.start && d <= self.end;
      });
    },

    /***
     * @method every(<increment>, [fn])
     * @returns Array
     * @short Iterates through the range for every <increment>, calling [fn] if it is passed. Returns an array of each increment visited.
     * @extra In the case of date ranges, <increment> can also be a string, in which case it will increment a number of  units. Note that %(2).months()% first resolves to a number, which will be interpreted as milliseconds and is an approximation, so stepping through the actual months by passing %"2 months"% is usually preferable.
     * @example
     *
     *   Number.range(2, 8).every(2)                                       -> [2,4,6,8]
     *   Date.range(new Date(2003, 1), new Date(2003,3)).every("2 months") -> [...]
     *
     ***/
    'every': function(increment, fn) {
      var start   = this.start,
          end     = this.end,
          inverse = end < start,
          current = start,
          index   = 0,
          result  = [];
      if(isFunction(increment)) {
        fn = increment;
        increment = null;
      }
      increment = increment || 1;
      // Avoiding infinite loops
      if(inverse && increment > 0) {
        increment *= -1;
      }
      while(inverse ? current >= end : current <= end) {
        result.push(current);
        if(fn) {
          fn(current, index);
        }
        current = incrementRangeMember(current, increment);
        index++;
      }
      return result;
    },

    /***
     * @method union(<range>)
     * @returns Range
     * @short Returns a new range with the earliest starting point as its start, and the latest ending point as its end. If the two ranges do not intersect this will effectively remove the "gap" between them.
     * @example
     *
     *   Number.range(1, 3).union(Number.range(2, 5)) -> 1..5
     *   Date.range(new Date(2003, 1), new Date(2005, 1)).union(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2003..Jan 1, 2006
     *
     ***/
    'union': function(range) {
      return new Range(
        this.start < range.start ? this.start : range.start,
        this.end   > range.end   ? this.end   : range.end
      );
    },

    /***
     * @method intersect(<range>)
     * @returns Range
     * @short Returns a new range with the latest starting point as its start, and the earliest ending point as its end. If the two ranges do not intersect this will effectively produce an invalid range.
     * @example
     *
     *   Number.range(1, 5).intersect(Number.range(4, 8)) -> 4..5
     *   Date.range(new Date(2003, 1), new Date(2005, 1)).intersect(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2004..Jan 1, 2005
     *
     ***/
    'intersect': function(range) {
      if(range.start > this.end || range.end < this.start) {
        return new Range(NaN, NaN);
      }
      return new Range(
        this.start > range.start ? this.start : range.start,
        this.end   < range.end   ? this.end   : range.end
      );
    },

    /***
     * @method clone()
     * @returns Range
     * @short Clones the range.
     * @extra Members of the range will also be cloned.
     * @example
     *
     *   Number.range(1, 5).clone() -> Returns a copy of the range.
     *
     ***/
    'clone': function(range) {
      return new Range(this.start, this.end);
    },

    /***
     * @method clamp(<obj>)
     * @returns Mixed
     * @short Clamps <obj> to be within the range if it falls outside.
     * @example
     *
     *   Number.range(1, 5).clamp(8) -> 5
     *   Date.range(new Date(2010, 0), new Date(2012, 0)).clamp(new Date(2013, 0)) -> 2012-01
     *
     ***/
    'clamp': function(obj) {
      var clamped,
          start = this.start,
          end = this.end,
          min = end < start ? end : start,
          max = start > end ? start : end;
      if(obj < min) {
        clamped = min;
      } else if(obj > max) {
        clamped = max;
      } else {
        clamped = obj;
      }
      return cloneRangeMember(clamped);
    }

  });

  extend(Range, true, false, {

     /***
     * @method step()
     * @returns Array
     * @alias every
     *
     ***/
    'step': Range.prototype.every

  });

  /***
   * Number module
   ***
   * @method Number.range([start], [end])
   * @returns Range
   * @short Creates a new range between [start] and [end].
   ***
   * String module
   ***
   * @method String.range([start], [end])
   * @returns Range
   * @short Creates a new range between [start] and [end].
   ***
   * Date module
   ***
   * @method Date.range([start], [end])
   * @returns Range
   * @short Creates a new range between [start] and [end].
   * @extra If either [start] or [end] are null, they will default to the current date.
   ***/
  [number, string, date].forEach(function(klass) {
     extend(klass, false, false, {

      'range': function(start, end) {
        if(klass.create) {
          start = klass.create(start);
          end   = klass.create(end);
        }
        return new Range(start, end);
      }

    });

  });

  /***
   * Number module
   *
   ***/

  extend(number, true, false, {

    /***
     * @method upto(<num>, [fn], [step] = 1)
     * @returns Array
     * @short Returns an array containing numbers from the number up to <num>.
     * @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
     * @example
     *
     *   (2).upto(6) -> [2, 3, 4, 5, 6]
     *   (2).upto(6, function(n) {
     *     // This function is called 5 times receiving n as the value.
     *   });
     *   (2).upto(8, null, 2) -> [2, 4, 6, 8]
     *
     ***/
    'upto': function(num, fn, step) {
      return number.range(this, num).step(step, fn);
    },

     /***
     * @method clamp([start] = Infinity, [end] = Infinity)
     * @returns Number
     * @short Constrains the number so that it is between [start] and [end].
     * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method.
     *
     ***/
    'clamp': function(start, end) {
      return new Range(start, end).clamp(this);
    },

     /***
     * @method cap([max] = Infinity)
     * @returns Number
     * @short Constrains the number so that it is between [start] and [end].
     * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method.
     *
     ***/
    'cap': function(max) {
      return this.clamp(Undefined, max);
    }

  });

  extend(number, true, false, {

    /***
     * @method downto(<num>, [fn], [step] = 1)
     * @returns Array
     * @short Returns an array containing numbers from the number down to <num>.
     * @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
     * @example
     *
     *   (8).downto(3) -> [8, 7, 6, 5, 4, 3]
     *   (8).downto(3, function(n) {
     *     // This function is called 6 times receiving n as the value.
     *   });
     *   (8).downto(2, null, 2) -> [8, 6, 4, 2]
     *
     ***/
    'downto': number.prototype.upto

  });

  /***
   * Array module
   *
   ***/

  extend(array, false, function(a) { return a instanceof Range; }, {

    'create': function(range) {
      return range.step();
    }

  });

<|endoftext|>