<commit_before>/**
 * This file is part of the JS Money library
 *
 * Copyright (c) 2014 David Kalosi
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

var _ = require('lodash');
var currencies = require('./currency');

var isInt = function (n) {
    return Number(n) === n && n % 1 === 0;
};

var decimalPlaces = function (num) {
    var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);

    if (!match)
        return 0;

    return Math.max(0,
        (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
};

var assertSameCurrency = function (left, right) {
    if (left.currency !== right.currency)
        throw new Error('Different currencies');
};

var assertType = function (other) {
    if (!(other instanceof Money))
        throw new TypeError('Instance of Money required');
};

var assertOperand = function (operand) {
    if (isNaN(parseFloat(operand)) && !isFinite(operand))
        throw new TypeError('Operand must be a number');
};

/**
 * Creates a new Money instance.
 * The created Money instances is a value object thus it is immutable.
 *
 * @param {Number} amount
 * @param {Object/String} currency
 * @returns {Money}
 * @constructor
 */
function Money(amount, currency) {
    if (_.isString(currency))
        currency = currencies[currency];

    if (!_.isPlainObject(currency))
        throw new TypeError('Invalid currency');

    if (!isInt(amount))
        throw new TypeError('Amount must be an integer');

    this.amount = amount;
    this.currency = currency.code;
    Object.freeze(this);
}

Money.fromInteger = function (amount, currency) {
    if (_.isObject(amount)) {
        if (amount.amount === undefined || amount.currency === undefined)
            throw new TypeError('Missing required parameters amount,currency');

        currency = amount.currency;
        amount = amount.amount;
    }

    if (!isInt(amount))
        throw new TypeError('Amount must be an integer value');

    return new Money(amount, currency);
};

Money.fromDecimal = function (amount, currency) {
    var multipliers = [1, 10, 100, 1000];

    if (_.isObject(amount)) {
        if (amount.amount === undefined || amount.currency === undefined)
            throw new TypeError('Missing required parameters amount,currency');

        currency = amount.currency;
        amount = amount.amount;
    }

    if (_.isString(currency))
        currency = currencies[currency];

    if (!_.isPlainObject(currency))
        throw new TypeError('Invalid currency');

    var decimals = decimalPlaces(amount);

    if (decimals > currency.decimal_digits)
        throw new Error("The currency " + currency.code + " supports only "
            + currency.decimal_digits + " decimal digits");

    var integerAmount = amount * multipliers[currency.decimal_digits];
    return new Money(Math.round(integerAmount), currency);
};

/**
 * Returns true if the two instances of Money are equal, false otherwise.
 *
 * @param {Money} other
 * @returns {Boolean}
 */
Money.prototype.equals = function (other) {
    var self = this;
    assertType(other);

    return self.amount === other.amount &&
            self.currency === other.currency;
};

/**
 * Adds the two objects together creating a new Money instance that holds the result of the operation.
 *
 * @param {Money} other
 * @returns {Money}
 */
Money.prototype.add = function (other) {
    var self = this;
    assertType(other);
    assertSameCurrency(self, other);

    return new Money(self.amount + other.amount, self.currency);
};

/**
 * Subtracts the two objects creating a new Money instance that holds the result of the operation.
 *
 * @param {Money} other
 * @returns {Money}
 */
Money.prototype.subtract = function (other) {
    var self = this;
    assertType(other);
    assertSameCurrency(self, other);

    return new Money(self.amount - other.amount, self.currency);
};

/**
 * Multiplies the object by the multiplier returning a new Money instance that holds the result of the operation.
 *
 * @param {Number} multiplier
 * @param {Function} [fn=Math.round]
 * @returns {Money}
 */
Money.prototype.multiply = function (multiplier, fn) {
    if (!_.isFunction(fn))
        fn = Math.round;

    assertOperand(multiplier);
    var amount = fn(this.amount * multiplier);

    return new Money(amount, this.currency);
};

/**
 * Divides the object by the multiplier returning a new Money instance that holds the result of the operation.
 *
 * @param {Number} divisor
 * @param {Function} [fn=Math.round]
 * @returns {Money}
 */
Money.prototype.divide = function (divisor, fn) {
    if (!_.isFunction(fn))
        fn = Math.round;

    assertOperand(divisor);
    var amount = fn(this.amount / divisor);

    return new Money(amount, this.currency);
};

/**
 * Allocates fund bases on the ratios provided returing an array of objects as a product of the allocation.
 *
 * @param {Array} other
 * @returns {Array.Money}
 */
Money.prototype.allocate = function (ratios) {
    var self = this;
    var remainder = self.amount;
    var results = [];
    var total = 0;

    ratios.forEach(function (ratio) {
        total += ratio;
    });

    ratios.forEach(function (ratio) {
        var share = Math.floor(self.amount * ratio / total)
        results.push(new Money(share, self.currency));
        remainder -= share;
    });

    for (var i = 0; remainder > 0; i++) {
        results[i] = new Money(results[i].amount + 1, results[i].currency);
        remainder--;
    }

    return results;
};

/**
 * Compares two instances of Money.
 *
 * @param {Money} other
 * @returns {Number}
 */
Money.prototype.compare = function (other) {
    var self = this;

    assertType(other);
    assertSameCurrency(self, other);

    if (self.amount === other.amount)
        return 0;

    return self.amount > other.amount ? 1 : -1;
};

/**
 * Checks whether the value represented by this object is greater than the other.
 *
 * @param {Money} other
 * @returns boolean
 */
Money.prototype.greaterThan = function (other) {
    return 1 === this.compare(other);
};

/**
 * Checks whether the value represented by this object is greater or equal to the other.
 *
 * @param {Money} other
 * @returns boolean
 */
Money.prototype.greaterThanOrEqual = function (other) {
    return 0 <= this.compare(other);
};

/**
 * Checks whether the value represented by this object is less than the other.
 *
 * @param {Money} other
 * @returns boolean
 */
Money.prototype.lessThan = function (other) {
    return -1 === this.compare(other);
};

/**
 * Checks whether the value represented by this object is less than or equal to the other.
 *
 * @param {Money} other
 * @returns boolean
 */
Money.prototype.lessThanOrEqual = function (other) {
    return 0 >= this.compare(other);
};

/**
 * Returns true if the amount is zero.
 *
 * @returns {boolean}
 */
Money.prototype.isZero = function () {
    return this.amount === 0;
};

/**
 * Returns true if the amount is positive.
 *
 * @returns {boolean}
 */
Money.prototype.isPositive = function () {
    return this.amount > 0;
};

/**
 * Returns true if the amount is negative.
 *
 * @returns {boolean}
 */
Money.prototype.isNegative = function () {
    return this.amount < 0;
};

/**
 * Returns the decimal value as a float.
 *
 * @returns {number}
 */
Money.prototype.toDecimal = function () {
    return Number(this.toString());
};

/**
 * Returns the decimal value as a string.
 *
 * @returns {string}
 */
Money.prototype.toString = function () {
    var currency = currencies[this.currency];
    return (this.amount / Math.pow(10, currency.decimal_digits)).toFixed(currency.decimal_digits);
};

/**
 * Returns a serialised version of the instance.
 *
 * @returns {{amount: number, currency: string}}
 */
Money.prototype.toJSON = function () {
    return {
        amount: this.amount,
        currency: this.currency
    };
};

/**
 * Returns the amount represented by this object.
 *
 * @returns {number}
 */
Money.prototype.getAmount = function () {
    return this.amount;
};

/**
 * Returns the currency represented by this object.
 *
 * @returns {string}
 */
Money.prototype.getCurrency = function () {
    return this.currency;
};

module.exports = _.extend(Money, currencies);
<commit_msg>JSDoc refactoring<commit_after>/**
 * This file is part of the JS Money library
 *
 * Copyright (c) 2014 David Kalosi
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

var _ = require('lodash');
var currencies = require('./currency');

var isInt = function (n) {
    return Number(n) === n && n % 1 === 0;
};

var decimalPlaces = function (num) {
    var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);

    if (!match)
        return 0;

    return Math.max(0,
        (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
};

var assertSameCurrency = function (left, right) {
    if (left.currency !== right.currency)
        throw new Error('Different currencies');
};

var assertType = function (other) {
    if (!(other instanceof Money))
        throw new TypeError('Instance of Money required');
};

var assertOperand = function (operand) {
    if (isNaN(parseFloat(operand)) && !isFinite(operand))
        throw new TypeError('Operand must be a number');
};

/**
 * Creates a new Money instance.
 * The created Money instances is a value object thus it is immutable.
 *
 * @param {Number} amount
 * @param {Object/String} currency
 * @returns {Money}
 * @constructor
 */
function Money(amount, currency) {
    if (_.isString(currency))
        currency = currencies[currency];

    if (!_.isPlainObject(currency))
        throw new TypeError('Invalid currency');

    if (!isInt(amount))
        throw new TypeError('Amount must be an integer');

    this.amount = amount;
    this.currency = currency.code;
    Object.freeze(this);
}

Money.fromInteger = function (amount, currency) {
    if (_.isObject(amount)) {
        if (amount.amount === undefined || amount.currency === undefined)
            throw new TypeError('Missing required parameters amount,currency');

        currency = amount.currency;
        amount = amount.amount;
    }

    if (!isInt(amount))
        throw new TypeError('Amount must be an integer value');

    return new Money(amount, currency);
};

Money.fromDecimal = function (amount, currency) {
    var multipliers = [1, 10, 100, 1000];

    if (_.isObject(amount)) {
        if (amount.amount === undefined || amount.currency === undefined)
            throw new TypeError('Missing required parameters amount,currency');

        currency = amount.currency;
        amount = amount.amount;
    }

    if (_.isString(currency))
        currency = currencies[currency];

    if (!_.isPlainObject(currency))
        throw new TypeError('Invalid currency');

    var decimals = decimalPlaces(amount);

    if (decimals > currency.decimal_digits)
        throw new Error("The currency " + currency.code + " supports only "
            + currency.decimal_digits + " decimal digits");

    var integerAmount = amount * multipliers[currency.decimal_digits];
    return new Money(Math.round(integerAmount), currency);
};

/**
 * Returns true if the two instances of Money are equal, false otherwise.
 *
 * @param {Money} other
 * @returns {Boolean}
 */
Money.prototype.equals = function (other) {
    var self = this;
    assertType(other);

    return self.amount === other.amount &&
            self.currency === other.currency;
};

/**
 * Adds the two objects together creating a new Money instance that holds the result of the operation.
 *
 * @param {Money} other
 * @returns {Money}
 */
Money.prototype.add = function (other) {
    var self = this;
    assertType(other);
    assertSameCurrency(self, other);

    return new Money(self.amount + other.amount, self.currency);
};

/**
 * Subtracts the two objects creating a new Money instance that holds the result of the operation.
 *
 * @param {Money} other
 * @returns {Money}
 */
Money.prototype.subtract = function (other) {
    var self = this;
    assertType(other);
    assertSameCurrency(self, other);

    return new Money(self.amount - other.amount, self.currency);
};

/**
 * Multiplies the object by the multiplier returning a new Money instance that holds the result of the operation.
 *
 * @param {Number} multiplier
 * @param {Function} [fn=Math.round]
 * @returns {Money}
 */
Money.prototype.multiply = function (multiplier, fn) {
    if (!_.isFunction(fn))
        fn = Math.round;

    assertOperand(multiplier);
    var amount = fn(this.amount * multiplier);

    return new Money(amount, this.currency);
};

