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

/**
 * @fileoverview This event wrapper will dispatch an event when the user uses
 * the mouse wheel to scroll an element. You can get the direction by checking
 * the deltaX and deltaY properties of the event.
 *
 * This class aims to smooth out inconsistencies between browser platforms with
 * regards to mousewheel events, but we do not cover every possible
 * software/hardware combination out there, some of which occasionally produce
 * very large deltas in mousewheel events. If your application wants to guard
 * against extremely large deltas, use the setMaxDeltaX and setMaxDeltaY APIs
 * to set maximum values that make sense for your application.
 *
 * @author arv@google.com (Erik Arvidsson)
 * @see ../demos/mousewheelhandler.html
 */

goog.provide('goog.events.MouseWheelEvent');
goog.provide('goog.events.MouseWheelHandler');
goog.provide('goog.events.MouseWheelHandler.EventType');

goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.math');
goog.require('goog.style');
goog.require('goog.userAgent');

/**
 * This event handler allows you to catch mouse wheel events in a consistent
 * manner.
 * @param {Element|Document} element The element to listen to the mouse wheel
 *     event on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.events.MouseWheelHandler = function(element) {
  goog.events.EventTarget.call(this);

  /**
   * This is the element that we will listen to the real mouse wheel events on.
   * @type {Element|Document}
   * @private
   */
  this.element_ = element;

  var rtlElement = goog.dom.isElement(this.element_) ?
      /** @type {Element} */ (this.element_) :
      (this.element_ ? /** @type {Document} */ (this.element_).body : null);

  /**
   * True if the element exists and is RTL, false otherwise.
   * @type {boolean}
   * @private
   */
  this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);

  var type = goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';

  /**
   * The key returned from the goog.events.listen.
   * @type {?number}
   * @private
   */
  this.listenKey_ = goog.events.listen(this.element_, type, this);
};
goog.inherits(goog.events.MouseWheelHandler, goog.events.EventTarget);

/**
 * Enum type for the events fired by the mouse wheel handler.
 * @enum {string}
 */
goog.events.MouseWheelHandler.EventType = {
  MOUSEWHEEL: 'mousewheel'
};

/**
 * Optional maximum magnitude for x delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaX_;

/**
 * Optional maximum magnitude for y delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaY_;

/**
 * @param {number} maxDeltaX Maximum magnitude for x delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaX = function(maxDeltaX) {
  this.maxDeltaX_ = maxDeltaX;
};

/**
 * @param {number} maxDeltaY Maximum magnitude for y delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaY = function(maxDeltaY) {
  this.maxDeltaY_ = maxDeltaY;
};

/**
 * Handles the events on the element.
 * @param {goog.events.BrowserEvent} e The underlying browser event.
 */
goog.events.MouseWheelHandler.prototype.handleEvent = function(e) {
  var deltaX = 0;
  var deltaY = 0;
  var detail = 0;
  var be = e.getBrowserEvent();
  if (be.type == 'mousewheel') {
    var wheelDeltaScaleFactor = 1;
    if (goog.userAgent.IE ||
        goog.userAgent.WEBKIT &&
        (goog.userAgent.WINDOWS || goog.userAgent.isVersion('532.0'))) {
      // In IE we get a multiple of 120; we adjust to a multiple of 3 to
      // represent number of lines scrolled (like Gecko).
      // Newer versions of Webkit match IE behavior, and WebKit on
      // Windows also matches IE behavior.
      // See bug https://bugs.webkit.org/show_bug.cgi?id=24368
      wheelDeltaScaleFactor = 40;
    }

    detail = goog.events.MouseWheelHandler.smartScale_(
        -be.wheelDelta, wheelDeltaScaleFactor);
    if (goog.isDef(be.wheelDeltaX)) {
      // Webkit has two properties to indicate directional scroll, and
      // can scroll both directions at once.
      deltaX = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaX, wheelDeltaScaleFactor);
      deltaY = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaY, wheelDeltaScaleFactor);
    } else {
      deltaY = detail;
    }

    // Historical note: Opera (pre 9.5) used to negate the detail value.
  } else { // Gecko
    // Gecko returns multiple of 3 (representing the number of lines scrolled)
    detail = be.detail;

    // Gecko sometimes returns really big values if the user changes settings to
    // scroll a whole page per scroll
    if (detail > 100) {
      detail = 3;
    } else if (detail < -100) {
      detail = -3;
    }

    // Firefox 3.1 adds an axis field to the event to indicate direction of
    // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
    if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
      deltaX = detail;
    } else {
      deltaY = detail;
    }
  }

  if (goog.isNumber(this.maxDeltaX_)) {
    deltaX = goog.math.clamp(deltaX, -this.maxDeltaX_, this.maxDeltaX_);
  }
  if (goog.isNumber(this.maxDeltaY_)) {
    deltaY = goog.math.clamp(deltaY, -this.maxDeltaY_, this.maxDeltaY_);
  }
  // Don't clamp 'detail', since it could be ambiguous which axis it refers to
  // and because it's informally deprecated anyways.

  // For horizontal scrolling we need to flip the value for RTL grids.
  if (this.isRtl_) {
    deltaX = -deltaX;
  }
  var newEvent = new goog.events.MouseWheelEvent(detail, be, deltaX, deltaY);
  this.dispatchEvent(newEvent);
};

/**
 * Helper for scaling down a mousewheel delta by a scale factor, if appropriate.
 * @param {number} mouseWheelDelta Delta from a mouse wheel event. Expected to
 *     be an integer.
 * @param {number} scaleFactor Factor to scale the delta down by. Expected to
 *     be an integer.
 * @return {number} Scaled-down delta value, or the original delta if the
 *     scaleFactor does not appear to be applicable.
 * @private
 */
goog.events.MouseWheelHandler.smartScale_ = function(mouseWheelDelta,
    scaleFactor) {
  // The basic problem here is that in Webkit on Mac and Linux, we can get two
  // very different types of mousewheel events: from continuous devices
  // (touchpads, Mighty Mouse) or non-continuous devices (normal wheel mice).
  //
  // Non-continuous devices in Webkit get their wheel deltas scaled up to
  // behave like IE. Continuous devices return much smaller unscaled values
  // (which most of the time will not be cleanly divisible by the IE scale
  // factor), so we should not try to normalize them down.
  //
  // Detailed discussion:
  //   https://bugs.webkit.org/show_bug.cgi?id=29601
  //   http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
  if (goog.userAgent.WEBKIT &&
      (goog.userAgent.MAC || goog.userAgent.LINUX) &&
      (mouseWheelDelta % scaleFactor) != 0) {
    return mouseWheelDelta;
  } else {
    return mouseWheelDelta / scaleFactor;
  }
};

/** @override */
goog.events.MouseWheelHandler.prototype.disposeInternal = function() {
  goog.events.MouseWheelHandler.superClass_.disposeInternal.call(this);
  goog.events.unlistenByKey(this.listenKey_);
  delete this.listenKey_;
};

/**
 * A base class for mouse wheel events. This is used with the
 * MouseWheelHandler.
 *
 * @param {number} detail The number of rows the user scrolled.
 * @param {Event} browserEvent Browser event object.
 * @param {number} deltaX The number of rows the user scrolled in the X
 *     direction.
 * @param {number} deltaY The number of rows the user scrolled in the Y
 *     direction.
 * @constructor
 * @extends {goog.events.BrowserEvent}
 */
goog.events.MouseWheelEvent = function(detail, browserEvent, deltaX, deltaY) {
  goog.events.BrowserEvent.call(this, browserEvent);

  this.type = goog.events.MouseWheelHandler.EventType.MOUSEWHEEL;

  /**
   * The number of lines the user scrolled
   * @type {number}
   * NOTE: Informally deprecated. Use deltaX and deltaY instead, they provide
   * more information.
   */
  this.detail = detail;

  /**
   * The number of "lines" scrolled in the X direction.
   *
   * Note that not all browsers provide enough information to distinguish
   * horizontal and vertical scroll events, so for these unsupported browsers,
   * we will always have a deltaX of 0, even if the user scrolled their mouse
   * wheel or trackpad sideways.
   *
   * Currently supported browsers are Webkit and Firefox 3.1 or later.
   *
   * @type {number}
   */
  this.deltaX = deltaX;

  /**
   * The number of lines scrolled in the Y direction.
   * @type {number}
   */
  this.deltaY = deltaY;
};
goog.inherits(goog.events.MouseWheelEvent, goog.events.BrowserEvent);
<commit_msg><commit_after>// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview This event wrapper will dispatch an event when the user uses
 * the mouse wheel to scroll an element. You can get the direction by checking
 * the deltaX and deltaY properties of the event.
 *
 * This class aims to smooth out inconsistencies between browser platforms with
 * regards to mousewheel events, but we do not cover every possible
 * software/hardware combination out there, some of which occasionally produce
 * very large deltas in mousewheel events. If your application wants to guard
 * against extremely large deltas, use the setMaxDeltaX and setMaxDeltaY APIs
 * to set maximum values that make sense for your application.
 *
 * @author arv@google.com (Erik Arvidsson)
 * @see ../demos/mousewheelhandler.html
 */

