<commit_before>var assert = require('assert')
var crypto = require('./crypto')
var sec = require('./sec')
var ecparams = sec("secp256k1")

var BigInteger = require('bigi')
var ECPointFp = require('./ec').ECPointFp

var P_OVER_FOUR = null

function implShamirsTrick(P, k, Q, l) {
  var m = Math.max(k.bitLength(), l.bitLength())
  var Z = P.add2D(Q)
  var R = P.curve.getInfinity()

  for (var i = m - 1; i >= 0; --i) {
    R = R.twice2D()

    R.z = BigInteger.ONE

    if (k.testBit(i)) {
      if (l.testBit(i)) {
        R = R.add2D(Z)
      } else {
        R = R.add2D(P)
      }
    } else {
      if (l.testBit(i)) {
        R = R.add2D(Q)
      }
    }
  }

  return R
}

var ecdsa = {
  deterministicGenerateK: function(hash, D) {
    assert(Buffer.isBuffer(hash), 'Hash must be a Buffer')
    assert.equal(hash.length, 32, 'Hash must be 256 bit')
    assert(D instanceof BigInteger, 'Private key must be a BigInteger')

    var x = D.toBuffer(32)
    var k = new Buffer(32)
    var v = new Buffer(32)
    k.fill(0)
    v.fill(1)

    k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k)
    v = crypto.HmacSHA256(v, k)

    k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k)
    v = crypto.HmacSHA256(v, k)
    v = crypto.HmacSHA256(v, k)

    var n = ecparams.getN()
    var kB = BigInteger.fromBuffer(v).mod(n)
    assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value')
    assert(kB.compareTo(ecparams.getN()) < 0, 'Invalid k value')

    return kB
  },

  sign: function (hash, D) {
    var k = ecdsa.deterministicGenerateK(hash, D)

    var n = ecparams.getN()
    var G = ecparams.getG()
    var Q = G.multiply(k)
    var e = BigInteger.fromBuffer(hash)

    var r = Q.getX().toBigInteger().mod(n)
    assert.notEqual(r.signum(), 0, 'Invalid R value')

    var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n)
    assert.notEqual(s.signum(), 0, 'Invalid S value')

    var N_OVER_TWO = n.divide(BigInteger.valueOf(2))

    // Make 's' value 'low' as per bip62
    if (s.compareTo(N_OVER_TWO) > 0) {
      s = n.subtract(s)
    }

    return ecdsa.serializeSig(r, s)
  },

  verify: function (hash, sig, pubkey) {
    var r,s
    if (Array.isArray(sig) || Buffer.isBuffer(sig)) {
      var obj = ecdsa.parseSig(sig)
      r = obj.r
      s = obj.s
    } else if ("object" === typeof sig && sig.r && sig.s) {
      r = sig.r
      s = sig.s
    } else {
      throw new Error("Invalid value for signature")
    }

    var Q
    if (pubkey instanceof ECPointFp) {
      Q = pubkey
    } else if (Array.isArray(pubkey) || Buffer.isBuffer(pubkey)) {
      Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey)
    } else {
      throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp")
    }
    var e = BigInteger.fromBuffer(hash)

    return ecdsa.verifyRaw(e, r, s, Q)
  },

  verifyRaw: function (e, r, s, Q) {
    var n = ecparams.getN()
    var G = ecparams.getG()

    if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) {
      return false
    }

    if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) {
      return false
    }

    var c = s.modInverse(n)

    var u1 = e.multiply(c).mod(n)
    var u2 = r.multiply(c).mod(n)

    // TODO(!!!): For some reason Shamir's trick isn't working with
    // signed message verification!? Probably an implementation
    // error!
    //var point = implShamirsTrick(G, u1, Q, u2)
    var point = G.multiply(u1).add(Q.multiply(u2))

    var v = point.getX().toBigInteger().mod(n)

    return v.equals(r)
  },

  /**
   * Serialize a signature into DER format.
   *
   * Takes two BigIntegers representing r and s and returns a byte array.
   */
  serializeSig: function (r, s) {
    var rBa = r.toByteArraySigned()
    var sBa = s.toByteArraySigned()

    var sequence = []
    sequence.push(0x02); // INTEGER
    sequence.push(rBa.length)
    sequence = sequence.concat(rBa)

    sequence.push(0x02); // INTEGER
    sequence.push(sBa.length)
    sequence = sequence.concat(sBa)

    sequence.unshift(sequence.length)
    sequence.unshift(0x30); // SEQUENCE

    return sequence
  },

  /**
   * Parses a byte array containing a DER-encoded signature.
   *
   * This function will return an object of the form:
   *
   * {
   *   r: BigInteger,
   *   s: BigInteger
   * }
   */
  parseSig: function (sig) {
    if(Array.isArray(sig)) sig = new Buffer(sig);

    var cursor
    if (sig[0] != 0x30) {
      throw new Error("Signature not a valid DERSequence")
    }

    cursor = 2
    if (sig[cursor] != 0x02) {
      throw new Error("First element in signature must be a DERInteger")
    }
    var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])

    cursor += 2+sig[cursor+1]
    if (sig[cursor] != 0x02) {
      throw new Error("Second element in signature must be a DERInteger")
    }
    var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])

    cursor += 2+sig[cursor+1]

    //if (cursor != sig.length)
    //  throw new Error("Extra bytes in signature")

    var r = BigInteger.fromBuffer(rBa)
    var s = BigInteger.fromBuffer(sBa)

    return {r: r, s: s}
  },

  serializeSigCompact: function(r, s, i, compressed) {
    if (compressed) {
      i += 4
    }

    i += 27

    var buffer = new Buffer(65)
    buffer.writeUInt8(i, 0)
    r.toBuffer(32).copy(buffer, 1)
    s.toBuffer(32).copy(buffer, 33)

    return buffer
  },

  parseSigCompact: function (sig) {
    if (sig.length !== 65) {
      throw new Error("Signature has the wrong length")
    }

    // Signature is prefixed with a type byte storing three bits of
    // information.
    var i = sig[0] - 27
    if (i < 0 || i > 7) {
      throw new Error("Invalid signature type")
    }

    var n = ecparams.getN()
    var r = BigInteger.fromBuffer(sig.slice(1, 33)).mod(n)
    var s = BigInteger.fromBuffer(sig.slice(33, 65)).mod(n)

    return {r: r, s: s, i: i}
  },

  /**
   * Recover a public key from a signature.
   *
   * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
   * Key Recovery Operation".
   *
   * http://www.secg.org/download/aid-780/sec1-v2.pdf
   */
  recoverPubKey: function (r, s, hash, i) {
    // The recovery parameter i has two bits.
    i = i & 3

    // The less significant bit specifies whether the y coordinate
    // of the compressed point is even or not.
    var isYEven = i & 1

    // The more significant bit specifies whether we should use the
    // first or second candidate key.
    var isSecondKey = i >> 1

    var n = ecparams.getN()
    var G = ecparams.getG()
    var curve = ecparams.getCurve()
    var p = curve.getQ()
    var a = curve.getA().toBigInteger()
    var b = curve.getB().toBigInteger()

    // We precalculate (p + 1) / 4 where p is if the field order
    if (!P_OVER_FOUR) {
      P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4))
    }

    // 1.1 Compute x
    var x = isSecondKey ? r.add(n) : r

    // 1.3 Convert x to point
    var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p)
    var beta = alpha.modPow(P_OVER_FOUR, p)

    //    var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2)
    // If beta is even, but y isn't or vice versa, then convert it,
    // otherwise we're done and y == beta.
    var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta)

    // 1.4 Check that nR is at infinity
    var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y))
    R.validate()

    // 1.5 Compute e from M
    var e = BigInteger.fromBuffer(hash)
    var eNeg = BigInteger.ZERO.subtract(e).mod(n)

    // 1.6 Compute Q = r^-1 (sR - eG)
    var rInv = r.modInverse(n)
    var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv)

    Q.validate()
    if (!ecdsa.verifyRaw(e, r, s, Q)) {
      throw new Error("Pubkey recovery unsuccessful")
    }

    return Q
  },

  /**
   * Calculate pubkey extraction parameter.
   *
   * When extracting a pubkey from a signature, we have to
   * distinguish four different cases. Rather than putting this
   * burden on the verifier, Bitcoin includes a 2-bit value with the
   * signature.
   *
   * This function simply tries all four cases and returns the value
   * that resulted in a successful pubkey recovery.
   */
  calcPubKeyRecoveryParam: function (origPubKey, r, s, hash) {
    for (var i = 0; i < 4; i++) {
      var pubKey = ecdsa.recoverPubKey(r, s, hash, i)

      if (pubKey.equals(origPubKey)) {
        return i
      }
    }

    throw new Error("Unable to find valid recovery factor")
  }
}

module.exports = ecdsa
<commit_msg>ecdsa: parseSig now uses buffer API<commit_after>var assert = require('assert')
var crypto = require('./crypto')
var sec = require('./sec')
var ecparams = sec("secp256k1")

var BigInteger = require('bigi')
var ECPointFp = require('./ec').ECPointFp

var P_OVER_FOUR = null

function implShamirsTrick(P, k, Q, l) {
  var m = Math.max(k.bitLength(), l.bitLength())
  var Z = P.add2D(Q)
  var R = P.curve.getInfinity()

  for (var i = m - 1; i >= 0; --i) {
    R = R.twice2D()

    R.z = BigInteger.ONE

    if (k.testBit(i)) {
      if (l.testBit(i)) {
        R = R.add2D(Z)
      } else {
        R = R.add2D(P)
      }
    } else {
      if (l.testBit(i)) {
        R = R.add2D(Q)
      }
    }
  }

  return R
}

