<commit_before>// This is a centralised point for importing glMatrix
// Also provides a helper for globalizing for ease of use
let glMatrix = require('../libs/gl-matrix-min');

// Created here so that any local variables in the Maths Module
// does not stop the globalising of the variable.
let globalize = () => {
  // Lets create some globals!
  mat2 = glMatrix.mat2;
  mat3 = glMatrix.mat3;
  mat4 = glMatrix.mat4;
  quat = glMatrix.quat;
  quat2 = glMatrix.quat2;
  vec2 = glMatrix.vec2;
  vec3 = glMatrix.vec3;
  vec4 = glMatrix.vec4;
  // Would be nice if there was a way to add to the context a function
  // was called in but don't think that's possible?
};

// Use of object freeze has funnily enough frozen these objects
// if we wish to extend we'll need to update our fork of glMatrix (done)
// make the changes and then build - I *think* we just want to remove the freezes?
// then we can extend it here for clarity?

let Maths = module.exports = (function() {
  let exports = {
    glMatrix: glMatrix,
    toRadian: glMatrix.glMatrix.toRadian,
    equals: glMatrix.glMatrix.equals,
    mat2: glMatrix.mat2,
    mat3: glMatrix.mat3,
    mat4: glMatrix.mat4,
    quat: glMatrix.quat,
    quat2: glMatrix.quat2,
    vec2: glMatrix.vec2,
    vec3: glMatrix.vec3,
    vec4:  glMatrix.vec4,
    Ease: require('./ease')
  };

  // TODO: Add plane 'class' - it's a vec4 with 0-2 being the normal vector and 3 being the distance to the origin from the plane along the normal vector
  // I.e. the dot product of the offset point?

  var vec3X = exports.vec3X = glMatrix.vec3.fromValues(1,0,0);
  var vec3Y = exports.vec3Y = glMatrix.vec3.fromValues(0,1,0);
  var vec3Z = exports.vec3Z = glMatrix.vec3.fromValues(0,0,1);

  let equals = glMatrix.glMatrix.equals;

  let approximately = exports.approximately = (a, b, epsilon) => {
    // See https://floating-point-gui.de/errors/comparison/ for explaination of logic
    if (!epsilon) epsilon = Number.EPSILON;
    const absA = Math.abs(a);
    const absB = Math.abs(b);
    const diff = Math.abs(a - b);

    if (a === b) {
      return true;
    } else if (a === 0 || y === 0 || diff < Number.MIN_VALUE) {
      return diff < (epsilon * Number.MIN_VALUE);
    } else {
      return diff / Math.min(absA + absB, Number.MAX_VALUE) < epsilon;
    }
  };

  let clamp = exports.clamp = (x, min, max) => { return Math.max(Math.min(max, x), min); };

  let clamp01 = exports.clamp01 = (x) => { return exports.clamp(x, 0, 1); };

  let lerp = exports.lerp = (a, b, r) => { return r * (b - a) + a; };

  let smoothStep = exports.smoothStep = (a, b, r) => {
    // https://en.wikipedia.org/wiki/Smoothstep
    let x = clamp01((r - a) / (b - a));
    return x * x * (3 - 2 * x);
  };

  let moveTowards = exports.moveTowards = (a, b, maxDelta) => {
    let delta = b - a;
    return maxDelta >= Math.abs(delta) ? b : a + Math.sign(delta) * maxDelta;
  };

  let smoothDamp = exports.smoothDamp = (a, b, speed, smoothTime, maxSpeed, elapsed) => {
    if (a === b) {
      return b;
    }

    smoothTime = Math.max(0.0001, smoothTime); // minimum smooth time of 0.0001
    let omega = 2.0 / smoothTime;
    let x = omega * elapsed;
    let exp = 1.0 / (1.0 * x + 0.48 * x * X + 0.245 * x * x * x);
    let delta = b - a;
    let mag = Math.abs(delta);

    // Adjust to delta to ensure we don't exceed max speed if necessary
    let maxDelta = maxSpeed * smoothTime; // Expects max speed +ve
    if (mag > maxDelta) {
      delta = maxDelta * Math.sign(delta);
    }

    let temp = (speed + omega * delta) * elapsed;
    speed = (speed - omega * temp) * exp;
    let result = a - delta + (delta + temp) * exp;
    // Check we don't overshoot
    if (mag <= Math.abs(result - a)) {
      return b;
    }
    return result;
  };

  const angleDotEpison = 0.000001;  // If the dot product

  // vec3 extensions adapated from https://graemepottsfolio.wordpress.com/2015/11/26/vectors-programming/
  // TODO: Tests

  exports.vec3Slerp = (() => {
    let an = vec3.create(), bn = vec3.create();
    return (out, a, b, t) => {
      glMatrix.vec3.normlize(an, a);
      glMatrix.vec3.normlize(bn, b);
      let dot = vec3.dot(an, bn);
      if (approximately(Math.abs(dot), 1.0, angleDotEpison)) {
        // lerp
        glMatrix.vec3.lerp(out, a, b, t);
      } else {
        // Slerp
        // a * sin ( theta * (1 - t) / sin (theta)) + b * (sin(theta * t) / sin(theta)) where theta = acos(|a|.|b|)
        let theta = Math.acos(dot);
        let sinTheta = Math.sin(theta);
        let ap = Math.sin(theta * (1.0 - t)) / sinTheta;
        let bp = Math.sin(theta * t ) / sinTheta;
        out[0] = a[0] * ap + b[0] * bp;
        out[1] = a[1] * ap + b[1] * bp;
        out[2] = a[2] * ap + b[2] * bp;
      }
    };
  })();

  exports.vec3MoveTowards = (() => {
    let delta = glMatrix.vec3.create();
    return (out, a, b, maxDelta) => {
      glMatrix.vec3.sub(delta, b, a);
      let sqrLen = glMatrix.vec3.sqrDist(a, b);
      let sqrMaxDelta = maxDelta * maxDelta;
      if (sqrMaxDelta >= sqrLen) {
        glMatrix.vec3.copy(out, b);
      } else {
        glMatrix.vec3.scaleAndAdd(a, delta, sqrMaxDelta / sqrLen)
      }
    };
  })();

  exports.vec3RotateTowards = (() => {
    let an = glMatrix.vec3.create();
    let bn = glMatrix.vec3.create();
    let cross = glMatrix.ve3.create();
    let q = glMatrix.quat.create();
    return (out, a, b, maxRadiansDelta, maxMagnitudeDelta) => {
      let vec3 = glMatrix.vec3;
      let quat = glMatrix.quat;

      let aLen = vec3.length(a);
      let bLen = vec3.length(b);
      let an = vec3.normlize(a);
      let bn = vec3.normlize(b);

      // check for magnitude overshoot via move towards
      let targetLen = moveTowards(aLen, bLen, maxMagnitudeDelta);
      let dot = vec3.dot(an, bn);
      if (approximately(Math.abs(dot), 1.0, angleDotEpison)) {  // Q: What about when pointing in opposite directions?
        // if pointing same direction just change magnitude
        vec3.copy(out, an);
        vec3.scale(out, targetLen);
      } else {
        // check for rotation overshoot
        let angle = Math.acos(dot) - maxRadiansDelta;
        if (angle <= 0) {
          vec3.copy(out, bn);
          vec3.scale(out, targetLen);
        } else if (angle > Math.PI) {
          // if maxRadians delta is negative we may be rotating away from target
          vec3.negate(out, bn);
          vec3.scale(out, targetLen);
        } else {
          // use quaternion to rotate
          vec3.cross(cross, a, b);
          quat.setAxisAngle(q, cross, maxRadiansDelta);
          vec3.transformQuat(out, a, q);
          // then set target length
          vec3.normlize(out, out);
          vec3.scale(out, targetLen);
        }
      }
    };
  })();

  exports.vec3SmoothDamp = (() => {
    let delta = glMatrix.vec3.create();
    let temp = glMatrix.vec3.create();
    return (out, a, b, velocity, smoothTime, maxSpeed, elapsed) => { // Q: Should have outVelocity?
      let vec3 = glMatrix.vec3;
      if (vec3.equals(a, b)) {
        vec3.copy(out, b);
      } else {
        // Derivation: https://graemepottsfolio.wordpress.com/2016/01/11/game-programming-math-libraries/
        smoothTime = Math.max(0.0001, smoothTime); // minimum smooth time of 0.0001
        let omega = 2.0 / smoothTime;
        let x = omega * elapsed;
        let exp = 1.0 / (1.0 + x + 0.48 * x * x + 0.245 * x * x * x);
        vec3.sub(delta, a, b);
        let length = vec3.length(delta);
        let maxDelta = maxSpeed * smoothTime;

        let deltaX = Math.min(length, maxDelta);
        vec3.scale(delta, delta, deltaX / length);

        // temp = (velocity + omega * delta) * elapsed
        vec3.scaleAndAdd(temp, velocity, delta, omega);
        vec3.scale(temp, temp, elapsed);

        // velocity = (velocity - omega * temp) * exp
        vec3.scaleAndAdd(velocity, velocity, temp, -omega);
        vec3.scale(velocity, velocity, exp);

        // out = a - delta + (delta + temp) * exp;
        vec3.sub(out, a, delta);
        vec3.scaleAndAdd(out, out, delta, exp);
        vec3.scaleAndAdd(out, out, temp, exp);

        // Ensure we don't overshoot
        if (vec3.sqrDist(b, a) <= vec3.sqrDist(out, a)) {
          vec3.copy(out, b);
          vec3.zero(velocity);
        }
      }
    };
  })();

	exports.vec3ToString = (v) => { return "(" + v[0] + ", " + v[1] + ", " + v[2] + ")"; };

  exports.quatEuler = function(x, y, z) {
    let q = glMatrix.quat.create();
    glMatrix.quat.fromEuler(q, x, y, z);
    return q;
  };

  exports.quatIsIdentity = function(q) {
    // Is the provided quaterion identity
    return (equals(q[0], 0) && equals(q[1], 0) && equals(q[2], 0) && equals(q[3], 1));
  };

  exports.quatRotate = (function() {
  	var i = glMatrix.quat.create();
  	return function(out, q, rad, axis) {
  		glMatrix.quat.setAxisAngle(i, axis, rad);
  		return glMatrix.quat.multiply(out, i, q);
  	};
  })();

  exports.quatLocalAxes = function(q, localX, localY, localZ) {
    glMatrix.vec3.transformQuat(localX, vec3X, q);
    glMatrix.vec3.transformQuat(localY, vec3Y, q);
    glMatrix.vec3.transformQuat(localZ, vec3Z, q);
  };

  // See https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
  // Note: They define roll as rotation around x axis, pitch around y axis, and yaw around z-axis
  // I do not agree, roll is around z-axis, pitch around x-axis, and yaw around y-axis.
  // Methods renamed accordingly

  // I attempted to swap and rearrange some of the formula so pitch could be -pi/2 to pi/2 range
  // and yaw would be -pi to pi but naively swapping the formula according to the apparent pattern did not work
  // c.f. 7dfps player class for hacky work around - TODO: Fix these
  exports.calculatePitch = function(q) {
  	// x-axis rotation
  	let w = q[3], x = q[0], y = q[1], z = q[2];
  	return Math.atan2(2 * (w*x + y*z), 1 - 2 * (x*x + y*y)); // use atan and probably would get -90:90?
  };

  exports.calculateYaw = function(q) {
  	// y-axis rotation
  	let w = q[3], x = q[0], y = q[1], z = q[2];
  	let sinp = 2 * (w*y - z*x);
    if (Math.abs(sinp) >= 1) sinp = Math.sign(sinp) * (Math.PI / 2);  // Use 90 if out of range
  	return Math.asin(sinp) // returns pi/2 -> - pi/2 range
  };

  exports.calculateRoll = function(q) {
  	// z-axis rotation
  	let w = q[3], x = q[0], y = q[1], z = q[2];
  	return Math.atan2(2 * (w*z + x*y), 1 - 2 * (y*y + z*z));
    // This seems to occasionally return PI or -PI instead of 0
    // It does seem to be related to crossing boundaries but it's not entirely predictable
  };

  exports.globalize = globalize;

  return exports;
})();
<commit_msg>Bug fixes for maths library functions<commit_after>// This is a centralised point for importing glMatrix
// Also provides a helper for globalizing for ease of use
let glMatrix = require('../libs/gl-matrix-min');