goog.provide('goog.events.MouseWheelEvent');
goog.provide('goog.events.MouseWheelHandler');
goog.provide('goog.events.MouseWheelHandler.EventType');

goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.math');
goog.require('goog.style');
goog.require('goog.userAgent');

/**
 * This event handler allows you to catch mouse wheel events in a consistent
 * manner.
 * @param {Element|Document} element The element to listen to the mouse wheel
 *     event on.
 * @param {boolean=} opt_capture Whether to handle the mouse wheel event in
 *     capture phase.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
goog.events.MouseWheelHandler = function(element, opt_capture) {
  goog.events.EventTarget.call(this);

  /**
   * This is the element that we will listen to the real mouse wheel events on.
   * @type {Element|Document}
   * @private
   */
  this.element_ = element;

  var rtlElement = goog.dom.isElement(this.element_) ?
      /** @type {Element} */ (this.element_) :
      (this.element_ ? /** @type {Document} */ (this.element_).body : null);

  /**
   * True if the element exists and is RTL, false otherwise.
   * @type {boolean}
   * @private
   */
  this.isRtl_ = !!rtlElement && goog.style.isRightToLeft(rtlElement);

  var type = goog.userAgent.GECKO ? 'DOMMouseScroll' : 'mousewheel';

  /**
   * The key returned from the goog.events.listen.
   * @type {?number}
   * @private
   */
  this.listenKey_ = goog.events.listen(this.element_, type, this, opt_capture);
};
goog.inherits(goog.events.MouseWheelHandler, goog.events.EventTarget);

/**
 * Enum type for the events fired by the mouse wheel handler.
 * @enum {string}
 */
goog.events.MouseWheelHandler.EventType = {
  MOUSEWHEEL: 'mousewheel'
};

/**
 * Optional maximum magnitude for x delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaX_;

/**
 * Optional maximum magnitude for y delta on each mousewheel event.
 * @type {number|undefined}
 * @private
 */
goog.events.MouseWheelHandler.prototype.maxDeltaY_;

/**
 * @param {number} maxDeltaX Maximum magnitude for x delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaX = function(maxDeltaX) {
  this.maxDeltaX_ = maxDeltaX;
};

/**
 * @param {number} maxDeltaY Maximum magnitude for y delta on each mousewheel
 *     event. Should be non-negative.
 */
goog.events.MouseWheelHandler.prototype.setMaxDeltaY = function(maxDeltaY) {
  this.maxDeltaY_ = maxDeltaY;
};

/**
 * Handles the events on the element.
 * @param {goog.events.BrowserEvent} e The underlying browser event.
 */
goog.events.MouseWheelHandler.prototype.handleEvent = function(e) {
  var deltaX = 0;
  var deltaY = 0;
  var detail = 0;
  var be = e.getBrowserEvent();
  if (be.type == 'mousewheel') {
    var wheelDeltaScaleFactor = 1;
    if (goog.userAgent.IE ||
        goog.userAgent.WEBKIT &&
        (goog.userAgent.WINDOWS || goog.userAgent.isVersion('532.0'))) {
      // In IE we get a multiple of 120; we adjust to a multiple of 3 to
      // represent number of lines scrolled (like Gecko).
      // Newer versions of Webkit match IE behavior, and WebKit on
      // Windows also matches IE behavior.
      // See bug https://bugs.webkit.org/show_bug.cgi?id=24368
      wheelDeltaScaleFactor = 40;
    }

    detail = goog.events.MouseWheelHandler.smartScale_(
        -be.wheelDelta, wheelDeltaScaleFactor);
    if (goog.isDef(be.wheelDeltaX)) {
      // Webkit has two properties to indicate directional scroll, and
      // can scroll both directions at once.
      deltaX = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaX, wheelDeltaScaleFactor);
      deltaY = goog.events.MouseWheelHandler.smartScale_(
          -be.wheelDeltaY, wheelDeltaScaleFactor);
    } else {
      deltaY = detail;
    }

    // Historical note: Opera (pre 9.5) used to negate the detail value.
  } else { // Gecko
    // Gecko returns multiple of 3 (representing the number of lines scrolled)
    detail = be.detail;

    // Gecko sometimes returns really big values if the user changes settings to
    // scroll a whole page per scroll
    if (detail > 100) {
      detail = 3;
    } else if (detail < -100) {
      detail = -3;
    }

    // Firefox 3.1 adds an axis field to the event to indicate direction of
    // scroll.  See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
    if (goog.isDef(be.axis) && be.axis === be.HORIZONTAL_AXIS) {
      deltaX = detail;
    } else {
      deltaY = detail;
    }
  }

  if (goog.isNumber(this.maxDeltaX_)) {
    deltaX = goog.math.clamp(deltaX, -this.maxDeltaX_, this.maxDeltaX_);
  }
  if (goog.isNumber(this.maxDeltaY_)) {
    deltaY = goog.math.clamp(deltaY, -this.maxDeltaY_, this.maxDeltaY_);
  }
  // Don't clamp 'detail', since it could be ambiguous which axis it refers to
  // and because it's informally deprecated anyways.

  // For horizontal scrolling we need to flip the value for RTL grids.
  if (this.isRtl_) {
    deltaX = -deltaX;
  }
  var newEvent = new goog.events.MouseWheelEvent(detail, be, deltaX, deltaY);
  this.dispatchEvent(newEvent);
};

/**
 * Helper for scaling down a mousewheel delta by a scale factor, if appropriate.
 * @param {number} mouseWheelDelta Delta from a mouse wheel event. Expected to
 *     be an integer.
 * @param {number} scaleFactor Factor to scale the delta down by. Expected to
 *     be an integer.
 * @return {number} Scaled-down delta value, or the original delta if the
 *     scaleFactor does not appear to be applicable.
 * @private
 */
goog.events.MouseWheelHandler.smartScale_ = function(mouseWheelDelta,
    scaleFactor) {
  // The basic problem here is that in Webkit on Mac and Linux, we can get two
  // very different types of mousewheel events: from continuous devices
  // (touchpads, Mighty Mouse) or non-continuous devices (normal wheel mice).
  //
  // Non-continuous devices in Webkit get their wheel deltas scaled up to
  // behave like IE. Continuous devices return much smaller unscaled values
  // (which most of the time will not be cleanly divisible by the IE scale
  // factor), so we should not try to normalize them down.
  //
  // Detailed discussion:
  //   https://bugs.webkit.org/show_bug.cgi?id=29601
  //   http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
  if (goog.userAgent.WEBKIT &&
      (goog.userAgent.MAC || goog.userAgent.LINUX) &&
      (mouseWheelDelta % scaleFactor) != 0) {
    return mouseWheelDelta;
  } else {
    return mouseWheelDelta / scaleFactor;
  }
};

/** @override */
goog.events.MouseWheelHandler.prototype.disposeInternal = function() {
  goog.events.MouseWheelHandler.superClass_.disposeInternal.call(this);
  goog.events.unlistenByKey(this.listenKey_);
  delete this.listenKey_;
};

/**
 * A base class for mouse wheel events. This is used with the
 * MouseWheelHandler.
 *
 * @param {number} detail The number of rows the user scrolled.
 * @param {Event} browserEvent Browser event object.
 * @param {number} deltaX The number of rows the user scrolled in the X
 *     direction.
 * @param {number} deltaY The number of rows the user scrolled in the Y
 *     direction.
 * @constructor
 * @extends {goog.events.BrowserEvent}
 */
goog.events.MouseWheelEvent = function(detail, browserEvent, deltaX, deltaY) {
  goog.events.BrowserEvent.call(this, browserEvent);

  this.type = goog.events.MouseWheelHandler.EventType.MOUSEWHEEL;

  /**
   * The number of lines the user scrolled
   * @type {number}
   * NOTE: Informally deprecated. Use deltaX and deltaY instead, they provide
   * more information.
   */
  this.detail = detail;

  /**
   * The number of "lines" scrolled in the X direction.
   *
   * Note that not all browsers provide enough information to distinguish
   * horizontal and vertical scroll events, so for these unsupported browsers,
   * we will always have a deltaX of 0, even if the user scrolled their mouse
   * wheel or trackpad sideways.
   *
   * Currently supported browsers are Webkit and Firefox 3.1 or later.
   *
   * @type {number}
   */
  this.deltaX = deltaX;

  /**
   * The number of lines scrolled in the Y direction.
   * @type {number}
   */
  this.deltaY = deltaY;
};
goog.inherits(goog.events.MouseWheelEvent, goog.events.BrowserEvent);
<|endoftext|>


<commit_before>var vows = require("vows"),
    load = require("../load"),
    assert = require("../assert");

var suite = vows.describe("d3.random");

var STDDEV = 5;
var MEAN = 38;

/**
 * Testing a random number generator is a bit more complicated than testing
 * deterministic code, so we use a different methodology.
 *
 * If the RNG is correct, each test in this suite will pass with probability
 * at least P. The tests have been designed so that P is 98%+. If the tests
 * fail consistently, the RNG is broken. The value of P is given above each
 * test case.
 *
 * More on RNG testing here:
 * http://www.johndcook.com/Beautiful_Testing_ch10.pdf
 *
 * @author Daniel Goldbach
 */