/**
 * Divides the object by the multiplier returning a new Money instance that holds the result of the operation.
 *
 * @param {Number} divisor
 * @param {Function} [fn=Math.round]
 * @returns {Money}
 */
Money.prototype.divide = function (divisor, fn) {
    if (!_.isFunction(fn))
        fn = Math.round;

    assertOperand(divisor);
    var amount = fn(this.amount / divisor);

    return new Money(amount, this.currency);
};

/**
 * Allocates fund bases on the ratios provided returing an array of objects as a product of the allocation.
 *
 * @param {Array} other
 * @returns {Array.Money}
 */
Money.prototype.allocate = function (ratios) {
    var self = this;
    var remainder = self.amount;
    var results = [];
    var total = 0;

    ratios.forEach(function (ratio) {
        total += ratio;
    });

    ratios.forEach(function (ratio) {
        var share = Math.floor(self.amount * ratio / total)
        results.push(new Money(share, self.currency));
        remainder -= share;
    });

    for (var i = 0; remainder > 0; i++) {
        results[i] = new Money(results[i].amount + 1, results[i].currency);
        remainder--;
    }

    return results;
};

/**
 * Compares two instances of Money.
 *
 * @param {Money} other
 * @returns {Number}
 */
Money.prototype.compare = function (other) {
    var self = this;

    assertType(other);
    assertSameCurrency(self, other);

    if (self.amount === other.amount)
        return 0;

    return self.amount > other.amount ? 1 : -1;
};

/**
 * Checks whether the value represented by this object is greater than the other.
 *
 * @param {Money} other
 * @returns {boolean}
 */
Money.prototype.greaterThan = function (other) {
    return 1 === this.compare(other);
};

/**
 * Checks whether the value represented by this object is greater or equal to the other.
 *
 * @param {Money} other
 * @returns {boolean}
 */
Money.prototype.greaterThanOrEqual = function (other) {
    return 0 <= this.compare(other);
};

/**
 * Checks whether the value represented by this object is less than the other.
 *
 * @param {Money} other
 * @returns {boolean}
 */
Money.prototype.lessThan = function (other) {
    return -1 === this.compare(other);
};

/**
 * Checks whether the value represented by this object is less than or equal to the other.
 *
 * @param {Money} other
 * @returns {boolean}
 */
Money.prototype.lessThanOrEqual = function (other) {
    return 0 >= this.compare(other);
};

/**
 * Returns true if the amount is zero.
 *
 * @returns {boolean}
 */
Money.prototype.isZero = function () {
    return this.amount === 0;
};

/**
 * Returns true if the amount is positive.
 *
 * @returns {boolean}
 */
Money.prototype.isPositive = function () {
    return this.amount > 0;
};

/**
 * Returns true if the amount is negative.
 *
 * @returns {boolean}
 */
Money.prototype.isNegative = function () {
    return this.amount < 0;
};

/**
 * Returns the decimal value as a float.
 *
 * @returns {number}
 */
Money.prototype.toDecimal = function () {
    return Number(this.toString());
};

/**
 * Returns the decimal value as a string.
 *
 * @returns {string}
 */
Money.prototype.toString = function () {
    var currency = currencies[this.currency];
    return (this.amount / Math.pow(10, currency.decimal_digits)).toFixed(currency.decimal_digits);
};

/**
 * Returns a serialised version of the instance.
 *
 * @returns {{amount: number, currency: string}}
 */
Money.prototype.toJSON = function () {
    return {
        amount: this.amount,
        currency: this.currency
    };
};

/**
 * Returns the amount represented by this object.
 *
 * @returns {number}
 */
Money.prototype.getAmount = function () {
    return this.amount;
};

/**
 * Returns the currency represented by this object.
 *
 * @returns {string}
 */
Money.prototype.getCurrency = function () {
    return this.currency;
};

module.exports = _.extend(Money, currencies);
<|endoftext|>


<commit_before>//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

const cliArgs = WScript.Arguments || [];
WScript.Flag("-wasmI64");

if (cliArgs.length < 1) {
  print("usage: <exe> spec.js -args <filename.json> [start index] [end index] [-v] [-nt] -endargs");
  WScript.quit(0);
}

if (typeof IMPORTS_FROM_OTHER_SCRIPT === "undefined") {
  IMPORTS_FROM_OTHER_SCRIPT = {};
}

let passed = 0;
let failed = 0;

// Parse arguments
const iVerbose = cliArgs.indexOf("-v");
const verbose = iVerbose !== -1;
if (verbose) {
  cliArgs.splice(iVerbose, 1);
}
const iNoTrap = cliArgs.indexOf("-nt");
const noTrap = iNoTrap !== -1;
const trap = !noTrap;
if (noTrap) {
  cliArgs.splice(iNoTrap, 1);
}

let file = "";
let iTest = 0;
function getValueStr(value) {
  if (typeof value === "object") {
    const {high, low} = value;
    const convert = a => (a >>> 0).toString(16).padStart(8, "0");
    return `0x${convert(high)}${convert(low)}`;
  }
  return "" + value;
}

function getArgsStr(args) {
  return args
    .map(({type, value}) => `${type}:${getValueStr(mapWasmArg({type, value}))}`)
    .join(", ");
}

function getActionStr(action) {
  switch (action.type) {
    case "invoke": return `${action.field}(${getArgsStr(action.args)})`;
    case "get": return `${action.module || "$$"}[${action.field}]`;
    default: return "Unkown action type";
  }
}

function getCommandStr(command) {
  const base = `(${iTest}) ${file}:${command.line}`;
  switch (command.type) {
    case "module": return `${base}: generate module ${command.filename}${command.name ? ` as ${command.name}` : ""}`;
    case "register": return `${base}: register module ${command.name || "$$"} as ${command.as}`;
    case "assert_malformed": return `${base}: assert_malformed module ${command.filename}`;
    case "assert_unlinkable": return `${base}: assert_unlinkable module ${command.filename}`;
    case "assert_invalid": return `${base}: assert_invalid module ${command.filename}`;
    case "action": return `${base}: action ${getActionStr(command.action)}`;
    case "assert_trap": return `${base}: assert_trap ${getActionStr(command.action)}`;
    case "assert_return": return `${base}: assert_return ${getActionStr(command.action)} == ${getArgsStr(command.expected)}`;
    case "assert_return_nan": return `${base}: assert_return_nan ${getActionStr(command.action)}`;
  }
  return base;
}

function run(inPath, iStart, iEnd) {
  const lastSlash = Math.max(inPath.lastIndexOf("/"), inPath.lastIndexOf("\\"));
  const inDir = lastSlash === -1 ? "." : inPath.slice(0, lastSlash);
  const data = read(inPath);
  const jsonData = JSON.parse(data);
  file = jsonData.source_filename;

  const registry = Object.assign({spectest: {
    print,
    global: 666,
    table: new WebAssembly.Table({initial: 10, maximum: 20, element: "anyfunc"}),
    memory: new WebAssembly.Memory({initial: 1, maximum: 2})
  }}, IMPORTS_FROM_OTHER_SCRIPT);

  const moduleRegistry = {};
  moduleRegistry.currentModule = null;

  for (const command of jsonData.commands) {
    ++iTest;
    if (iTest < iStart) {
      // always run module/register commands that happens before iStart
      if (command.type !== "module" && command.type !== "register") {
        continue;
      }
    } else if (iTest > iEnd) {
      continue;
    }

    if (verbose) {
      print(`Running:${getCommandStr(command)}`);
    }

    switch (command.type) {
      case "module":
        moduleCommand(inDir, command, registry, moduleRegistry);
        break;

      case "assert_malformed":
        assertMalformed(inDir, command);
        break;

      case "assert_unlinkable":
        assertUnlinkable(inDir, command, registry);
        break;

      case "assert_invalid":
        assertMalformed(inDir, command, registry);
        break;

      case "register": {
        const {as, name = "currentModule"} = command;
        registry[as] = moduleRegistry[name].exports;
        break;
      }

      case "action":
        runSimpleAction(moduleRegistry, command);
        break;

      case "assert_return":
        assertReturn(moduleRegistry, command);
        break;

      case "assert_return_nan":
        assertReturn(moduleRegistry, command, true);
        break;

      case "assert_trap":
        if (trap) {
          assertTrap(moduleRegistry, command);
        } else {
          print(`${getCommandStr(command)} skipped because it has traps`);
        }
        break;
      default:
        print(`Unknown command: ${JSON.stringify(command)}`);
    }
  }
  end();
}

function createModule(baseDir, filename, registry, output) {
  const moduleFile = readbuffer(baseDir + "/" + filename);
  const u8a = new Uint8Array(moduleFile);
  output.module = new WebAssembly.Module(u8a);
  // We'll know if an error occurs at instanciation because output.module will be set
  output.instance = new WebAssembly.Instance(output.module, registry);
}

function moduleCommand(baseDir, command, registry, moduleRegistry) {
  const {filename, name} = command;
  try {
    const output = {};
    createModule(baseDir, filename, registry, output);
    if (name) {
      moduleRegistry[name] = output.instance;
    }
    moduleRegistry.currentModule = output.instance;
    ++passed;
  } catch (e) {
    ++failed;
    print(`${getCommandStr(command)} failed. Unexpected Error: ${e}`);
  }
  return null;
}

function assertMalformed(baseDir, command) {
  const {filename, text} = command;
  // Test hook to prevent deferred parsing
  WScript.Flag("-off:wasmdeferred");
  const output = {};
  try {
    createModule(baseDir, filename, null, output);
    ++failed;
    print(`${getCommandStr(command)} failed. Should have had an error`);
  } catch (e) {
    if (output.module) {
      ++failed;
      print(`${getCommandStr(command)} failed. Had a linking error, expected a compile error: ${e}`);
    } else if (e instanceof WebAssembly.CompileError) {
      ++passed;
      if (verbose) {
        print(`${getCommandStr(command)} passed. Had compile error: ${e}`);
        print(`  Spec expected error: ${text}`);
      }
    } else {
      ++failed;
      print(`${getCommandStr(command)} failed. Expected a compile error: ${e}`);
    }
  } finally {
    // Reset the test hook
    WScript.Flag("-on:wasmdeferred");
  }
}

function assertUnlinkable(baseDir, command, registry) {
  const {filename, text} = command;
  // Test hook to prevent deferred parsing
  WebAssembly.Module.prototype.deferred = false;
  const output = {};
  try {
    createModule(baseDir, filename, registry, output);
    ++failed;
    print(`${getCommandStr(command)} failed. Should have had an error`);
  } catch (e) {
    if (e instanceof WebAssembly.RuntimeError) {
      ++passed;
      if (verbose) {
        print(`${getCommandStr(command)} passed. Had linking error: ${e}`);
        print(`  Spec expected error: ${text}`);
      }
    } else {
      ++failed;
      print(`${getCommandStr(command)} failed. Expected a linking error: ${e}`);
    }
  } finally {
    // Reset the test hook
    WebAssembly.Module.prototype.deferred = undefined;
  }
}