var ecdsa = {
  deterministicGenerateK: function(hash, D) {
    assert(Buffer.isBuffer(hash), 'Hash must be a Buffer')
    assert.equal(hash.length, 32, 'Hash must be 256 bit')
    assert(D instanceof BigInteger, 'Private key must be a BigInteger')

    var x = D.toBuffer(32)
    var k = new Buffer(32)
    var v = new Buffer(32)
    k.fill(0)
    v.fill(1)

    k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k)
    v = crypto.HmacSHA256(v, k)

    k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k)
    v = crypto.HmacSHA256(v, k)
    v = crypto.HmacSHA256(v, k)

    var n = ecparams.getN()
    var kB = BigInteger.fromBuffer(v).mod(n)
    assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value')
    assert(kB.compareTo(ecparams.getN()) < 0, 'Invalid k value')

    return kB
  },

  sign: function (hash, D) {
    var k = ecdsa.deterministicGenerateK(hash, D)

    var n = ecparams.getN()
    var G = ecparams.getG()
    var Q = G.multiply(k)
    var e = BigInteger.fromBuffer(hash)

    var r = Q.getX().toBigInteger().mod(n)
    assert.notEqual(r.signum(), 0, 'Invalid R value')

    var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n)
    assert.notEqual(s.signum(), 0, 'Invalid S value')

    var N_OVER_TWO = n.divide(BigInteger.valueOf(2))

    // Make 's' value 'low' as per bip62
    if (s.compareTo(N_OVER_TWO) > 0) {
      s = n.subtract(s)
    }

    return ecdsa.serializeSig(r, s)
  },

  verify: function (hash, sig, pubkey) {
    var r,s
    if (Array.isArray(sig) || Buffer.isBuffer(sig)) {
      var obj = ecdsa.parseSig(sig)
      r = obj.r
      s = obj.s
    } else if ("object" === typeof sig && sig.r && sig.s) {
      r = sig.r
      s = sig.s
    } else {
      throw new Error("Invalid value for signature")
    }

    var Q
    if (pubkey instanceof ECPointFp) {
      Q = pubkey
    } else if (Array.isArray(pubkey) || Buffer.isBuffer(pubkey)) {
      Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey)
    } else {
      throw new Error("Invalid format for pubkey value, must be byte array or ECPointFp")
    }
    var e = BigInteger.fromBuffer(hash)

    return ecdsa.verifyRaw(e, r, s, Q)
  },

  verifyRaw: function (e, r, s, Q) {
    var n = ecparams.getN()
    var G = ecparams.getG()

    if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) {
      return false
    }

    if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) {
      return false
    }

    var c = s.modInverse(n)

    var u1 = e.multiply(c).mod(n)
    var u2 = r.multiply(c).mod(n)

    // TODO(!!!): For some reason Shamir's trick isn't working with
    // signed message verification!? Probably an implementation
    // error!
    //var point = implShamirsTrick(G, u1, Q, u2)
    var point = G.multiply(u1).add(Q.multiply(u2))

    var v = point.getX().toBigInteger().mod(n)

    return v.equals(r)
  },

  /**
   * Serialize a signature into DER format.
   *
   * Takes two BigIntegers representing r and s and returns a byte array.
   */
  serializeSig: function (r, s) {
    var rBa = r.toByteArraySigned()
    var sBa = s.toByteArraySigned()

    var sequence = []
    sequence.push(0x02); // INTEGER
    sequence.push(rBa.length)
    sequence = sequence.concat(rBa)

    sequence.push(0x02); // INTEGER
    sequence.push(sBa.length)
    sequence = sequence.concat(sBa)

    sequence.unshift(sequence.length)
    sequence.unshift(0x30); // SEQUENCE

    return sequence
  },

  /**
   * Parses a buffer containing a DER-encoded signature.
   *
   * This function will return an object of the form:
   *
   * {
   *   r: BigInteger,
   *   s: BigInteger
   * }
   */
  parseSig: function (buffer) {
    if (Array.isArray(buffer)) buffer = new Buffer(buffer) // FIXME: transitionary

    assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence')
    assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length')

    assert.equal(buffer.readUInt8(2), 0x02, 'Expected DER integer')
    var rLen = buffer.readUInt8(3)
    var rB = buffer.slice(4, 4 + rLen)

    var offset = 4 + rLen
    assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a 2nd DER integer')
    var sLen = buffer.readUInt8(1 + offset)
    var sB = buffer.slice(2 + offset)

    return {
      r: BigInteger.fromByteArraySigned(rB),
      s: BigInteger.fromByteArraySigned(sB)
    }
  },

  serializeSigCompact: function(r, s, i, compressed) {
    if (compressed) {
      i += 4
    }

    i += 27

    var buffer = new Buffer(65)
    buffer.writeUInt8(i, 0)
    r.toBuffer(32).copy(buffer, 1)
    s.toBuffer(32).copy(buffer, 33)

    return buffer
  },

  parseSigCompact: function (sig) {
    if (sig.length !== 65) {
      throw new Error("Signature has the wrong length")
    }

    // Signature is prefixed with a type byte storing three bits of
    // information.
    var i = sig[0] - 27
    if (i < 0 || i > 7) {
      throw new Error("Invalid signature type")
    }

    var n = ecparams.getN()
    var r = BigInteger.fromBuffer(sig.slice(1, 33)).mod(n)
    var s = BigInteger.fromBuffer(sig.slice(33, 65)).mod(n)

    return {r: r, s: s, i: i}
  },

  /**
   * Recover a public key from a signature.
   *
   * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
   * Key Recovery Operation".
   *
   * http://www.secg.org/download/aid-780/sec1-v2.pdf
   */
  recoverPubKey: function (r, s, hash, i) {
    // The recovery parameter i has two bits.
    i = i & 3

    // The less significant bit specifies whether the y coordinate
    // of the compressed point is even or not.
    var isYEven = i & 1

    // The more significant bit specifies whether we should use the
    // first or second candidate key.
    var isSecondKey = i >> 1

    var n = ecparams.getN()
    var G = ecparams.getG()
    var curve = ecparams.getCurve()
    var p = curve.getQ()
    var a = curve.getA().toBigInteger()
    var b = curve.getB().toBigInteger()

    // We precalculate (p + 1) / 4 where p is if the field order
    if (!P_OVER_FOUR) {
      P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4))
    }

    // 1.1 Compute x
    var x = isSecondKey ? r.add(n) : r

    // 1.3 Convert x to point
    var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p)
    var beta = alpha.modPow(P_OVER_FOUR, p)

    //    var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2)
    // If beta is even, but y isn't or vice versa, then convert it,
    // otherwise we're done and y == beta.
    var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta)

    // 1.4 Check that nR is at infinity
    var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y))
    R.validate()

    // 1.5 Compute e from M
    var e = BigInteger.fromBuffer(hash)
    var eNeg = BigInteger.ZERO.subtract(e).mod(n)

    // 1.6 Compute Q = r^-1 (sR - eG)
    var rInv = r.modInverse(n)
    var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv)

    Q.validate()
    if (!ecdsa.verifyRaw(e, r, s, Q)) {
      throw new Error("Pubkey recovery unsuccessful")
    }

    return Q
  },

  /**
   * Calculate pubkey extraction parameter.
   *
   * When extracting a pubkey from a signature, we have to
   * distinguish four different cases. Rather than putting this
   * burden on the verifier, Bitcoin includes a 2-bit value with the
   * signature.
   *
   * This function simply tries all four cases and returns the value
   * that resulted in a successful pubkey recovery.
   */
  calcPubKeyRecoveryParam: function (origPubKey, r, s, hash) {
    for (var i = 0; i < 4; i++) {
      var pubKey = ecdsa.recoverPubKey(r, s, hash, i)

      if (pubKey.equals(origPubKey)) {
        return i
      }
    }

    throw new Error("Unable to find valid recovery factor")
  }
}

module.exports = ecdsa
<|endoftext|>


<commit_before>module.exports = function(grunt, config, parameters, done) {
	var zipUrl = parameters.releaseSourceZip || 'https://github.com/concrete5/concrete5/archive/release/5.7.5.2.zip';
	var workFolder = parameters.releaseWorkFolder || './release';
	function endForError(e) {
		process.stderr.write(e.message || e);
		done(false);
	}
	try {
		var fs = require('fs'),
			path = require('path')
			download = require('download'),
			c5fs = require('../../libraries/fs'),
			shell = require('shelljs');
		if(c5fs.isDirectory(workFolder)) {
			process.stdout.write('Removing working folder... ');
			shell.rm('-rf', workFolder);
			if(c5fs.isDirectory(workFolder)) {
				throw new Error('Unable to remove ' + workFolder);
			}
			process.stdout.write('done.\n')
		}
		fs.mkdir(workFolder);
		process.stdout.write('Downloading & unzipping archive... ');
		var stream = download(
			zipUrl,
			workFolder,
			{
				extract: true
			}
		);
		stream.on('close', function() {
			process.stdout.write('done.\n');
			var extractedFolder = null;
			fs.readdirSync(workFolder).forEach(function(item) {
				if(item.indexOf('.') !== 0) {
					if(extractedFolder === null) {
						extractedFolder = item;
					}
					else {
						throw new Error('Multiple items in the root of the extract archive!');
					}
				}
			});
			if(extractedFolder === null) {
				throw new Error('No items extracted!');
			}
			fs.renameSync(path.join(workFolder, extractedFolder), path.join(workFolder, 'source'));
			done();
		});
	}
	catch(e) {
		endForError(e);
		return;
	}
};<commit_msg>5.7.5.3 download<commit_after>module.exports = function(grunt, config, parameters, done) {
	var zipUrl = parameters.releaseSourceZip || 'https://github.com/concrete5/concrete5/archive/release/5.7.5.3.zip';
	var workFolder = parameters.releaseWorkFolder || './release';
	function endForError(e) {
		process.stderr.write(e.message || e);
		done(false);
	}
	try {
		var fs = require('fs'),
			path = require('path')
			download = require('download'),
			c5fs = require('../../libraries/fs'),
			shell = require('shelljs');
		if(c5fs.isDirectory(workFolder)) {
			process.stdout.write('Removing working folder... ');
			shell.rm('-rf', workFolder);
			if(c5fs.isDirectory(workFolder)) {
				throw new Error('Unable to remove ' + workFolder);
			}
			process.stdout.write('done.\n')
		}
		fs.mkdir(workFolder);
		process.stdout.write('Downloading & unzipping archive... ');
		var stream = download(
			zipUrl,
			workFolder,
			{
				extract: true
			}
		);
		stream.on('close', function() {
			process.stdout.write('done.\n');
			var extractedFolder = null;
			fs.readdirSync(workFolder).forEach(function(item) {
				if(item.indexOf('.') !== 0) {
					if(extractedFolder === null) {
						extractedFolder = item;
					}
					else {
						throw new Error('Multiple items in the root of the extract archive!');
					}
				}
			});
			if(extractedFolder === null) {
				throw new Error('No items extracted!');
			}
			fs.renameSync(path.join(workFolder, extractedFolder), path.join(workFolder, 'source'));
			done();
		});
	}
	catch(e) {
		endForError(e);
		return;
	}
};<|endoftext|>


<commit_before>/**
* Js.Edgar is a lightweight Spy/Mock library for testing JavaScript.
*
* @package Js.Edgar
* @version 0.0.4
* @author  Jordan Hawker <hawker.jordan@gmail.com>
* @license MIT
*/