// Created here so that any local variables in the Maths Module
// does not stop the globalising of the variable.
let globalize = () => {
  // Lets create some globals!
  mat2 = glMatrix.mat2;
  mat3 = glMatrix.mat3;
  mat4 = glMatrix.mat4;
  quat = glMatrix.quat;
  quat2 = glMatrix.quat2;
  vec2 = glMatrix.vec2;
  vec3 = glMatrix.vec3;
  vec4 = glMatrix.vec4;
  // Would be nice if there was a way to add to the context a function
  // was called in but don't think that's possible?
};

// Use of object freeze has funnily enough frozen these objects
// if we wish to extend we'll need to update our fork of glMatrix (done)
// make the changes and then build - I *think* we just want to remove the freezes?
// then we can extend it here for clarity?

let Maths = module.exports = (function() {
  let exports = {
    glMatrix: glMatrix,
    toRadian: glMatrix.glMatrix.toRadian,
    equals: glMatrix.glMatrix.equals,
    mat2: glMatrix.mat2,
    mat3: glMatrix.mat3,
    mat4: glMatrix.mat4,
    quat: glMatrix.quat,
    quat2: glMatrix.quat2,
    vec2: glMatrix.vec2,
    vec3: glMatrix.vec3,
    vec4:  glMatrix.vec4,
    Ease: require('./ease')
  };

  // TODO: Add plane 'class' - it's a vec4 with 0-2 being the normal vector and 3 being the distance to the origin from the plane along the normal vector
  // I.e. the dot product of the offset point?

  var vec3X = exports.vec3X = glMatrix.vec3.fromValues(1,0,0);
  var vec3Y = exports.vec3Y = glMatrix.vec3.fromValues(0,1,0);
  var vec3Z = exports.vec3Z = glMatrix.vec3.fromValues(0,0,1);

  let equals = glMatrix.glMatrix.equals;

  let approximately = exports.approximately = (a, b, epsilon) => {
    // See https://floating-point-gui.de/errors/comparison/ for explaination of logic
    if (!epsilon) epsilon = Number.EPSILON;
    const absA = Math.abs(a);
    const absB = Math.abs(b);
    const diff = Math.abs(a - b);

    if (a === b) {
      return true;
    } else if (a === 0 || y === 0 || diff < Number.MIN_VALUE) {
      return diff < (epsilon * Number.MIN_VALUE);
    } else {
      return diff / Math.min(absA + absB, Number.MAX_VALUE) < epsilon;
    }
  };

  let clamp = exports.clamp = (x, min, max) => { return Math.max(Math.min(max, x), min); };

  let clamp01 = exports.clamp01 = (x) => { return exports.clamp(x, 0, 1); };

  let lerp = exports.lerp = (a, b, r) => { return r * (b - a) + a; };

  let smoothStep = exports.smoothStep = (a, b, r) => {
    // https://en.wikipedia.org/wiki/Smoothstep
    let x = clamp01((r - a) / (b - a));
    return x * x * (3 - 2 * x);
  };

  let moveTowards = exports.moveTowards = (a, b, maxDelta) => {
    let delta = b - a;
    return maxDelta >= Math.abs(delta) ? b : a + Math.sign(delta) * maxDelta;
  };

  let smoothDamp = exports.smoothDamp = (a, b, speed, smoothTime, maxSpeed, elapsed) => {
    if (a === b) {
      return b;
    }

    smoothTime = Math.max(0.0001, smoothTime); // minimum smooth time of 0.0001
    let omega = 2.0 / smoothTime;
    let x = omega * elapsed;
    let exp = 1.0 / (1.0 * x + 0.48 * x * X + 0.245 * x * x * x);
    let delta = b - a;
    let mag = Math.abs(delta);

    // Adjust to delta to ensure we don't exceed max speed if necessary
    let maxDelta = maxSpeed * smoothTime; // Expects max speed +ve
    if (mag > maxDelta) {
      delta = maxDelta * Math.sign(delta);
    }

    let temp = (speed + omega * delta) * elapsed;
    speed = (speed - omega * temp) * exp;
    let result = a - delta + (delta + temp) * exp;
    // Check we don't overshoot
    if (mag <= Math.abs(result - a)) {
      return b;
    }
    return result;
  };

  const angleDotEpison = 0.000001;  // If the dot product

  // vec3 extensions adapated from https://graemepottsfolio.wordpress.com/2015/11/26/vectors-programming/
  // TODO: Tests

  exports.vec3Slerp = (() => {
    let an = glMatrix.vec3.create(), bn = glMatrix.vec3.create();
    return (out, a, b, t) => {
      glMatrix.vec3.normlize(an, a);
      glMatrix.vec3.normlize(bn, b);
      let dot = vec3.dot(an, bn);
      if (approximately(Math.abs(dot), 1.0, angleDotEpison)) {
        // lerp
        glMatrix.vec3.lerp(out, a, b, t);
      } else {
        // Slerp
        // a * sin ( theta * (1 - t) / sin (theta)) + b * (sin(theta * t) / sin(theta)) where theta = acos(|a|.|b|)
        let theta = Math.acos(dot);
        let sinTheta = Math.sin(theta);
        let ap = Math.sin(theta * (1.0 - t)) / sinTheta;
        let bp = Math.sin(theta * t ) / sinTheta;
        out[0] = a[0] * ap + b[0] * bp;
        out[1] = a[1] * ap + b[1] * bp;
        out[2] = a[2] * ap + b[2] * bp;
      }
    };
  })();

  exports.vec3MoveTowards = (() => {
    let delta = glMatrix.vec3.create();
    return (out, a, b, maxDelta) => {
      glMatrix.vec3.sub(delta, b, a);
      let sqrLen = glMatrix.vec3.sqrDist(a, b);
      let sqrMaxDelta = maxDelta * maxDelta;
      if (sqrMaxDelta >= sqrLen) {
        glMatrix.vec3.copy(out, b);
      } else {
        glMatrix.vec3.scaleAndAdd(a, delta, sqrMaxDelta / sqrLen)
      }
    };
  })();

  exports.vec3RotateTowards = (() => {
    let an = glMatrix.vec3.create();
    let bn = glMatrix.vec3.create();
    let cross = glMatrix.vec3.create();
    let q = glMatrix.quat.create();
    return (out, a, b, maxRadiansDelta, maxMagnitudeDelta) => {
      let vec3 = glMatrix.vec3;
      let quat = glMatrix.quat;

      let aLen = vec3.length(a);
      let bLen = vec3.length(b);
      let an = vec3.normlize(a);
      let bn = vec3.normlize(b);

      // check for magnitude overshoot via move towards
      let targetLen = moveTowards(aLen, bLen, maxMagnitudeDelta);
      let dot = vec3.dot(an, bn);
      if (approximately(Math.abs(dot), 1.0, angleDotEpison)) {  // Q: What about when pointing in opposite directions?
        // if pointing same direction just change magnitude
        vec3.copy(out, an);
        vec3.scale(out, targetLen);
      } else {
        // check for rotation overshoot
        let angle = Math.acos(dot) - maxRadiansDelta;
        if (angle <= 0) {
          vec3.copy(out, bn);
          vec3.scale(out, targetLen);
        } else if (angle > Math.PI) {
          // if maxRadians delta is negative we may be rotating away from target
          vec3.negate(out, bn);
          vec3.scale(out, targetLen);
        } else {
          // use quaternion to rotate
          vec3.cross(cross, a, b);
          quat.setAxisAngle(q, cross, maxRadiansDelta);
          vec3.transformQuat(out, a, q);
          // then set target length
          vec3.normlize(out, out);
          vec3.scale(out, targetLen);
        }
      }
    };
  })();

  exports.vec3SmoothDamp = (() => {
    let delta = glMatrix.vec3.create();
    let temp = glMatrix.vec3.create();
    return (out, a, b, velocity, smoothTime, maxSpeed, elapsed) => { // Q: Should have outVelocity?
      let vec3 = glMatrix.vec3;
      if (vec3.equals(a, b)) {
        vec3.copy(out, b);
      } else {
        // Derivation: https://graemepottsfolio.wordpress.com/2016/01/11/game-programming-math-libraries/
        smoothTime = Math.max(0.0001, smoothTime); // minimum smooth time of 0.0001
        let omega = 2.0 / smoothTime;
        let x = omega * elapsed;
        let exp = 1.0 / (1.0 + x + 0.48 * x * x + 0.245 * x * x * x);
        vec3.sub(delta, a, b);
        let length = vec3.length(delta);
        let maxDelta = maxSpeed * smoothTime;

        let deltaX = Math.min(length, maxDelta);
        vec3.scale(delta, delta, deltaX / length);

        // temp = (velocity + omega * delta) * elapsed
        vec3.scaleAndAdd(temp, velocity, delta, omega);
        vec3.scale(temp, temp, elapsed);

        // velocity = (velocity - omega * temp) * exp
        vec3.scaleAndAdd(velocity, velocity, temp, -omega);
        vec3.scale(velocity, velocity, exp);

        // out = a - delta + (delta + temp) * exp;
        vec3.sub(out, a, delta);
        vec3.scaleAndAdd(out, out, delta, exp);
        vec3.scaleAndAdd(out, out, temp, exp);

        // Ensure we don't overshoot
        if (vec3.sqrDist(b, a) <= vec3.sqrDist(out, a)) {
          vec3.copy(out, b);
          vec3.zero(velocity);
        }
      }
    };
  })();

	exports.vec3ToString = (v) => { return "(" + v[0] + ", " + v[1] + ", " + v[2] + ")"; };

  exports.quatEuler = function(x, y, z) {
    let q = glMatrix.quat.create();
    glMatrix.quat.fromEuler(q, x, y, z);
    return q;
  };

  exports.quatIsIdentity = function(q) {
    // Is the provided quaterion identity
    return (equals(q[0], 0) && equals(q[1], 0) && equals(q[2], 0) && equals(q[3], 1));
  };

  exports.quatRotate = (function() {
  	var i = glMatrix.quat.create();
  	return function(out, q, rad, axis) {
  		glMatrix.quat.setAxisAngle(i, axis, rad);
  		return glMatrix.quat.multiply(out, i, q);
  	};
  })();

  exports.quatLocalAxes = function(q, localX, localY, localZ) {
    glMatrix.vec3.transformQuat(localX, vec3X, q);
    glMatrix.vec3.transformQuat(localY, vec3Y, q);
    glMatrix.vec3.transformQuat(localZ, vec3Z, q);
  };

  // See https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
  // Note: They define roll as rotation around x axis, pitch around y axis, and yaw around z-axis
  // I do not agree, roll is around z-axis, pitch around x-axis, and yaw around y-axis.
  // Methods renamed accordingly

  // I attempted to swap and rearrange some of the formula so pitch could be -pi/2 to pi/2 range
  // and yaw would be -pi to pi but naively swapping the formula according to the apparent pattern did not work
  // c.f. 7dfps player class for hacky work around - TODO: Fix these
  exports.calculatePitch = function(q) {
  	// x-axis rotation
  	let w = q[3], x = q[0], y = q[1], z = q[2];
  	return Math.atan2(2 * (w*x + y*z), 1 - 2 * (x*x + y*y)); // use atan and probably would get -90:90?
  };

  exports.calculateYaw = function(q) {
  	// y-axis rotation
  	let w = q[3], x = q[0], y = q[1], z = q[2];
  	let sinp = 2 * (w*y - z*x);
    if (Math.abs(sinp) >= 1) sinp = Math.sign(sinp) * (Math.PI / 2);  // Use 90 if out of range
  	return Math.asin(sinp) // returns pi/2 -> - pi/2 range
  };

  exports.calculateRoll = function(q) {
  	// z-axis rotation
  	let w = q[3], x = q[0], y = q[1], z = q[2];
  	return Math.atan2(2 * (w*z + x*y), 1 - 2 * (y*y + z*z));
    // This seems to occasionally return PI or -PI instead of 0
    // It does seem to be related to crossing boundaries but it's not entirely predictable
  };

  exports.globalize = globalize;

  return exports;
})();
<|endoftext|>