function genConverters() {
  /*
  (module $converterBuffer
    (func (export "convertI64") (param i64) (result i64) (get_local 0))
    (func (export "toF32") (param i32) (result f32) (f32.reinterpret/i32 (get_local 0)))
    (func (export "toF64") (param i64) (result f64) (f64.reinterpret/i64 (get_local 0)))
  )
  */
  const converterBuffer = "\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x90\x80\x80\x80\x00\x03\x60\x01\x7e\x01\x7e\x60\x01\x7f\x01\x7d\x60\x01\x7e\x01\x7c\x03\x84\x80\x80\x80\x00\x03\x00\x01\x02\x07\x9e\x80\x80\x80\x00\x03\x0a\x63\x6f\x6e\x76\x65\x72\x74\x49\x36\x34\x00\x00\x05\x74\x6f\x46\x33\x32\x00\x01\x05\x74\x6f\x46\x36\x34\x00\x02\x0a\x9e\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xbe\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xbf\x0b";
  const buffer = new ArrayBuffer(converterBuffer.length);
  const view = new Uint8Array(buffer);
  for (let i = 0; i < converterBuffer.length; ++i) {
    view[i] = converterBuffer.charCodeAt(i);
  }
  const module = new WebAssembly.Module(buffer);
  const instance = new WebAssembly.Instance(module);
  return instance.exports;
}

const {toF32, toF64, convertI64} = genConverters();

function mapWasmArg({type, value}) {
  switch (type) {
    case "i32": return parseInt(value)|0;
    case "i64": return convertI64(value); // will convert string to {high, low}
    case "f32": return toF32(parseInt(value));
    case "f64": return toF64(value);
  }
  throw new Error("Unknown argument type");
}

function assertReturn(moduleRegistry, command, checkNaN) {
  const {action, expected} = command;
  try {
    const res = runAction(moduleRegistry, action);
    let success = true;
    if (expected.length === 0) {
      success = typeof res === "undefined";
    } else {
      // We don't support multi return right now
      const [ex1] = expected;
      const expectedResult = mapWasmArg(ex1);
      if (ex1.type === "i64") {
        success = expectedResult.low === res.low && expectedResult.high === res.high;
      } else if (checkNaN || isNaN(expectedResult)) {
        success = isNaN(res);
      } else {
        success = res === expectedResult;
      }
    }

    if (success) {
      passed++;
      if (verbose) {
        print(`${getCommandStr(command)} passed.`);
      }
    } else {
      print(`${getCommandStr(command)} failed. Returned ${getValueStr(res)}`);
      failed++;
    }
  } catch (e) {
    print(`${getCommandStr(command)} failed. Unexpectedly threw: ${e}`);
    failed++;
  }
}

function assertTrap(moduleRegistry, command) {
  const {action, text} = command;
  try {
    runAction(moduleRegistry, action);
    print(`${getCommandStr(command)} failed. Should have had an error`);
    failed++;
  } catch (e) {
    if (e instanceof WebAssembly.RuntimeError || e.message.indexOf("Out of stack") !== -1) {
      passed++;
      if (verbose) {
        print(`${getCommandStr(command)} passed. Error thrown: ${e}`);
        print(`  Spec error message expected: ${text}`);
      }
    } else {
      failed++;
      print(`${getCommandStr(command)} failed. Unexpected error thrown: ${e}`);
    }
  }
}

function runSimpleAction(moduleRegistry, command) {
  try {
    const res = runAction(moduleRegistry, command.action);
    ++passed;
    if (verbose) {
      print(`${getCommandStr(command)} = ${res}`);
    }
  } catch (e) {
    ++failed;
    print(`${getCommandStr(command)} failed. Unexpectedly threw: ${e}`);
  }
}

function runAction(moduleRegistry, action) {
  const m = action.module ? moduleRegistry[action.module] : moduleRegistry.currentModule;
  if (!m) {
    print("Module unavailable to run action");
    return;
  }
  switch (action.type) {
    case "invoke": {
      const {field, args} = action;
      const mappedArgs = args.map(({value}) => value);
      const wasmFn = m.exports[field];
      const res = wasmFn(...mappedArgs);
      return res;
    }
    case "get": {
      const {field} = action;
      return m.exports[field];
    }
    default:
      print(`Unknown action: ${JSON.stringify(action)}`);
  }
}

function end() {
  print(`${passed}/${passed + failed} tests passed.`);
}

run(cliArgs[0], cliArgs[1] | 0, cliArgs[2] === undefined ? undefined : cliArgs[2] | 0);
<commit_msg>Code review fix<commit_after>//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
// This file has been modified by Microsoft on [12/2016].

/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * 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.
 */

const cliArgs = WScript.Arguments || [];
WScript.Flag("-wasmI64");

if (cliArgs.length < 1) {
  print("usage: <exe> spec.js -args <filename.json> [start index] [end index] [-v] [-nt] -endargs");
  WScript.quit(0);
}

if (typeof IMPORTS_FROM_OTHER_SCRIPT === "undefined") {
  IMPORTS_FROM_OTHER_SCRIPT = {};
}

let passed = 0;
let failed = 0;

// Parse arguments
const iVerbose = cliArgs.indexOf("-v");
const verbose = iVerbose !== -1;
if (verbose) {
  cliArgs.splice(iVerbose, 1);
}
const iNoTrap = cliArgs.indexOf("-nt");
const noTrap = iNoTrap !== -1;
const trap = !noTrap;
if (noTrap) {
  cliArgs.splice(iNoTrap, 1);
}

let file = "";
let iTest = 0;
function getValueStr(value) {
  if (typeof value === "object") {
    const {high, low} = value;
    const convert = a => (a >>> 0).toString(16).padStart(8, "0");
    return `0x${convert(high)}${convert(low)}`;
  }
  return "" + value;
}

function getArgsStr(args) {
  return args
    .map(({type, value}) => `${type}:${getValueStr(mapWasmArg({type, value}))}`)
    .join(", ");
}

function getActionStr(action) {
  switch (action.type) {
    case "invoke": return `${action.field}(${getArgsStr(action.args)})`;
    case "get": return `${action.module || "$$"}[${action.field}]`;
    default: return "Unkown action type";
  }
}

function getCommandStr(command) {
  const base = `(${iTest}) ${file}:${command.line}`;
  switch (command.type) {
    case "module": return `${base}: generate module ${command.filename}${command.name ? ` as ${command.name}` : ""}`;
    case "register": return `${base}: register module ${command.name || "$$"} as ${command.as}`;
    case "assert_malformed": return `${base}: assert_malformed module ${command.filename}`;
    case "assert_unlinkable": return `${base}: assert_unlinkable module ${command.filename}`;
    case "assert_invalid": return `${base}: assert_invalid module ${command.filename}`;
    case "action": return `${base}: action ${getActionStr(command.action)}`;
    case "assert_trap": return `${base}: assert_trap ${getActionStr(command.action)}`;
    case "assert_return": return `${base}: assert_return ${getActionStr(command.action)} == ${getArgsStr(command.expected)}`;
    case "assert_return_nan": return `${base}: assert_return_nan ${getActionStr(command.action)}`;
  }
  return base;
}

function run(inPath, iStart, iEnd) {
  const lastSlash = Math.max(inPath.lastIndexOf("/"), inPath.lastIndexOf("\\"));
  const inDir = lastSlash === -1 ? "." : inPath.slice(0, lastSlash);
  const data = read(inPath);
  const jsonData = JSON.parse(data);
  file = jsonData.source_filename;

  const registry = Object.assign({spectest: {
    print,
    global: 666,
    table: new WebAssembly.Table({initial: 10, maximum: 20, element: "anyfunc"}),
    memory: new WebAssembly.Memory({initial: 1, maximum: 2})
  }}, IMPORTS_FROM_OTHER_SCRIPT);

  const moduleRegistry = {};
  moduleRegistry.currentModule = null;

  for (const command of jsonData.commands) {
    ++iTest;
    if (iTest < iStart) {
      // always run module/register commands that happens before iStart
      if (command.type !== "module" && command.type !== "register") {
        continue;
      }
    } else if (iTest > iEnd) {
      continue;
    }

    if (verbose) {
      print(`Running:${getCommandStr(command)}`);
    }

    switch (command.type) {
      case "module":
        moduleCommand(inDir, command, registry, moduleRegistry);
        break;

      case "assert_malformed":
        assertMalformed(inDir, command);
        break;

      case "assert_unlinkable":
        assertUnlinkable(inDir, command, registry);
        break;

      case "assert_invalid":
        assertMalformed(inDir, command, registry);
        break;

      case "register": {
        const {as, name = "currentModule"} = command;
        registry[as] = moduleRegistry[name].exports;
        break;
      }

      case "action":
        runSimpleAction(moduleRegistry, command);
        break;

      case "assert_return":
        assertReturn(moduleRegistry, command);
        break;

      case "assert_return_nan":
        assertReturn(moduleRegistry, command, true);
        break;

      case "assert_trap":
        if (trap) {
          assertTrap(moduleRegistry, command);
        } else {
          print(`${getCommandStr(command)} skipped because it has traps`);
        }
        break;
      default:
        print(`Unknown command: ${JSON.stringify(command)}`);
    }
  }
  end();
}

function createModule(baseDir, filename, registry, output) {
  const moduleFile = readbuffer(baseDir + "/" + filename);
  const u8a = new Uint8Array(moduleFile);
  output.module = new WebAssembly.Module(u8a);
  // We'll know if an error occurs at instanciation because output.module will be set
  output.instance = new WebAssembly.Instance(output.module, registry);
}

function moduleCommand(baseDir, command, registry, moduleRegistry) {
  const {filename, name} = command;
  try {
    const output = {};
    createModule(baseDir, filename, registry, output);
    if (name) {
      moduleRegistry[name] = output.instance;
    }
    moduleRegistry.currentModule = output.instance;
    ++passed;
  } catch (e) {
    ++failed;
    print(`${getCommandStr(command)} failed. Unexpected Error: ${e}`);
  }
  return null;
}

function assertMalformed(baseDir, command) {
  const {filename, text} = command;
  // Test hook to prevent deferred parsing
  WScript.Flag("-off:wasmdeferred");
  const output = {};
  try {
    createModule(baseDir, filename, null, output);
    ++failed;
    print(`${getCommandStr(command)} failed. Should have had an error`);
  } catch (e) {
    if (output.module) {
      ++failed;
      print(`${getCommandStr(command)} failed. Had a linking error, expected a compile error: ${e}`);
    } else if (e instanceof WebAssembly.CompileError) {
      ++passed;
      if (verbose) {
        print(`${getCommandStr(command)} passed. Had compile error: ${e}`);
        print(`  Spec expected error: ${text}`);
      }
    } else {
      ++failed;
      print(`${getCommandStr(command)} failed. Expected a compile error: ${e}`);
    }
  } finally {
    // Reset the test hook
    WScript.Flag("-on:wasmdeferred");
  }
}

function assertUnlinkable(baseDir, command, registry) {
  const {filename, text} = command;
  // Test hook to prevent deferred parsing
  WebAssembly.Module.prototype.deferred = false;
  const output = {};
  try {
    createModule(baseDir, filename, registry, output);
    ++failed;
    print(`${getCommandStr(command)} failed. Should have had an error`);
  } catch (e) {
    if (e instanceof WebAssembly.RuntimeError) {
      ++passed;
      if (verbose) {
        print(`${getCommandStr(command)} passed. Had linking error: ${e}`);
        print(`  Spec expected error: ${text}`);
      }
    } else {
      ++failed;
      print(`${getCommandStr(command)} failed. Expected a linking error: ${e}`);
    }
  } finally {
    // Reset the test hook
    WebAssembly.Module.prototype.deferred = undefined;
  }
}