(function() {
	Edgar = {
		spies: {},
		mocks: {},

		/**
		* Adds the Spy to a hash so it is accessible later when needed
		*
		* @param {Spy} spy The Spy to be tracked
		* @param {String} method The name of the method the Spy is watching
		*/
		addSpy: function(spy, method) {
			var spies = this.spies[method];

			if (!spies) {
				this.spies[method] = spies = [];
			}

			spies.push(spy);
		},

		/**
		* Retrieves a Spy from the hash lookup
		*
		* @param {Object} obj The object being watched
		* @param {String} method The name of the method being watched
		* @returns {Spy} The Spy that corresponds to the given object/method
		*/
		getSpy: function(obj, method) {
			if (typeof method !== 'string') {
				throw 'Failed to find spy: method name was invalid.';
			}
			if (this.spies.hasOwnProperty(method)) {
				var spies = this.spies[method];

				if (spies && spies.length) {
					return spies.filter(function(spy) {
						return spy.obj === obj;
					})[0];
				}
			}
		},

		/**
		* External API to create a Spy
		*
		* @param {Object} obj The object to watch
		* @param {String} method The name of the method to watch
		* @param {*} [Value] A mock value to return or method to invoke
		* @returns {Spy} A Spy watching the given object/method
		*/
		createSpy: function(obj, method, value) {
			var type = typeof obj,
				spy;

			if (type === 'undefined' || type === 'function') { // Create a callback
				throw 'Mocking without objects is not yet supported';
				// return this.createMock(obj);
			} else if (type === 'object' && obj !== null) {
				if (typeof method === 'string') { // Create a normal spy
					spy = this.getSpy(obj, method);
					if (!spy) {
						// Don't create a new spy if one already exists for this method
						spy = new this.Spy(obj, method, value);
						this.addSpy(spy, method);
					} else if (value !== undefined) {
						spy.value = value; // Update the value if a new one was passed in
					}
					return spy;
				}
				throw 'Spy Creation Failed: Method name must be a string.';
			} else {
				throw 'Spy Creation Failed: Object was not a valid type.';
			}
		},

		// /**
		//  *
		//  */
		// createMock: function(method) {
		// 	if (typeof method === 'function') {
		// 		var mock = new this.Mock(method);
		// 		this.mocks.push(mock);
		// 		return mock;
		// 	}
		// 	throw 'Mock Creation Failed: Input must be a function.'
		// },

		/**
		* Releases every Spy tracked by Edgar
		*/
		releaseAll: function() {
			var spies = this.spies,
				spyList,
				i;
			for (var key in spies) {
				if (spies.hasOwnProperty(key)) {
					spyList = spies[key];
					for (i = 0; i < spyList.length; i++) {
						spyList[i].release();
					}
				}
			}
		},

		/**
		* Resets Edgar's Spy tracking
		*/
		removeSpies: function() {
			this.spies = {};
		}
	};

	/**
	* Spy constructor
	*
	* @param {Object} obj The object to watch
	* @param {String} method The name of the method to watch
	* @param {*} [value] A mock value to return or method to invoke
	* @constructor
	*/
	Edgar.Spy = (function() {

		function Spy(obj, method, value) {
			if (!(this instanceof Spy)) {
				return new Spy(obj, method, value);
			}

			var self = this;

			/**
			* Track a new call to the watched method,
			* mocking or executing it as needed
			*
			* @returns {*} The return value from the mock or real method
			*/
			self.mock = function() {
				var args = arguments,
					id = self.calls.length,
					returned = self.value;

				self.calls.push({ args: args });

				if (self.execute) {
					returned = self.method.apply(self.obj, args);
				} else if (self.invoke) {
					returned = self.value.apply(self.obj, args);
				}

				self.calls[id].returned = returned;

				return returned;
			};

			/**
			* Setup the Spy to invoke its mocked value
			* rather than just returning it
			*
			* @returns {Spy} Itself
			*/
			self.andInvoke = self.startInvoking = function() {
				if (typeof this.value === 'function') {
					self.invoke = true;
					return self;
				}
				throw 'Cannot invoke value that is not a function.';
			};

			/**
			* Setup the Spy to call the live method being watched
			*
			* @returns {Spy} Itself
			*/
			self.andExecute = self.startExecuting = function() {
				self.execute = true;
				return self;
			};

			/**
			* Setup the Spy to mock the method being watched
			*
			* @param {*} [value] The value to return/invoke for the mock
			* @returns {Spy} Itself
			*/
			self.andMock = self.startMocking = function(value) {
				if (value !== undefined) {
					self.value = value;
				}
				self.execute = false;
				return self;
			};

			/**
			* Find out how many calls have been made to the watched method
			*
			* @returns {Number} The number of calls made
			*/
			self.called = function() {
				return self.calls.length;
			};

			/**
			* Get the arguments passed to the watched method,
			* defaulting to the most recent call if no id is passed
			*
			* @param {Number} [id] The id of the call, in case of multiple
			* @returns {Array} The arguments array for that call
			*/
			self.calledWith = function(id) {
				if (id !== undefined && id !== null) {
					if (id >= 0 && id < self.calls.length) {
						return self.calls[id].args;
					}
					throw 'Cannot get arguments for invalid call index.';
				} else if (self.calls.length) {
					return self.calls[self.calls.length - 1].args;
				}
				throw 'Cannot get arguments, spy has not been called.';
			};

			/**
			* Get the return value passed from Spy.mock(),
			* defaulting to the most recent call if no id is passed
			*
			* @param {Number} [id] The id of the call, in case of multiple
			* @returns {*} The return value for that call
			*/
			self.returnedWith = function(id) {
				if (id !== undefined && id !== null) {
					if (id >= 0 && id < self.calls.length) {
						return self.calls[id].returned;
					}
					throw 'Cannot get return value for invalid call index.';
				} else {
					return self.calls[self.calls.length - 1].returned;
				}
			};

			/**
			* Reset the calls tracked by the Spy
			*
			* @returns {Spy} Itself
			*/
			self.reset = function() {
				var calls = self.calls;
				self.calls = [];
				return self;
			};

			/**
			* Release the watched method back
			* to its original functionality
			*
			* @returns {Spy} Itself
			*/
			self.release = function() {
				self.obj[self.name] = self.method;
				return self;
			};

			/**
			* Resumes mocking the watched method
			*
			* @returns {Spy} Itself
			*/
			self.resume = function() {
				self.obj[self.name] = self.mock;
				return self;
			};

			// Defaults and Storage
			self.obj = obj;
			self.name = method;
			self.method = obj[method];
			self.value = value;
			self.execute = false;
			self.invoke = null;
			self.calls = [];

			obj[method] = self.mock;
		}

		return Spy;
	})();

	/**
	 *
	 * @param method
	 * @constructor
	 */
	// Edgar.prototype.Mock = function(method) {
	// 	var spy = new Edgar.Spy({}, 'mock', method, true),
	// 		mock = spy.mock;
	//
	// 	mock.prototype = spy.prototype;
	//
	// 	return mock;
	// };

	if (typeof QUnit !== 'undefined') {
		QUnit.testDone(function() {
			Edgar.releaseAll();
			Edgar.removeSpies();
		});
	}

	if (typeof mocha !== 'undefined') {
		mocha.afterEach(function() {
			Edgar.releaseAll();
			Edgar.removeSpies();
		});
	}
}).call(this);
<commit_msg>Add AMD/Node JS module support<commit_after>/**
* Js.Edgar is a lightweight Spy/Mock library for testing JavaScript.
*
* @package Js.Edgar
* @version 0.0.4
* @author  Jordan Hawker <hawker.jordan@gmail.com>
* @license MIT
*/
(function (root, factory) {
    if (typeof define === 'function' && define.amd) { // AMD module
        define([], factory);
    } else if (typeof exports === 'object') {
			if (typeof module === 'object' && module.exports) {
        module.exports = factory();
			}
			exports.Edgar = factory();
    } else { // Browser globals (root is window)
        root.Edgar = factory();
  }
}(this, function () {
	var Edgar = {
		spies: {},
		mocks: {},

		/**
		* Adds the Spy to a hash so it is accessible later when needed
		*
		* @param {Spy} spy The Spy to be tracked
		* @param {String} method The name of the method the Spy is watching
		*/
		addSpy: function(spy, method) {
			var spies = this.spies[method];

			if (!spies) {
				this.spies[method] = spies = [];
			}

			spies.push(spy);
		},

		/**
		* Retrieves a Spy from the hash lookup
		*
		* @param {Object} obj The object being watched
		* @param {String} method The name of the method being watched
		* @returns {Spy} The Spy that corresponds to the given object/method
		*/
		getSpy: function(obj, method) {
			if (typeof method !== 'string') {
				throw 'Failed to find spy: method name was invalid.';
			}
			if (this.spies.hasOwnProperty(method)) {
				var spies = this.spies[method];

				if (spies && spies.length) {
					return spies.filter(function(spy) {
						return spy.obj === obj;
					})[0];
				}
			}
		},

		/**
		* External API to create a Spy
		*
		* @param {Object} obj The object to watch
		* @param {String} method The name of the method to watch
		* @param {*} [Value] A mock value to return or method to invoke
		* @returns {Spy} A Spy watching the given object/method
		*/
		createSpy: function(obj, method, value) {
			var type = typeof obj,
				spy;

			if (type === 'undefined' || type === 'function') { // Create a callback
				throw 'Mocking without objects is not yet supported';
				// return this.createMock(obj);
			} else if (type === 'object' && obj !== null) {
				if (typeof method === 'string') { // Create a normal spy
					spy = this.getSpy(obj, method);
					if (!spy) {
						// Don't create a new spy if one already exists for this method
						spy = new this.Spy(obj, method, value);
						this.addSpy(spy, method);
					} else if (value !== undefined) {
						spy.value = value; // Update the value if a new one was passed in
					}
					return spy;
				}
				throw 'Spy Creation Failed: Method name must be a string.';
			} else {
				throw 'Spy Creation Failed: Object was not a valid type.';
			}
		},

		// /**
		//  *
		//  */
		// createMock: function(method) {
		// 	if (typeof method === 'function') {
		// 		var mock = new this.Mock(method);
		// 		this.mocks.push(mock);
		// 		return mock;
		// 	}
		// 	throw 'Mock Creation Failed: Input must be a function.'
		// },

		/**
		* Releases every Spy tracked by Edgar
		*/
		releaseAll: function() {
			var spies = this.spies,
				spyList,
				i;
			for (var key in spies) {
				if (spies.hasOwnProperty(key)) {
					spyList = spies[key];
					for (i = 0; i < spyList.length; i++) {
						spyList[i].release();
					}
				}
			}
		},

		/**
		* Resets Edgar's Spy tracking
		*/
		removeSpies: function() {
			this.spies = {};
		}
	};

	/**
	* Spy constructor
	*
	* @param {Object} obj The object to watch
	* @param {String} method The name of the method to watch
	* @param {*} [value] A mock value to return or method to invoke
	* @constructor
	*/
	Edgar.Spy = (function() {

		function Spy(obj, method, value) {
			if (!(this instanceof Spy)) {
				return new Spy(obj, method, value);
			}

			var self = this;

			/**
			* Track a new call to the watched method,
			* mocking or executing it as needed
			*
			* @returns {*} The return value from the mock or real method
			*/
			self.mock = function() {
				var args = arguments,
					id = self.calls.length,
					returned = self.value;

				self.calls.push({ args: args });

				if (self.execute) {
					returned = self.method.apply(self.obj, args);
				} else if (self.invoke) {
					returned = self.value.apply(self.obj, args);
				}

				self.calls[id].returned = returned;

				return returned;
			};

			/**
			* Setup the Spy to invoke its mocked value
			* rather than just returning it
			*
			* @returns {Spy} Itself
			*/
			self.andInvoke = self.startInvoking = function() {
				if (typeof this.value === 'function') {
					self.invoke = true;
					return self;
				}
				throw 'Cannot invoke value that is not a function.';
			};

			/**
			* Setup the Spy to call the live method being watched
			*
			* @returns {Spy} Itself
			*/
			self.andExecute = self.startExecuting = function() {
				self.execute = true;
				return self;
			};

			/**
			* Setup the Spy to mock the method being watched
			*
			* @param {*} [value] The value to return/invoke for the mock
			* @returns {Spy} Itself
			*/
			self.andMock = self.startMocking = function(value) {
				if (value !== undefined) {
					self.value = value;
				}
				self.execute = false;
				return self;
			};

			/**
			* Find out how many calls have been made to the watched method
			*
			* @returns {Number} The number of calls made
			*/
			self.called = function() {
				return self.calls.length;
			};

			/**
			* Get the arguments passed to the watched method,
			* defaulting to the most recent call if no id is passed
			*
			* @param {Number} [id] The id of the call, in case of multiple
			* @returns {Array} The arguments array for that call
			*/
			self.calledWith = function(id) {
				if (id !== undefined && id !== null) {
					if (id >= 0 && id < self.calls.length) {
						return self.calls[id].args;
					}
					throw 'Cannot get arguments for invalid call index.';
				} else if (self.calls.length) {
					return self.calls[self.calls.length - 1].args;
				}
				throw 'Cannot get arguments, spy has not been called.';
			};

			/**
			* Get the return value passed from Spy.mock(),
			* defaulting to the most recent call if no id is passed
			*
			* @param {Number} [id] The id of the call, in case of multiple
			* @returns {*} The return value for that call
			*/
			self.returnedWith = function(id) {
				if (id !== undefined && id !== null) {
					if (id >= 0 && id < self.calls.length) {
						return self.calls[id].returned;
					}
					throw 'Cannot get return value for invalid call index.';
				} else {
					return self.calls[self.calls.length - 1].returned;
				}
			};

			/**
			* Reset the calls tracked by the Spy
			*
			* @returns {Spy} Itself
			*/
			self.reset = function() {
				var calls = self.calls;
				self.calls = [];
				return self;
			};

			/**
			* Release the watched method back
			* to its original functionality
			*
			* @returns {Spy} Itself
			*/
			self.release = function() {
				self.obj[self.name] = self.method;
				return self;
			};

			/**
			* Resumes mocking the watched method
			*
			* @returns {Spy} Itself
			*/
			self.resume = function() {
				self.obj[self.name] = self.mock;
				return self;
			};

			// Defaults and Storage
			self.obj = obj;
			self.name = method;
			self.method = obj[method];
			self.value = value;
			self.execute = false;
			self.invoke = null;
			self.calls = [];

			obj[method] = self.mock;
		}

		return Spy;
	})();

	/**
	 *
	 * @param method
	 * @constructor
	 */
	// Edgar.prototype.Mock = function(method) {
	// 	var spy = new Edgar.Spy({}, 'mock', method, true),
	// 		mock = spy.mock;
	//
	// 	mock.prototype = spy.prototype;
	//
	// 	return mock;
	// };

	if (typeof QUnit !== 'undefined') {
		QUnit.testDone(function() {
			Edgar.releaseAll();
			Edgar.removeSpies();
		});
	}

	if (typeof mocha !== 'undefined') {
		mocha.afterEach(function() {
			Edgar.releaseAll();
			Edgar.removeSpies();
		});
	}

	return Edgar;
}));
<|endoftext|>