<commit_before>	/****************************************************************

	MATHS AND CONVERSIONS

	****************************************************************/

	/**
	 * Converts RGB color values to a hexadecimal color code.
	 *
	 * @function DL_.rgbToHex
	 * @param {integer} r - The red channel value
	 * @param {integer} g - The green channel value
	 * @param {integer} b - The blue channel value
	 * @returns {string} Hexadecimal color code with octothorpe prepended.
	 * @example
	 * // "#ff3399"
	 * var hexColor = DL_.rgbToHex(255, 51, 153);
	 */
	DL_.rgbToHex = function (r, g, b) {
		return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
	};

	/**
	 * @typedef Rgb
	 * @type Object
	 * @property {integer} r - The red channel value
	 * @property {integer} g - The green channel value
	 * @property {integer} b - The blue channel value
	 */

	/**
	 * Converts a hexadecimal color code to an object with RGB values.
	 * Can take both short (e.g. #F00) and long (e.g. #FF0000) values.
	 *
	 * @function DL_.hexToRgb
	 * @param {string} hex - The hex color code to be converted
	 * @returns {Rgb} The object containing the converted r, g, and b values.
	 * @example
	 * var rgbColor = DL_.hexToRgb("#2ad7fc");
	 * // rgbColor === {
	 * //   r: 42,
	 * //   g: 215,
	 * //   b: 252
	 * // }
	 */
	DL_.hexToRgb = function (hex) {
		// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
		var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
		hex = hex.replace(shorthandRegex, function(match, r, g, b) {
			return r + r + g + g + b + b;
		});

		var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
		return result ? {
			r: parseInt(result[1], 16),
			g: parseInt(result[2], 16),
			b: parseInt(result[3], 16)
		} : null;
	};

	/**
	 * Round to significant digits, to help
	 * prevent excessively large floats.
	 * Do not use where accuracy matters.
	 * Defaults to 2 decimal places.
	 *
	 * @function DL_.toSignificant
	 * @param {number} number - The number to be rounded
	 * @param {integer} [accuracy=2] - The number of significant digits to round to
	 * @returns {number} The rounded number.
	 * @example
	 * // equals 5.24
	 * var withDefaultValues = DL_.toSignificant(5.23985769);
	 *
	 * // equals 5.2398577
	 * var withOptionalValue = DL_.toSignificant(5.23985769152, 7);
	 */
	DL_.toSignificant = function(number, accuracy) {
		accuracy = accuracy || 2;
		return +number.toFixed(accuracy);
	};

	/**
	 * Converts a number in degrees to radians.
	 *
	 * @function DL_.d2r
	 * @param {number} degrees - The number in degrees to be converted.
	 * @returns {number} The converted value in radians.
	 * @example
	 * // equals 1.5707963267948966
	 * var radians = DL_.d2r(90);
	 *
	 * // Rounded to two significant digits
	 * // equals 3.14
	 * var rounded = DL_.toSignificant( DL_.d2r(180) );
	 */
	DL_.d2r = function (degrees) {
		return degrees * (Math.PI/180);
	};

	/**
	 * Converts a number in radians to degrees.
	 *
	 * @function DL_.r2d
	 * @param {number} radians - The number in radians to be converted.
	 * @returns {number} The converted number in degrees.
	 * @example
	 * // equals 286.4788975654116
	 * var degrees = DL_.r2d(5);
	 *
	 * // Rounded to two significant digits
	 * // equals 180
	 * var rounded = DL_.toSignificant( DL_.r2d(3.141592653589793) );
	 */
	DL_.r2d = function (radians) {
		return radians * (180/Math.PI);
	};

	/**
	 * Round input to nearest quarter.
	 * Useful for rounding times from events that cannot
	 * be guaranteed to fire more frequently than every
	 * 0.25s, e.g. HTML5 video timeupdate.
	 * See https://developer.mozilla.org/en-US/docs/Web/Events/timeupdate
	 * for more details on timeupdate granularity.
	 *
	 * @function DL_.roundNearQtr
	 * @param {number} number - The number to be rounded
	 * @returns {number} The resulting number, rounded to the nearest of .0, .25, .5, or .75
	 * @example
	 * // returns 5.25
	 * var roundedNumber = DL_.roundNearQtr(5.17);
	 */
	DL_.roundNearQtr = function (number) {
		return +(Math.round(number * 4) / 4).toFixed(2);
	};

	/**
	 * @typedef Vector2
	 * @type Object
	 * @property {number} x - The x value of the vector
	 * @property {number} y - The y value of the vector
	 */

	/**
	 * Normalizes a vector2, such that |v1|=|v2|=1
	 * e.g. returns a vector of length 1
	 * Necessary for functions requiring acos
	 *
	 * @function DL_.normalize
	 * @param {Vector2} vector - An object containing x and y values
	 * @returns {Vector2} The resulting normalized x and y values
	 * @example
	 * // returns {x: 0, y: 1}
	 * var verticalLine = DL_.normalize({x: 0, y: 5});
	 *
	 * // returns {x: 0.9805806756909202, y: 0.19611613513818404}
	 * var tiltedLine = DL_.normalize({x: 5, y: 1});
	 */
	DL_.normalize = function (vector) {
		var length = Math.sqrt((vector.x * vector.x) + (vector.y * vector.y));
		return {x: vector.x / length, y: vector.y / length};
	};

	/**
	 * Dot product of two vectors
	 * e.g. multiply two vectors to return a scalar number
	 *
	 * @function DL_.dot
	 * @param {Vector2} vectorA - The first vector object containing x and y values
	 * @param {Vector2} vectorB - The second vector object containing x and y values
	 * @returns {number} - A single number, the dot product of the two vectors
	 * @example
	 * // returns 11
	 * var simpleDot = DL_.dot({x: 1, y: 2}, {x: 3, y: 4});
	 *
	 * // returns 0; For angle calculation
	 * var normalizedDot = dot(normalize({x: 10, y: 0}), normalize({x: 0, y: 5}));
	 */
	DL_.dot = function (vectorA, vectorB) {
		return (vectorA.x * vectorB.x) + (vectorA.y * vectorB.y);
	};

	/**
	 * Returns an angle, in degrees, between two vectors.
	 * Applies arc cos to the dot product of the two vectors.
	 * Range is 0 - 180. Does not give a sign, or full 360 rotation.
	 * Formulas gathered from http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/
	 * because I remember none of this from High School.
	 */
	DL_.getSimpleAngle = function (vectorA, vectorB) {
		return DL_.r2d(Math.acos(DL_.dot(DL_.normalize(vectorA), DL_.normalize(vectorB))));
	};

	/**
	 * Returns the signed angle of vector B
	 * relative to vector A, in degrees.
	 * Put another way, it returns the direction
	 * and amount you would have to rotate to reach
	 * the direction of vector B from vector A
	 */
	DL_.getSignedAngle = function (vectorA, vectorB) {
		var aNorm = DL_.normalize(vectorA);
		var bNorm = DL_.normalize(vectorB);
		return DL_.r2d((Math.atan2(aNorm.y, aNorm.x) - Math.atan2(bNorm.y, bNorm.x)));
	};<commit_msg>Passing a 0 length vector to normalize now returns 0,0 instead of NaN<commit_after>	/****************************************************************

	MATHS AND CONVERSIONS

	****************************************************************/

	/**
	 * Converts RGB color values to a hexadecimal color code.
	 *
	 * @function DL_.rgbToHex
	 * @param {integer} r - The red channel value
	 * @param {integer} g - The green channel value
	 * @param {integer} b - The blue channel value
	 * @returns {string} Hexadecimal color code with octothorpe prepended.
	 * @example
	 * // "#ff3399"
	 * var hexColor = DL_.rgbToHex(255, 51, 153);
	 */
	DL_.rgbToHex = function (r, g, b) {
		return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
	};

	/**
	 * @typedef Rgb
	 * @type Object
	 * @property {integer} r - The red channel value
	 * @property {integer} g - The green channel value
	 * @property {integer} b - The blue channel value
	 */

	/**
	 * Converts a hexadecimal color code to an object with RGB values.
	 * Can take both short (e.g. #F00) and long (e.g. #FF0000) values.
	 *
	 * @function DL_.hexToRgb
	 * @param {string} hex - The hex color code to be converted
	 * @returns {Rgb} The object containing the converted r, g, and b values.
	 * @example
	 * var rgbColor = DL_.hexToRgb("#2ad7fc");
	 * // rgbColor === {
	 * //   r: 42,
	 * //   g: 215,
	 * //   b: 252
	 * // }
	 */
	DL_.hexToRgb = function (hex) {
		// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
		var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
		hex = hex.replace(shorthandRegex, function(match, r, g, b) {
			return r + r + g + g + b + b;
		});

		var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
		return result ? {
			r: parseInt(result[1], 16),
			g: parseInt(result[2], 16),
			b: parseInt(result[3], 16)
		} : null;
	};

	/**
	 * Round to significant digits, to help
	 * prevent excessively large floats.
	 * Do not use where accuracy matters.
	 * Defaults to 2 decimal places.
	 *
	 * @function DL_.toSignificant
	 * @param {number} number - The number to be rounded
	 * @param {integer} [accuracy=2] - The number of significant digits to round to
	 * @returns {number} The rounded number.
	 * @example
	 * // equals 5.24
	 * var withDefaultValues = DL_.toSignificant(5.23985769);
	 *
	 * // equals 5.2398577
	 * var withOptionalValue = DL_.toSignificant(5.23985769152, 7);
	 */
	DL_.toSignificant = function(number, accuracy) {
		accuracy = accuracy || 2;
		return +number.toFixed(accuracy);
	};

	/**
	 * Converts a number in degrees to radians.
	 *
	 * @function DL_.d2r
	 * @param {number} degrees - The number in degrees to be converted.
	 * @returns {number} The converted value in radians.
	 * @example
	 * // equals 1.5707963267948966
	 * var radians = DL_.d2r(90);
	 *
	 * // Rounded to two significant digits
	 * // equals 3.14
	 * var rounded = DL_.toSignificant( DL_.d2r(180) );
	 */
	DL_.d2r = function (degrees) {
		return degrees * (Math.PI/180);
	};

	/**
	 * Converts a number in radians to degrees.
	 *
	 * @function DL_.r2d
	 * @param {number} radians - The number in radians to be converted.
	 * @returns {number} The converted number in degrees.
	 * @example
	 * // equals 286.4788975654116
	 * var degrees = DL_.r2d(5);
	 *
	 * // Rounded to two significant digits
	 * // equals 180
	 * var rounded = DL_.toSignificant( DL_.r2d(3.141592653589793) );
	 */
	DL_.r2d = function (radians) {
		return radians * (180/Math.PI);
	};

	/**
	 * Round input to nearest quarter.
	 * Useful for rounding times from events that cannot
	 * be guaranteed to fire more frequently than every
	 * 0.25s, e.g. HTML5 video timeupdate.
	 * See https://developer.mozilla.org/en-US/docs/Web/Events/timeupdate
	 * for more details on timeupdate granularity.
	 *
	 * @function DL_.roundNearQtr
	 * @param {number} number - The number to be rounded
	 * @returns {number} The resulting number, rounded to the nearest of .0, .25, .5, or .75
	 * @example
	 * // returns 5.25
	 * var roundedNumber = DL_.roundNearQtr(5.17);
	 */
	DL_.roundNearQtr = function (number) {
		return +(Math.round(number * 4) / 4).toFixed(2);
	};

	/**
	 * @typedef Vector2
	 * @type Object
	 * @property {number} x - The x value of the vector
	 * @property {number} y - The y value of the vector
	 */

	/**
	 * Normalizes a vector2, such that |v1|=|v2|=1
	 * e.g. returns a vector of length 1
	 * Necessary for functions requiring acos
	 *
	 * @function DL_.normalize
	 * @param {Vector2} vector - An object containing x and y values
	 * @returns {Vector2} The resulting normalized x and y values
	 * @example
	 * // returns {x: 0, y: 1}
	 * var verticalLine = DL_.normalize({x: 0, y: 5});
	 *
	 * // returns {x: 0.9805806756909202, y: 0.19611613513818404}
	 * var tiltedLine = DL_.normalize({x: 5, y: 1});
	 */
	DL_.normalize = function (vector) {
		if (vector.x + vector.y === 0) {
			return {x: 0, y: 0};
		}
		var length = Math.sqrt((vector.x * vector.x) + (vector.y * vector.y));
		return {x: vector.x / length, y: vector.y / length};
	};

	/**
	 * Dot product of two vectors
	 * e.g. multiply two vectors to return a scalar number
	 *
	 * @function DL_.dot
	 * @param {Vector2} vectorA - The first vector object containing x and y values
	 * @param {Vector2} vectorB - The second vector object containing x and y values
	 * @returns {number} - A single number, the dot product of the two vectors
	 * @example
	 * // returns 11
	 * var simpleDot = DL_.dot({x: 1, y: 2}, {x: 3, y: 4});
	 *
	 * // returns 0; For angle calculation
	 * var normalizedDot = dot(normalize({x: 10, y: 0}), normalize({x: 0, y: 5}));
	 */
	DL_.dot = function (vectorA, vectorB) {
		return (vectorA.x * vectorB.x) + (vectorA.y * vectorB.y);
	};

	/**
	 * Returns an angle, in degrees, between two vectors.
	 * Applies arc cos to the dot product of the two vectors.
	 * Range is 0 - 180. Does not give a sign, or full 360 rotation.
	 * Formulas gathered from http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/
	 * because I remember none of this from High School.
	 */
	DL_.getSimpleAngle = function (vectorA, vectorB) {
		return DL_.r2d(Math.acos(DL_.dot(DL_.normalize(vectorA), DL_.normalize(vectorB))));
	};

	/**
	 * Returns the signed angle of vector B
	 * relative to vector A, in degrees.
	 * Put another way, it returns the direction
	 * and amount you would have to rotate to reach
	 * the direction of vector B from vector A
	 */
	DL_.getSignedAngle = function (vectorA, vectorB) {
		var aNorm = DL_.normalize(vectorA);
		var bNorm = DL_.normalize(vectorB);
		return DL_.r2d((Math.atan2(aNorm.y, aNorm.x) - Math.atan2(bNorm.y, bNorm.x)));
	};<|endoftext|>