suite.addBatch({
  "random": {
    topic: load("math/random").expression("d3.random"),
    "normal": {
      "topic": function(random) {
        return random.normal(MEAN, STDDEV);
      },

      // Mean of n normals is within µ ± 3σ/√n with P=99.7%.
      "has expected mean" : function(normalRNG) {
        var normals = [];
        for (var i=0 ; i<2500 ; i++) {
          normals.push(normalRNG());
        }
        assert.inDelta(d3_test_math_random_mean(normals), MEAN, 3 * STDDEV/50);
      },

      // Variance of n normals is within σ² ± 3*σ²√(2/(n-1)) with P=99.7%.
      "has expected variance" : function(normalRNG) {
        var normals = [];
        for (var i=0 ; i<2500 ; i++) {
          normals.push(normalRNG());
        }
        var sampleVar = d3_test_math_random_variance(normals);
        var radiusAroundMean = 3 * Math.pow(STDDEV, 2) * Math.sqrt(2 / 2499);
        assert.inDelta(sampleVar, Math.pow(STDDEV, 2), radiusAroundMean);
      },

      "has normal distribution" : KSTest(normalCDF(MEAN, STDDEV))
    },
    "logNormal": {
      "topic": function(random) {
        return random.logNormal(MEAN, STDDEV);
      },
      "has log-normal distribution" : KSTest(logNormalCDF(MEAN, STDDEV))
    },
    "irwinHall": {
      "topic": function(random) {
        return random.irwinHall(10);
      },
      "has Irwin-Hall distribution" : KSTest(irwinHallCDF(10))
    }
  }
});

/**
 * A macro that that takes a RNG and asserts that the values generated by the
 * RNG could be generated by a random variable with cumulative distribution
 * function `cdf'.
 *
 * Passes with P≈98%.
 */
function KSTest(cdf) {
  return function(rng) {
    var n = 1000, values = [];
    for (var i = 0; i < n; i++) {
      values.push(rng());
    }
    values.sort(function(a, b) { return a - b; });

    K_positive = -Infinity;  // Identity of max() function
    for (var i = 0; i < n; i++) {
      var edf_x = i/n, x = values[i];
      console.log(edf_x, cdf(x));
      K_positive = Math.max(K_positive, edf_x - cdf(x));
    }
    K_positive *= Math.sqrt(n);

    // Derivation of this interval is difficult.
    // @see K-S test in Knuth's AoCP vol.2
    assert.inDelta(K_positive, 0.723255, 0.794145);
  }
}

function normalCDF(mean, stddev) {
  // Logistic approximation to normal CDF around N(mean, stddev).
  return function(x) {
    return 1 / (1 + Math.exp(-0.07056 * Math.pow((x-mean)/stddev, 3) - 1.5976 * (x-mean)/stddev));
  }
}

function logNormalCDF(mean, stddev) {
  var normal = normalCDF(1, 1);
  return function(x) {
    return normal((Math.exp(x) - mean) / stddev);
  }
}

function irwinHallCDF(n) {
  var multiplier = 1 / factorial(n);

  // Precompute binom(n, k), k=0..n for efficiency. (<3 closures)
  var binoms = [];
  for (var k = 0; k <= n; k++) {
    binoms.push(binom(n, k));
  }

  return function(x) {
    var t = 0;
    x *= n;
    for (var k = 0; k < x; k++) {
      t += Math.pow(-1, k % 2) * binoms[k] * Math.pow(x - k, n);
    }
    return multiplier * t;
  }
}

function factorial(n) {
  var t = 1;
  for (var i = 2; i <= n; i++) {
    t *= i;
  }
  return t;
}

function binom(n, k) {
  if (k > n) return undefined;
  return factorial(n) / (factorial(k) * factorial(n - k));
}

function d3_test_math_random_mean(arr) {
  if (arr.length === 0) {
    return 0;
  }

  var mean = 0;
  for (var i = 0; i < arr.length; i++) {
    mean += arr[i];
  }
  return mean / arr.length;
}

/**
 * Sample variance implementation borrowed from science.js.
 */
function d3_test_math_random_variance(x) {
  var n = x.length;
  if (n < 1) return NaN;
  if (n === 1) return 0;
  var mean = d3_test_math_random_mean(x),
      i = -1,
      s = 0;
  while (++i < n) {
    var v = x[i] - mean;
    s += v * v;
  }
  return s / (n - 1);
};
suite.export(module);
<commit_msg>Remove console.logs and add more comments<commit_after>var vows = require("vows"),
    load = require("../load"),
    assert = require("../assert");

var suite = vows.describe("d3.random");

var STDDEV = 5;
var MEAN = 38;

/**
 * Testing a random number generator is a bit more complicated than testing
 * deterministic code, so we use a different methodology.
 *
 * If the RNG is correct, each test in this suite will pass with probability
 * at least P. The tests have been designed so that P is 98%+. If the tests
 * fail consistently, the RNG is broken. The value of P is given above each
 * test case.
 *
 * More on RNG testing here:
 * http://www.johndcook.com/Beautiful_Testing_ch10.pdf
 *
 * @author Daniel Goldbach
 */
suite.addBatch({
  "random": {
    topic: load("math/random").expression("d3.random"),
    "normal": {
      "topic": function(random) {
        return random.normal(MEAN, STDDEV);
      },

      // Mean of n normals is within µ ± 3σ/√n with P=99.7%.
      "has expected mean" : function(normalRNG) {
        var normals = [];
        for (var i=0 ; i<2500 ; i++) {
          normals.push(normalRNG());
        }
        assert.inDelta(d3_test_math_random_mean(normals), MEAN, 3 * STDDEV/50);
      },

      // Variance of n normals is within σ² ± 3*σ²√(2/(n-1)) with P=99.7%.
      "has expected variance" : function(normalRNG) {
        var normals = [];
        for (var i=0 ; i<2500 ; i++) {
          normals.push(normalRNG());
        }
        var sampleVar = d3_test_math_random_variance(normals);
        var radiusAroundMean = 3 * Math.pow(STDDEV, 2) * Math.sqrt(2 / 2499);
        assert.inDelta(sampleVar, Math.pow(STDDEV, 2), radiusAroundMean);
      },

      "has normal distribution" : KSTest(normalCDF(MEAN, STDDEV))
    },
    "logNormal": {
      "topic": function(random) {
        return random.logNormal(MEAN, STDDEV);
      },
      "has log-normal distribution" : KSTest(logNormalCDF(MEAN, STDDEV))
    },
    "irwinHall": {
      "topic": function(random) {
        return random.irwinHall(10);
      },
      "has Irwin-Hall distribution" : KSTest(irwinHallCDF(10))
    }
  }
});

/**
 * A macro that that takes a RNG and asserts that the values generated by the
 * RNG could be generated by a random variable with cumulative distribution
 * function `cdf'. `n' is the number of sample points to use. Higher n = better
 * evaluation, slower test.
 *
 * Passes with P≈98%.
 */
function KSTest(cdf, n) {
  return function(rng) {
    var n = 1000;
    var values = [];
    for (var i = 0; i < n; i++) {
      values.push(rng());
    }
    values.sort(function(a, b) { return a - b; });

    K_positive = -Infinity;  // Identity of max() function
    for (var i = 0; i < n; i++) {
      var edf_i = i / n;  // Empirical distribution function evaluated at x=values[i]
      K_positive = Math.max(K_positive, edf_i - cdf(values[i]));
    }
    K_positive *= Math.sqrt(n);

    // Derivation of this interval is difficult.
    // @see K-S test in Knuth's AoCP vol.2
    assert.inDelta(K_positive, 0.723255, 0.794145);
  }
}

function normalCDF(mean, stddev) {
  // Logistic approximation to normal CDF around N(mean, stddev).
  return function(x) {
    return 1 / (1 + Math.exp(-0.07056 * Math.pow((x-mean)/stddev, 3) - 1.5976 * (x-mean)/stddev));
  }
}

function logNormalCDF(mean, stddev) {
  var normal = normalCDF(1, 1);
  return function(x) {
    return normal((Math.exp(x) - mean) / stddev);
  }
}

function irwinHallCDF(n) {
  var multiplier = 1 / factorial(n);

  // Precompute binom(n, k), k=0..n for efficiency. (<3 closures)
  var binoms = [];
  for (var k = 0; k <= n; k++) {
    binoms.push(binom(n, k));
  }

  return function(x) {
    var t = 0;
    x *= n;
    for (var k = 0; k < x; k++) {
      t += Math.pow(-1, k % 2) * binoms[k] * Math.pow(x - k, n);
    }
    return multiplier * t;
  }
}

function factorial(n) {
  var t = 1;
  for (var i = 2; i <= n; i++) {
    t *= i;
  }
  return t;
}

function binom(n, k) {
  if (k > n) return undefined;
  return factorial(n) / (factorial(k) * factorial(n - k));
}

function d3_test_math_random_mean(arr) {
  if (arr.length === 0) {
    return 0;
  }

  var mean = 0;
  for (var i = 0; i < arr.length; i++) {
    mean += arr[i];
  }
  return mean / arr.length;
}

/**
 * Sample variance implementation borrowed from science.js.
 */