<commit_before>const fs = require('fs')
const util = require('util')
const parser = require('json-schema-parser')
const Immutable = require('immutable')

const buf = fs.readFileSync('./docs/schema/schema.json')
// console.log(JSON.stringify({}, null, 2))
const schemaDef = Immutable.fromJS(parser.parse(JSON.parse(buf.toString())))

const exampleJson = (obj, depth) => {
  return obj.map((v, k) => {
    if (k !== 'definitions') {
      if (v.getIn(['type', 0]).toLowerCase() === 'object') {
        return Immutable.fromJS({[k]: exampleJson(v.get('properties'), depth + 1)})
      } else if (v.getIn(['type', 0]).toLowerCase() === 'array') {
        const arrayProp = v.getIn(['items', 'properties'])
        if (arrayProp) {
          return Immutable.fromJS({[k]: [exampleJson(arrayProp, depth + 1, k)]})
        }
        // TODO: arrayの中身がobjectではない場合を考慮する
      } else if (v.getIn(['type', 0]).toLowerCase() === 'string') {
        return Immutable.fromJS({[k]: '文字列'})
      } else if (v.getIn(['type', 0]).toLowerCase() === 'number') {
        return Immutable.fromJS({[k]: 0})
      }
    }
  }).reduce((r, v) => r.mergeDeep(v), Immutable.fromJS({}))
}

const generateVariable = (obj, depth) => {
  const indent = Immutable.Range(0, depth).map(() => '  ').reduce((r, v) => r + v, '')
  return obj.map((v, k) => {
    if (k !== 'definitions') {
      if (v.getIn(['type', 0]).toLowerCase() === 'object') {
        return `${indent}var ${k}: ${k[0].toUpperCase()}${k.substring(1)}`
      } else if (v.getIn(['type', 0]).toLowerCase() === 'array') {
        const arrayProp = v.getIn(['items', 'properties'])
        if (arrayProp) {
          return `${indent}var ${k}: List<${k[0].toUpperCase()}${k.substring(1)}>`
        }
        // TODO: arrayの中身がobjectではない場合を考慮する
      } else if (v.getIn(['type', 0]).toLowerCase() === 'string') {
        return `${indent}var ${k}: String`
      } else if (v.getIn(['type', 0]).toLowerCase() === 'number') {
        return `${indent}var ${k}: Int`
      }
    }
  }).map(v => `\n${v}`)
  .join(',')
}

const generateClassComment = (obj, depth) => {
  const indent = Immutable.Range(0, depth - 1).map(() => '  ').reduce((r, v) => r + v, '')
  const propertiesComment = obj.map((v, k) => {
    if (k !== 'definitions') {
      return `${indent} * @property ${k} ${v.get('description').replace('\n', '<br />')}`
    }
  }).join('\n')
  return `${indent}/**\n${propertiesComment}\n${indent} */\n`
}

const generateKotlinDataClass = (obj, depth) => {
  const indent = Immutable.Range(0, depth).map(() => '  ').reduce((r, v) => r + v, '')
  const classDefs = obj.map((v, k) => {
    if (k !== 'definitions') {
      // console.log(`[${depth}]${k} ${v.getIn(['type', 0])} ${v.get('description')}`)
      if (v.getIn(['type', 0]).toLowerCase() === 'object') {
        return `${generateClassComment(v.get('properties'), depth + 1)}${indent}data class ${k[0].toUpperCase()}${k.substring(1)} (${generateVariable(v.get('properties'), depth + 1)})${generateKotlinDataClass(v.get('properties'), depth + 1)}`
        // return Immutable.fromJS({[k]: generateKotlinDataClass(v.get('properties'), depth + 1)})
      } else if (v.getIn(['type', 0]).toLowerCase() === 'array') {
        const arrayProp = v.getIn(['items', 'properties'])
        if (arrayProp) {
          return `${generateClassComment(v.getIn(['items', 'properties']), depth + 1)}${indent}data class ${k[0].toUpperCase()}${k.substring(1)} (${generateVariable(v.getIn(['items', 'properties']), depth + 1)})${generateKotlinDataClass(v.getIn(['items', 'properties']), depth + 1)}`
        }
        // TODO: arrayの中身がobjectではない場合を考慮する
      }
    }
  }).filter(v => v)

  if (classDefs.size == 0) {
    return ''
  } else {
    return ` {\n${classDefs.join('\n')}\n${indent}}`
  }
}

schemaDef.get('properties').map((v, k) => {
  if (k !== 'error') {
    const obj = exampleJson(v.get('properties'), 1).toJS()
    console.log(`***** ${k} *****`)
    console.log(JSON.stringify(obj, null, 2))
    console.log(`${generateClassComment(v.get('properties'), 1)}data class ${k}(${generateVariable(v.get('properties'), 1)})${generateKotlinDataClass(v.get('properties'), 1)}`)
    console.log('')
  }
})
<commit_msg>一行が長かったのでリファクタ<commit_after>const fs = require('fs')
const util = require('util')
const parser = require('json-schema-parser')
const Immutable = require('immutable')

const buf = fs.readFileSync('./docs/schema/schema.json')
// console.log(JSON.stringify({}, null, 2))
const schemaDef = Immutable.fromJS(parser.parse(JSON.parse(buf.toString())))

const exampleJson = (obj, depth) => {
  return obj.map((v, k) => {
    if (k !== 'definitions') {
      if (v.getIn(['type', 0]).toLowerCase() === 'object') {
        return Immutable.fromJS({[k]: exampleJson(v.get('properties'), depth + 1)})
      } else if (v.getIn(['type', 0]).toLowerCase() === 'array') {
        const arrayProp = v.getIn(['items', 'properties'])
        if (arrayProp) {
          return Immutable.fromJS({[k]: [exampleJson(arrayProp, depth + 1, k)]})
        }
        // TODO: arrayの中身がobjectではない場合を考慮する
      } else if (v.getIn(['type', 0]).toLowerCase() === 'string') {
        return Immutable.fromJS({[k]: '文字列'})
      } else if (v.getIn(['type', 0]).toLowerCase() === 'number') {
        return Immutable.fromJS({[k]: 0})
      }
    }
  }).reduce((r, v) => r.mergeDeep(v), Immutable.fromJS({}))
}

const generateVariable = (obj, depth) => {
  const indent = Immutable.Range(0, depth).map(() => '  ').reduce((r, v) => r + v, '')
  return obj.map((v, k) => {
    if (k !== 'definitions') {
      if (v.getIn(['type', 0]).toLowerCase() === 'object') {
        return `${indent}var ${k}: ${k[0].toUpperCase()}${k.substring(1)}`
      } else if (v.getIn(['type', 0]).toLowerCase() === 'array') {
        const arrayProp = v.getIn(['items', 'properties'])
        if (arrayProp) {
          return `${indent}var ${k}: List<${k[0].toUpperCase()}${k.substring(1)}>`
        }
        // TODO: arrayの中身がobjectではない場合を考慮する
      } else if (v.getIn(['type', 0]).toLowerCase() === 'string') {
        return `${indent}var ${k}: String`
      } else if (v.getIn(['type', 0]).toLowerCase() === 'number') {
        return `${indent}var ${k}: Int`
      }
    }
  }).map(v => `\n${v}`)
  .join(',')
}

const generateClassComment = (obj, depth) => {
  const indent = Immutable.Range(0, depth - 1).map(() => '  ').reduce((r, v) => r + v, '')
  const propertiesComment = obj.map((v, k) => {
    if (k !== 'definitions') {
      return `${indent} * @property ${k} ${v.get('description').replace('\n', '<br />')}`
    }
  }).join('\n')
  return `${indent}/**\n${propertiesComment}\n${indent} */\n`
}