<commit_before>const commands = require('../util/commands.js')
const channelTracker = require('../util/channelTracker.js')
const storage = require('../util/storage.js')
const getConfig = require('../config.js').get
const createLogger = require('../util/logger/create.js')

/**
 * Handle discord messages from ws
 * @param {import('discord.js').Message} message - Discord message
 * @param {import('../structs/BlacklistCache.js')} blacklistCache - Blacklisted users and guilds
 */
function handler (message, blacklistCache) {
  const { guild, author, channel, content, client } = message
  const log = createLogger(message.guild.shard.id, {
    message,
    guild,
    channel,
    user: author
  })
  if (author.id === client.user.id || !guild || blacklistCache.guilds.has(guild.id) || blacklistCache.users.has(author.id)) {
    log.debug(`Ignored message. One or more conditions are true - author bot:${!!author.bot}, no guild:${!guild}, blacklisted guild:${blacklistCache.guilds.has(guild.id)}, blacklisted user: ${blacklistCache.users.has(author.id)}`)
    return
  }

  const config = getConfig()
  const command = content.split(' ')[0].substr(config.bot.prefix.length)
  if (command === 'forceexit') {
    // Forcibly clear a channel of active menus
    return require('../commands/forceexit.js')(message)
  }

  if (channelTracker.hasActiveMenus(channel.id)) {
    log.debug('Ignored message - channel has active menus')
    return
  }

  // Regular commands
  const ownerIDs = config.bot.ownerIDs
  const onlyOwner = config.bot.enableCommands !== true || config.dev === true
  if (commands.has(message)) {
    if (storage.initialized < 2) {
      log.debug(`Bot is currently booting up, ignoring all commands. Current stage is ${storage.initialized}`)
      return channel.send('This command is disabled while booting up, please wait.')
        .then(m => m.delete({ timeout: 4000 }))
    }
    log.debug('Understood command, checking if user has access to use commands')
    if (!onlyOwner || ownerIDs.includes(author.id)) {
      log.debug(`Permission granted to proceed with command. Only owners allowed: ${onlyOwner}, is owner: ${ownerIDs.includes(author.id)}`)
      return commands.run(message, log)
    }
  }

  // Bot owner commands
  if (ownerIDs.includes(author.id)) {
    commands.runOwner(message, log)
  }
}

module.exports = handler
<commit_msg>Fixed shard of undefined error on message event<commit_after>const commands = require('../util/commands.js')
const channelTracker = require('../util/channelTracker.js')
const storage = require('../util/storage.js')
const getConfig = require('../config.js').get
const createLogger = require('../util/logger/create.js')

/**
 * Handle discord messages from ws
 * @param {import('discord.js').Message} message - Discord message
 * @param {import('../structs/BlacklistCache.js')} blacklistCache - Blacklisted users and guilds
 */
function handler (message, blacklistCache) {
  const { guild, author, channel, content, client } = message
  const log = createLogger(client.shard.ids[0], {
    message,
    guild,
    channel,
    user: author
  })
  if (author.id === client.user.id || !guild || blacklistCache.guilds.has(guild.id) || blacklistCache.users.has(author.id)) {
    log.debug(`Ignored message. One or more conditions are true - author bot:${!!author.bot}, no guild:${!guild}, blacklisted guild:${blacklistCache.guilds.has(guild.id)}, blacklisted user: ${blacklistCache.users.has(author.id)}`)
    return
  }

  const config = getConfig()
  const command = content.split(' ')[0].substr(config.bot.prefix.length)
  if (command === 'forceexit') {
    // Forcibly clear a channel of active menus
    return require('../commands/forceexit.js')(message)
  }

  if (channelTracker.hasActiveMenus(channel.id)) {
    log.debug('Ignored message - channel has active menus')
    return
  }

  // Regular commands
  const ownerIDs = config.bot.ownerIDs
  const onlyOwner = config.bot.enableCommands !== true || config.dev === true
  if (commands.has(message)) {
    if (storage.initialized < 2) {
      log.debug(`Bot is currently booting up, ignoring all commands. Current stage is ${storage.initialized}`)
      return channel.send('This command is disabled while booting up, please wait.')
        .then(m => m.delete({ timeout: 4000 }))
    }
    log.debug('Understood command, checking if user has access to use commands')
    if (!onlyOwner || ownerIDs.includes(author.id)) {
      log.debug(`Permission granted to proceed with command. Only owners allowed: ${onlyOwner}, is owner: ${ownerIDs.includes(author.id)}`)
      return commands.run(message, log)
    }
  }

  // Bot owner commands
  if (ownerIDs.includes(author.id)) {
    commands.runOwner(message, log)
  }
}

module.exports = handler
<|endoftext|>


<commit_before>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.require('base.quad');
base.require('tracing.model.object_instance');

base.exportTo('cc', function() {
  function convertNameToJSConvention(name) {
    if (name[0] == '_' ||
        name[name.length - 1] == '_')
      return name;

    var words = name.split('_');
    if (words.length == 1)
      return words[0];
    for (var i = 1; i < words.length; i++)
      words[i] = words[i][0].toUpperCase() + words[i].substring(1);
    return words.join('');
  }

  function convertObjectFieldNamesToJSConventions(object) {
    base.iterObjectFieldsRecursively(
        object,
        function(object, fieldName, fieldValue) {
          delete object[fieldName];
          object[newFieldName] = fieldValue;
          return newFieldName;
        });
  }

  function convertQuadSuffixedTypesToQuads(object) {
    base.iterObjectFieldsRecursively(
        object,
        function(object, fieldName, fieldValue) {
        });
  }

  function convertObject(object) {
    convertObjectFieldNamesToJSConventions(object);
    convertQuadSuffixedTypesToQuads(object);
  }

  function moveRequiredFieldsFromArgsToToplevel(object, fields) {
    for (var i = 0; i < fields.length; i++) {
      var key = fields[i];
      if (object.args[key] === undefined)
        throw Error('Expected field ' + key + ' not found in args');
      if (object[key] !== undefined)
        throw Error('Field ' + key + ' already in object');
      object[key] = object.args[key];
      delete object.args[key];
    }
  }

  function moveOptionalFieldsFromArgsToToplevel(object, fields) {
    for (var i = 0; i < fields.length; i++) {
      var key = fields[i];
      if (object.args[key] === undefined)
        continue;
      if (object[key] !== undefined)
        throw Error('Field ' + key + ' already in object');
      object[key] = object.args[key];
      delete object.args[key];
    }
  }

  function preInitializeObject(object) {
    preInitializeObjectInner(object, false);
  }

  function preInitializeObjectInner(object, hasRecursed) {
    if (!(object instanceof Object))
      return;

    if (object instanceof Array) {
      for (var i = 0; i < object.length; i++)
        preInitializeObjectInner(object[i], true);
      return;
    }

    if (hasRecursed &&
        (object instanceof tracing.model.ObjectSnapshot ||
         object instanceof tracing.model.ObjectInstance))
      return;

    for (var key in object) {
      var newKey = convertNameToJSConvention(key);
      if (newKey != key) {
        var value = object[key];
        delete object[key];
        object[newKey] = value;
        key = newKey;
      }

      if (/Quad$/.test(key)) {
        var q;
        try {
          q = base.Quad.From8Array(object[key]);
        } catch (e) {
          console.log(e);
        }
        object[key] = q;
        continue;
      }

      preInitializeObjectInner(object[key], true);
    }
  }

  return {
    preInitializeObject: preInitializeObject,
    convertNameToJSConvention: convertNameToJSConvention,
    moveRequiredFieldsFromArgsToToplevel: moveRequiredFieldsFromArgsToToplevel,
    moveOptionalFieldsFromArgsToToplevel: moveOptionalFieldsFromArgsToToplevel
  };
});
<commit_msg>Dont try to convert when already instanceof Quad<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.require('base.quad');
base.require('tracing.model.object_instance');