function d3_test_math_random_variance(x) {
  var n = x.length;
  if (n < 1) return NaN;
  if (n === 1) return 0;
  var mean = d3_test_math_random_mean(x),
      i = -1,
      s = 0;
  while (++i < n) {
    var v = x[i] - mean;
    s += v * v;
  }
  return s / (n - 1);
};
suite.export(module);
<|endoftext|>


<commit_before>"use strict";
(function(window) {
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$hooks = opts.hooks || {created: function() {}, mounted: function() {}, updated: function() {}, destroyed: function() {}};
        this.$methods = opts.methods || {};
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};
        this.$destroyed = false;

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        // Initialize 🎉
        this.init();
    }

    /**
    * Logs a Message
    * @param {String} msg
    */
    Moon.prototype.log = function(msg) {
      if(!config.silent) console.log(msg);
    }

    /**
    * Throws an Error
    * @param {String} msg
    */
    Moon.prototype.error = function(msg) {
      console.log("Moon ERR: " + msg);
    }

    /**
    * Creates an object to be used in a Virtual DOM
    * @param {String} type
    * @param {Array} children
    * @param {String} val
    * @param {Object} props
    * @param {Node} node
    * @return {Object} Object usable in Virtual DOM
    */
    Moon.prototype.createElement = function(type, children, val, props, node) {
      return {type: type, children: children, val: val, props: props, node: node};
    }

    /**
    * Create Elements Recursively For all Children
    * @param {Array} children
    * @return {Array} Array of elements usable in Virtual DOM
    */
    Moon.prototype.recursiveChildren = function(children) {
      var recursiveChildrenArr = [];
      for(var i = 0; i < children.length; i++) {
        var child = children[i];
        recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
      }
      return recursiveChildrenArr;
    }

    /**
    * Creates Virtual DOM
    * @param {Node} node
    */
    Moon.prototype.createVirtualDOM = function(node) {
      var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
      this.$dom = vdom;
    }

    /**
    * Sets Value in Data
    * @param {String} key
    * @param {String} val
    */
    Moon.prototype.set = function(key, val) {
      this.$data[key] = val;
      if(!this.$destroyed) this.build(this.$dom.children);
      if(this.$hooks.updated) {
        this.$hooks.updated();
      }
    }

    /**
    * Gets Value in Data
    * @param {String} key
    * @return {String} Value of key in data
    */
    Moon.prototype.get = function(key) {
      return this.$data[key];
    }

    /**
    * Calls a method
    * @param {String} method
    */
    Moon.prototype.method = function(method) {
      this.$methods[method]();
    }

    /**
    * Destroys Moon Instance
    */
    Moon.prototype.destroy = function() {
      Object.defineProperty(this, '$data', {
        set: function(value) {
          _data = value;
        }
      });
      this.$destroyed = true;
      if(this.$hooks.destroyed) this.$hooks.destroyed();
    }

    /**
    * Builds the DOM With Data
    * @param {Array} children
    */
    Moon.prototype.build = function(children) {
      for(var i = 0; i < children.length; i++) {
        var el = children[i];

        if(el.type === "#text") {
          el.node.textContent = compileTemplate(el.val, this.$data);
        } else if(el.props) {
          for(var prop in el.props) {
            var propVal = el.props[prop];
            var compiledProperty = compileTemplate(propVal, this.$data);
            var directive = directives[prop];
            if(directive) {
              el.node.removeAttribute(prop);
              directive(el.node, compiledProperty, el);
            }

            if(!directive) el.node.setAttribute(prop, compiledProperty);
          }
        }

        this.build(el.children);
      }
    }

    /**
    * Initializes Moon
    */
    Moon.prototype.init = function() {
      this.log("======= Moon =======");
      if(this.$hooks.created) {
        this.$hooks.created();
      }
      this.createVirtualDOM(this.$el);
      this.build(this.$dom.children);
      if(this.$hooks.mounted) {
        this.$hooks.mounted();
      }
    }

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, opts) {
      components[name] = opts;
    }

    /**
    * Creates Subclass of Moon
    * @param {Object} opts
    */
    Moon.extend = function(opts) {
      var Parent = this;
      function MoonComponent() {
        Moon.call(this, opts);
      }
      MoonComponent.prototype = Object.create(Parent.prototype);
      MoonComponent.prototype.constructor = MoonComponent;
      return MoonComponent;
    }

    window.Moon = Moon;
})(window);
<commit_msg>configuration<commit_after>"use strict";
(function(window) {
    var config = {
      silent: false
    }
    var directives = {};
    var components = {};

    /**
    * Converts attributes into key-value pairs
    * @param {Node} node
    * @return {Object} Key-Value pairs of Attributes
    */
    var extractAttrs = function(node) {
      var attrs = {};
      if(!node.attributes) return attrs;
      var rawAttrs = node.attributes;
      for(var i = 0; i < rawAttrs.length; i++) {
        attrs[rawAttrs[i].name] = rawAttrs[i].value
      }

      return attrs;
    }

    /**
    * Compiles a template with given data
    * @param {String} template
    * @param {Object} data
    * @return {String} Template with data rendered
    */
    var compileTemplate = function(template, data) {
      var code = template,
          re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
      code.replace(re, function(match, p) {
        code = code.replace(match, "` + data." + p + " + `");
      });
      var compile = new Function("data", "var out = `" + code + "`; return out");
      var output = compile(data);
      return output;
    }

    /**
    * Gets Root Element
    * @param {String} html
    * @return {Node} Root Element
    */
    var getRootElement = function(html) {
      var dummy = document.createElement('div');
      dummy.innerHTML = html;
      return dummy.firstChild;
    }

    /**
    * Merges two Objects
    * @param {Object} obj
    * @param {Object} obj2
    * @return {Object} Merged Objects
    */
    function merge(obj, obj2) {
      for (var key in obj2) {
        if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
      }
      return obj;
    }

    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var self = this;
        this.$el = document.querySelector(_el);
        this.$hooks = opts.hooks || {created: function() {}, mounted: function() {}, updated: function() {}, destroyed: function() {}};
        this.$methods = opts.methods || {};
        this.$components = merge(opts.components || {}, components);
        this.$dom = {type: this.$el.nodeName, children: [], node: this.$el};
        this.$destroyed = false;

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.$dom.children);
            },
            configurable: true
        });

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-show"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.style.display = 'none';
          } else {
            el.style.display = 'block';
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        directives["m-model"] = function(el, val, vdom) {
          el.value = self.get(val);
          el.addEventListener("input", function() {
            self.set(val, el.value);
          });
          el.removeAttribute("m-model");
          delete vdom.props["m-model"];
        }

        directives["m-once"] = function(el, val, vdom) {
          vdom.val = el.textContent;
          for(var child in vdom.children) {
            vdom.children[child].val = compileTemplate(vdom.children[child].val, self.$data);
          }
        }

        directives["m-text"] = function(el, val, vdom) {
          el.textContent = val;
        }

        directives["m-html"] = function(el, val, vdom) {
          el.innerHTML = val;
        }

        directives["m-mask"] = function(el, val, vdom) {

        }

        // Initialize 🎉
        this.init();
    }

    /**
    * Logs a Message
    * @param {String} msg
    */
    Moon.prototype.log = function(msg) {
      if(!config.silent) console.log(msg);
    }

    /**
    * Throws an Error
    * @param {String} msg
    */
    Moon.prototype.error = function(msg) {
      console.log("Moon ERR: " + msg);
    }

    /**
    * Creates an object to be used in a Virtual DOM
    * @param {String} type
    * @param {Array} children
    * @param {String} val
    * @param {Object} props
    * @param {Node} node
    * @return {Object} Object usable in Virtual DOM
    */
    Moon.prototype.createElement = function(type, children, val, props, node) {
      return {type: type, children: children, val: val, props: props, node: node};
    }

    /**
    * Create Elements Recursively For all Children
    * @param {Array} children
    * @return {Array} Array of elements usable in Virtual DOM
    */
    Moon.prototype.recursiveChildren = function(children) {
      var recursiveChildrenArr = [];
      for(var i = 0; i < children.length; i++) {
        var child = children[i];
        recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
      }
      return recursiveChildrenArr;
    }

    /**
    * Creates Virtual DOM
    * @param {Node} node
    */
    Moon.prototype.createVirtualDOM = function(node) {
      var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
      this.$dom = vdom;
    }

    /**
    * Sets Value in Data
    * @param {String} key
    * @param {String} val
    */
    Moon.prototype.set = function(key, val) {
      this.$data[key] = val;
      if(!this.$destroyed) this.build(this.$dom.children);
      if(this.$hooks.updated) {
        this.$hooks.updated();
      }
    }

    /**
    * Gets Value in Data
    * @param {String} key
    * @return {String} Value of key in data
    */
    Moon.prototype.get = function(key) {
      return this.$data[key];
    }

    /**
    * Calls a method
    * @param {String} method
    */
    Moon.prototype.method = function(method) {
      this.$methods[method]();
    }

    /**
    * Destroys Moon Instance
    */
    Moon.prototype.destroy = function() {
      Object.defineProperty(this, '$data', {
        set: function(value) {
          _data = value;
        }
      });
      this.$destroyed = true;
      if(this.$hooks.destroyed) this.$hooks.destroyed();
    }

    /**
    * Builds the DOM With Data
    * @param {Array} children
    */
    Moon.prototype.build = function(children) {
      for(var i = 0; i < children.length; i++) {
        var el = children[i];

        if(el.type === "#text") {
          el.node.textContent = compileTemplate(el.val, this.$data);
        } else if(el.props) {
          for(var prop in el.props) {
            var propVal = el.props[prop];
            var compiledProperty = compileTemplate(propVal, this.$data);
            var directive = directives[prop];
            if(directive) {
              el.node.removeAttribute(prop);
              directive(el.node, compiledProperty, el);
            }

            if(!directive) el.node.setAttribute(prop, compiledProperty);
          }
        }

        this.build(el.children);
      }
    }

    /**
    * Initializes Moon
    */
    Moon.prototype.init = function() {
      this.log("======= Moon =======");
      if(this.$hooks.created) {
        this.$hooks.created();
      }
      this.createVirtualDOM(this.$el);
      this.build(this.$dom.children);
      if(this.$hooks.mounted) {
        this.$hooks.mounted();
      }
    }

    /* ================= *
     *  Global API       *
     * ================= */

    /**
    * Sets the Configuration of Moon
    * @param {Object} opts
    */
    Moon.config = function(opts) {
      if(opts.silent) {
        config.silent = opts.silent;
      }
    }

    /**
    * Runs an external Plugin
    * @param {Object} plugin
    */
    Moon.use = function(plugin) {
      plugin.init(Moon);
    }

    /**
    * Creates a Directive
    * @param {String} name
    * @param {Function} action
    */
    Moon.directive = function(name, action) {
      directives["m-" + name] = action;
    }

    /**
    * Creates a Component
    * @param {String} name
    * @param {Function} action
    */
    Moon.component = function(name, opts) {
      components[name] = opts;
    }

    /**
    * Creates Subclass of Moon
    * @param {Object} opts
    */
    Moon.extend = function(opts) {
      var Parent = this;
      function MoonComponent() {
        Moon.call(this, opts);
      }
      MoonComponent.prototype = Object.create(Parent.prototype);
      MoonComponent.prototype.constructor = MoonComponent;
      return MoonComponent;
    }

    window.Moon = Moon;
})(window);
<|endoftext|>