const generateKotlinDataClass = (obj, depth) => {
  const indent = Immutable.Range(0, depth).map(() => '  ').reduce((r, v) => r + v, '')
  const classDefs = obj.map((v, k) => {
    if (k !== 'definitions') {
      // console.log(`[${depth}]${k} ${v.getIn(['type', 0])} ${v.get('description')}`)
      if (v.getIn(['type', 0]).toLowerCase() === 'object') {
        return `${generateClassComment(v.get('properties'), depth + 1)}${indent}data class ${k[0].toUpperCase()}${k.substring(1)} (${generateVariable(v.get('properties'), depth + 1)})${generateKotlinDataClass(v.get('properties'), depth + 1)}`
        // return Immutable.fromJS({[k]: generateKotlinDataClass(v.get('properties'), depth + 1)})
      } else if (v.getIn(['type', 0]).toLowerCase() === 'array') {
        const arrayProp = v.getIn(['items', 'properties'])
        if (arrayProp) {
          return `${generateClassComment(v.getIn(['items', 'properties']), depth + 1)}${indent}data class ${k[0].toUpperCase()}${k.substring(1)} (${generateVariable(v.getIn(['items', 'properties']), depth + 1)})${generateKotlinDataClass(v.getIn(['items', 'properties']), depth + 1)}`
        }
        // TODO: arrayの中身がobjectではない場合を考慮する
      }
    }
  }).filter(v => v)

  if (classDefs.size == 0) {
    return ''
  } else {
    return ` {\n${classDefs.join('\n')}\n${indent}}`
  }
}

schemaDef.get('properties').map((v, k) => {
  if (k !== 'error') {
    const obj = exampleJson(v.get('properties'), 1).toJS()
    console.log(`***** ${k} *****`)
    console.log(JSON.stringify(obj, null, 2))
    const classComment = generateClassComment(v.get('properties'), 1)
    const variables = generateVariable(v.get('properties'), 1)
    const dataClasses = generateKotlinDataClass(v.get('properties'), 1)
    console.log(`${classComment}data class ${k}(${variables})${dataClasses}`)
    console.log('')
  }
})
<|endoftext|>


<commit_before>'use strict';

if (!Error.captureStackTrace) throw new Error('Error.captureStackTrace does not exist!');

var util = require('./util');
var BaseError = require('./base_error');
var v = require('./validator').v;
var check = require('./validator').check;

var Errors = {
    template: null,
};
exports = module.exports = Errors;

/**
 * set the separator for multi error stacks.
 *
 * Default to "\n==== Pre-Error-Stack ====\n"
 *
 * @param  {String} separator
 * @method setErrorStackSeparator(separator)
 */
exports.setErrorStackSeparator = function(separator) {
    BaseError.prototype.ERROR_STACK_SEPARATOR = separator;
};

/**
 * set the connector for multi error messages. Default to " && "
 *
 * @param  {String} connector
 * @return {Undefined}
 * @method setMessageConnector(connector)
 */
exports.setMessageConnector = function(connector) {
    BaseError.prototype.MESSAGE_CONNECTOR = connector;
};

/**
 * define a subclass of BaseError
 *
 * @param  {Object} definition
 * @param  {String} name  the name of Error Class
 * @return {Function}  Error Class
 * @method defineError(definition, name)
 */
exports.defineError = function(definition, name) {
    if (!Errors.template) throw new Error('The error template should be defined firstly!');
    if (util.isObject(definition) === false) throw new Error('definition should be an object! Current error name=' + name);

    var E = function() {
        BaseError.apply(this, arguments);
    };
    util.inherits(E, BaseError);

    util.each(Errors.template, function(opts, key) {
        var value = definition[key];
        if (value !== undefined) {
            E.prototype[key] = value;
        } else {
            if (opts.required === true) {
                throw new Error('Missing the key "' + key + '", which is required. Current error name=' + name);
            } else {
                E.prototype[key] = opts.default;
            }
        }
    });

    E.prototype.name = name;

    Errors[name] = E;

    return E;
};

/**
 * To determine whether it is your custom error.
 *
 * if the error is an instance of the native Error, return `false`.
 *
 * @param  {*}  error
 * @return {Boolean}
 * @method isCustomError(err)
 */
exports.isCustomError = function(err) {
    return err instanceof BaseError;
};
/**
 * alias for isCustomError
 */
exports.isError = Errors.isCustomError;

/**
 * @param  {Object} template
 * @return {Undefined}
 * @method setTemplate(template)
 */
exports.setTemplate = function(template) {
    if (util.isObject(template) === false) {
        throw new Error('template should be an object!');
    }

    var t = {};
    util.each(template, function(params, key) {
        if (util.isString(params)) {
            t[key] = {
                message: params,
                required: true,
            };
        } else if (util.isObject(params)) {
            t[key] = check(params, v.templateProp());
        } else {
            throw new Error('The value of template item should be an object or a string. Actual key=' + key + ' and value=' + JSON.stringify(params));
        }
    });
    Errors.template = t;
};

/**
 * Assigns own enumerable properties of meta to the err.meta.
 *
 * If the err.meta is undefined before assigning, it will be assigned a new object,
 * and then the own enumerable properties of second parameter will be assigned to err.meta.
 * If the err.meta is not undefined, it should be a plain object.
 *
 * the properties of meta will overwrite the properties of err.meta, if they have same name.
 *
 * @side_effect  err, err.meta
 * @param  {Error}  err  the instance of Error class or Error subclass
 * @param  {Object} meta
 * @return {Undefined}
 * @method addMeta
 */
exports.addMeta = function(err, meta) {
    if (!err.meta) err.meta = {};
    util.extend(err.meta, meta);
};

/**
 * initialize module
 *
 * @param  {Object} params
 * @param  {Object} params.template  a template for all error sub-classes
 * @param  {Array<Object>} params.definitions the definitions of error sub-classes
 * @return {Object}  a map of Error classes
 * @method init(params)
 */
exports.init = function(params) {
    Errors.setTemplate(params.template);
    var _Errors = {};
    util.each(params.definitions, function(definition, name) {
        var E = Errors.defineError(definition, name);
        _Errors[name] = E;
    });

    return _Errors;
};
<commit_msg>chore: change code comments for jsduck<commit_after>'use strict';

if (!Error.captureStackTrace) throw new Error('Error.captureStackTrace does not exist!');

var util = require('./util');
var BaseError = require('./base_error');
var v = require('./validator').v;
var check = require('./validator').check;

/**
 * @class Errors
 * @singleton
 */
var Errors = {
    template: null,
};
exports = module.exports = Errors;

/**
 * set the separator for multi error stacks.
 *
 * Default to "\n==== Pre-Error-Stack ====\n"
 *
 * @param  {String} separator
 * @return {undefined}
 * @method setErrorStackSeparator
 */
exports.setErrorStackSeparator = function(separator) {
    BaseError.prototype.ERROR_STACK_SEPARATOR = separator;
};

/**
 * set the connector for multi error messages. Default to " && "
 *
 * @param  {String} connector
 * @return {undefined}
 * @method setMessageConnector
 */
exports.setMessageConnector = function(connector) {
    BaseError.prototype.MESSAGE_CONNECTOR = connector;
};

/**
 * define a subclass of BaseError
 *
 * @param  {Object} definition
 * @param  {String} name  the name of Error Class
 * @return {Function}  Error Class
 * @method defineError
 */
exports.defineError = function(definition, name) {
    if (!Errors.template) throw new Error('The error template should be defined firstly!');
    if (util.isObject(definition) === false) throw new Error('definition should be an object! Current error name=' + name);

    var E = function() {
        BaseError.apply(this, arguments);
    };
    util.inherits(E, BaseError);

    util.each(Errors.template, function(opts, key) {
        var value = definition[key];
        if (value !== undefined) {
            E.prototype[key] = value;
        } else {
            if (opts.required === true) {
                throw new Error('Missing the key "' + key + '", which is required. Current error name=' + name);
            } else {
                E.prototype[key] = opts.default;
            }
        }
    });

    E.prototype.name = name;

    Errors[name] = E;

    return E;
};

/**
 * To determine whether it is your custom error.
 *
 * if the error is an instance of the native Error, return `false`.
 *
 * @param  {*}  error
 * @return {Boolean}
 * @method isCustomError
 */
exports.isCustomError = function(err) {
    return err instanceof BaseError;
};
/**
 * @alias #isCustomError
 * @method isError
 */
exports.isError = Errors.isCustomError;

/**
 * @param  {Object} template
 * @return {undefined}
 * @method setTemplate
 */
exports.setTemplate = function(template) {
    if (util.isObject(template) === false) {
        throw new Error('template should be an object!');
    }

    var t = {};
    util.each(template, function(params, key) {
        if (util.isString(params)) {
            t[key] = {
                message: params,
                required: true,
            };
        } else if (util.isObject(params)) {
            t[key] = check(params, v.templateProp());
        } else {
            throw new Error('The value of template item should be an object or a string. Actual key=' + key + ' and value=' + JSON.stringify(params));
        }
    });
    Errors.template = t;
};

/**
 * Assigns own enumerable properties of meta to the err.meta.
 *
 * If the err.meta is undefined before assigning, it will be assigned a new object,
 * and then the own enumerable properties of second parameter will be assigned to err.meta.
 * If the err.meta is not undefined, it should be a plain object.
 *
 * the properties of meta will overwrite the properties of err.meta, if they have same name.
 *
 * @side_effect  err, err.meta
 * @param  {Error}  err  the instance of Error class or Error subclass
 * @param  {Object} meta
 * @return {undefined}
 * @method addMeta
 */
exports.addMeta = function(err, meta) {
    if (!err.meta) err.meta = {};
    util.extend(err.meta, meta);
};

/**
 * initialize module
 *
 * @param  {Object} params
 * @param  {Object} params.template  a template for all error sub-classes
 * @param  {Object[]} params.definitions the definitions of error sub-classes
 * @return {Object}  a map of Error classes
 * @method init
 */