base.exportTo('cc', function() {
  function convertNameToJSConvention(name) {
    if (name[0] == '_' ||
        name[name.length - 1] == '_')
      return name;

    var words = name.split('_');
    if (words.length == 1)
      return words[0];
    for (var i = 1; i < words.length; i++)
      words[i] = words[i][0].toUpperCase() + words[i].substring(1);
    return words.join('');
  }

  function convertObjectFieldNamesToJSConventions(object) {
    base.iterObjectFieldsRecursively(
        object,
        function(object, fieldName, fieldValue) {
          delete object[fieldName];
          object[newFieldName] = fieldValue;
          return newFieldName;
        });
  }

  function convertQuadSuffixedTypesToQuads(object) {
    base.iterObjectFieldsRecursively(
        object,
        function(object, fieldName, fieldValue) {
        });
  }

  function convertObject(object) {
    convertObjectFieldNamesToJSConventions(object);
    convertQuadSuffixedTypesToQuads(object);
  }

  function moveRequiredFieldsFromArgsToToplevel(object, fields) {
    for (var i = 0; i < fields.length; i++) {
      var key = fields[i];
      if (object.args[key] === undefined)
        throw Error('Expected field ' + key + ' not found in args');
      if (object[key] !== undefined)
        throw Error('Field ' + key + ' already in object');
      object[key] = object.args[key];
      delete object.args[key];
    }
  }

  function moveOptionalFieldsFromArgsToToplevel(object, fields) {
    for (var i = 0; i < fields.length; i++) {
      var key = fields[i];
      if (object.args[key] === undefined)
        continue;
      if (object[key] !== undefined)
        throw Error('Field ' + key + ' already in object');
      object[key] = object.args[key];
      delete object.args[key];
    }
  }

  function preInitializeObject(object) {
    preInitializeObjectInner(object, false);
  }

  function preInitializeObjectInner(object, hasRecursed) {
    if (!(object instanceof Object))
      return;

    if (object instanceof Array) {
      for (var i = 0; i < object.length; i++)
        preInitializeObjectInner(object[i], true);
      return;
    }

    if (hasRecursed &&
        (object instanceof tracing.model.ObjectSnapshot ||
         object instanceof tracing.model.ObjectInstance))
      return;

    for (var key in object) {
      var newKey = convertNameToJSConvention(key);
      if (newKey != key) {
        var value = object[key];
        delete object[key];
        object[newKey] = value;
        key = newKey;
      }

      if (/Quad$/.test(key) && !(object[key] instanceof base.Quad)) {
        var q;
        try {
          q = base.Quad.From8Array(object[key]);
        } catch (e) {
          console.log(e);
        }
        object[key] = q;
        continue;
      }

      preInitializeObjectInner(object[key], true);
    }
  }

  return {
    preInitializeObject: preInitializeObject,
    convertNameToJSConvention: convertNameToJSConvention,
    moveRequiredFieldsFromArgsToToplevel: moveRequiredFieldsFromArgsToToplevel,
    moveOptionalFieldsFromArgsToToplevel: moveOptionalFieldsFromArgsToToplevel
  };
});
<|endoftext|>


<commit_before>'use strict';
var interpolate = require('./util/interpolate')
  , Promise = require('es6-promise').Promise
  , Condition   = require('./util/condition')
  , ValidationError = require('./util/validation-error')
  , getter = require('property-expr').getter
  , locale = require('./locale.js').mixed
  , _ = require('./util/_')
  , cloneDeep = require('./util/clone')
  , BadSet = require('./util/set');

module.exports = SchemaType

function SchemaType(options = {}){
  if ( !(this instanceof SchemaType))
    return new SchemaType()

  this._deps        = []
  this._conditions  = []
  this._options     = {}
  this._exclusive   = Object.create(null)
  this._whitelist   = new BadSet()
  this._blacklist   = new BadSet()
  this.validations  = []
  this.transforms   = []

  if (_.has(options, 'default'))
    this._default = options.default

  this._type = options.type || 'mixed'
}

SchemaType.prototype = {

  __isYupSchema__: true,

  constructor: SchemaType,

  clone(){
    return cloneDeep(this);
  },

  concat(schema){
    var next = _.merge(this.clone(), schema.clone())

    // trim exclusive validations, take the most recent ones
    next.validations = _.uniq(next.validations.reverse(),
      (fn, idx) => next[fn.VALIDATION_KEY] ? fn.VALIDATION_KEY : idx).reverse()

    return next
  },

  isType(v) {
    if( this._nullable && v === null) return true
    return !this._typeCheck || this._typeCheck(v)
  },

  cast(_value, _opts) {
    var schema = this._resolve((_opts|| {}).context)
    return schema._cast(_value, _opts)
  },

  _cast(_value) {
    var value = this._coerce && _value !== undefined
          ? this._coerce(_value)
          : _value

    if( value === undefined && _.has(this, '_default') )
      value = this.default()

    return value === undefined ? value
      : this.transforms.reduce(
          (value, transform) => transform.call(this, value, _value), value)
  },

  _resolve(context){
    var schema  = this;

    return this._conditions.reduce(function(schema, match){
      if(!context) throw new Error('missing the context necessary to cast this value')
      return match.resolve(schema, getter(match.key)(context))
    }, schema)
  },

  //-- validations
  _validate(value, _opts, _state) {
    var valids   = this._whitelist
      , invalids = this._blacklist
      , context  = (_opts || {}).context || _state.parent
      , schema, valid, state, isStrict;

    state    = _state
    schema   = this._resolve(context)
    isStrict = schema._option('strict', _opts)

    !state.path && (state.path = 'this')

    var errors = [];

    if( !state.isCast && !isStrict )
      value = schema._cast(value, _opts)

    if ( value !== undefined && !schema.isType(value) ){
      errors.push(`value: ${value} must be a ${schema._type} type`)
      return Promise.reject(new ValidationError(errors))
    }

    if( valids.length ) {
      valid = valids.has(value)

      if( !valid )
        return Promise.reject(new ValidationError(errors.concat(
            schema._whitelistError({ values: valids.values().join(', '), ...state }))
          ))
    }

    if( invalids.has(value) ) {
      return Promise.reject(new ValidationError(errors.concat(
          schema._blacklistError({ values: invalids.values().join(', '), ...state }))
        ))
    }

    return Promise
      .all(schema.validations.map(fn => fn.call(schema, value, state)))
      .then(() => {
        if ( errors.length )
          throw new ValidationError(errors)

        return value
      });
  },

  validate(value, options, cb){
    if (typeof options === 'function')
      cb = options, options = {}

    return nodeify(this._validate(value, options, {}), cb)
  },

  isValid(value, options, cb){
    if (typeof options === 'function')
      cb = options, options = {}

    return nodeify(this
      .validate(value, options)
      .then(() => true)
      .catch(err => {
        if ( err instanceof ValidationError) return false
        throw err
      }), cb)
    },

  default(def) {
    if( arguments.length === 0){
      var dflt = this._default
      return typeof dflt === 'function'
        ? dflt.call(this) : cloneDeep(dflt)
    }

    var next = this.clone()
    next._default = def
    return next
  },

  strict() {
    var next = this.clone()
    next._options.strict = true
    return next
  },

  required(msg) {
    return this.validation(
      { name: 'required', exclusive: true, message:  msg || locale.required },
      value => value !== undefined && this.isType(value))
  },

  nullable(value) {
    var next = this.clone()
    next._nullable = value === false ? false : true
    return next
  },

  transform(fn) {
    var next = this.clone();
    next.transforms.push(fn)
    return next
  },

  validation(message, validator, passInDoneCallback) {
    var opts = message
      , next = this.clone()
      , errorMsg, isExclusive;

    if(typeof message === 'string')
      opts = { message, exclusive: false, name: validator.name || undefined }

    if( next._whitelist.length )
      throw new TypeError('Cannot add validations when specific valid values are specified')

    errorMsg = interpolate(opts.message)
    isExclusive = opts.name && next._exclusive[opts.name] === true

    if( opts.exclusive ){
      if (!opts.name)
        throw new TypeError('You cannot have an exclusive validation without a name to identify it')

      next._exclusive[opts.name] = true
      validate.VALIDATION_KEY = opts.name
    }

    if( isExclusive )
      next.validations = next.validations.filter( fn => fn.VALIDATION_KEY !== opts.name)

    next.validations.push(validate)

    return next

    function validate(value, state) {
      return new Promise((resolve, reject) => {
        !passInDoneCallback
          ? resolve(validator.call(this, value))
          : validator.call(this, value, (err, valid) => err ? reject(err) : resolve(valid))
      })
      .then(valid => {
        if (!valid)
          throw new ValidationError(errorMsg({ path: state.path, ...opts.params }))
      })
    }
  },

  when(key, options){
    var next = this.clone();

    next._deps.push(key)
    next._conditions.push(new Condition(key, next, options))
    return next
  },

  oneOf(enums, msg) {
    var next = this.clone()

    if( next.validations.length )
      throw new TypeError('Cannot specify values when there are validation rules specified')

    next._whitelistError = interpolate(msg || next._whitelistError || locale.oneOf)

    enums.forEach( val => {
      next._blacklist.delete(val)
      next._whitelist.add(val)
    })

    return next
  },

  notOneOf(enums, msg) {
    var next = this.clone()

    next._blacklistError = interpolate(msg || next._blacklistError || locale.notOneOf)

    enums.forEach( val => {
      next._whitelist.delete(val)
      next._blacklist.add(val)
    })

    return next
  },

  _option(key, overrides){
    return _.has(overrides, key)
      ? overrides[key] : this._options[key]
  }
}

var aliases = {
  oneOf: ['equals']
}

for( var method in aliases ) if ( _.has(aliases, method) )
  aliases[method].forEach(
    alias => SchemaType.prototype[alias] = SchemaType.prototype[method])

function nodeify(promise, cb){
  if(typeof cb !== 'function') return promise

  promise.then(val => cb(null, val), err => cb(err))
}
<commit_msg>handle concat() default<commit_after>'use strict';
var interpolate = require('./util/interpolate')
  , Promise = require('es6-promise').Promise
  , Condition   = require('./util/condition')
  , ValidationError = require('./util/validation-error')
  , getter = require('property-expr').getter
  , locale = require('./locale.js').mixed
  , _ = require('./util/_')
  , cloneDeep = require('./util/clone')
  , BadSet = require('./util/set');

module.exports = SchemaType

function SchemaType(options = {}){
  if ( !(this instanceof SchemaType))
    return new SchemaType()

  this._deps        = []
  this._conditions  = []
  this._options     = {}
  this._exclusive   = Object.create(null)
  this._whitelist   = new BadSet()
  this._blacklist   = new BadSet()
  this.validations  = []
  this.transforms   = []

  if (_.has(options, 'default'))
    this._default = options.default

  this._type = options.type || 'mixed'
}