<commit_before>ChatApp.factory('appService', function(/*$location, $rootScope, $http, $state, $ionicLoading*/ ) {
	function checkLogin(callback){
		window.FB.getLoginStatus(function(response) {
			if (response.status === 'connected') {
				FB.api('/me', {fields: 'name'}, function(nameRes) {
					response.authResponse.name = nameRes.name;
					callback && callback(response.authResponse);
				});

			}
			else{
				callback && callback(false);
			}
		});
	};

	function doLogin(callback){
		FB.login(function(response) {
			if(response.authResponse) {
				FB.api('/me', {fields: 'name'}, function(nameRes) {
					response.authResponse.name = nameRes.name;
					callback && callback(response.authResponse);
				});
			} else {
				//console.log('User cancelled login or did not fully authorize.');
				callback && callback(false);
			}
		});
	};

	return {
		checkLogin: checkLogin,
		doLogin: doLogin
	};
});

ChatApp.factory('chatService', function(/*$location, $rootScope, $http, $state, $ionicLoading*/ WebsocketService){
	// callbackSendMessage, callbackReceiveMessage, callbackConnect, callbackClose
	var callbacks = {};
	function init(_callbacks){
		callbacks = _callbacks;
		ensureConnect();
	};

	function onConnect(){
		//console.log('Websocket connected!!');
		callbacks.callbackConnect && callbacks.callbackConnect();
	};

	function onClose(){
		//console.log('Websocket close');
		callbacks.callbackClose && callbacks.callbackClose();
	};

	function ensureConnect(){
		var state = WebsocketService.getState();
		if (state !== 0 && state !== 1) {
			var uuid = 'browser';
			if (window.device){
				uuid = window.device.uuid;
			}
			var url = Config.urlWebSocket + '?userID=' + userData.userID + '&accessToken=' + userData.accessToken + '&uuid=' + uuid;

			//console.log('Connecting... ' + url);
			WebsocketService.unbind('open', onConnect);
			WebsocketService.unbind('close', onClose);
			WebsocketService.unbind('message', onReceiceMessage);

			WebsocketService.bind('open', onConnect);
			WebsocketService.bind('close', onClose);
			WebsocketService.bind('message', onReceiceMessage);
			WebsocketService.connect(url);

			return false;
		}
		if (state === 0){
			return false;
		}
		return true;
	};

	function onReceiceMessage(payload){
		//console.log(payload);
		payload = angular.fromJson(payload);

		callbacks.callbackReceiveMessage && callbacks.callbackReceiveMessage(payload);
	};

	function sendMessage(messageData){
		//if (typeof messageData == 'object'){
			messageData = JSON.stringify(messageData);
		//}
		WebsocketService.send('message', messageData);
		callbacks.callbackSendMessage && callbacks.callbackSendMessage();
	};

	function exit(){
		WebsocketService.disconnect();
	};

	return {
		init: init,
		ensureConnect: ensureConnect,
		sendMessage: sendMessage,
		exit: exit

	};
});

ChatApp.factory('WebsocketService', function(){
	var callbacks = {};
	var conn;
	function bind(event_name, callback){
		callbacks[event_name] = callbacks[event_name] || [];
		callbacks[event_name].push(callback);
		return this;// chainable
	}

	function unbind(event_name, callback){
		var chain = callbacks[event_name];
		if (typeof chain == 'undefined') return;
		for (var i = chain.length -1 ; i >= 0 ; i--){
			if(chain[i] == callback) {
				chain.splice(i, 1);
				return this;
			}
		}
		return this;// chainable
	}

	function send(event_name, event_data){
		//console.log('websocket send: ' + event_data);
		if (conn.readyState == 1){
			conn.send(event_data);
		}
		return this;
	}

	function connect(url) {
		if (typeof(MozWebSocket) == 'function')
			conn = new MozWebSocket(url);
		else
			conn = new WebSocket(url);

		// dispatch to the right handlers
		conn.onmessage = function(evt){
			dispatch('message', evt.data);
		};

		conn.onclose = function(){dispatch('close',null)};
		conn.onopen = function(){dispatch('open',null)};
	}

	function getState() {
		if(!conn) return -1;
		return conn.readyState;
	}

	function disconnect() {
		if(conn) {
			conn.close();
		}
	}

	function dispatch(event_name, message){
		var chain = callbacks[event_name];
		if(typeof chain == 'undefined') return; // no callbacks for this event
		for(var i = 0; i < chain.length; i++){
			chain[i]( message );
		}
	}
	return {
		bind:bind,
		unbind:unbind,
		send:send,
		connect:connect,
		disconnect:disconnect,
		getState:getState
	};
});<commit_msg>fix bug<commit_after>ChatApp.factory('appService', function(/*$location, $rootScope, $http, $state, $ionicLoading*/ ) {
	function checkLogin(callback){
		window.FB.getLoginStatus(function(response) {
			if (response.status === 'connected') {
				FB.api('/me', {fields: 'name'}, function(nameRes) {
					if (!nameRes || nameRes.error){
						callback && callback(false);
					}
					else{
						response.authResponse.name = nameRes.name;
						callback && callback(response.authResponse);
					}
				});

			}
			else{
				callback && callback(false);
			}
		});
	};

	function doLogin(callback){
		FB.login(function(response) {
			if(response.authResponse) {
				FB.api('/me', {fields: 'name'}, function(nameRes) {
					response.authResponse.name = nameRes.name;
					callback && callback(response.authResponse);
				});
			} else {
				//console.log('User cancelled login or did not fully authorize.');
				callback && callback(false);
			}
		});
	};

	return {
		checkLogin: checkLogin,
		doLogin: doLogin
	};
});

ChatApp.factory('chatService', function(/*$location, $rootScope, $http, $state, $ionicLoading*/ WebsocketService){
	// callbackSendMessage, callbackReceiveMessage, callbackConnect, callbackClose
	var callbacks = {};
	function init(_callbacks){
		callbacks = _callbacks;
		ensureConnect();
	};

	function onConnect(){
		//console.log('Websocket connected!!');
		callbacks.callbackConnect && callbacks.callbackConnect();
	};

	function onClose(){
		//console.log('Websocket close');
		callbacks.callbackClose && callbacks.callbackClose();
	};

	function ensureConnect(){
		var state = WebsocketService.getState();
		if (state !== 0 && state !== 1) {
			var uuid = 'browser';
			if (window.device){
				uuid = window.device.uuid;
			}
			var url = Config.urlWebSocket + '?userID=' + userData.userID + '&accessToken=' + userData.accessToken + '&uuid=' + uuid;

			//console.log('Connecting... ' + url);
			WebsocketService.unbind('open', onConnect);
			WebsocketService.unbind('close', onClose);
			WebsocketService.unbind('message', onReceiceMessage);

			WebsocketService.bind('open', onConnect);
			WebsocketService.bind('close', onClose);
			WebsocketService.bind('message', onReceiceMessage);
			WebsocketService.connect(url);

			return false;
		}
		if (state === 0){
			return false;
		}
		return true;
	};

	function onReceiceMessage(payload){
		//console.log(payload);
		payload = angular.fromJson(payload);

		callbacks.callbackReceiveMessage && callbacks.callbackReceiveMessage(payload);
	};

	function sendMessage(messageData){
		//if (typeof messageData == 'object'){
			messageData = JSON.stringify(messageData);
		//}
		WebsocketService.send('message', messageData);
		callbacks.callbackSendMessage && callbacks.callbackSendMessage();
	};

	function exit(){
		WebsocketService.disconnect();
	};

	return {
		init: init,
		ensureConnect: ensureConnect,
		sendMessage: sendMessage,
		exit: exit

	};
});