function genConverters() {
  /*
  (module $converterBuffer
    (func (export "convertI64") (param i64) (result i64) (get_local 0))
    (func (export "toF32") (param i32) (result f32) (f32.reinterpret/i32 (get_local 0)))
    (func (export "toF64") (param i64) (result f64) (f64.reinterpret/i64 (get_local 0)))
  )
  */
  const converterBuffer = "\x00\x61\x73\x6d\x0d\x00\x00\x00\x01\x90\x80\x80\x80\x00\x03\x60\x01\x7e\x01\x7e\x60\x01\x7f\x01\x7d\x60\x01\x7e\x01\x7c\x03\x84\x80\x80\x80\x00\x03\x00\x01\x02\x07\x9e\x80\x80\x80\x00\x03\x0a\x63\x6f\x6e\x76\x65\x72\x74\x49\x36\x34\x00\x00\x05\x74\x6f\x46\x33\x32\x00\x01\x05\x74\x6f\x46\x36\x34\x00\x02\x0a\x9e\x80\x80\x80\x00\x03\x84\x80\x80\x80\x00\x00\x20\x00\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xbe\x0b\x85\x80\x80\x80\x00\x00\x20\x00\xbf\x0b";
  const buffer = new ArrayBuffer(converterBuffer.length);
  const view = new Uint8Array(buffer);
  for (let i = 0; i < converterBuffer.length; ++i) {
    view[i] = converterBuffer.charCodeAt(i);
  }
  const module = new WebAssembly.Module(buffer);
  const instance = new WebAssembly.Instance(module);
  return instance.exports;
}

const {toF32, toF64, convertI64} = genConverters();

function mapWasmArg({type, value}) {
  switch (type) {
    case "i32": return parseInt(value)|0;
    case "i64": return convertI64(value); // will convert string to {high, low}
    case "f32": return toF32(parseInt(value));
    case "f64": return toF64(value);
  }
  throw new Error("Unknown argument type");
}

function assertReturn(moduleRegistry, command, checkNaN) {
  const {action, expected} = command;
  try {
    const res = runAction(moduleRegistry, action);
    let success = true;
    if (expected.length === 0) {
      success = typeof res === "undefined";
    } else {
      // We don't support multi return right now
      const [ex1] = expected;
      const expectedResult = mapWasmArg(ex1);
      if (ex1.type === "i64") {
        success = expectedResult.low === res.low && expectedResult.high === res.high;
      } else if (checkNaN || isNaN(expectedResult)) {
        success = isNaN(res);
      } else {
        success = res === expectedResult;
      }
    }

    if (success) {
      passed++;
      if (verbose) {
        print(`${getCommandStr(command)} passed.`);
      }
    } else {
      print(`${getCommandStr(command)} failed. Returned ${getValueStr(res)}`);
      failed++;
    }
  } catch (e) {
    print(`${getCommandStr(command)} failed. Unexpectedly threw: ${e}`);
    failed++;
  }
}

function assertTrap(moduleRegistry, command) {
  const {action, text} = command;
  try {
    runAction(moduleRegistry, action);
    print(`${getCommandStr(command)} failed. Should have had an error`);
    failed++;
  } catch (e) {
    if (e instanceof WebAssembly.RuntimeError || e.message.indexOf("Out of stack") !== -1) {
      passed++;
      if (verbose) {
        print(`${getCommandStr(command)} passed. Error thrown: ${e}`);
        print(`  Spec error message expected: ${text}`);
      }
    } else {
      failed++;
      print(`${getCommandStr(command)} failed. Unexpected error thrown: ${e}`);
    }
  }
}

function runSimpleAction(moduleRegistry, command) {
  try {
    const res = runAction(moduleRegistry, command.action);
    ++passed;
    if (verbose) {
      print(`${getCommandStr(command)} = ${res}`);
    }
  } catch (e) {
    ++failed;
    print(`${getCommandStr(command)} failed. Unexpectedly threw: ${e}`);
  }
}

function runAction(moduleRegistry, action) {
  const m = action.module ? moduleRegistry[action.module] : moduleRegistry.currentModule;
  if (!m) {
    print("Module unavailable to run action");
    return;
  }
  switch (action.type) {
    case "invoke": {
      const {field, args} = action;
      const mappedArgs = args.map(({value}) => value);
      const wasmFn = m.exports[field];
      const res = wasmFn(...mappedArgs);
      return res;
    }
    case "get": {
      const {field} = action;
      return m.exports[field];
    }
    default:
      print(`Unknown action: ${JSON.stringify(action)}`);
  }
}

function end() {
  print(`${passed}/${passed + failed} tests passed.`);
}

run(cliArgs[0], cliArgs[1] | 0, cliArgs[2] === undefined ? undefined : cliArgs[2] | 0);
<|endoftext|>


<commit_before>zeus.simpleModule('apps.projects', function (projects, region, _, Marionette) {

    projects.Controller = Marionette.Controller.extend({
        projectLayoutView: null,
        contentRegion: null,

        initialize: function (options) {
            this.project = options.project;
            this.region = options.region;
            _.bindAll(this,
                'initializeProjectLayoutView',
                'showProject',
                'showProjectDetails',
                'showBuildset'
            );
            this.initializeProjectLayoutView();
        },

        initializeProjectLayoutView: function () {
            this.layout = new projects.views.ProjectLayout({
                model: this.project
            });
            this.contentRegion = this.layout.contentRegion;
            this.region.show(this.layout);
        },

        showProject: function () {
            console.log(" => show project");
            this.showProjectDetails();
            zeus.navigate(zeus.project.url);
        },

        showProjectDetails: function () {
            var view = new projects.views.ProjectDetails({model: this.project});
            this.contentRegion.show(view);
        },

        showBuildset: function (name, number) {
            console.log(" => show buildset " + number);
            var buildset = new projects.models.Buildset({number: number});
            var view = new projects.views.BuildsetDetails({model: buildset});
            var self = this;
            buildset.fetch({
                success: function () {
                    self.contentRegion.show(view);
                    zeus.navigate(buildset.get('url'));
                }
            });
        }
    });

    // We need to initialize controller before initialization so router can
    // trigger 'show:project' but we cannot do it before Project model is
    // defined
    // TODO: we can change order of how modules are loaded and put app.js
    // at the end of this chain
    zeus.on('initialize:before', function () {
        var controller = new projects.Controller({
            region: region,
            project: new projects.models.Project(zeus.project)
        });

        zeus.on('show:project', controller.showProject);
        zeus.on('show:buildset', controller.showBuildset);

        projects.controller = controller;
    });

}, zeus.mainRegion, _, Marionette);
<commit_msg>Rename method<commit_after>zeus.simpleModule('apps.projects', function (projects, region, _, Marionette) {

    projects.Controller = Marionette.Controller.extend({
        projectLayoutView: null,
        contentRegion: null,

        initialize: function (options) {
            this.project = options.project;
            this.region = options.region;
            _.bindAll(this,
                'initializeLayout',
                'showProject',
                'showProjectDetails',
                'showBuildset'
            );
            this.initializeLayout();
        },

        initializeLayout: function () {
            this.layout = new projects.views.ProjectLayout({
                model: this.project
            });
            this.contentRegion = this.layout.contentRegion;
            this.region.show(this.layout);
        },

        showProject: function () {
            console.log(" => show project");
            this.showProjectDetails();
            zeus.navigate(zeus.project.url);
        },

        showProjectDetails: function () {
            var view = new projects.views.ProjectDetails({model: this.project});
            this.contentRegion.show(view);
        },

        showBuildset: function (name, number) {
            console.log(" => show buildset " + number);
            var buildset = new projects.models.Buildset({number: number});
            var view = new projects.views.BuildsetDetails({model: buildset});
            var self = this;
            buildset.fetch({
                success: function () {
                    self.contentRegion.show(view);
                    zeus.navigate(buildset.get('url'));
                }
            });
        }
    });

    // We need to initialize controller before initialization so router can
    // trigger 'show:project' but we cannot do it before Project model is
    // defined
    // TODO: we can change order of how modules are loaded and put app.js
    // at the end of this chain
    zeus.on('initialize:before', function () {
        var controller = new projects.Controller({
            region: region,
            project: new projects.models.Project(zeus.project)
        });

        zeus.on('show:project', controller.showProject);
        zeus.on('show:buildset', controller.showBuildset);

        projects.controller = controller;
    });

}, zeus.mainRegion, _, Marionette);
<|endoftext|>


<commit_before>'use strict';

var mysql = require('mysql');
var debug_log = require('debug')('express-mysql-session:log');
var debug_error = require('debug')('express-mysql-session:error');
var deprecate = require('depd')('express-mysql-session');
var util = require('util');

var defaultOptions = {
	checkExpirationInterval: 900000,// How frequently expired sessions will be cleared; milliseconds.
	expiration: 86400000,// The maximum age of a valid session; milliseconds.
	createDatabaseTable: true,// Whether or not to create the sessions database table, if one does not already exist.
	connectionLimit: 1,// Number of connections when creating a connection pool
	charset: 'utf8mb4_bin',
	schema: {
		tableName: 'sessions',
		columnNames: {
			session_id: 'session_id',
			expires: 'expires',
			data: 'data'
		}
	}
};