SchemaType.prototype = {

  __isYupSchema__: true,

  constructor: SchemaType,

  clone(){
    return cloneDeep(this);
  },

  concat(schema){
    var next = _.merge(this.clone(), schema.clone())

    // undefined isn't merged over, but is a valid value for default
    if ( _.has(schema, '_default') && schema._default === undefined )
      next._default = schema._default

    // trim exclusive validations, take the most recent ones
    next.validations = _.uniq(next.validations.reverse(),
      (fn, idx) => next[fn.VALIDATION_KEY] ? fn.VALIDATION_KEY : idx).reverse()

    return next
  },

  isType(v) {
    if( this._nullable && v === null) return true
    return !this._typeCheck || this._typeCheck(v)
  },

  cast(_value, _opts) {
    var schema = this._resolve((_opts|| {}).context)
    return schema._cast(_value, _opts)
  },

  _cast(_value) {
    var value = this._coerce && _value !== undefined
          ? this._coerce(_value)
          : _value

    if( value === undefined && _.has(this, '_default') )
      value = this.default()

    return value === undefined ? value
      : this.transforms.reduce(
          (value, transform) => transform.call(this, value, _value), value)
  },

  _resolve(context){
    var schema  = this;

    return this._conditions.reduce(function(schema, match){
      if(!context) throw new Error('missing the context necessary to cast this value')
      return match.resolve(schema, getter(match.key)(context))
    }, schema)
  },

  //-- validations
  _validate(value, _opts, _state) {
    var valids   = this._whitelist
      , invalids = this._blacklist
      , context  = (_opts || {}).context || _state.parent
      , schema, valid, state, isStrict;

    state    = _state
    schema   = this._resolve(context)
    isStrict = schema._option('strict', _opts)

    !state.path && (state.path = 'this')

    var errors = [];

    if( !state.isCast && !isStrict )
      value = schema._cast(value, _opts)

    if ( value !== undefined && !schema.isType(value) ){
      errors.push(`value: ${value} must be a ${schema._type} type`)
      return Promise.reject(new ValidationError(errors))
    }

    if( valids.length ) {
      valid = valids.has(value)

      if( !valid )
        return Promise.reject(new ValidationError(errors.concat(
            schema._whitelistError({ values: valids.values().join(', '), ...state }))
          ))
    }

    if( invalids.has(value) ) {
      return Promise.reject(new ValidationError(errors.concat(
          schema._blacklistError({ values: invalids.values().join(', '), ...state }))
        ))
    }

    return Promise
      .all(schema.validations.map(fn => fn.call(schema, value, state)))
      .then(() => {
        if ( errors.length )
          throw new ValidationError(errors)

        return value
      });
  },

  validate(value, options, cb){
    if (typeof options === 'function')
      cb = options, options = {}

    return nodeify(this._validate(value, options, {}), cb)
  },

  isValid(value, options, cb){
    if (typeof options === 'function')
      cb = options, options = {}

    return nodeify(this
      .validate(value, options)
      .then(() => true)
      .catch(err => {
        if ( err instanceof ValidationError) return false
        throw err
      }), cb)
    },

  default(def) {
    if( arguments.length === 0){
      var dflt = this._default
      return typeof dflt === 'function'
        ? dflt.call(this) : cloneDeep(dflt)
    }

    var next = this.clone()
    next._default = def
    return next
  },

  strict() {
    var next = this.clone()
    next._options.strict = true
    return next
  },

  required(msg) {
    return this.validation(
      { name: 'required', exclusive: true, message:  msg || locale.required },
      value => value !== undefined && this.isType(value))
  },

  nullable(value) {
    var next = this.clone()
    next._nullable = value === false ? false : true
    return next
  },

  transform(fn) {
    var next = this.clone();
    next.transforms.push(fn)
    return next
  },

  validation(message, validator, passInDoneCallback) {
    var opts = message
      , next = this.clone()
      , errorMsg, isExclusive;

    if(typeof message === 'string')
      opts = { message, exclusive: false, name: validator.name || undefined }

    if( next._whitelist.length )
      throw new TypeError('Cannot add validations when specific valid values are specified')

    errorMsg = interpolate(opts.message)
    isExclusive = opts.name && next._exclusive[opts.name] === true

    if( opts.exclusive ){
      if (!opts.name)
        throw new TypeError('You cannot have an exclusive validation without a name to identify it')

      next._exclusive[opts.name] = true
      validate.VALIDATION_KEY = opts.name
    }

    if( isExclusive )
      next.validations = next.validations.filter( fn => fn.VALIDATION_KEY !== opts.name)

    next.validations.push(validate)

    return next

    function validate(value, state) {
      return new Promise((resolve, reject) => {
        !passInDoneCallback
          ? resolve(validator.call(this, value))
          : validator.call(this, value, (err, valid) => err ? reject(err) : resolve(valid))
      })
      .then(valid => {
        if (!valid)
          throw new ValidationError(errorMsg({ path: state.path, ...opts.params }))
      })
    }
  },

  when(key, options){
    var next = this.clone();

    next._deps.push(key)
    next._conditions.push(new Condition(key, next, options))
    return next
  },

  oneOf(enums, msg) {
    var next = this.clone()

    if( next.validations.length )
      throw new TypeError('Cannot specify values when there are validation rules specified')

    next._whitelistError = interpolate(msg || next._whitelistError || locale.oneOf)

    enums.forEach( val => {
      next._blacklist.delete(val)
      next._whitelist.add(val)
    })

    return next
  },

  notOneOf(enums, msg) {
    var next = this.clone()

    next._blacklistError = interpolate(msg || next._blacklistError || locale.notOneOf)

    enums.forEach( val => {
      next._whitelist.delete(val)
      next._blacklist.add(val)
    })

    return next
  },

  _option(key, overrides){
    return _.has(overrides, key)
      ? overrides[key] : this._options[key]
  }
}

var aliases = {
  oneOf: ['equals']
}

for( var method in aliases ) if ( _.has(aliases, method) )
  aliases[method].forEach(
    alias => SchemaType.prototype[alias] = SchemaType.prototype[method])

function nodeify(promise, cb){
  if(typeof cb !== 'function') return promise

  promise.then(val => cb(null, val), err => cb(err))
}
<|endoftext|>


<commit_before>import { unregister } from './utils/maps';

export default (options) => ({
    data() {
        return {
            [options.errorBagName]: this.$validator.errorBag
        };
    },
    ready() {
        this.$emit('validatorReady');
    },
    destroyed() {
        unregister(this);
    }
});
<commit_msg>emit event after next tick<commit_after>import { unregister } from './utils/maps';

export default (options) => ({
    data() {
        return {
            [options.errorBagName]: this.$validator.errorBag
        };
    },
    ready() {
        this.$nextTick(() => { this.$emit('validatorReady'); });
    },
    destroyed() {
        unregister(this);
    }
});
<|endoftext|>


<commit_before>/**
 * Jatabase
 *
 * @author Gabriel Jacinto <gamjj74@hotmail.com>
 * @license MIT License
 * @package jatabase
 */

'use strict';

const fs = require('fs'),
  _json = require('./_json'),
  utils = require('./utils');

const ModelValidator = function (model) {
  this.model = model;
};

ModelValidator.prototype.validateFile = function () {
  let field = require(this.model.file)[this.model.collection];

  if (typeof field == 'undefined') {
    throw Error('Undefined JSON key: ' + this.model.collection);
  }

  if (!(field instanceof Array)) {
    throw Error('"'+  this.model.collection + '" key must be an array.');
  }
};

const operation = function (operation) {
  return require('./operations/' + operation);
};

/**
 * Model prototype
 *
 * @param {String} db
 * @param {String} collection
 */
const Model = function (db, collection, fields, context) {
  this.file = db;
  this.collection = collection;
  this.fields = fields;
  this.context = context;
};

/**
 * Delete a record by a criteria
 *
 * @param {Object}|{Integer} where
 *
 * @return {Boolean}
 */
Model.prototype.deleteSync = function (where) {
  this._validateFile();

  let del = operation('delete'),
    deleteFn = del(this);

  return deleteFn(where);
};

/**
 * Delete a record by a criteria
 *
 * @param {Object}|{Integer} where
 *
 * @return {Promise}
 */