exports.init = function(params) {
    Errors.setTemplate(params.template);
    var _Errors = {};
    util.each(params.definitions, function(definition, name) {
        var E = Errors.defineError(definition, name);
        _Errors[name] = E;
    });

    return _Errors;
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

import {getMode} from './mode';
import {exponentialBackoff} from './exponential-backoff';
import {isLoadErrorMessage} from './event-helper';
import {USER_ERROR_SENTINEL, isUserErrorMessage} from './log';
import {makeBodyVisible} from './style-installer';
import {urls} from './config';

const CANCELLED = 'CANCELLED';

/**
 * A wrapper around our exponentialBackoff, to lazy initialize it to avoid an
 * un-DCE'able side-effect.
 * @param {function()} work the function to execute after backoff
 * @return {number} the setTimeout id
 */
let globalExponentialBackoff = function(work) {
  // Set globalExponentialBackoff as the lazy-created function. JS Vooodoooo.
  globalExponentialBackoff = exponentialBackoff(1.5);
  return globalExponentialBackoff(work);
};

/**
 * Reports an error. If the error has an "associatedElement" property
 * the element is marked with the -amp-element-error and displays
 * the message itself. The message is always send to the console.
 * If the error has a "messageArray" property, that array is logged.
 * This way one gets the native fidelity of the console for things like
 * elements instead of stringification.
 * @param {*} error
 * @param {!Element=} opt_associatedElement
 */
export function reportError(error, opt_associatedElement) {
  if (!self.console) {
    return;
  }
  if (!error) {
    error = new Error('no error supplied');
  }
  if (error.reported) {
    return;
  }
  error.reported = true;
  const element = opt_associatedElement || error.associatedElement;
  if (element && element.classList) {
    element.classList.add('-amp-error');
    if (getMode().development) {
      element.classList.add('-amp-element-error');
      element.setAttribute('error-message', error.message);
    }
  }
  if (error.messageArray) {
    (console.error || console.log).apply(console,
        error.messageArray);
  } else {
    if (element) {
      (console.error || console.log).call(console,
          element.tagName + '#' + element.id, error.message);
    } else if (!getMode().minified) {
      (console.error || console.log).call(console, error.stack);
    } else {
      (console.error || console.log).call(console, error.message);
    }

  }
  if (element && element.dispatchCustomEventForTesting) {
    element.dispatchCustomEventForTesting('amp:error', error.message);
  }
  // 'call' to make linter happy. And .call to make compiler happy
  // that expects some @this.
  reportErrorToServer['call'](undefined, undefined, undefined, undefined,
      undefined, error);
}

/**
 * Returns an error for a cancellation of a promise.
 * @return {!Error}
 */
export function cancellation() {
  return new Error(CANCELLED);
}

/**
 * Install handling of global unhandled exceptions.
 * @param {!Window} win
 */
export function installErrorReporting(win) {
  win.onerror = /** @type {!Function} */ (reportErrorToServer);
  win.addEventListener('unhandledrejection', event => {
    reportError(event.reason || new Error('rejected promise ' + event));
  });
}

/**
 * Signature designed, so it can work with window.onerror
 * @param {string|undefined} message
 * @param {string|undefined} filename
 * @param {string|undefined} line
 * @param {string|undefined} col
 * @param {*|undefined} error
 * @this {!Window|undefined}
 */
function reportErrorToServer(message, filename, line, col, error) {
  // Make an attempt to unhide the body.
  if (this && this.document) {
    makeBodyVisible(this.document);
  }
  if (getMode().localDev || getMode().development || getMode().test) {
    return;
  }
  let hasNonAmpJs = false;
  try {
    hasNonAmpJs = detectNonAmpJs(self);
  } catch (ignore) {
    // Ignore errors during error report generation.
  }
  if (hasNonAmpJs && Math.random() > 0.01) {
    // Only report 1% of errors on pages with non-AMP JS.
    // These errors can almost never be acted upon, but spikes such as
    // due to buggy browser extensions may be helpful to notify authors.
    return;
  }
  const url = getErrorReportUrl(message, filename, line, col, error,
      hasNonAmpJs);
  globalExponentialBackoff(() => {
    if (url) {
      new Image().src = url;
    }
  });
}

/**
 * Signature designed, so it can work with window.onerror
 * @param {string|undefined} message
 * @param {string|undefined} filename
 * @param {string|undefined} line
 * @param {string|undefined} col
 * @param {*|undefined} error
 * @param {boolean} hasNonAmpJs
 * @return {string|undefined} The URL
 * visibleForTesting
 */
export function getErrorReportUrl(message, filename, line, col, error,
    hasNonAmpJs) {
  message = error && error.message ? error.message : message;
  if (/_reported_/.test(message)) {
    return;
  }
  if (message == CANCELLED) {
    return;
  }
  if (!message) {
    message = 'Unknown error';
  }
  if (isLoadErrorMessage(message)) {
    return;
  }

  // This is the App Engine app in
  // ../tools/errortracker
  // It stores error reports via https://cloud.google.com/error-reporting/
  // for analyzing production issues.
  let url = urls.errorReporting +
      '?v=' + encodeURIComponent('$internalRuntimeVersion$') +
      '&noAmp=' + (hasNonAmpJs ? 1 : 0) +
      '&m=' + encodeURIComponent(message.replace(USER_ERROR_SENTINEL, '')) +
      '&a=' + (isUserErrorMessage(message) ? 1 : 0);
  if (self.context && self.context.location) {
    url += '&3p=1';
  }
  if (self.AMP_CONFIG && self.AMP_CONFIG.canary) {
    url += '&ca=1';
  }
  if (self.location.ancestorOrigins && self.location.ancestorOrigins[0]) {
    url += '&or=' + encodeURIComponent(self.location.ancestorOrigins[0]);
  }
  if (self.viewerState) {
    url += '&vs=' + encodeURIComponent(self.viewerState);
  }
  // Is embedded?
  if (self.parent && self.parent != self) {
    url += '&iem=1';
  }

  if (self.AMP.viewer) {
    const resolvedViewerUrl = self.AMP.viewer.getResolvedViewerUrl();
    const messagingOrigin = self.AMP.viewer.maybeGetMessagingOrigin();
    if (resolvedViewerUrl) {
      url += `&rvu=${encodeURIComponent(resolvedViewerUrl)}`;
    }
    if (messagingOrigin) {
      url += `&mso=${encodeURIComponent(messagingOrigin)}`;
    }
  }

  if (error) {
    const tagName = error && error.associatedElement
      ? error.associatedElement.tagName
      : 'u';  // Unknown
    url += '&el=' + encodeURIComponent(tagName) +
        '&s=' + encodeURIComponent(error.stack || '');
    error.message += ' _reported_';
  } else {
    url += '&f=' + encodeURIComponent(filename || '') +
        '&l=' + encodeURIComponent(line || '') +
        '&c=' + encodeURIComponent(col || '');
  }
  url += '&r=' + encodeURIComponent(self.document.referrer);

  // Shorten URLs to a value all browsers will send.
  return url.substr(0, 2000);
}

/**
 * Returns true if it appears like there is non-AMP JS on the
 * current page.
 * @param {!Window} win
 * @visibleForTesting
 */
export function detectNonAmpJs(win) {
  const scripts = win.document.querySelectorAll('script[src]');
  for (let i = 0; i < scripts.length; i++) {
    if (scripts[i].src.toLowerCase().indexOf(urls.cdn) != 0) {
      return true;
    }
  }
  return false;
}
<commit_msg>Other JS errors: Use startsWith (#6006)<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * 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.
 */

import {getMode} from './mode';
import {exponentialBackoff} from './exponential-backoff';
import {isLoadErrorMessage} from './event-helper';
import {USER_ERROR_SENTINEL, isUserErrorMessage} from './log';
import {makeBodyVisible} from './style-installer';
import {urls} from './config';
import {startsWith} from './string';

const CANCELLED = 'CANCELLED';

/**
 * A wrapper around our exponentialBackoff, to lazy initialize it to avoid an
 * un-DCE'able side-effect.
 * @param {function()} work the function to execute after backoff
 * @return {number} the setTimeout id
 */
let globalExponentialBackoff = function(work) {
  // Set globalExponentialBackoff as the lazy-created function. JS Vooodoooo.
  globalExponentialBackoff = exponentialBackoff(1.5);
  return globalExponentialBackoff(work);
};

/**
 * Reports an error. If the error has an "associatedElement" property
 * the element is marked with the -amp-element-error and displays
 * the message itself. The message is always send to the console.
 * If the error has a "messageArray" property, that array is logged.
 * This way one gets the native fidelity of the console for things like
 * elements instead of stringification.
 * @param {*} error
 * @param {!Element=} opt_associatedElement
 */
export function reportError(error, opt_associatedElement) {
  if (!self.console) {
    return;
  }
  if (!error) {
    error = new Error('no error supplied');
  }
  if (error.reported) {
    return;
  }
  error.reported = true;
  const element = opt_associatedElement || error.associatedElement;
  if (element && element.classList) {
    element.classList.add('-amp-error');
    if (getMode().development) {
      element.classList.add('-amp-element-error');
      element.setAttribute('error-message', error.message);
    }
  }
  if (error.messageArray) {
    (console.error || console.log).apply(console,
        error.messageArray);
  } else {
    if (element) {
      (console.error || console.log).call(console,
          element.tagName + '#' + element.id, error.message);
    } else if (!getMode().minified) {
      (console.error || console.log).call(console, error.stack);
    } else {
      (console.error || console.log).call(console, error.message);
    }

  }
  if (element && element.dispatchCustomEventForTesting) {
    element.dispatchCustomEventForTesting('amp:error', error.message);
  }
  // 'call' to make linter happy. And .call to make compiler happy
  // that expects some @this.
  reportErrorToServer['call'](undefined, undefined, undefined, undefined,
      undefined, error);
}

/**
 * Returns an error for a cancellation of a promise.
 * @return {!Error}
 */
export function cancellation() {
  return new Error(CANCELLED);
}

/**
 * Install handling of global unhandled exceptions.
 * @param {!Window} win
 */
export function installErrorReporting(win) {
  win.onerror = /** @type {!Function} */ (reportErrorToServer);
  win.addEventListener('unhandledrejection', event => {
    reportError(event.reason || new Error('rejected promise ' + event));
  });
}

/**
 * Signature designed, so it can work with window.onerror
 * @param {string|undefined} message
 * @param {string|undefined} filename
 * @param {string|undefined} line
 * @param {string|undefined} col
 * @param {*|undefined} error
 * @this {!Window|undefined}
 */
function reportErrorToServer(message, filename, line, col, error) {
  // Make an attempt to unhide the body.
  if (this && this.document) {
    makeBodyVisible(this.document);
  }
  if (getMode().localDev || getMode().development || getMode().test) {
    return;
  }
  let hasNonAmpJs = false;
  try {
    hasNonAmpJs = detectNonAmpJs(self);
  } catch (ignore) {
    // Ignore errors during error report generation.
  }
  if (hasNonAmpJs && Math.random() > 0.01) {
    // Only report 1% of errors on pages with non-AMP JS.
    // These errors can almost never be acted upon, but spikes such as
    // due to buggy browser extensions may be helpful to notify authors.
    return;
  }
  const url = getErrorReportUrl(message, filename, line, col, error,
      hasNonAmpJs);
  globalExponentialBackoff(() => {
    if (url) {
      new Image().src = url;
    }
  });
}

/**
 * Signature designed, so it can work with window.onerror
 * @param {string|undefined} message
 * @param {string|undefined} filename
 * @param {string|undefined} line
 * @param {string|undefined} col
 * @param {*|undefined} error
 * @param {boolean} hasNonAmpJs
 * @return {string|undefined} The URL
 * visibleForTesting
 */
export function getErrorReportUrl(message, filename, line, col, error,
    hasNonAmpJs) {
  message = error && error.message ? error.message : message;
  if (/_reported_/.test(message)) {
    return;
  }
  if (message == CANCELLED) {
    return;
  }
  if (!message) {
    message = 'Unknown error';
  }
  if (isLoadErrorMessage(message)) {
    return;
  }

  // This is the App Engine app in
  // ../tools/errortracker
  // It stores error reports via https://cloud.google.com/error-reporting/
  // for analyzing production issues.
  let url = urls.errorReporting +
      '?v=' + encodeURIComponent('$internalRuntimeVersion$') +
      '&noAmp=' + (hasNonAmpJs ? 1 : 0) +
      '&m=' + encodeURIComponent(message.replace(USER_ERROR_SENTINEL, '')) +
      '&a=' + (isUserErrorMessage(message) ? 1 : 0);
  if (self.context && self.context.location) {
    url += '&3p=1';
  }
  if (self.AMP_CONFIG && self.AMP_CONFIG.canary) {
    url += '&ca=1';
  }
  if (self.location.ancestorOrigins && self.location.ancestorOrigins[0]) {
    url += '&or=' + encodeURIComponent(self.location.ancestorOrigins[0]);
  }
  if (self.viewerState) {
    url += '&vs=' + encodeURIComponent(self.viewerState);
  }
  // Is embedded?
  if (self.parent && self.parent != self) {
    url += '&iem=1';
  }

  if (self.AMP.viewer) {
    const resolvedViewerUrl = self.AMP.viewer.getResolvedViewerUrl();
    const messagingOrigin = self.AMP.viewer.maybeGetMessagingOrigin();
    if (resolvedViewerUrl) {
      url += `&rvu=${encodeURIComponent(resolvedViewerUrl)}`;
    }
    if (messagingOrigin) {
      url += `&mso=${encodeURIComponent(messagingOrigin)}`;
    }
  }

  if (error) {
    const tagName = error && error.associatedElement
      ? error.associatedElement.tagName
      : 'u';  // Unknown
    url += '&el=' + encodeURIComponent(tagName) +
        '&s=' + encodeURIComponent(error.stack || '');
    error.message += ' _reported_';
  } else {
    url += '&f=' + encodeURIComponent(filename || '') +
        '&l=' + encodeURIComponent(line || '') +
        '&c=' + encodeURIComponent(col || '');
  }
  url += '&r=' + encodeURIComponent(self.document.referrer);

  // Shorten URLs to a value all browsers will send.
  return url.substr(0, 2000);
}

/**
 * Returns true if it appears like there is non-AMP JS on the
 * current page.
 * @param {!Window} win
 * @visibleForTesting
 */
export function detectNonAmpJs(win) {
  const scripts = win.document.querySelectorAll('script[src]');
  for (let i = 0; i < scripts.length; i++) {
    if (!startsWith(scripts[i].src.toLowerCase(), urls.cdn)) {
      return true;
    }
  }
  return false;
}
<|endoftext|>


<commit_before>const fs = require('fs');
const file = fs.createWriteStream('./largefile.txt');
let txt=`access, adduser, audit, bin, bugs, c, cache, ci, cit,
completion, config, create, ddp, dedupe, deprecate,
dist-tag, docs, doctor, edit, explore, get, help,
help-search, hook, i, init, install, install-test, it, link,
list, ln, login, logout, ls, outdated, owner, pack, ping,
prefix, profile, prune, publish, rb, rebuild, repo, restart,
root, run, run-script, s, se, search, set, shrinkwrap, star,
stars, start, stop, t, team, test, token, tst, un,
uninstall, unpublish, unstar, up, update, v, version, view,
whoami
\n`;
for(let i=0; i<= 1e6; i++) {
  file.write(txt);
}

file.end();<commit_msg>remove generated file<commit_after>//run node test/LargeFileCreator.js to create the file
const fs = require('fs');
const file = fs.createWriteStream('./largefile.txt');
let txt=`access, adduser, audit, bin, bugs, c, cache, ci, cit,
completion, config, create, ddp, dedupe, deprecate,
dist-tag, docs, doctor, edit, explore, get, help,
help-search, hook, i, init, install, install-test, it, link,
list, ln, login, logout, ls, outdated, owner, pack, ping,
prefix, profile, prune, publish, rb, rebuild, repo, restart,
root, run, run-script, s, se, search, set, shrinkwrap, star,
stars, start, stop, t, team, test, token, tst, un,
uninstall, unpublish, unstar, up, update, v, version, view,
whoami
\n`;
for(let i=0; i<= 1e6; i++) {
  file.write(txt);
}

file.end();<|endoftext|>


<commit_before>/*
events - a fast, flexible event system for javascript

------------------------------------------------------------------------
This file is loosely based on Dean Edwards's addEvent implementation and
modified to work with non-HTMLElement targets and custom event types
------------------------------------------------------------------------

event.add(targetObj, evName, handlerFunc)
event.listen(targetObj, evName, handlerFunc)
  add an event listener to targetObj. the listener function should take
  3 arguments, the event target, the event type, and a data object

event.oneTimer(targetObj, evName, handlerFunc)
  add an event listener to targetObj (like add/listen), but have it be
  automatically removed after the first call

event.remove(targetObj, [evName[, handlerFunc]])
event.unlisten(targetObj, evName, handlerFunc)
  undo an add

event.dispatch(targetObj, evName)
  trigger an event yourself
*/

/*global TJP*/
//context:browser
//context:console

TJP.event = TJP.event || {};

var fixEvent = function(target) {
  target.preventDefault = fixEvent.preventDefault;
  target.stopPropagation = fixEvent.stopPropagation;
  return target;
};
fixEvent.preventDefault = function() { this.returnValue = false; };
fixEvent.stopPropagation = function() { this.cancelBubble = true; };

var
  guid = 1,
  events = {},
  handleEvent = function(target, type, data) {
    var i, rv = true, hs, errors = [];
    try {
      target = target || fixEvent(((this.ownerDocument || this.document ||
              this).parentWindow || window).event || {type: type});
    } catch (err) { target = fixEvent({type: type}); }
    hs = (events[target] ? events[target][type] : {}) || {};
    for (i in hs) {
      this.handleEvent = hs[i];
      try {
        if (this.handleEvent(target, type, data) === false) rv = false;
      } catch(e) { errors.push(e); }
    }
    delete this.handleEvent;
    if (errors.length) throw errors[0];
    return rv;
  };

TJP.event.add = TJP.event.listen = function(target, type, handler) {
  if (!handler.__guid) handler.__guid = guid++;
  if (!events[target]) events[target] = {};
  var handlers = events[target][type];
  if (!handlers) {
    handlers = events[target][type] = {};
    if (target["on" + type]) handlers[0] = target["on" + type];
  }
  handlers[handler.__guid] = handler;
  target["on" + type] = function(data) {
    this.handleEvent = handleEvent;
    return this.handleEvent(target, type, data);
  };
};

TJP.event.remove = TJP.event.unlisten = function(target, type, handler) {
  if (type === undefined)
    delete events[target];
  else if (handler === undefined && events[target])
    delete events[target][type]
  else if (events[target] && events[target][type])
    delete events[target][type][handler.__guid];
};

TJP.event.oneTimer = function(target, type, handler) {
  TJP.event.add(target, type, function(target, type, data) {
    TJP.event.remove(target, type, arguments.callee);
    return handler(target, type, data);
  });
};

TJP.event.dispatch = function(target, type, data) {
  if (target["on" + type]) return target["on" + type](data);
};
<commit_msg>little documentation addition<commit_after>/*
events - a fast, flexible event system for javascript

------------------------------------------------------------------------
This file is loosely based on Dean Edwards's addEvent implementation and
modified to work with non-HTMLElement targets and custom event types
------------------------------------------------------------------------

event.add(targetObj, evName, handlerFunc)
event.listen(targetObj, evName, handlerFunc)
  add an event listener to targetObj. the listener function should take
  3 arguments, the event target, the event type, and a data object

event.oneTimer(targetObj, evName, handlerFunc)
  add an event listener to targetObj (like add/listen), but have it be
  automatically removed after the first call

event.remove(targetObj, [evName[, handlerFunc]])
event.unlisten(targetObj, evName, handlerFunc)
  undo an add

event.dispatch(targetObj, evName, data)
  trigger an event yourself. the 3 arguments will be passed straight to
  any active handler functions.
*/

/*global TJP*/
//context:browser
//context:console

TJP.event = TJP.event || {};

var fixEvent = function(target) {
  target.preventDefault = fixEvent.preventDefault;
  target.stopPropagation = fixEvent.stopPropagation;
  return target;
};
fixEvent.preventDefault = function() { this.returnValue = false; };
fixEvent.stopPropagation = function() { this.cancelBubble = true; };

var
  guid = 1,
  events = {},
  handleEvent = function(target, type, data) {
    var i, rv = true, hs, errors = [];
    try {
      target = target || fixEvent(((this.ownerDocument || this.document ||
              this).parentWindow || window).event || {type: type});
    } catch (err) { target = fixEvent({type: type}); }
    hs = (events[target] ? events[target][type] : {}) || {};
    for (i in hs) {
      this.handleEvent = hs[i];
      try {
        if (this.handleEvent(target, type, data) === false) rv = false;
      } catch(e) { errors.push(e); }
    }
    delete this.handleEvent;
    if (errors.length) throw errors[0];
    return rv;
  };

TJP.event.add = TJP.event.listen = function(target, type, handler) {
  if (!handler.__guid) handler.__guid = guid++;
  if (!events[target]) events[target] = {};
  var handlers = events[target][type];
  if (!handlers) {
    handlers = events[target][type] = {};
    if (target["on" + type]) handlers[0] = target["on" + type];
  }
  handlers[handler.__guid] = handler;
  target["on" + type] = function(data) {
    this.handleEvent = handleEvent;
    return this.handleEvent(target, type, data);
  };
};

TJP.event.remove = TJP.event.unlisten = function(target, type, handler) {
  if (type === undefined)
    delete events[target];
  else if (handler === undefined && events[target])
    delete events[target][type]
  else if (events[target] && events[target][type])
    delete events[target][type][handler.__guid];
};

TJP.event.oneTimer = function(target, type, handler) {
  TJP.event.add(target, type, function(target, type, data) {
    TJP.event.remove(target, type, arguments.callee);
    return handler(target, type, data);
  });
};

TJP.event.dispatch = function(target, type, data) {
  if (target["on" + type]) return target["on" + type](data);
};
<|endoftext|>


<commit_before>class Fader {
    /**
     * @constructor
     *
     * @param {string} element The selector for the container element.
     * @param {options} options The options for the instance.
     */
    constructor(element, options) {
        if(!options) options = {};

        /**
         * @prop {String|Number} width The width of the container.
         */
        this.width = options.width || 'full';

        /**
         * @prop {String|Number} height The height of the container.
         */
        this.height = options.height || 400;

        /**
         * @prop {HTMLElement} element The container element.
         */
        this.element = document.querySelector(element);

        /**
         * @prop {Number} wait The time in milliseconds to wait between transitions.
         */
        this.wait = options.wait || 3000;

        /**
         * @prop {Number} transitionTime The time the fade transition takes.
         */
        this.transitionTime = options.transitionTime || 500;

        /**
         * @prop {String} ease The ease to use for the transition. This supports any CSS3 ease the transition property supports.
         */
        this.ease = options.ease || 'ease-in-out';

        /**
         * @prop {Boolean} loop Should the slider loop when it reaches the last slide.
         */
        this.loop = options.loop || false;

        /**
         * @prop {Array} slides Array of slides inside the container.
         *
         * @private
         */
        this._slides = [];

        /**
         * @prop {Number} currentSlide The current active slide index.
         *
         * @private
         */
        this._currentSlide = 0;

        /**
         * @prop {Number|Null} previousSlide The last slide which was active before the current slide.
         *
         * @private
         */
        this._previousSlide = null;

        this._getSlides();
        this._resizeSlider();
        this._setupSlides();
        this._setupSlider();
        this._startTimer();
    }

    /**
     * Search the container element for all list items.
     *
     * @method _getSlides
     *
     * @private
     */
    _getSlides() {
        this._slides = this.element.querySelectorAll('li');
    }

    /**
     * Resize the slider to match the set width / height.
     *
     * @method _resizeSlider
     *
     * @private
     */
    _resizeSlider() {
        let width = this._calculateWidth();
        let height = this._calculateHeight();

        this.element.style.width = width;
        this.element.style.height = height;
    }

    /**
     * Setup the slides setting their base styles.
     *
     * @method _setupSlides
     *
     * @private
     */
    _setupSlides() {
        this._each(this._slides, (item, index) => {
            item.style.width = '100%';
            item.style.height = '100%';
            item.style.position = 'absolute';
            item.style.top = '0';
            item.style.bottom = '0';
            item.style.opacity = '0';
            item.style.transition = 'opacity ' + this.transitionTime + 'ms ' + this.ease;
        });
    }

    /**
     * Set the slider styles and activate the first slide.
     *
     * @method _setupSlider
     *
     * @private
     */
    _setupSlider() {
        this.element.style.position = 'relative';

        this.element.querySelector('ul').style.padding = '0';

        this._activateSlide(0);
    }

    /**
     * Active the slide at the given index, this fades the slide in.
     *
     * @param {Number} index The index of the slide to active.
     *
     * @method _activateSlide
     *
     * @private
     */
    _activateSlide(index) {
        let slide = this._slides[index];
        slide.className += ' active';
        slide.className = slide.className.trim();

        slide.style.opacity = '1';
    }

    /**
     * Deactive the slide at the given index, this fades the slide out.
     *
     * @param {Number} index The index of the slide to deactivate.
     *
     * @method _deactivateSlide
     *
     * @private
     */
    _deactivateSlide(index) {
        let slide = this._slides[index];
        slide.className = slide.className.replace('active', '').trim();

        slide.style.opacity = '0';
    }

    /**
     * Start the auto slide change timer.
     *
     * @method _startTimer
     *
     * @private
     */
    _startTimer() {
        this.timer = setInterval(this.nextSlide.bind(this), this.wait);
    }

    /**
     * Reset the timer.
     *
     * @method _resetTimer
     *
     * @private
     */
    _resetTimer() {
        clearInterval(this.timer);
        this._startTimer();
    }

    /**
     * Change the slide to the new index, fading out the old index.
     *
     * @param {Number} newSlide The index of the new slide to show.
     * @param {Number} oldSlide The index of the old slide to hide.
     *
     * @method _changeSlide
     *
     * @private
     */
    _changeSlide(newSlide, oldSlide) {
        this._activateSlide(newSlide);

        if(oldSlide !== null) {
            this._deactivateSlide(oldSlide);
        }
    }

    /**
     * Switch to the next available slide.
     *
     * @method nextSlide
     */
    nextSlide() {
        if(this._currentSlide < this._slides.length - 1) {
            this._previousSlide = this._currentSlide;
            this._currentSlide++;

            this._changeSlide(this._currentSlide, this._previousSlide);
        } else if(this.loop) {
            this._previousSlide = this._currentSlide;
            this._currentSlide = 0;

            this._changeSlide(this._currentSlide, null);
        }

        this._resetTimer();
    }

    /**
     * Switch the previous slide.
     *
     * @method prevSlide
     */
    prevSlide() {
        if(this._currentSlide > 0) {
            this._previousSlide = this._currentSlide;
            this._currentSlide--;
        } else if(this.loop) {
            this._previousSlide = this._currentSlide;
            this._currentSlide = this._slides.length - 1;
        }

        this._resetTimer();
        this._changeSlide(this._currentSlide, this._previousSlide);
    }

    /**
     * Set the height of the slider.
     *
     * @param {Number} height The new height in pixels.
     *
     * @method setHeight
     */
    setHeight(height) {
        this.height = height;

        this._resizeSlider();
    }

    /**
     * Set the width of the slider.
     *
     * @param {Number} width The new width in pixels.
     */
    setWidth(width) {
        this.width = width;

        this._resizeSlider();
    }

    /**
     * Calculate the width of the slider, taking into account special values
     *
     * @method _calculateWidth
     *
     * @private
     *
     * @returns {String} The calculated width.
     */
    _calculateWidth() {
        if(this.width === 'full') {
            return '100%';
        } else {
            return this.width + 'px';
        }
    }

    /**
     * Calculate the height of the slider, taking into account special values.
     *
     * @method _calculateHeight
     *
     * @private
     *
     * @returns {String} The calculated height.
     */
    _calculateHeight() {
        if(this.height === 'full') {
            return '100%';
        } else {
            return this.height + 'px';
        }
    }

    _each(object, callback) {
        for(let i = 0; i < object.length; i++) {
            callback(object[i], i);
        }
    }
}
<commit_msg>Fixes #4<commit_after>class Fader {
    /**
     * @constructor
     *
     * @param {string} element The selector for the container element.
     * @param {options} options The options for the instance.
     */
    constructor(element, options) {
        if(!options) options = {};

        /**
         * @prop {String|Number} width The width of the container.
         */
        this.width = options.width || 'full';

        /**
         * @prop {String|Number} height The height of the container.
         */
        this.height = options.height || 400;

        /**
         * @prop {HTMLElement} element The container element.
         */
        this.element = document.querySelector(element);

        /**
         * @prop {Number} wait The time in milliseconds to wait between transitions.
         */
        this.wait = options.wait || 3000;

        /**
         * @prop {Number} transitionTime The time the fade transition takes.
         */
        this.transitionTime = options.transitionTime || 500;

        /**
         * @prop {String} ease The ease to use for the transition. This supports any CSS3 ease the transition property supports.
         */
        this.ease = options.ease || 'ease-in-out';

        /**
         * @prop {Boolean} loop Should the slider loop when it reaches the last slide.
         */
        this.loop = options.loop || false;

        /**
         * @prop {Array} slides Array of slides inside the container.
         *
         * @private
         */
        this._slides = [];

        /**
         * @prop {Number} currentSlide The current active slide index.
         *
         * @private
         */
        this._currentSlide = 0;

        /**
         * @prop {Number|Null} previousSlide The last slide which was active before the current slide.
         *
         * @private
         */
        this._previousSlide = null;

        this._getSlides();
        this._resizeSlider();
        this._setupSlides();
        this._setupSlider();
        this._startTimer();
    }

    /**
     * Search the container element for all list items.
     *
     * @method _getSlides
     *
     * @private
     */
    _getSlides() {
        this._slides = this.element.querySelectorAll('li');
    }

    /**
     * Resize the slider to match the set width / height.
     *
     * @method _resizeSlider
     *
     * @private
     */
    _resizeSlider() {
        let width = this._calculateWidth();
        let height = this._calculateHeight();

        this.element.style.width = width;
        this.element.style.height = height;
    }

    /**
     * Setup the slides setting their base styles.
     *
     * @method _setupSlides
     *
     * @private
     */
    _setupSlides() {
        this._each(this._slides, (item, index) => {
            item.style.width = '100%';
            item.style.height = '100%';
            item.style.position = 'absolute';
            item.style.top = '0';
            item.style.bottom = '0';
            item.style.opacity = '0';
            item.style.transition = 'opacity ' + this.transitionTime + 'ms ' + this.ease;
        });
    }

    /**
     * Set the slider styles and activate the first slide.
     *
     * @method _setupSlider
     *
     * @private
     */
    _setupSlider() {
        this.element.style.position = 'relative';

        this.element.querySelector('ul').style.padding = '0';

        this._activateSlide(0);
    }

    /**
     * Active the slide at the given index, this fades the slide in.
     *
     * @param {Number} index The index of the slide to active.
     *
     * @method _activateSlide
     *
     * @private
     */
    _activateSlide(index) {
        let slide = this._slides[index];
        slide.className += ' active';
        slide.className = slide.className.trim();

        slide.style.opacity = '1';
    }

    /**
     * Deactive the slide at the given index, this fades the slide out.
     *
     * @param {Number} index The index of the slide to deactivate.
     *
     * @method _deactivateSlide
     *
     * @private
     */
    _deactivateSlide(index) {
        let slide = this._slides[index];
        slide.className = slide.className.replace('active', '').trim();

        slide.style.opacity = '0';
    }

    /**
     * Start the auto slide change timer.
     *
     * @method _startTimer
     *
     * @private
     */
    _startTimer() {
        this.timer = setInterval(this.nextSlide.bind(this), this.wait);
    }

    /**
     * Reset the timer.
     *
     * @method _resetTimer
     *
     * @private
     */
    _resetTimer() {
        clearInterval(this.timer);
        this._startTimer();
    }

    /**
     * Change the slide to the new index, fading out the old index.
     *
     * @param {Number} newSlide The index of the new slide to show.
     * @param {Number} oldSlide The index of the old slide to hide.
     *
     * @method _changeSlide
     *
     * @private
     */
    _changeSlide(newSlide, oldSlide) {
        this._activateSlide(newSlide);

        if(oldSlide !== null) {
            this._deactivateSlide(oldSlide);
        }
    }

    /**
     * Switch to the next available slide.
     *
     * @method nextSlide
     */
    nextSlide() {
        if(this._currentSlide < this._slides.length - 1) {
            this._previousSlide = this._currentSlide;
            this._currentSlide++;

            this._changeSlide(this._currentSlide, this._previousSlide);
        } else if(this.loop) {
            this._previousSlide = this._currentSlide;
            this._currentSlide = 0;

            this._changeSlide(this._currentSlide, this._previousSlide);
        }

        this._resetTimer();
    }

    /**
     * Switch the previous slide.
     *
     * @method prevSlide
     */
    prevSlide() {
        if(this._currentSlide > 0) {
            this._previousSlide = this._currentSlide;
            this._currentSlide--;
        } else if(this.loop) {
            this._previousSlide = this._currentSlide;
            this._currentSlide = this._slides.length - 1;
        }

        this._resetTimer();
        this._changeSlide(this._currentSlide, this._previousSlide);
    }

    /**
     * Set the height of the slider.
     *
     * @param {Number} height The new height in pixels.
     *
     * @method setHeight
     */
    setHeight(height) {
        this.height = height;

        this._resizeSlider();
    }

    /**
     * Set the width of the slider.
     *
     * @param {Number} width The new width in pixels.
     */
    setWidth(width) {
        this.width = width;

        this._resizeSlider();
    }

    /**
     * Calculate the width of the slider, taking into account special values
     *
     * @method _calculateWidth
     *
     * @private
     *
     * @returns {String} The calculated width.
     */
    _calculateWidth() {
        if(this.width === 'full') {
            return '100%';
        } else {
            return this.width + 'px';
        }
    }

    /**
     * Calculate the height of the slider, taking into account special values.
     *
     * @method _calculateHeight
     *
     * @private
     *
     * @returns {String} The calculated height.
     */
    _calculateHeight() {
        if(this.height === 'full') {
            return '100%';
        } else {
            return this.height + 'px';
        }
    }

    _each(object, callback) {
        for(let i = 0; i < object.length; i++) {
            callback(object[i], i);
        }
    }
}
<|endoftext|>