module.exports = function(session) {

	var constructorArgs;

	if (typeof session.Store === 'undefined') {
		session = require('express-session');
		constructorArgs = Array.prototype.slice.call(arguments);
	}

	var Store = session.Store;

	var MySQLStore = function(options, connection, cb) {

		debug_log('Creating session store');

		this.options = this.clone(options || {});
		this.setDefaultOptions();

		if (this.options.debug) {
			deprecate('The \'debug\' option has been removed. This module now uses the debug module to output logs and error messages. Run your app with `DEBUG=express-mysql-session* node your-app.js` to have all logs and errors outputted to the console.');
		}

		if (typeof connection === 'function') {
			cb = connection;
			connection = null;
		}

		this.connection = connection || mysql.createPool(this.options);

		var done = function() {

			this.setExpirationInterval();

			if (cb) {
				cb.apply(undefined, arguments);
			}

		}.bind(this);

		if (!this.options.createDatabaseTable) {
			setTimeout(done, 0);
			return;
		}

		this.createDatabaseTable(done);
	};

	util.inherits(MySQLStore, Store);

	MySQLStore.prototype.setDefaultOptions = function() {

		debug_log('Setting default options');

		this.options = this.defaults(this.options, defaultOptions, { recursive: true });
	};

	MySQLStore.prototype.createDatabaseTable = function(cb) {

		debug_log('Creating sessions database table');

		var fs = require('fs');

		fs.readFile(__dirname + '/../schema.sql', 'utf-8', function(error, sql) {

			if (error) {
				return cb && cb(error);
			}

			sql = sql.replace(/`[^`]+`/g, '??');

			var params = [
				this.options.schema.tableName,
				this.options.schema.columnNames.session_id,
				this.options.schema.columnNames.expires,
				this.options.schema.columnNames.data,
				this.options.schema.columnNames.session_id
			];

			this.connection.query(sql, params, function(error) {

				if (error) {
					debug_error('Failed to create sessions database table.');
					debug_error(error);
					return cb && cb(error);
				}

				cb && cb();
			});

		}.bind(this));
	};

	MySQLStore.prototype.get = function(session_id, cb) {

		debug_log('Getting session: ' + session_id);

		var sql = 'SELECT ?? AS data FROM ?? WHERE ?? = ? LIMIT 1';

		var params = [
			this.options.schema.columnNames.data,
			this.options.schema.tableName,
			this.options.schema.columnNames.session_id,
			session_id
		];

		this.connection.query(sql, params, function(error, rows) {

			if (error) {
				debug_error('Failed to get session.');
				debug_error(error);
				return cb(error, null);
			}

			try {
				var session = rows[0] ? JSON.parse(rows[0].data) : null;
			} catch (error) {
				debug_error(error);
				return cb(new Error('Failed to parse data for session: ' + session_id));
			}

			cb(null, session);
		});
	};

	MySQLStore.prototype.set = function(session_id, data, cb) {

		debug_log('Setting session: ' + session_id);

		var expires;

		if (data.cookie) {
			if (data.cookie.expires) {
				expires = data.cookie.expires;
			} else if (data.cookie._expires) {
				expires = data.cookie._expires;
			}
		}

		if (!expires) {
			expires = Date.now() + this.options.expiration;
		}

		if (!(expires instanceof Date)) {
			expires = new Date(expires);
		}

		// Use whole seconds here; not milliseconds.
		expires = Math.round(expires.getTime() / 1000);

		data = JSON.stringify(data);

		var sql = 'INSERT INTO ?? (??, ??, ??) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE ?? = VALUES(??), ?? = VALUES(??)';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.session_id,
			this.options.schema.columnNames.expires,
			this.options.schema.columnNames.data,
			session_id,
			expires,
			data,
			this.options.schema.columnNames.expires,
			this.options.schema.columnNames.expires,
			this.options.schema.columnNames.data,
			this.options.schema.columnNames.data
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug_error('Failed to insert session data.');
				debug_error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.touch = function(session_id, data, cb) {

		debug_log('Touching session: ' + session_id);

		var expires;

		if (data.cookie) {
			if (data.cookie.expires) {
				expires = data.cookie.expires;
			} else if (data.cookie._expires) {
				expires = data.cookie._expires;
			}
		}

		if (!expires) {
			expires = Date.now() + this.options.expiration;
		}

		if (!(expires instanceof Date)) {
			expires = new Date(expires);
		}

		// Use whole seconds here; not milliseconds.
		expires = Math.round(expires.getTime() / 1000);

		var sql = 'UPDATE ?? SET ?? = ? WHERE ?? = ? LIMIT 1';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.expires,
			expires,
			this.options.schema.columnNames.session_id,
			session_id
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug_error('Failed to touch session.');
				debug_error(error);
				return cb && cb(error);
			}

			return cb && cb();
		});
	};

	MySQLStore.prototype.destroy = function(session_id, cb) {

		debug_log('Destroying session: ' + session_id);

		var sql = 'DELETE FROM ?? WHERE ?? = ? LIMIT 1';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.session_id,
			session_id
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug_error('Failed to destroy session.');
				debug_error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.length = function(cb) {

		debug_log('Getting number of sessions');

		var sql = 'SELECT COUNT(*) FROM ??';

		var params = [
			this.options.schema.tableName
		];

		this.connection.query(sql, params, function(error, rows) {

			if (error) {
				debug_error('Failed to get number of sessions.');
				debug_error(error);
				return cb && cb(error);
			}

			var count = rows[0] ? rows[0]['COUNT(*)'] : 0;

			cb(null, count);
		});
	};

	MySQLStore.prototype.clear = function(cb) {

		debug_log('Clearing all sessions');

		var sql = 'DELETE FROM ??';

		var params = [
			this.options.schema.tableName
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug_error('Failed to clear all sessions.');
				debug_error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.clearExpiredSessions = function(cb) {

		debug_log('Clearing expired sessions');

		var sql = 'DELETE FROM ?? WHERE ?? < ?';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.expires,
			Math.round(Date.now() / 1000)
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug_error('Failed to clear expired sessions.');
				debug_error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.setExpirationInterval = function(interval) {

		interval || (interval = this.options.checkExpirationInterval);

		debug_log('Setting expiration interval: ' + interval + 'ms');

		this.clearExpirationInterval();
		this._expirationInterval = setInterval(this.clearExpiredSessions.bind(this), interval);
	};

	MySQLStore.prototype.clearExpirationInterval = function() {

		debug_log('Clearing expiration interval');

		clearInterval(this._expirationInterval);
		this._expirationInterval = null;
	};

	MySQLStore.prototype.close = function(cb) {

		debug_log('Closing session store');

		this.clearExpirationInterval();

		if (this.connection) {
			this.connection.end(cb);
		} else {
			cb && cb(null);
		}
	};

	MySQLStore.prototype.defaults = function(object, defaultValues, options) {

		if (!this.isObject(object)) {
			return object;
		}

		object = this.clone(object);
		defaultValues = defaultValues || {};
		options = options || {};
		options.recursive = options.recursive === true;

		for (var key in defaultValues) {

			if (typeof object[key] === 'undefined') {
				object[key] = defaultValues[key];
			}

			if (options.recursive) {
				object[key] = this.defaults(object[key], defaultValues[key], options);
			}
		}

		return object;
	}

	MySQLStore.prototype.clone = function(object) {

		return JSON.parse(JSON.stringify(object));
	}

	MySQLStore.prototype.isObject = function(value) {

		var type = typeof value;
		return type === 'function' || type === 'object' && !!value;
	}

	// For backwards compatibility:
	MySQLStore.prototype.closeStore = deprecate.function(
		MySQLStore.prototype.close,
		'The closeStore() method has been deprecated. Use close() instead.'
	);

	// For backwards compatibility:
	MySQLStore.prototype.sync = deprecate.function(
		MySQLStore.prototype.createDatabaseTable,
		'The sync() method has been deprecated. Use createDatabaseTable() instead.'
	);

	if (constructorArgs) {
		// For backwards compatibility.
		// Immediately call as a constructor.
		return new (MySQLStore.bind.apply(MySQLStore, [undefined/* context */].concat(constructorArgs)))();
	}

	return MySQLStore;
};
<commit_msg>Added underscore (defaults, clone, isObject) and refactoring<commit_after>'use strict';

var _ = require('underscore');
var mysql = require('mysql');
var path = require('path');
var util = require('util');

var debug = {
	log: require('debug')('express-mysql-session:log'),
	error: require('debug')('express-mysql-session:error')
};

var deprecate = require('depd')('express-mysql-session');

var defaultOptions = {
	checkExpirationInterval: 900000,// How frequently expired sessions will be cleared; milliseconds.
	expiration: 86400000,// The maximum age of a valid session; milliseconds.
	createDatabaseTable: true,// Whether or not to create the sessions database table, if one does not already exist.
	connectionLimit: 1,// Number of connections when creating a connection pool
	charset: 'utf8mb4_bin',
	schema: {
		tableName: 'sessions',
		columnNames: {
			session_id: 'session_id',
			expires: 'expires',
			data: 'data'
		}
	}
};

module.exports = function(session) {

	var constructorArgs;

	if (_.isUndefined(session.Store)) {
		session = require('express-session');
		constructorArgs = Array.prototype.slice.call(arguments);
	}

	var Store = session.Store;

	var MySQLStore = function(options, connection, cb) {

		debug.log('Creating session store');

		this.options = _.defaults(options || {}, defaultOptions);
		this.options.schema = _.defaults(this.options.schema, defaultOptions.schema);
		this.options.schema.columnNames = _.defaults(this.options.schema.columnNames, defaultOptions.schema.columnNames);

		if (this.options.debug) {
			deprecate('The \'debug\' option has been removed. This module now uses the debug module to output logs and error messages. Run your app with `DEBUG=express-mysql-session* node your-app.js` to have all logs and errors outputted to the console.');
		}

		if (_.isFunction(connection)) {
			cb = connection;
			connection = null;
		}

		this.connection = connection || mysql.createPool(this.options);

		var done = function() {

			this.setExpirationInterval();

			if (cb) {
				cb.apply(undefined, arguments);
			}

		}.bind(this);

		if (this.options.createDatabaseTable) {
			this.createDatabaseTable(done);
		} else {
			_.defer(done);
		}
	};

	util.inherits(MySQLStore, Store);

	MySQLStore.prototype.createDatabaseTable = function(cb) {

		debug.log('Creating sessions database table');

		var fs = require('fs');
		var schemaFilePath = path.join(__dirname, '..', 'schema.sql');

		fs.readFile(schemaFilePath, 'utf-8', function(error, sql) {

			if (error) {
				debug.error('Failed to read schema file.');
				return cb && cb(error);
			}

			sql = sql.replace(/`[^`]+`/g, '??');

			var params = [
				this.options.schema.tableName,
				this.options.schema.columnNames.session_id,
				this.options.schema.columnNames.expires,
				this.options.schema.columnNames.data,
				this.options.schema.columnNames.session_id
			];

			this.connection.query(sql, params, function(error) {

				if (error) {
					debug.error('Failed to create sessions database table.');
					debug.error(error);
					return cb && cb(error);
				}

				cb && cb();
			});

		}.bind(this));
	};

	MySQLStore.prototype.get = function(session_id, cb) {

		debug.log('Getting session:', session_id);

		var sql = 'SELECT ?? AS data FROM ?? WHERE ?? = ? LIMIT 1';

		var params = [
			this.options.schema.columnNames.data,
			this.options.schema.tableName,
			this.options.schema.columnNames.session_id,
			session_id
		];

		this.connection.query(sql, params, function(error, rows) {

			if (error) {
				debug.error('Failed to get session:', session_id);
				debug.error(error);
				return cb(error, null);
			}

			try {
				var session = rows[0] ? JSON.parse(rows[0].data) : null;
			} catch (error) {
				debug.error(error);
				return cb(new Error('Failed to parse data for session:', session_id));
			}

			cb(null, session);
		});
	};

	MySQLStore.prototype.set = function(session_id, data, cb) {

		debug.log('Setting session:', session_id);

		var expires;

		if (data.cookie) {
			if (data.cookie.expires) {
				expires = data.cookie.expires;
			} else if (data.cookie._expires) {
				expires = data.cookie._expires;
			}
		}

		if (!expires) {
			expires = Date.now() + this.options.expiration;
		}

		if (!(expires instanceof Date)) {
			expires = new Date(expires);
		}

		// Use whole seconds here; not milliseconds.
		expires = Math.round(expires.getTime() / 1000);

		data = JSON.stringify(data);

		var sql = 'INSERT INTO ?? (??, ??, ??) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE ?? = VALUES(??), ?? = VALUES(??)';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.session_id,
			this.options.schema.columnNames.expires,
			this.options.schema.columnNames.data,
			session_id,
			expires,
			data,
			this.options.schema.columnNames.expires,
			this.options.schema.columnNames.expires,
			this.options.schema.columnNames.data,
			this.options.schema.columnNames.data
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug.error('Failed to insert session data.');
				debug.error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.touch = function(session_id, data, cb) {

		debug.log('Touching session:', session_id);

		var expires;

		if (data.cookie) {
			if (data.cookie.expires) {
				expires = data.cookie.expires;
			} else if (data.cookie._expires) {
				expires = data.cookie._expires;
			}
		}

		if (!expires) {
			expires = Date.now() + this.options.expiration;
		}

		if (!(expires instanceof Date)) {
			expires = new Date(expires);
		}

		// Use whole seconds here; not milliseconds.
		expires = Math.round(expires.getTime() / 1000);

		var sql = 'UPDATE ?? SET ?? = ? WHERE ?? = ? LIMIT 1';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.expires,
			expires,
			this.options.schema.columnNames.session_id,
			session_id
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug.error('Failed to touch session:', session_id);
				debug.error(error);
				return cb && cb(error);
			}

			return cb && cb();
		});
	};

	MySQLStore.prototype.destroy = function(session_id, cb) {

		debug.log('Destroying session:', session_id);

		var sql = 'DELETE FROM ?? WHERE ?? = ? LIMIT 1';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.session_id,
			session_id
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug.error('Failed to destroy session:', session_id);
				debug.error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.length = function(cb) {

		debug.log('Getting number of sessions');

		var sql = 'SELECT COUNT(*) FROM ??';

		var params = [
			this.options.schema.tableName
		];

		this.connection.query(sql, params, function(error, rows) {

			if (error) {
				debug.error('Failed to get number of sessions.');
				debug.error(error);
				return cb && cb(error);
			}

			var count = rows[0] ? rows[0]['COUNT(*)'] : 0;

			cb(null, count);
		});
	};

	MySQLStore.prototype.clear = function(cb) {

		debug.log('Clearing all sessions');

		var sql = 'DELETE FROM ??';

		var params = [
			this.options.schema.tableName
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug.error('Failed to clear all sessions.');
				debug.error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.clearExpiredSessions = function(cb) {

		debug.log('Clearing expired sessions');

		var sql = 'DELETE FROM ?? WHERE ?? < ?';

		var params = [
			this.options.schema.tableName,
			this.options.schema.columnNames.expires,
			Math.round(Date.now() / 1000)
		];

		this.connection.query(sql, params, function(error) {

			if (error) {
				debug.error('Failed to clear expired sessions.');
				debug.error(error);
				return cb && cb(error);
			}

			cb && cb();
		});
	};

	MySQLStore.prototype.setExpirationInterval = function(interval) {

		interval || (interval = this.options.checkExpirationInterval);

		debug.log('Setting expiration interval to', interval + 'ms');

		this.clearExpirationInterval();
		this._expirationInterval = setInterval(this.clearExpiredSessions.bind(this), interval);
	};

	MySQLStore.prototype.clearExpirationInterval = function() {

		debug.log('Clearing expiration interval');

		clearInterval(this._expirationInterval);
		this._expirationInterval = null;
	};

	MySQLStore.prototype.close = function(cb) {

		debug.log('Closing session store');

		this.clearExpirationInterval();

		if (this.connection) {
			this.connection.end(cb);
		} else {
			cb && cb(null);
		}
	};

	MySQLStore.prototype.closeStore = deprecate.function(
		MySQLStore.prototype.close,
		'The closeStore() method has been deprecated. Use close() instead.'
	);

	MySQLStore.prototype.sync = deprecate.function(
		MySQLStore.prototype.createDatabaseTable,
		'The sync() method has been deprecated. Use createDatabaseTable() instead.'
	);

	MySQLStore.prototype.defaults = deprecate.function(
		function defaults(object, defaultValues, options) {

			object = _.clone(object);

			if (!_.isObject(object)) {
				return object;
			}

			options = options || {};

			_.each(defaultValues, function(value, key) {

				if (_.isUndefined(object[key])) {
					object[key] = value;
				}

				if (options.recursive) {
					object[key] = defaults(object[key], value, options);
				}
			});

			return object;
		},
		'The defaults() method has been deprecated and will be removed in a future version.'
	);

	MySQLStore.prototype.clone = deprecate.function(
		_.clone,
		'The clone() method has been deprecated and will be removed in a future version.'
	);

	MySQLStore.prototype.isObject = deprecate.function(
		_.isObject,
		'The isObject() method has been deprecated and will be removed in a future version.'
	);

	MySQLStore.prototype.setDefaultOptions = deprecate.function(
		_.noop,
		'The setDefaultOptions() method has been deprecated and will be removed in a future version.'
	);

	if (constructorArgs) {
		// For backwards compatibility.
		// Immediately call as a constructor.
		return new (MySQLStore.bind.apply(MySQLStore, [undefined/* context */].concat(constructorArgs)))();
	}

	return MySQLStore;
};
<|endoftext|>


<commit_before>export function getAbsoluteUrl({ path, getSassFileGlobPattern, glob }, url, includePaths = []) {
  const { dir, base } = path.parse(url);
  const baseGlobPattern = getSassFileGlobPattern(base);
  includePaths.some((includePath) => {
    glob.sync(path.resolve(includePath, dir, baseGlobPattern));
    return false;
  });
}

export default function getAbsoluteUrlFactory() {
  return getAbsoluteUrl.bind(null);
}
<commit_msg>Implement the simplified testable module pattern.<commit_after>import globModule from 'glob';
import pathModule from 'path';

import getSassFileGlobPatternModule from './get-sass-file-glob-pattern';

export function getAbsoluteUrl({ path, getSassFileGlobPattern, glob }, url, includePaths = []) {
  const { dir, base } = path.parse(url);
  const baseGlobPattern = getSassFileGlobPattern(base);
  includePaths.some((includePath) => {
    glob.sync(path.resolve(includePath, dir, baseGlobPattern));
    return false;
  });
}

export default getAbsoluteUrl.bind(null, {
  path: pathModule,
  getSassFileGlobPattern: getSassFileGlobPatternModule,
  glob: globModule,
});
<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * IO type for PhetButton, to interface with phet-io api.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var joist = require( 'JOIST/joist' );
  var NodeProperty = require( 'SCENERY/util/NodeProperty' );
  var PropertyIO = require( 'AXON/PropertyIO' );

  // ifphetio
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var BooleanIO = require( 'ifphetio!PHET_IO/types/BooleanIO' );
  var NullableIO = require( 'ifphetio!PHET_IO/types/NullableIO' );
  var ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );

  /**
   * IO type for phet/joist's PhetButton
   * @param {PhetButton} phetButton
   * @param {string} phetioID
   * @constructor
   */
  function PhetButtonIO( phetButton, phetioID ) {
    assert && assertInstanceOf( phetButton, phet.joist.PhetButton );
    ObjectIO.call( this, phetButton, phetioID );

    // The PhetButtonIO acts as the main phet-io branding/logo in the sim. It doesn't inherit from NodeIO because we don't
    // all of NodeIO's interactive methods, nor do we want to support maintaining overriding no-ops in this file
    // see https://github.com/phetsims/scenery/issues/711 for more info.
    // Note that this code is duplicated with similar code in NodeIO
    var pickableProperty = new NodeProperty( phetButton, 'pickability', 'pickable', {

      // pick the following values from the parent Node
      phetioReadOnly: phetButton.phetioReadOnly,
      phetioState: phetButton.phetioState,

      tandem: phetButton.tandem.createTandem( 'pickableProperty' ),
      phetioType: PropertyIO( NullableIO( BooleanIO ) ),
      phetioInstanceDocumentation: 'Set whether the phetButton will be pickable (and hence interactive), see the NodeIO documentation for more details'
    } );

    // @private
    this.disposePhetButtonIO = function() {
      pickableProperty.dispose();
    };
  }

  phetioInherit( ObjectIO, 'PhetButtonIO', PhetButtonIO, {

    /**
     * @public - called by PhetioObject when the wrapper is done
     */
    dispose: function() {
      this.disposePhetButtonIO();
    }
  }, {
    documentation: 'The PhET Button in the bottom right of the screen',

    /**
     * See NodeIO.toStateObject
     * @returns {undefined} - We don't use null because other types want that value in the state, see `NullableIO` for example.
     * @override
     */
    toStateObject: function() {
      return undefined;
    },

    /**
     * See NodeIO.fromStateObject
     * @param {Node} o
     * @returns {Object}
     * @override - to prevent attempted JSON serialization of circular Node
     */
    fromStateObject: function( o ) {
      return o; // Pass through values defined by subclasses, such as AquaRadioButtonIO.enabled
    }
  } );

  joist.register( 'PhetButtonIO', PhetButtonIO );

  return PhetButtonIO;
} );