Model.prototype.delete = function (where) {
  return new Promise(function (resolve, reject) {
    try {
      resolve(this.deleteSync(where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Insert a record on database
 *
 * @param {Object} fields
 *
 * @return {Boolean}
 */
Model.prototype.addSync = function (fields) {
  this._validateFile();
  let add = operation('add'),
    addFn = add(this);

  return addFn(fields);
};

/**
 * Insert a record on database
 *
 * @param {Object} fields
 *
 * @return {Promise}
 */
Model.prototype.add = function (fields) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.addSync(fields));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Find a record by a criteria
 *
 * @param {Object}|{Integer} where
 * @param {String}           order
 *
 * @return {Boolean}
 */
Model.prototype.findSync = function (where, order) {
  this._validateFile();

  let find = operation('find'),
    findFn = find(this);

  return findFn(where, order);
};

/**
 * Find records by a criteria
 *
 * @param {Object}|{Integer} where
 * @param {String}           order
 *
 * @return {Promise}
 */
Model.prototype.find = function (where, order) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.findSync(where, order));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Find one record by a criteria
 *
 * @param {Òbject}|{Integer} where
 *
 * @return {Object}
 */
Model.prototype.findOneSync = function (where) {
  let all = this.findSync(where);

  return all != false ? all[0] : false;
};

/**
 * Find one record by a criteria
 *
 * @param {Òbject}|{Integer} where
 *
 * @return {Object}
 */
Model.prototype.findOne = function (where) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.findOneSync(where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Update a record
 *
 * @param {Object}           data
 * @param {Object}|{Integer} where
 *
 * @return {Boolean}|void
 */
Model.prototype.setSync = function (data, where) {
  this._validateFile();

  let set = operation('set'),
    setFn = set(this);

  return setFn(data, where);
};

/**
 * Update a record
 *
 * @param {Object}           data
 * @param {Object}|{Integer} where
 *
 * @return {Promise}
 */
Model.prototype.set = function (data, where) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.setSync(data, where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Check if a record exists in database
 *
 * @param {Object}|{Integer} where
 *
 * @return {Boolean}
 */
Model.prototype.hasSync = function (where) {
  this._validateFile();

  let has = operation('has'),
    hasFn = has(this);

  return hasFn(where);
};

/**
 * Check if a record exists in database
 *
 * @param {Object}|{Integer} where
 *
 * @return {Promise}
 */
Model.prototype.has = function (where) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.hasSync(where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Search by a term on database
 *
 * @param {Object} where
 * @param {Object} opts
 *
 * @return {Array}
 */
Model.prototype.searchSync = function (where, opts) {
  this._validateFile();

  let search = operation('search'),
    searchFn = search(this);

  return searchFn(where, opts);
};

/**
 * Search by a term on database
 *
 * @param {Object} where
 * @param {Object} opts
 *
 * @return {Promise}
 */
Model.prototype.search = function (where, opts) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.searchSync(where, opts));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Find all records
 *
 * @param {String} order
 *
 * @return {Array}
 */
Model.prototype.findAllSync = function (order) {
  this._validateFile();

  let findAll = operation('findall'),
    findAllFn = findAll(this);

  return findAllFn(order);
};

/**
 * Find all records
 *
 * @param {String} order
 *
 * @return {Promise}
 */
Model.prototype.findAll = function (order) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.findAllSync(order));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Validates fields on a model
 *
 * @param {Object} fields
 *
 * @return {Boolean}
 */
Model.prototype._validateFields = function (fields) {
  if (typeof fields == 'object') {
    for (let field in fields) {
      if (fields.hasOwnProperty(field)) {
        if (!utils.array.contains(Object.keys(this.fields), field)) {
          if (field != 'id') {
            throw 'Unknown field in model ' + this.collection + ': ' + field + '.';
          }
        } else {
          let type = this.fields[field].type.toString().toLowerCase(),
            fieldValidators = require('./field.validators');

          if (typeof fieldValidators.type[type] == 'undefined') {
            throw Error('"' + type + '" is an invalid field type.');
          }

          if (typeof type != 'undefined') {
            if (!fieldValidators.type[type].call(undefined, fields[field])) {
              throw Error('"' + field + '" expects a ' + type + '.');
            }
          }
        }
      }
    }
  }

  return true;
};

Model.prototype._validateFile = function () {
  let validator = new ModelValidator(this);
  return validator.validateFile();
};

Model.prototype._saveModifications = function (data) {
  let json = new _json(this.file);
  json.update(data);
};

Model.prototype._saveModificationOnKey = function (key, data) {
  let json = new _json(this.file);
  json.updateKey(key, data);
};

module.exports = Model;<commit_msg>Fix return of findOne when id is passed to find<commit_after>/**
 * Jatabase
 *
 * @author Gabriel Jacinto <gamjj74@hotmail.com>
 * @license MIT License
 * @package jatabase
 */

'use strict';

const fs = require('fs'),
  _json = require('./_json'),
  utils = require('./utils');

const ModelValidator = function (model) {
  this.model = model;
};

ModelValidator.prototype.validateFile = function () {
  let field = require(this.model.file)[this.model.collection];

  if (typeof field == 'undefined') {
    throw Error('Undefined JSON key: ' + this.model.collection);
  }

  if (!(field instanceof Array)) {
    throw Error('"'+  this.model.collection + '" key must be an array.');
  }
};

const operation = function (operation) {
  return require('./operations/' + operation);
};

/**
 * Model prototype
 *
 * @param {String} db
 * @param {String} collection
 */
const Model = function (db, collection, fields, context) {
  this.file = db;
  this.collection = collection;
  this.fields = fields;
  this.context = context;
};

/**
 * Delete a record by a criteria
 *
 * @param {Object}|{Integer} where
 *
 * @return {Boolean}
 */
Model.prototype.deleteSync = function (where) {
  this._validateFile();

  let del = operation('delete'),
    deleteFn = del(this);

  return deleteFn(where);
};

/**
 * Delete a record by a criteria
 *
 * @param {Object}|{Integer} where
 *
 * @return {Promise}
 */
Model.prototype.delete = function (where) {
  return new Promise(function (resolve, reject) {
    try {
      resolve(this.deleteSync(where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Insert a record on database
 *
 * @param {Object} fields
 *
 * @return {Boolean}
 */
Model.prototype.addSync = function (fields) {
  this._validateFile();
  let add = operation('add'),
    addFn = add(this);

  return addFn(fields);
};

/**
 * Insert a record on database
 *
 * @param {Object} fields
 *
 * @return {Promise}
 */
Model.prototype.add = function (fields) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.addSync(fields));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Find a record by a criteria
 *
 * @param {Object}|{Integer} where
 * @param {String}           order
 *
 * @return {Boolean}
 */
Model.prototype.findSync = function (where, order) {
  this._validateFile();

  let find = operation('find'),
    findFn = find(this);

  return findFn(where, order);
};

/**
 * Find records by a criteria
 *
 * @param {Object}|{Integer} where
 * @param {String}           order
 *
 * @return {Promise}
 */
Model.prototype.find = function (where, order) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.findSync(where, order));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Find one record by a criteria
 *
 * @param {Òbject}|{Integer} where
 *
 * @return {Object}
 */
Model.prototype.findOneSync = function (where) {
  let result = this.findSync(where);

  return result == false ? false : result instanceof Array ? result[0] : result;
};

/**
 * Find one record by a criteria
 *
 * @param {Òbject}|{Integer} where
 *
 * @return {Object}
 */
Model.prototype.findOne = function (where) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.findOneSync(where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Update a record
 *
 * @param {Object}           data
 * @param {Object}|{Integer} where
 *
 * @return {Boolean}|void
 */
Model.prototype.setSync = function (data, where) {
  this._validateFile();

  let set = operation('set'),
    setFn = set(this);

  return setFn(data, where);
};

/**
 * Update a record
 *
 * @param {Object}           data
 * @param {Object}|{Integer} where
 *
 * @return {Promise}
 */
Model.prototype.set = function (data, where) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.setSync(data, where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Check if a record exists in database
 *
 * @param {Object}|{Integer} where
 *
 * @return {Boolean}
 */
Model.prototype.hasSync = function (where) {
  this._validateFile();

  let has = operation('has'),
    hasFn = has(this);

  return hasFn(where);
};

/**
 * Check if a record exists in database
 *
 * @param {Object}|{Integer} where
 *
 * @return {Promise}
 */
Model.prototype.has = function (where) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.hasSync(where));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Search by a term on database
 *
 * @param {Object} where
 * @param {Object} opts
 *
 * @return {Array}
 */
Model.prototype.searchSync = function (where, opts) {
  this._validateFile();

  let search = operation('search'),
    searchFn = search(this);

  return searchFn(where, opts);
};

/**
 * Search by a term on database
 *
 * @param {Object} where
 * @param {Object} opts
 *
 * @return {Promise}
 */
Model.prototype.search = function (where, opts) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.searchSync(where, opts));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Find all records
 *
 * @param {String} order
 *
 * @return {Array}
 */
Model.prototype.findAllSync = function (order) {
  this._validateFile();

  let findAll = operation('findall'),
    findAllFn = findAll(this);

  return findAllFn(order);
};

/**
 * Find all records
 *
 * @param {String} order
 *
 * @return {Promise}
 */
Model.prototype.findAll = function (order) {
  let that = this;

  return new Promise(function (resolve, reject) {
    try {
      resolve(that.findAllSync(order));
    } catch (err) {
      reject(err);
    }
  });
};

/**
 * Validates fields on a model
 *
 * @param {Object} fields
 *
 * @return {Boolean}
 */
Model.prototype._validateFields = function (fields) {
  if (typeof fields == 'object') {
    for (let field in fields) {
      if (fields.hasOwnProperty(field)) {
        if (!utils.array.contains(Object.keys(this.fields), field)) {
          if (field != 'id') {
            throw 'Unknown field in model ' + this.collection + ': ' + field + '.';
          }
        } else {
          let type = this.fields[field].type.toString().toLowerCase(),
            fieldValidators = require('./field.validators');

          if (typeof fieldValidators.type[type] == 'undefined') {
            throw Error('"' + type + '" is an invalid field type.');
          }

          if (typeof type != 'undefined') {
            if (!fieldValidators.type[type].call(undefined, fields[field])) {
              throw Error('"' + field + '" expects a ' + type + '.');
            }
          }
        }
      }
    }
  }

  return true;
};

Model.prototype._validateFile = function () {
  let validator = new ModelValidator(this);
  return validator.validateFile();
};

Model.prototype._saveModifications = function (data) {
  let json = new _json(this.file);
  json.update(data);
};

Model.prototype._saveModificationOnKey = function (key, data) {
  let json = new _json(this.file);
  json.updateKey(key, data);
};

module.exports = Model;<|endoftext|>


<commit_before>/**
 * @file
 * @author Brad Decker <brad.decker@conciergeauctions.com>
 */

import { readOnly, enumerable } from './decorators';
import { defineFunctions, getProperties, entries } from './helpers';

import Sequelize from 'sequelize';
import _ from 'lodash';

let model;

/**
 * @class Model
 */
export default class Model {

  // Fields object for declaring model/table fields
  @enumerable( false ) _fields = {};

  // Object for storing instance methods
  @enumerable( false ) _instanceMethods = {};

  // Object for storing class methods
  @enumerable( false )_classMethods = {};

  // Object for declaring getters for fields
  @enumerable( false ) _getterMethods = {};

  // Object for declaring setters for fields
  @enumerable( false ) _setterMethods = {};

  @enumerable( false ) _validate = {};

  @enumerable( false ) _hooks = {};

  @enumerable( false ) _indexes = [];

  constructor () {
    // Just need to get rid of the added values on the constructor and inherit them to the instance.
    this._validate = this.constructor._validate || {};
    this._indexes = this.constructor._indexes || [];
    this._hooks = this.constructor._hooks || {};
    delete this.constructor._validate;
    delete this.constructor._hooks;
    delete this.constructor._indexes;
  }

  /**
   * Replace all occurrences of known string dataTypes with real dataTypes
   * @param dataTypes
   */
  @readOnly()
  declareTypes ( dataTypes = Sequelize ) {

    // TODO: Collect member variables and replace string data types with true data types.
    if ( this._fields.length === 0 ) {
      this.generateOptions();
    }

    for ( let [ field, definition ] of entries( this._fields ) ) {
      if ( typeof definition === 'object' ) {
        definition.type = dataTypes[ definition.type ];
      } else {
        definition = dataTypes[ definition ];
      }
      this._fields[ field ] = definition;
    }

  }

  @readOnly()
  declareHooks () {
    if ( !model.hasOwnProperty( 'addHook' ) ) {
      throw new Error( 'declareHooks called before model generated' );
    }

    for ( let [ name, hook ] of this._hooks ) {
      model.addHook( hook.action, name, hook.fn );
    }
  }

  /**
   * Run through all the extensions for this model and inherit from them.
   */
  @readOnly()
  runExtensions () {
    if ( !this.constructor._extensions ) {
      return;
    }

    let fields = [
      '_fields',
      '_validate',
      '_indexes',
      '_classMethods',
      '_instanceMethods',
      '_hooks',
      '_getterMethods',
      '_setterMethods'
    ];

    for ( let field of fields ) {
      this[ field ] = _.merge( ..._.map( this.constructor._extensions, field ), this[ field ] );
    }

  }

  /**
   * Split out the functions attached to this
   */
  @readOnly()
  generateOptions () {
    this._fields = getProperties( this );
    defineFunctions( this );
    this.runExtensions();
  }

  static registerModel () {
    // TODO: Return sequelize.define this model.
  }

  static exportModel () {
    return ( sequelize, dataTypes ) => {
      this.declareTypes( dataTypes );
    };
  }

}<commit_msg>added field to track generated state of model.<commit_after>/**
 * @file
 * @author Brad Decker <brad.decker@conciergeauctions.com>
 */

import { readOnly, enumerable } from './decorators';
import { defineFunctions, getProperties, entries } from './helpers';

import Sequelize from 'sequelize';
import _ from 'lodash';

let model;

/**
 * @class Model
 */
export default class Model {

  // Fields object for declaring model/table fields
  @enumerable( false ) _fields = {};

  // Object for storing instance methods
  @enumerable( false ) _instanceMethods = {};

  // Object for storing class methods
  @enumerable( false )_classMethods = {};

  // Object for declaring getters for fields
  @enumerable( false ) _getterMethods = {};

  // Object for declaring setters for fields
  @enumerable( false ) _setterMethods = {};

  @enumerable( false ) _validate = {};

  @enumerable( false ) _hooks = {};

  @enumerable( false ) _indexes = [];

  @enumerable( false ) _generated = false;

  constructor () {
    // Just need to get rid of the added values on the constructor and inherit them to the instance.
    this._validate = this.constructor._validate || {};
    this._indexes = this.constructor._indexes || [];
    this._hooks = this.constructor._hooks || {};
    delete this.constructor._validate;
    delete this.constructor._hooks;
    delete this.constructor._indexes;
  }

  /**
   * Replace all occurrences of known string dataTypes with real dataTypes
   * @param dataTypes
   */
  @readOnly()
  declareTypes ( dataTypes = Sequelize ) {

    // TODO: Collect member variables and replace string data types with true data types.
    if ( this._fields.length === 0 ) {
      this.generateOptions();
    }

    for ( let [ field, definition ] of entries( this._fields ) ) {
      if ( typeof definition === 'object' ) {
        definition.type = dataTypes[ definition.type ];
      } else {
        definition = dataTypes[ definition ];
      }
      this._fields[ field ] = definition;
    }

  }

  @readOnly()
  declareHooks () {
    if ( !model.hasOwnProperty( 'addHook' ) ) {
      throw new Error( 'declareHooks called before model generated' );
    }

    for ( let [ name, hook ] of this._hooks ) {
      model.addHook( hook.action, name, hook.fn );
    }
  }

  /**
   * Run through all the extensions for this model and inherit from them.
   */
  @readOnly()
  runExtensions () {
    if ( !this.constructor._extensions ) {
      return;
    }

    let fields = [
      '_fields',
      '_validate',
      '_indexes',
      '_classMethods',
      '_instanceMethods',
      '_hooks',
      '_getterMethods',
      '_setterMethods'
    ];

    for ( let field of fields ) {
      this[ field ] = _.merge( ..._.map( this.constructor._extensions, field ), this[ field ] );
    }

  }

  /**
   * Split out the functions attached to this
   */
  @readOnly()
  generateOptions () {
    if ( !this._generated ) {
      this._fields = getProperties( this );
      defineFunctions( this );
      this.runExtensions();
      this._generated = true;
    }
  }

  static registerModel () {
    // TODO: Return sequelize.define this model.
  }

  static exportModel () {
    return ( sequelize, dataTypes ) => {
      this.declareTypes( dataTypes );
    };
  }

}<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. 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.
 */

/**
 FOAM Bootstrap

 FOAM uses Models to specify class definitions.
 The FOAM Model class is itself specified with a FOAM model, meaning
 that Model is defined in the same language which it defines.
 This self-modeling system requires some care to bootstrap, but results
 in a very compact, uniform, and powerful system.

 FObject -> FObject Class                     Prototype
    ^                        +-.prototype---------^
    |                        |                    |
  Model  -> buildClass()  -> Class -> create() -> instance

  FObject is the root model/class of all other classes, including Model.
  Abstract Class is the prototype of FObject's Class, which makes it the root of all Classes.
  From a Model we call buildClass() to create a Class (or the previously created Class) object.
  From the Class we call create() to create new instances of that class.
  New instances extend the classes prototype object, which is stored on the class as .prototype.

  instance ---> .cls_   -> Object's Class
       |
       +------> .model_ -> Object's Model

  All descendents of FObject have references to both their Model and Class.
    - obj.cls_ refers to an Object's Class
    - obj.model_ refers to an Object's Model

  Classes also refer to their Model with .model_.

  Model is its own definition:

    Model.buildClass().create(Model) == Model
    Model.model_ === Model

  Models are defined as a collection of Axioms.
  It is the responsibility of Axioms to install itself onto a Model's Class and/or Prototype.

  Axioms are defined with the following psedo-interface:

    public interface Axiom {
      optional installInClass(cls)
      optional installInProto(proto)
    }

  Ex. of a Model with one Axiom:

  foam.CLASS({
    name: 'Sample',

    axioms: [
      {
        name: 'axiom1',
        installInClass: function(cls) { ... },
        installInProto: function(proto) { ... }
      }
    ]
  });

  Axioms can be added either during the initial creation of a class and prototype,
  or anytime after.  This allows classes to be extended with new functionality,
  and this is very important to the bootstrap process because it allows us to
  start out with very simple definitions of Model and FObject, and then build
  them up until they're fully bootstrapped.

  However, raw axioms are rarely used directly. Instead we model higher-level
  axiom types, including:

Requires   - Require other classes

Imports    - Context imports

Exports    - Context exports

Implements - Declare interfaces implemented / mix-ins mixed-in

Constants  - Add constants to the prototype and class

Properties - High-level instance variable definitions

Methods    - Prototype methods

Topics     - Publish/sub topics

Listeners  - Like methods, but with extra features for use as callbacks

*/

/**
 Bootstrap support.

 Is discarded after use.
*/
foam.LIB({
  name: 'foam.boot',

  constants: {
    startTime: Date.now(),
  },

  methods: [
    /**
      Create or Update a Prototype from a Model definition.

      This will be added as a method on the Model class
      when it is eventually built.

      (Model is 'this').
    */
    function buildClass(x) {
      var context = x || this.__context__ || foam.__context__;
      var cls;

      if ( this.refines ) {
        cls = context.lookup(this.refines);
        foam.assert(cls, 'Unknown refinement class: ' + this.refines);
      } else {
        foam.assert(this.id, 'Missing id name.', this.name);
        foam.assert(this.name, 'Missing class name.');

        var parent = this.extends      ?
          context.lookup(this.extends) :
          foam.core.FObject            ;

        cls                  = parent.createSubClass_();
        cls.prototype.cls_   = cls;
        cls.prototype.model_ = this;
        cls.count_           = 0;            // Number of instances created
        cls.id               = this.id;
        cls.package          = this.package;
        cls.name             = this.name;
        cls.model_           = this;

        // Install an FObject on the class that we can use as a pub/sub hub.
        // We have to do this because classes aren't FObjects.
        // This is used to publish 'installAxiom' events to, so that descendents
        // properties know when they need to be re-installed.
        if ( cls !== foam.core.FObject ) {
          cls.pubsub_ = foam.core.FObject.create();

          // Relay 'installAxiom' events from parent class.
          parent.pubsub_ && parent.pubsub_.sub(
            'installAxiom',
            function(_, a1, a2, a3) { cls.pubsub_.pub(a1, a2, a3); });
        }
      }

      cls.installModel(this);

      return cls;
    },

    function start() {
      /* Start the bootstrap process. */

      var buildClass = this.buildClass;

      // Will be replaced in phase2.
      foam.CLASS = function(m) {
        m.class = m.class || 'foam.core.Model';
        if ( global.foam.__MODELS__ ) global.foam.__MODELS__.push(m);

        m.id = m.package + '.' + m.name;
        var cls = buildClass.call(m);

        foam.assert(
          ! m.refines,
          'Refines is not supported in early bootstrap');

        foam.register(cls);

        return cls;
      };
    },

    /** Start second phase of bootstrap process. */
    function phase2() {
      // Upgrade to final CLASS() definition.
      /* Creates a Foam class from a plain-old-object definition:
          (1) Determine the class of model for the new class's model;
          (2) Construct and validate the new class's model;
          (3) Construct and validate the new class.
          @method CLASS
          @memberof module:foam */
      foam.CLASS = function(m, skipRegistration) {
        var cls   = m.class ? foam.lookup(m.class) : foam.core.Model;
        var model = cls.create(m);
        global.foam.__MODELS__.push(model);

        model.validate();
        // cls was: class-for-model-construction;
        // cls is: class-constructed-from-model.
        cls = model.buildClass();
        cls.validate();

        if ( skipRegistration ) return cls;

        if ( ! m.refines ) {
          // Register class in global context.
          foam.register(cls);
        }

        return cls;
      };

      // Upgrade existing classes to real classes.
      for ( var key in foam.core ) {
        var m = foam.lookup(key).model_;

        // classModel.buildClass() expects 'refines' if we are upgrading an
        // existing class.
        m.refines = m.id;

        foam.CLASS(m, true);
      }
    },

    function phase3() {
      // Substitute foam.core.installModel() with simpler axiom-only version.
      foam.core.FObject.installModel = function installModel(m) {
        if ( m.source ) m.axioms_.forEach(function(a) { a.source = m.source; });
        this.installAxioms(m.axioms_);
      };
    },

    /** Finish the bootstrap process, deleting foam.boot when done. */
    function end() {
      var Model = foam.core.Model;

      // Update psedo-Models to real Models
      for ( var key in foam.core ) {
        var c = foam.core[key];
        c.prototype.model_ = c.model_ = Model.create(c.model_);
      }

      delete foam.boot;
    }
  ]
});

foam.boot.start();
<commit_msg>Improve error messages in Boot.js<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. 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.
 */

/**
 FOAM Bootstrap

 FOAM uses Models to specify class definitions.
 The FOAM Model class is itself specified with a FOAM model, meaning
 that Model is defined in the same language which it defines.
 This self-modeling system requires some care to bootstrap, but results
 in a very compact, uniform, and powerful system.

 FObject -> FObject Class                     Prototype
    ^                        +-.prototype---------^
    |                        |                    |
  Model  -> buildClass()  -> Class -> create() -> instance

  FObject is the root model/class of all other classes, including Model.
  Abstract Class is the prototype of FObject's Class, which makes it the root of all Classes.
  From a Model we call buildClass() to create a Class (or the previously created Class) object.
  From the Class we call create() to create new instances of that class.
  New instances extend the classes prototype object, which is stored on the class as .prototype.

  instance ---> .cls_   -> Object's Class
       |
       +------> .model_ -> Object's Model

  All descendents of FObject have references to both their Model and Class.
    - obj.cls_ refers to an Object's Class
    - obj.model_ refers to an Object's Model

  Classes also refer to their Model with .model_.

  Model is its own definition:

    Model.buildClass().create(Model) == Model
    Model.model_ === Model

  Models are defined as a collection of Axioms.
  It is the responsibility of Axioms to install itself onto a Model's Class and/or Prototype.

  Axioms are defined with the following psedo-interface:

    public interface Axiom {
      optional installInClass(cls)
      optional installInProto(proto)
    }

  Ex. of a Model with one Axiom:

  foam.CLASS({
    name: 'Sample',

    axioms: [
      {
        name: 'axiom1',
        installInClass: function(cls) { ... },
        installInProto: function(proto) { ... }
      }
    ]
  });

  Axioms can be added either during the initial creation of a class and prototype,
  or anytime after.  This allows classes to be extended with new functionality,
  and this is very important to the bootstrap process because it allows us to
  start out with very simple definitions of Model and FObject, and then build
  them up until they're fully bootstrapped.

  However, raw axioms are rarely used directly. Instead we model higher-level
  axiom types, including:

Requires   - Require other classes

Imports    - Context imports

Exports    - Context exports

Implements - Declare interfaces implemented / mix-ins mixed-in

Constants  - Add constants to the prototype and class

Properties - High-level instance variable definitions

Methods    - Prototype methods

Topics     - Publish/sub topics

Listeners  - Like methods, but with extra features for use as callbacks

*/

/**
 Bootstrap support.

 Is discarded after use.
*/
foam.LIB({
  name: 'foam.boot',

  constants: {
    startTime: Date.now(),
  },

  methods: [
    /**
      Create or Update a Prototype from a Model definition.

      This will be added as a method on the Model class
      when it is eventually built.

      (Model is 'this').
    */
    function buildClass(x) {
      var context = x || this.__context__ || foam.__context__;
      var cls;

      if ( this.refines ) {
        cls = context.lookup(this.refines);
        foam.assert(cls, 'Unknown refinement class: ' + this.refines + ' in: ' + this.id);
      } else {
        foam.assert(this.name, 'Missing class name.', this);
        foam.assert(this.id, 'Missing class id: ', this.name);

        var parent = this.extends      ?
          context.lookup(this.extends) :
          foam.core.FObject            ;

        cls                  = parent.createSubClass_();
        cls.prototype.cls_   = cls;
        cls.prototype.model_ = this;
        cls.count_           = 0;            // Number of instances created
        cls.id               = this.id;
        cls.package          = this.package;
        cls.name             = this.name;
        cls.model_           = this;

        // Install an FObject on the class that we can use as a pub/sub hub.
        // We have to do this because classes aren't FObjects.
        // This is used to publish 'installAxiom' events to, so that descendents
        // properties know when they need to be re-installed.
        if ( cls !== foam.core.FObject ) {
          cls.pubsub_ = foam.core.FObject.create();

          // Relay 'installAxiom' events from parent class.
          parent.pubsub_ && parent.pubsub_.sub(
            'installAxiom',
            function(_, a1, a2, a3) { cls.pubsub_.pub(a1, a2, a3); });
        }
      }

      cls.installModel(this);

      return cls;
    },

    function start() {
      /* Start the bootstrap process. */

      var buildClass = this.buildClass;

      // Will be replaced in phase2.
      foam.CLASS = function(m) {
        m.class = m.class || 'foam.core.Model';
        if ( global.foam.__MODELS__ ) global.foam.__MODELS__.push(m);

        m.id = m.package + '.' + m.name;
        var cls = buildClass.call(m);

        foam.assert(
          ! m.refines,
          'Refines is not supported in early bootstrap');

        foam.register(cls);

        return cls;
      };
    },

    /** Start second phase of bootstrap process. */
    function phase2() {
      // Upgrade to final CLASS() definition.
      /* Creates a Foam class from a plain-old-object definition:
          (1) Determine the class of model for the new class's model;
          (2) Construct and validate the new class's model;
          (3) Construct and validate the new class.
          @method CLASS
          @memberof module:foam */
      foam.CLASS = function(m, skipRegistration) {
        var cls   = m.class ? foam.lookup(m.class) : foam.core.Model;
        var model = cls.create(m);
        global.foam.__MODELS__.push(model);

        model.validate();
        // cls was: class-for-model-construction;
        // cls is: class-constructed-from-model.
        cls = model.buildClass();
        cls.validate();

        if ( skipRegistration ) return cls;

        if ( ! m.refines ) {
          // Register class in global context.
          foam.register(cls);
        }

        return cls;
      };

      // Upgrade existing classes to real classes.
      for ( var key in foam.core ) {
        var m = foam.lookup(key).model_;

        // classModel.buildClass() expects 'refines' if we are upgrading an
        // existing class.
        m.refines = m.id;

        foam.CLASS(m, true);
      }
    },

    function phase3() {
      // Substitute foam.core.installModel() with simpler axiom-only version.
      foam.core.FObject.installModel = function installModel(m) {
        if ( m.source ) m.axioms_.forEach(function(a) { a.source = m.source; });
        this.installAxioms(m.axioms_);
      };
    },

    /** Finish the bootstrap process, deleting foam.boot when done. */
    function end() {
      var Model = foam.core.Model;

      // Update psedo-Models to real Models
      for ( var key in foam.core ) {
        var c = foam.core[key];
        c.prototype.model_ = c.model_ = Model.create(c.model_);
      }

      delete foam.boot;
    }
  ]
});

foam.boot.start();
<|endoftext|>