ChatApp.factory('WebsocketService', function(){
	var callbacks = {};
	var conn;
	function bind(event_name, callback){
		callbacks[event_name] = callbacks[event_name] || [];
		callbacks[event_name].push(callback);
		return this;// chainable
	}

	function unbind(event_name, callback){
		var chain = callbacks[event_name];
		if (typeof chain == 'undefined') return;
		for (var i = chain.length -1 ; i >= 0 ; i--){
			if(chain[i] == callback) {
				chain.splice(i, 1);
				return this;
			}
		}
		return this;// chainable
	}

	function send(event_name, event_data){
		//console.log('websocket send: ' + event_data);
		if (conn.readyState == 1){
			conn.send(event_data);
		}
		return this;
	}

	function connect(url) {
		if (typeof(MozWebSocket) == 'function')
			conn = new MozWebSocket(url);
		else
			conn = new WebSocket(url);

		// dispatch to the right handlers
		conn.onmessage = function(evt){
			dispatch('message', evt.data);
		};

		conn.onclose = function(){dispatch('close',null)};
		conn.onopen = function(){dispatch('open',null)};
	}

	function getState() {
		if(!conn) return -1;
		return conn.readyState;
	}

	function disconnect() {
		if(conn) {
			conn.close();
		}
	}

	function dispatch(event_name, message){
		var chain = callbacks[event_name];
		if(typeof chain == 'undefined') return; // no callbacks for this event
		for(var i = 0; i < chain.length; i++){
			chain[i]( message );
		}
	}
	return {
		bind:bind,
		unbind:unbind,
		send:send,
		connect:connect,
		disconnect:disconnect,
		getState:getState
	};
});<|endoftext|>


<commit_before>/*global IDBKeyRange,indexedDB */

"use strict";
O.esri.Edit.EditStore = function()
{
    this._db = null;

    // Public properties

    var dbName = "features_store";
    var objectStoreName = "features";
    var dbIndex = "featureId";

    // ENUMs

    this.ADD = "add";
    this.UPDATE = "update";
    this.DELETE = "delete";

    this.isSupported = function()
    {
        if(!window.indexedDB){
            return false;
        }
        return true;
    };

    /**
     * Commit an edit to the database
     * @param operation add, update or delete
     * @param layer the URL of the feature layer
     * @param graphic esri/graphic. The method will serialize to JSON
     * @param callback {true, edit} or {false, error}
     */
    this.pushEdit = function(operation,layer,graphic, callback)
    {

        var edit = {
                id: layer + "/" + graphic.attributes.objectid,
                operation: operation,
                layer: layer,
                graphic: graphic.toJson()
            };

        var transaction = this._db.transaction([objectStoreName],"readwrite");

        transaction.oncomplete = function(event){
            callback(true);
        };

        transaction.onerror = function(event){
            callback(false,event.target.error.message);
        };

        var objectStore = transaction.objectStore(objectStoreName);
        var request = objectStore.put(edit);
        request.onsuccess = function(event){
            //console.log("item added to db " + event.target.result);
        };
    };

    /**
     * Returns all the edits recursively via the callback
     * @param callback {value, message}
     */
    this.getAllEdits = function(callback){

        console.assert(this._db !== null, "indexeddb not initialized");

        if(this._db !== null){
            var transaction = this._db.transaction([objectStoreName])
                .objectStore(objectStoreName)
                .openCursor();

            transaction.onsuccess = function(event)
            {
                var cursor = event.target.result;
                if(cursor){
                    callback(cursor.value,null);
                    cursor.continue();
                }
                else
                {
                    callback(null, "end");
                }
            }.bind(this);
            transaction.onerror = function(err)
            {
                callback(null, err);
            };
        }
        else
        {
            callback(null, "no db");
        }
    };

    /**
     * Update an edit already exists in the database
     * @param operation add, update or delete
     * @param layer the URL of the feature layer
     * @param graphic esri/graphic. The method will serialize to JSON
     * @param callback {true, edit} or {false, error}
     */
    this.updateExistingEdit = function(operation,layer,graphic, callback){

        console.assert(this._db !== null, "indexeddb not initialized");

        var objectStore = this._db.transaction([objectStoreName],"readwrite").objectStore(objectStoreName);

        //Let's get the entry associated with the graphic
        var objectStoreGraphicRequest = objectStore.get(graphic.attributes.objectid);
        objectStoreGraphicRequest.onsuccess = function() {

            //Grab the data object returned as a result
            var data = objectStoreGraphicRequest.result;

            //Create a new update object
            var update = {
                id: layer + "/" + graphic.attributes.objectid,
                operation: operation,
                layer: layer,
                graphic: graphic.toJson()
            };

            // Insert the update into the database
            var updateGraphicRequest = objectStore.put(update);

            updateGraphicRequest.onsuccess = function(){
                callback(true);
            }

            updateGraphicRequest.onerror = function(err){
                callback(false,err);
            };
        }
    };

    /**
     * Delete a pending edit's record from the database.
     * IMPORTANT: Be aware of false negatives. See Step 4 in this function.
     *
     * @param layerUrl
     * @param graphic Graphic
     * @param callback {boolean}
     */
    this.delete = function(layerUrl, graphic, callback){

        // NOTE: the implementation of the IndexedDB spec has a major failure with respect to
        // handling deletes. The result of the operation is specified as undefined.
        // What this means is that there is no way to tell if an operation was successful or not.
        //
        // In order to get around this we have to verify if after the attempted deletion operation
        // if the record is or is not in the database. Kinda dumb, but that's how IndexedDB works.
        //http://stackoverflow.com/questions/17137879/is-there-a-way-to-get-information-on-deleted-record-when-calling-indexeddbs-obj

        var db = this._db;
        var deferred = null;
        var self = this;

        var edit = {
            id: layerUrl + "/" + graphic.attributes.objectid,
            operation: null,
            layer: layerUrl,
            graphic: graphic.toJson()
        };

        require(["dojo/Deferred"], function(Deferred){
            deferred = new Deferred();

            // Step 1 - lets see if record exits. If it does then return callback.
            self.editExists(edit).then(function(result){
               if(result.success){
                   // Step 4 - Then we check to see if the record actually exists or not.
                   deferred.then(function(edit){
                           self.editExists(edit).then(function(results){
                                   results.success == false ? callback(true) : callback(false);
                               },
                               function(){
                                   callback(true); //because we want this test to throw an error. That means item deleted.
                               })
                       },
                       // There was a problem with the delete operation on the database
                       function(err){
                           callback(err);
                       });

                   var objectStore = db.transaction([objectStoreName],"readwrite").objectStore(objectStoreName);

                   // Step 2 - go ahead and delete graphic
                   var objectStoreDeleteRequest = objectStore.delete(edit.id);

                   // Step 3 - We know that the onsuccess will always fire unless something serious goes wrong.
                   // So we go ahead and resolve the deferred here.
                   objectStoreDeleteRequest.onsuccess = function() {
                       deferred.resolve(edit);

                   };

                   objectStoreDeleteRequest.onerror = function(msg){
                       deferred.reject({success:false,error:msg});
                   }
               }
            },
            // If there is an error in editExists()
            function(){
                callback(false);
            });

        });

        //We return a deferred object so that when calling this function you can chain it with a then() statement.
        //return deferred;
    };

    this.resetEditsQueue = function(callback)
    {
        console.assert(this._db !== null, "indexeddb not initialized");

        var request = this._db.transaction([objectStoreName],"readwrite")
            .objectStore(objectStoreName)
            .clear();
        request.onsuccess = function(event){
            setTimeout(function(){callback(true);},0);
        };
        request.onerror = function(err){
            callback(false,err);
        };
    };

    this.pendingEditsCount = function(callback)
    {
        console.assert(this._db !== null, "indexeddb not initialized");

        var count = 0;

        var objectStore = this._db.transaction([objectStoreName]).objectStore(objectStoreName);
        objectStore.openCursor().onsuccess = function(evt)
        {
            var cursor = evt.target.result;
            if(cursor)
            {
                count++;
                cursor.continue();
            }
            else
            {
                callback(count);
            }
        };
    };

    /**
     * Verify is an edit already exists in the database. Checks the objectId and layerId.
     * @param newEdit {id,operation,layerId,graphicJSON}
     * @returns {deferred} {success: boolean, error: message}
     * @private
     */
    this.editExists = function(newEdit){

        var db = this._db;
        var deferred = null;

        require(["dojo/Deferred"], function(Deferred){
            deferred = new Deferred();

            var objectStore = db.transaction([objectStoreName],"readwrite").objectStore(objectStoreName);

            //Get the entry associated with the graphic
            var objectStoreGraphicRequest = objectStore.get(newEdit.id);

            objectStoreGraphicRequest.onsuccess = function() {
                var graphic = objectStoreGraphicRequest.result;
                if(graphic && (graphic.layer == newEdit.layer)){
                    deferred.resolve({success:true,error:null});
                }
                else{
                    deferred.reject({success:false,error:"Layer id is not a match."});
                }
            };

            objectStoreGraphicRequest.onerror = function(msg){
                deferred.reject({success:false,error:msg});
            }
        });

        //We return a deferred object so that when calling this function you can chain it with a then() statement.
        return deferred;
    };

    /**
     * Returns the approximate size of the database in bytes
     * @param callback  {usage, error} Whereas, the usage Object is {sizeBytes: number, editCount: number}
     */
    this.getUsage = function(callback)
    {
        console.assert(this._db !== null, "indexeddb not initialized");

        var usage = { sizeBytes: 0, editCount: 0 };

        var transaction = this._db.transaction([objectStoreName])
            .objectStore(objectStoreName)
            .openCursor();

        console.log("dumping keys");

        transaction.onsuccess = function(event)
        {
            var cursor = event.target.result;
            if(cursor)
            {
                var storedObject = cursor.value;
                var json = JSON.stringify(storedObject);
                usage.sizeBytes += json.length;
                usage.editCount += 1;
                cursor.continue();
            }
            else
            {
                callback(usage,null);
            }
        };
        transaction.onerror = function(err)
        {
            callback(null,err);
        };
    };

    // internal methods

    //
    // graphic serialization/deserialization
    //
    this._serialize = function(graphic)
    {
        // keep only attributes and geometry, that are the values that get sent to the server by applyEdits()
        // see http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Apply_Edits_Feature_Service_Layer/02r3000000r6000000/
        // use graphic's built-in serializing method
        var json = graphic.toJson();
        var jsonClean =
        {
            attributes: json.attributes,
            geometry: json.geometry
        };
        return JSON.stringify(jsonClean);
    };

    this._deserialize = function(json)
    {
        var graphic;

        require(["esri/graphic"],function(Graphic){
            graphic = new Graphic(JSON.parse(json));
        });

        return graphic;
    };

    this.init = function(callback)
    {
        console.log("init editsStore.js");

        var request = indexedDB.open(dbName, 11);
        callback = callback || function(success) { console.log("EditsStore::init() success:", success); }.bind(this);

        request.onerror = function(event)
        {
            console.log("indexedDB error: " + event.target.errorCode);
            callback(false,event.target.errorCode);
        }.bind(this);

        request.onupgradeneeded = function(event)
        {
            var db = event.target.result;

            if( db.objectStoreNames.contains(objectStoreName))
            {
                db.deleteObjectStore(objectStoreName);
            }

            var objectStore = db.createObjectStore(objectStoreName, { keyPath: "id" });
            objectStore.createIndex(dbIndex,dbIndex, {unique: false});
        }.bind(this);

        request.onsuccess = function(event)
        {
            this._db = event.target.result;
            console.log("database opened successfully");
            callback(true);
        }.bind(this);
    };

    ///
    /// DEPRECATED
    ///

    /**
     * Deprecated @ v2.5. Use pendingEditsCount() instead.
     */
    this.hasPendingEdits = function()
    {
        return "DEPRECATED at v2.5!";
    };

    /**
     * Deprecated @ v2.5. Use public function editExists() instead.
     */
    this._isEditDuplicated = function(newEdit,edits){
        return "DEPRECATED at v2.5!";
    };
};