<commit_msg>Converted AquaRadioButton to use FireListener, see https://github.com/phetsims/sun/issues/401<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * IO type for PhetButton, to interface with phet-io api.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var joist = require( 'JOIST/joist' );
  var NodeProperty = require( 'SCENERY/util/NodeProperty' );
  var PropertyIO = require( 'AXON/PropertyIO' );

  // ifphetio
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var BooleanIO = require( 'ifphetio!PHET_IO/types/BooleanIO' );
  var NullableIO = require( 'ifphetio!PHET_IO/types/NullableIO' );
  var ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );

  /**
   * IO type for phet/joist's PhetButton
   * @param {PhetButton} phetButton
   * @param {string} phetioID
   * @constructor
   */
  function PhetButtonIO( phetButton, phetioID ) {
    assert && assertInstanceOf( phetButton, phet.joist.PhetButton );
    ObjectIO.call( this, phetButton, phetioID );

    // The PhetButtonIO acts as the main phet-io branding/logo in the sim. It doesn't inherit from NodeIO because we don't
    // all of NodeIO's interactive methods, nor do we want to support maintaining overriding no-ops in this file
    // see https://github.com/phetsims/scenery/issues/711 for more info.
    // Note that this code is duplicated with similar code in NodeIO
    var pickableProperty = new NodeProperty( phetButton, 'pickability', 'pickable', {

      // pick the following values from the parent Node
      phetioReadOnly: phetButton.phetioReadOnly,
      phetioState: phetButton.phetioState,

      tandem: phetButton.tandem.createTandem( 'pickableProperty' ),
      phetioType: PropertyIO( NullableIO( BooleanIO ) ),
      phetioInstanceDocumentation: 'Set whether the phetButton will be pickable (and hence interactive), see the NodeIO documentation for more details'
    } );

    // @private
    this.disposePhetButtonIO = function() {
      pickableProperty.dispose();
    };
  }

  phetioInherit( ObjectIO, 'PhetButtonIO', PhetButtonIO, {

    /**
     * @public - called by PhetioObject when the wrapper is done
     */
    dispose: function() {
      this.disposePhetButtonIO();
    }
  }, {
    documentation: 'The PhET Button in the bottom right of the screen',

    /**
     * See NodeIO.toStateObject
     * @returns {undefined} - We don't use null because other types want that value in the state, see `NullableIO` for example.
     * @override
     */
    toStateObject: function() {
      return undefined;
    },

    /**
     * See NodeIO.fromStateObject
     * @param {Node} o
     * @returns {Object}
     * @override - to prevent attempted JSON serialization of circular Node
     */
    fromStateObject: function( o ) {
      return o; // Pass through values defined by subclasses
    }
  } );

  joist.register( 'PhetButtonIO', PhetButtonIO );

  return PhetButtonIO;
} );

<|endoftext|>


<commit_before>'use strict'

const Logger = require('./util/logger')
const logger = Logger.global.chain('[jirascope]')

/**
 * Provides an understanding of jira issues and their network.
 *
 * @param {Object} jiraClient - The client to use to talk to JIRA
 * @param {Object?} dataStore - The data store to use
 * @param {Object} options - Options to customise behaviour
 * @param {string} options.query - The initial query to use
 * @param {string[]} options.followStatusCategories - An array of status categories to follow
 * @param {string[]} options.followLinkTypes - An array of link types to follow
 *
 * @property {Object} issues - issues keyed by key
 */
class Jirascope {
  constructor (jiraClient, dataStore, options) {
    this.jiraClient = jiraClient
    this.dataStore = dataStore
    this.options = options
    this.issues = {}
    this.orphans = []
    this.graphs = []
  }

  async populate () {
    if (this.dataStore) {
      this.issues = await this.dataStore.readData('issues')
    }
    if (!this.issues) {
      await this.fetch()
    }
    this.analyse()
  }

  async fetch () {
    const issues = {}
    logger.info(`fetching initial issues`)
    let newIssues = await this.jiraClient.getIssuesByQuery(this.options.query, 1000)

    while (newIssues.length > 0) {
      newIssues.forEach((newIssue) => {
        issues[newIssue.key] = newIssue
        logger.trace(`${newIssue.key} found`)
      })

      let missingIssues = {}
      logger.info(`reviewing issue links`)
      newIssues.forEach((newIssue) => {
        newIssue.links = newIssue.links.filter((link) => { return this.options.followLinkTypes.includes(link.type) })
        if (newIssue.links.length === 0) {
          logger.trace(`${newIssue.key} - no links`)
        } else if (!this.options.followStatusCategories.includes(newIssue.statusCategory)) {
          logger.trace(`${newIssue.key} - skipping due to status category of '${newIssue.statusCategory}'`)
        } else {
          logger.trace(`${newIssue.key} - evaluating links`)
          newIssue.links.forEach((link) => {
            if (!issues[link.dstKey]) {
              missingIssues[link.dstKey] = link.dstKey
              logger.trace(`${link.srcKey} ${link.label} ${link.dstKey} - ${link.dstKey} not yet fetched`)
            } else {
              logger.trace(`${link.srcKey} ${link.label} ${link.dstKey} - ${link.dstKey} already fetched`)
            }
          })
        }
      })
      let lookups = Object.keys(missingIssues)
      if (lookups.length > 0) {
        logger.info(`fetching linked issues`)
        newIssues = await this.jiraClient.getIssuesByKey(lookups)
      } else {
        newIssues = []
      }
    }
    this.issues = issues
  }

  analyse () {
    const orphans = []
    const graphs = []
    const issues = Object.assign({}, this.issues)
    while (Object.keys(issues).length !== 0) {
      Logger.global.trace(`issues left to consider: ${Object.keys(issues).length}`)
      const stack = Object.keys(issues).splice(0, 1) // seed the stack
      if (stack.length !== 0) {
        const graph = {
          nodes: [],
          edges: []
        }
        Logger.global.trace(`starting a new graph with ${stack}`)
        while (stack.length !== 0) {
          const key = stack.pop()
          Logger.global.trace(`considering ${key}, remaining stack is ${stack}`)
          const issue = issues[key]
          if (issue) {
            graph.nodes.push(issue)
            delete issues[key]
            issue.links.forEach((link) => {
              if (this.issues[link.srcKey] && this.issues[link.dstKey]) { // only include edges that we have issues for
                graph.edges.push(link)
                stack.unshift(link.dstKey)
              }
            })
          }
        }
        if (graph.nodes.length === 1) {
          orphans.push(graph.nodes[0])
        } else {
          graphs.push(graph)
        }
      }
    }
    this.orphans = orphans
    this.graphs = graphs
  }

  async store () {
    if (this.dataStore) {
      await this.dataStore.writeData('issues', this.issues)
      await this.dataStore.writeData('orphans', this.orphans)
      await this.dataStore.writeData('graphs', this.graphs)
    }
  }

  async cleanup () {
    this.issues = {}
    if (this.dataStore) {
      await this.dataStore.deleteData('issues')
    }
  }
}

exports = module.exports = Jirascope
<commit_msg>only use inward links for graphs<commit_after>'use strict'

const Logger = require('./util/logger')
const logger = Logger.global.chain('[jirascope]')

/**
 * Provides an understanding of jira issues and their network.
 *
 * @param {Object} jiraClient - The client to use to talk to JIRA
 * @param {Object?} dataStore - The data store to use
 * @param {Object} options - Options to customise behaviour
 * @param {string} options.query - The initial query to use
 * @param {string[]} options.followStatusCategories - An array of status categories to follow
 * @param {string[]} options.followLinkTypes - An array of link types to follow
 *
 * @property {Object} issues - issues keyed by key
 */
class Jirascope {
  constructor (jiraClient, dataStore, options) {
    this.jiraClient = jiraClient
    this.dataStore = dataStore
    this.options = options
    this.issues = {}
    this.orphans = []
    this.graphs = []
  }

  async populate () {
    if (this.dataStore) {
      this.issues = await this.dataStore.readData('issues')
    }
    if (!this.issues) {
      await this.fetch()
    }
    this.analyse()
  }

  async fetch () {
    const issues = {}
    logger.info(`fetching initial issues`)
    let newIssues = await this.jiraClient.getIssuesByQuery(this.options.query, 1000)

    while (newIssues.length > 0) {
      newIssues.forEach((newIssue) => {
        issues[newIssue.key] = newIssue
        logger.trace(`${newIssue.key} found`)
      })

      let missingIssues = {}
      logger.info(`reviewing issue links`)
      newIssues.forEach((newIssue) => {
        newIssue.links = newIssue.links.filter((link) => { return this.options.followLinkTypes.includes(link.type) })
        if (newIssue.links.length === 0) {
          logger.trace(`${newIssue.key} - no links`)
        } else if (!this.options.followStatusCategories.includes(newIssue.statusCategory)) {
          logger.trace(`${newIssue.key} - skipping due to status category of '${newIssue.statusCategory}'`)
        } else {
          logger.trace(`${newIssue.key} - evaluating links`)
          newIssue.links.forEach((link) => {
            if (!issues[link.dstKey]) {
              missingIssues[link.dstKey] = link.dstKey
              logger.trace(`${link.srcKey} ${link.label} ${link.dstKey} - ${link.dstKey} not yet fetched`)
            } else {
              logger.trace(`${link.srcKey} ${link.label} ${link.dstKey} - ${link.dstKey} already fetched`)
            }
          })
        }
      })
      let lookups = Object.keys(missingIssues)
      if (lookups.length > 0) {
        logger.info(`fetching linked issues`)
        newIssues = await this.jiraClient.getIssuesByKey(lookups)
      } else {
        newIssues = []
      }
    }
    this.issues = issues
  }

  analyse () {
    const orphans = []
    const graphs = []
    const issues = Object.assign({}, this.issues)
    while (Object.keys(issues).length !== 0) {
      Logger.global.trace(`issues left to consider: ${Object.keys(issues).length}`)
      const stack = Object.keys(issues).splice(0, 1) // seed the stack
      if (stack.length !== 0) {
        const graph = {
          nodes: [],
          edges: []
        }
        Logger.global.trace(`starting a new graph with ${stack}`)
        while (stack.length !== 0) {
          const key = stack.pop()
          Logger.global.trace(`considering ${key}, remaining stack is ${stack}`)
          const issue = issues[key]
          if (issue) {
            graph.nodes.push(issue)
            delete issues[key]
            issue.links.forEach((link) => {
              if (this.issues[link.srcKey] && this.issues[link.dstKey]) { // only include edges that we have issues for
                if (link.direction === 'inward') {
                  graph.edges.push(link)
                  stack.unshift(link.dstKey)
                }
              }
            })
          }
        }
        if (graph.nodes.length === 1) {
          orphans.push(graph.nodes[0])
        } else {
          graphs.push(graph)
        }
      }
    }
    this.orphans = orphans
    this.graphs = graphs
  }

  async store () {
    if (this.dataStore) {
      await this.dataStore.writeData('issues', this.issues)
      await this.dataStore.writeData('orphans', this.orphans)
      await this.dataStore.writeData('graphs', this.graphs)
    }
  }

  async cleanup () {
    this.issues = {}
    if (this.dataStore) {
      await this.dataStore.deleteData('issues')
    }
  }
}

exports = module.exports = Jirascope
<|endoftext|>


<commit_before>'use strict';

define([ 'underscore' ],
function( _ ){

    var JST = {}

    JST.noteworthy = [
        '
',
            '
',
                '',
                    '
Noteworthy
',
                '',
                '

',
            '
',
        '
'
    ].join('\n')

    JST.brick = [
        '
',
            '
',
                '<img src="<%= src %>">',
            '
',
            '<a href="<%= link %>"<%= external %>>',
                '
',
                    '
<%= title %>
',
                    '<%= summary %>',
                '
',
            '',
        '
'
    ].join('\n')

    JST.quotes = [
        '
',
            '
',
                '

',
            '
',
        '
'
    ].join('\n')

    JST.quoteSlide = [
        '
',
            '

',
        '
'
    ].join('\n')

    JST.viewer = [
        '

',
        '

'
    ].join('\n')

    JST.projectDetails = [
        '',
            '
<%= title %>
',
            '<time datetime="<%= htmlDate %>"><%= date %></time>',
        '',
        '
<%= summary %>
',
        '
',
        '
',
        '

'
    ].join('\n')

    JST.profileLinks = [
        '
',
            '
',
                '
Bio/CV
',
                '
Press
',
                '
Selected Awards
',
                '
Photos of PA
',
                '
Articles by PA
',
                '
Articles About PA
',
                '
Interviews
',
                '
Transcripts
',
                '
Acknowledgements
',
            '
',
        '
'
    ].join('\n')

    JST.showcaseLinks = [
        '<a href="#" id="<%= cid %>">',
            '<%= title %>',
        ''
    ].join('\n')

    JST.tagLinks = [
        '',
            '<%= type %>',
        '',
        '

'
    ].join('\n')

    JST.tag = '<a href="/projects#filter=.<%= className %>"><%= tag %>'

    JST.textTemplate = [
        '<article class="<%= type %>">', // .project-info, .press, .bio
            '<%= content %>',
        '
'
    ].join('\n')

    JST.textTemplateHeader = [
        '',
            '
<%= title %>
',
            '<time datetime="<%= htmlDate %>"><%= date %></time>',
        ''
    ].join('\n')

    JST.bioImage = [
        '
',
            '<img src="<%= bioImg %>">',
        '
'
    ].join('\n')

    JST.textGallery = [
        '
',
            '<% _.each(images, function(image) { %>',
                '<% print( imageTemplate({ url : image.url, thumb : image.thumb, caption : image.caption }) ) %>',
            '<% }) %>',
        '
'
    ].join('\n')

    JST.textGalleryImage = [
        '
',
            '<a href="<%= url %>" class="fancybox" rel="gallery">',
                '<img src="<%= thumb %>" alt="<%= caption %>">',
            '',
        '
'
    ].join('\n')

    JST.backButton = [
        '<a href="<%= url %>" class="back" id="back"><%= buttonText %>'
    ].join('\n')

    JST.videoID = [
        '
',
            '<iframe src="http://<% youtube ? print("www.youtube.com/embed/") : print("player.vimeo.com/video/") %><%= videoSrc %>?autoplay=1" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen>',
        '
'
    ].join('\n')

    JST.iframeVideo = '
<%= videoSrc %>
'

    JST.videoCaption = [
        '
',
            '
<%= title %>
',
            '
<%= content %>
',
        '
'
    ].join('\n')

    JST.thumbTemplate = [
        '
',
            '<a href="<%= url %>" class="fancybox" id="<%= id %>" rel="gallery" title="<%= caption %>">',
                '<img src="<%= thumb %>">',
                '<% if (caption) { %>',
                    '
',
                        '
<%= caption %>
',
                    '
',
                '<% } %>',
            '',
        '
'
    ].join('\n')

    JST.projectCover = [
        '
',
            '<a href="<%= url %>" id="<%= id %>" title="<%= caption %>">',
                '<img src="<%= thumb %>" >',
                '<% if (caption) { %>',
                    '
',
                        '
<%= caption %>
',
                        '<% if (year) { %>',
                            '<%= year %>',
                        '<% } %>',
                    '
',
                '<% } %>',
            '',
        '
'
    ].join('\n')

    JST.listHeaderPartial = [
        '
',
            '
',
                '<date datetime="<%= htmlDate %>"><%= date %></date>',
            '
',
        '
'
    ].join('\n')

    JST.listHiddenHeaderPartial = [
        '
',
            '<%= title %>',
        '
'
    ].join('\n')

    JST.listItemPartial = [
        '<% if (url) { %>',
            '<a href="<%= path %>/<%= url %>" id="<%= id %>">',
        '<% } %>',
            '
',
                '<%= title %>',
            '
',
            '
',
                '<%= summary %>',
            '
',
        '<% if (url) { %>',
            '',
        '<% } %>',
    ].join('\n')

    JST.awardItemPartial = [
        '
',
            '
Award Title
',
            '
<%= summary %>
',
        '
'
    ].join('\n')

    JST.gridRow = '

'

    JST.gridThumb = [
        '<a href="<%= url %>">',
            '
',
                '<img src="<%= thumb %>">',
            '
',
            '
<%= title %>
',
            '
<%= summary %>
',
        ''
    ].join('\n')

    JST.projectFilter = [
        '
',
            '
Brand
',
            '

',
        '
',
        '
',
            '
Industry
',
            '

',
        '
',
        '
',
            '
Project Type
',
            '

',
        '
',
        '
',
            '
View All
',
        '
'
    ].join('\n')

    JST.controlsPartial = [
        '
',
            '<button class="icon-view active" id="logoView" type="button"></button>',
            '<button class="title-view" id="titleView" type="button"></button>',
        '
',
        '<button class="close" id="close" type="button"></button>'
    ].join('\n')

    JST.logoPartial = [
        '
',
            '<a href="#" data-hash="filter=.<%= tagFilter %>" class="has-tip tip-top" data-tooltip title="<%= tag %>">',
                '<img src="<%= logo %>" title="<%= tag %>">',
            '',
        '
'
    ].join('\n')

    JST.namePartial = [
        '
',
            '<a href="#" data-hash="filter=.<%= tagFilter %>">',
                '<%= tag %>',
            '',
        '
'
    ].join('\n')

    JST.jumps = [
        '
Jump To
',
        '

'
    ].join('\n')

    JST.mobileJumps = [
        '<select>',
            '<option>Jump To</option>',
        '</select>'
    ].join('\n')

    JST.sorts = [
        '
Sort By
',
        '
',
            '
',
                '
<button data-hash="sort=name" id="name" type="button">Name</button>
',
                '
<button data-hash="sort=date" id="date" type="button">Date</button>
',
            '
',
        '
'
    ].join('\n')

    JST.mobileSorts = [
        '<select>',
            '<option>Sort By</option>',
            '<option data-hash="sort=name" id="name">Name</option>',
            '<option data-hash="sort=date" id="date">Date</option>',
        '</select>'
    ].join('\n')

    JST.views = [
        '
View By
',
        '
',
            '
',
                '
<button class="icon-view active" data-hash="view=cover" id="cover" type="button">Image</button>
',
                '
<button class="title-view" data-hash="view=list" id="list" type="button">Title</button>
',
                '
<button class="random-view" data-hash="view=random" id="random" type="button">Stream</button>
',
           '
',
        '
'
    ].join('\n')

    JST.mobileViews = [
        '<select>',
            '<option>View By</option>',
            '<option data-hash="view=cover" id="cover">Image</option>',
            '<option data-hash="view=list" id="list">Title</option>',
            '<option data-hash="view=random" id="random">Stream</option>',
        '</select>'
    ].join('\n')

    for (var tmpl in JST) {
        if (JST.hasOwnProperty(tmpl)) {
            JST[tmpl] = _.template(JST[tmpl])
        }
    }

    return JST
})
<commit_msg>don't need p tag around summary JS template tag in photo grid cell templates<commit_after>'use strict';