<commit_msg>fix if parameters<commit_after>/*global IDBKeyRange,indexedDB */

"use strict";
O.esri.Edit.EditStore = function()
{
    this._db = null;

    // Public properties

    var dbName = "features_store";
    var objectStoreName = "features";
    var dbIndex = "featureId";

    // ENUMs

    this.ADD = "add";
    this.UPDATE = "update";
    this.DELETE = "delete";

    this.isSupported = function()
    {
        if(!window.indexedDB){
            return false;
        }
        return true;
    };

    /**
     * Commit an edit to the database
     * @param operation add, update or delete
     * @param layer the URL of the feature layer
     * @param graphic esri/graphic. The method will serialize to JSON
     * @param callback {true, edit} or {false, error}
     */
    this.pushEdit = function(operation,layer,graphic, callback)
    {

        var edit = {
                id: layer + "/" + graphic.attributes.objectid,
                operation: operation,
                layer: layer,
                graphic: graphic.toJson()
            };

        var transaction = this._db.transaction([objectStoreName],"readwrite");

        transaction.oncomplete = function(event){
            callback(true);
        };

        transaction.onerror = function(event){
            callback(false,event.target.error.message);
        };

        var objectStore = transaction.objectStore(objectStoreName);
        var request = objectStore.put(edit);
        request.onsuccess = function(event){
            //console.log("item added to db " + event.target.result);
        };
    };

    /**
     * Returns all the edits recursively via the callback
     * @param callback {value, message}
     */
    this.getAllEdits = function(callback){

        console.assert(this._db !== null, "indexeddb not initialized");

        if(this._db !== null){
            var transaction = this._db.transaction([objectStoreName])
                .objectStore(objectStoreName)
                .openCursor();

            transaction.onsuccess = function(event)
            {
                var cursor = event.target.result;
                if(cursor){
                    callback(cursor.value,null);
                    cursor.continue();
                }
                else
                {
                    callback(null, "end");
                }
            }.bind(this);
            transaction.onerror = function(err)
            {
                callback(null, err);
            };
        }
        else
        {
            callback(null, "no db");
        }
    };

    /**
     * Update an edit already exists in the database
     * @param operation add, update or delete
     * @param layer the URL of the feature layer
     * @param graphic esri/graphic. The method will serialize to JSON
     * @param callback {true, edit} or {false, error}
     */
    this.updateExistingEdit = function(operation,layer,graphic, callback){

        console.assert(this._db !== null, "indexeddb not initialized");

        var objectStore = this._db.transaction([objectStoreName],"readwrite").objectStore(objectStoreName);

        //Let's get the entry associated with the graphic
        var objectStoreGraphicRequest = objectStore.get(graphic.attributes.objectid);
        objectStoreGraphicRequest.onsuccess = function() {

            //Grab the data object returned as a result
            var data = objectStoreGraphicRequest.result;

            //Create a new update object
            var update = {
                id: layer + "/" + graphic.attributes.objectid,
                operation: operation,
                layer: layer,
                graphic: graphic.toJson()
            };

            // Insert the update into the database
            var updateGraphicRequest = objectStore.put(update);

            updateGraphicRequest.onsuccess = function(){
                callback(true);
            }

            updateGraphicRequest.onerror = function(err){
                callback(false,err);
            };
        }
    };

    /**
     * Delete a pending edit's record from the database.
     * IMPORTANT: Be aware of false negatives. See Step 4 in this function.
     *
     * @param layerUrl
     * @param graphic Graphic
     * @param callback {boolean}
     */
    this.delete = function(layerUrl, graphic, callback){

        // NOTE: the implementation of the IndexedDB spec has a major failure with respect to
        // handling deletes. The result of the operation is specified as undefined.
        // What this means is that there is no way to tell if an operation was successful or not.
        //
        // In order to get around this we have to verify if after the attempted deletion operation
        // if the record is or is not in the database. Kinda dumb, but that's how IndexedDB works.
        //http://stackoverflow.com/questions/17137879/is-there-a-way-to-get-information-on-deleted-record-when-calling-indexeddbs-obj

        var db = this._db;
        var deferred = null;
        var self = this;

        var edit = {
            id: layerUrl + "/" + graphic.attributes.objectid,
            operation: null,
            layer: layerUrl,
            graphic: graphic.toJson()
        };

        require(["dojo/Deferred"], function(Deferred){
            deferred = new Deferred();

            // Step 1 - lets see if record exits. If it does then return callback.
            self.editExists(edit).then(function(result){
               if(result.success){
                   // Step 4 - Then we check to see if the record actually exists or not.
                   deferred.then(function(edit){

                           // IF the delete was successful, then the record should return 'false' because it doesn't exist.
                           self.editExists(edit).then(function(results){
                                   results.success == false ? callback(true) : callback(false);
                               },
                               function(){
                                   callback(true); //because we want this test to throw an error. That means item deleted.
                               })
                       },
                       // There was a problem with the delete operation on the database
                       function(err){
                           callback(err);
                       });

                   var objectStore = db.transaction([objectStoreName],"readwrite").objectStore(objectStoreName);

                   // Step 2 - go ahead and delete graphic
                   var objectStoreDeleteRequest = objectStore.delete(edit.id);

                   // Step 3 - We know that the onsuccess will always fire unless something serious goes wrong.
                   // So we go ahead and resolve the deferred here.
                   objectStoreDeleteRequest.onsuccess = function() {
                       deferred.resolve(edit);
                   };

                   objectStoreDeleteRequest.onerror = function(msg){
                       deferred.reject({success:false,error:msg});
                   }
               }
            },
            // If there is an error in editExists()
            function(){
                callback(false);
            });
        });
    };

    this.resetEditsQueue = function(callback)
    {
        console.assert(this._db !== null, "indexeddb not initialized");

        var request = this._db.transaction([objectStoreName],"readwrite")
            .objectStore(objectStoreName)
            .clear();
        request.onsuccess = function(event){
            setTimeout(function(){callback(true);},0);
        };
        request.onerror = function(err){
            callback(false,err);
        };
    };

    this.pendingEditsCount = function(callback)
    {
        console.assert(this._db !== null, "indexeddb not initialized");

        var count = 0;

        var objectStore = this._db.transaction([objectStoreName]).objectStore(objectStoreName);
        objectStore.openCursor().onsuccess = function(evt)
        {
            var cursor = evt.target.result;
            if(cursor)
            {
                count++;
                cursor.continue();
            }
            else
            {
                callback(count);
            }
        };
    };

    /**
     * Verify is an edit already exists in the database. Checks the objectId and layerId.
     * @param newEdit {id,operation,layerId,graphicJSON}
     * @returns {deferred} {success: boolean, error: message}
     * @private
     */
    this.editExists = function(newEdit){

        var db = this._db;
        var deferred = null;

        require(["dojo/Deferred"], function(Deferred){
            deferred = new Deferred();

            var objectStore = db.transaction([objectStoreName],"readwrite").objectStore(objectStoreName);

            //Get the entry associated with the graphic
            var objectStoreGraphicRequest = objectStore.get(newEdit.id);

            objectStoreGraphicRequest.onsuccess = function() {
                var graphic = objectStoreGraphicRequest.result;
                if(graphic && (graphic.id == newEdit.id)){
                    deferred.resolve({success:true,error:null});
                }
                else{
                    deferred.reject({success:false,error:"Layer id is not a match."});
                }
            };

            objectStoreGraphicRequest.onerror = function(msg){
                deferred.reject({success:false,error:msg});
            }
        });

        //We return a deferred object so that when calling this function you can chain it with a then() statement.
        return deferred;
    };

    /**
     * Returns the approximate size of the database in bytes
     * @param callback  {usage, error} Whereas, the usage Object is {sizeBytes: number, editCount: number}
     */
    this.getUsage = function(callback)
    {
        console.assert(this._db !== null, "indexeddb not initialized");

        var usage = { sizeBytes: 0, editCount: 0 };

        var transaction = this._db.transaction([objectStoreName])
            .objectStore(objectStoreName)
            .openCursor();

        console.log("dumping keys");

        transaction.onsuccess = function(event)
        {
            var cursor = event.target.result;
            if(cursor)
            {
                var storedObject = cursor.value;
                var json = JSON.stringify(storedObject);
                usage.sizeBytes += json.length;
                usage.editCount += 1;
                cursor.continue();
            }
            else
            {
                callback(usage,null);
            }
        };
        transaction.onerror = function(err)
        {
            callback(null,err);
        };
    };

    // internal methods

    //
    // graphic serialization/deserialization
    //
    this._serialize = function(graphic)
    {
        // keep only attributes and geometry, that are the values that get sent to the server by applyEdits()
        // see http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Apply_Edits_Feature_Service_Layer/02r3000000r6000000/
        // use graphic's built-in serializing method
        var json = graphic.toJson();
        var jsonClean =
        {
            attributes: json.attributes,
            geometry: json.geometry
        };
        return JSON.stringify(jsonClean);
    };

    this._deserialize = function(json)
    {
        var graphic;

        require(["esri/graphic"],function(Graphic){
            graphic = new Graphic(JSON.parse(json));
        });

        return graphic;
    };

    this.init = function(callback)
    {
        console.log("init editsStore.js");

        var request = indexedDB.open(dbName, 11);
        callback = callback || function(success) { console.log("EditsStore::init() success:", success); }.bind(this);

        request.onerror = function(event)
        {
            console.log("indexedDB error: " + event.target.errorCode);
            callback(false,event.target.errorCode);
        }.bind(this);

        request.onupgradeneeded = function(event)
        {
            var db = event.target.result;

            if( db.objectStoreNames.contains(objectStoreName))
            {
                db.deleteObjectStore(objectStoreName);
            }

            var objectStore = db.createObjectStore(objectStoreName, { keyPath: "id" });
            objectStore.createIndex(dbIndex,dbIndex, {unique: false});
        }.bind(this);

        request.onsuccess = function(event)
        {
            this._db = event.target.result;
            console.log("database opened successfully");
            callback(true);
        }.bind(this);
    };

    ///
    /// DEPRECATED
    ///

    /**
     * Deprecated @ v2.5. Use pendingEditsCount() instead.
     */
    this.hasPendingEdits = function()
    {
        return "DEPRECATED at v2.5!";
    };

    /**
     * Deprecated @ v2.5. Use public function editExists() instead.
     */
    this._isEditDuplicated = function(newEdit,edits){
        return "DEPRECATED at v2.5!";
    };
};