define([ 'underscore' ],
function( _ ){

    var JST = {}

    JST.noteworthy = [
        '
',
            '
',
                '',
                    '
Noteworthy
',
                '',
                '

',
            '
',
        '
'
    ].join('\n')

    JST.brick = [
        '
',
            '
',
                '<img src="<%= src %>">',
            '
',
            '<a href="<%= link %>"<%= external %>>',
                '
',
                    '
<%= title %>
',
                    '<%= summary %>',
                '
',
            '',
        '
'
    ].join('\n')

    JST.quotes = [
        '
',
            '
',
                '

',
            '
',
        '
'
    ].join('\n')

    JST.quoteSlide = [
        '
',
            '

',
        '
'
    ].join('\n')

    JST.viewer = [
        '

',
        '

'
    ].join('\n')

    JST.projectDetails = [
        '',
            '
<%= title %>
',
            '<time datetime="<%= htmlDate %>"><%= date %></time>',
        '',
        '
<%= summary %>
',
        '
',
        '
',
        '

'
    ].join('\n')

    JST.profileLinks = [
        '
',
            '
',
                '
Bio/CV
',
                '
Press
',
                '
Selected Awards
',
                '
Photos of PA
',
                '
Articles by PA
',
                '
Articles About PA
',
                '
Interviews
',
                '
Transcripts
',
                '
Acknowledgements
',
            '
',
        '
'
    ].join('\n')

    JST.showcaseLinks = [
        '<a href="#" id="<%= cid %>">',
            '<%= title %>',
        ''
    ].join('\n')

    JST.tagLinks = [
        '',
            '<%= type %>',
        '',
        '

'
    ].join('\n')

    JST.tag = '<a href="/projects#filter=.<%= className %>"><%= tag %>'

    JST.textTemplate = [
        '<article class="<%= type %>">', // .project-info, .press, .bio
            '<%= content %>',
        '
'
    ].join('\n')

    JST.textTemplateHeader = [
        '',
            '
<%= title %>
',
            '<time datetime="<%= htmlDate %>"><%= date %></time>',
        ''
    ].join('\n')

    JST.bioImage = [
        '
',
            '<img src="<%= bioImg %>">',
        '
'
    ].join('\n')

    JST.textGallery = [
        '
',
            '<% _.each(images, function(image) { %>',
                '<% print( imageTemplate({ url : image.url, thumb : image.thumb, caption : image.caption }) ) %>',
            '<% }) %>',
        '
'
    ].join('\n')

    JST.textGalleryImage = [
        '
',
            '<a href="<%= url %>" class="fancybox" rel="gallery">',
                '<img src="<%= thumb %>" alt="<%= caption %>">',
            '',
        '
'
    ].join('\n')

    JST.backButton = [
        '<a href="<%= url %>" class="back" id="back"><%= buttonText %>'
    ].join('\n')

    JST.videoID = [
        '
',
            '<iframe src="http://<% youtube ? print("www.youtube.com/embed/") : print("player.vimeo.com/video/") %><%= videoSrc %>?autoplay=1" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen>',
        '
'
    ].join('\n')

    JST.iframeVideo = '
<%= videoSrc %>
'

    JST.videoCaption = [
        '
',
            '
<%= title %>
',
            '
<%= content %>
',
        '
'
    ].join('\n')

    JST.thumbTemplate = [
        '
',
            '<a href="<%= url %>" class="fancybox" id="<%= id %>" rel="gallery" title="<%= caption %>">',
                '<img src="<%= thumb %>">',
                '<% if (caption) { %>',
                    '
',
                        '
<%= caption %>
',
                    '
',
                '<% } %>',
            '',
        '
'
    ].join('\n')

    JST.projectCover = [
        '
',
            '<a href="<%= url %>" id="<%= id %>" title="<%= caption %>">',
                '<img src="<%= thumb %>" >',
                '<% if (caption) { %>',
                    '
',
                        '
<%= caption %>
',
                        '<% if (year) { %>',
                            '<%= year %>',
                        '<% } %>',
                    '
',
                '<% } %>',
            '',
        '
'
    ].join('\n')

    JST.listHeaderPartial = [
        '
',
            '
',
                '<date datetime="<%= htmlDate %>"><%= date %></date>',
            '
',
        '
'
    ].join('\n')

    JST.listHiddenHeaderPartial = [
        '
',
            '<%= title %>',
        '
'
    ].join('\n')

    JST.listItemPartial = [
        '<% if (url) { %>',
            '<a href="<%= path %>/<%= url %>" id="<%= id %>">',
        '<% } %>',
            '
',
                '<%= title %>',
            '
',
            '
',
                '<%= summary %>',
            '
',
        '<% if (url) { %>',
            '',
        '<% } %>',
    ].join('\n')

    JST.awardItemPartial = [
        '
',
            '
Award Title
',
            '
<%= summary %>
',
        '
'
    ].join('\n')

    JST.gridRow = '

'

    JST.gridThumb = [
        '<a href="<%= url %>">',
            '
',
                '<img src="<%= thumb %>">',
            '
',
            '
<%= title %>
',
            '<%= summary %>',
        ''
    ].join('\n')

    JST.projectFilter = [
        '
',
            '
Brand
',
            '

',
        '
',
        '
',
            '
Industry
',
            '

',
        '
',
        '
',
            '
Project Type
',
            '

',
        '
',
        '
',
            '
View All
',
        '
'
    ].join('\n')

    JST.controlsPartial = [
        '
',
            '<button class="icon-view active" id="logoView" type="button"></button>',
            '<button class="title-view" id="titleView" type="button"></button>',
        '
',
        '<button class="close" id="close" type="button"></button>'
    ].join('\n')

    JST.logoPartial = [
        '
',
            '<a href="#" data-hash="filter=.<%= tagFilter %>" class="has-tip tip-top" data-tooltip title="<%= tag %>">',
                '<img src="<%= logo %>" title="<%= tag %>">',
            '',
        '
'
    ].join('\n')

    JST.namePartial = [
        '
',
            '<a href="#" data-hash="filter=.<%= tagFilter %>">',
                '<%= tag %>',
            '',
        '
'
    ].join('\n')

    JST.jumps = [
        '
Jump To
',
        '

'
    ].join('\n')

    JST.mobileJumps = [
        '<select>',
            '<option>Jump To</option>',
        '</select>'
    ].join('\n')

    JST.sorts = [
        '
Sort By
',
        '
',
            '
',
                '
<button data-hash="sort=name" id="name" type="button">Name</button>
',
                '
<button data-hash="sort=date" id="date" type="button">Date</button>
',
            '
',
        '
'
    ].join('\n')

    JST.mobileSorts = [
        '<select>',
            '<option>Sort By</option>',
            '<option data-hash="sort=name" id="name">Name</option>',
            '<option data-hash="sort=date" id="date">Date</option>',
        '</select>'
    ].join('\n')

    JST.views = [
        '
View By
',
        '
',
            '
',
                '
<button class="icon-view active" data-hash="view=cover" id="cover" type="button">Image</button>
',
                '
<button class="title-view" data-hash="view=list" id="list" type="button">Title</button>
',
                '
<button class="random-view" data-hash="view=random" id="random" type="button">Stream</button>
',
           '
',
        '
'
    ].join('\n')

    JST.mobileViews = [
        '<select>',
            '<option>View By</option>',
            '<option data-hash="view=cover" id="cover">Image</option>',
            '<option data-hash="view=list" id="list">Title</option>',
            '<option data-hash="view=random" id="random">Stream</option>',
        '</select>'
    ].join('\n')

    for (var tmpl in JST) {
        if (JST.hasOwnProperty(tmpl)) {
            JST[tmpl] = _.template(JST[tmpl])
        }
    }

    return JST
})
<|endoftext|>