<|endoftext|>


<commit_before>let find = function () {}

find.prototype.player = function (data, key) {
	if (!key) {
		return new Error(`find.player was not given a key.`);
	} else if (typeof (key) !== 'string') {
		return new Error(`find.player expected key to be a string, but was given a ${typeof(key)}.`);
	}

	function hasMatchingKey(player) {
		let keyUpper = key.toUpperCase();
		if (player.player_id === keyUpper || player.team_abbrev === keyUpper) {
			return true;
		} else {
			return false;
		}
	}

	let results = data.search_player_all.queryResults.row;
	let resultsCount = data.search_player_all.queryResults.totalSize;
	let requestedPlayer = {};

	if (resultsCount > 1) {
		let p = results.findIndex(hasMatchingKey);
		requestedPlayer = results[p];
	} else if (resultsCount == 1) {
		requestedPlayer = results;
	}

	if (!requestedPlayer) {
		return new Error('find.player could not find a player with a matching key.');
	}

	return requestedPlayer;
}

find.prototype.teamId = function (key) {
	key = key.toUpperCase();
	let teamID;
	let teams = require('./team.manifest.json');

	for (let i = 0; i < teams.length; i++) {
		let team = teams[i];
		let team_abbrev = team.name_abbrev.toUpperCase();
		let team_name = team.name_display_full.toUpperCase();

		if (key == team_abbrev || key == team.team_id || key == team_name) {
			teamID = team.team_id;
		}
	}

	return teamID;
}

module.exports = new find;<commit_msg>check for key and key type<commit_after>let find = function () {}

find.prototype.player = function (data, key) {
	if (!key) {
		return new Error(`find.player was not given a key.`);
	} else if (typeof (key) !== 'string') {
		return new Error(`find.player expected key to be a string, but was given a ${typeof(key)}.`);
	}

	function hasMatchingKey(player) {
		let keyUpper = key.toUpperCase();
		if (player.player_id === keyUpper || player.team_abbrev === keyUpper) {
			return true;
		} else {
			return false;
		}
	}

	let results = data.search_player_all.queryResults.row;
	let resultsCount = data.search_player_all.queryResults.totalSize;
	let requestedPlayer = {};

	if (resultsCount > 1) {
		let p = results.findIndex(hasMatchingKey);
		requestedPlayer = results[p];
	} else if (resultsCount == 1) {
		requestedPlayer = results;
	}

	if (!requestedPlayer) {
		return new Error('find.player could not find a player with a matching key.');
	}

	return requestedPlayer;
}

find.prototype.teamId = function (key) {
	if (!key) {
		return new Error(`find.teamId was not given a key.`);
	} else if (typeof (key) !== 'string') {
		return new Error(`find.teamId expected key to be a string, but was given a ${typeof(key)}.`);
	}

	key = key.toUpperCase();
	let teamID;
	let teams = require('./team.manifest.json');

	for (let i = 0; i < teams.length; i++) {
		let team = teams[i];
		let team_abbrev = team.name_abbrev.toUpperCase();
		let team_name = team.name_display_full.toUpperCase();

		if (key == team_abbrev || key == team.team_id || key == team_name) {
			teamID = team.team_id;
		}
	}

	return teamID;
}

module.exports = new find;<|endoftext|>


<commit_before>/**
 * Copyright 2017 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.
 */

// TODO(DEVELOPER): Import the Cloud Functions for Firebase and the Firebase Admin modules here.

// TODO(DEVELOPER): Write the addWelcomeMessages Function here.

// TODO(DEVELOPER): Write the blurOffensiveImages Function here.

// TODO(DEVELOPER): Write the sendNotifications Function here.<commit_msg>update codelab with newest Cloud Vision API<commit_after>/**
 * Copyright 2017 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.
 */

// TODO(DEVELOPER): Import the Cloud Functions for Firebase and the Firebase Admin modules here.

// TODO(DEVELOPER): Write the addWelcomeMessages Function here.

// TODO(DEVELOPER): Write the blurOffensiveImages Function here.

// TODO(DEVELOPER): Write the sendNotifications Function here.
<|endoftext|>