Show:
+(function (global, factory) {
  if (typeof exports === 'undefined') {
    factory(global.webduino || {});
  } else {
    module.exports = factory;
  }
}(this, function (scope) {
  'use strict';

  var PinEvent = scope.PinEvent,
    Pin = scope.Pin,
    Module = scope.Module;

  var ButtonEvent = {

    /**
     * Fires when a button is pressed.
     *
     * @event ButtonEvent.PRESS
     */
    PRESS: 'pressed',

    /**
     * Fires when a button is released.
     *
     * @event ButtonEvent.RELEASE
     */
    RELEASE: 'released',

    /**
     * Fires when a button is long-pressed.
     *
     * @event ButtonEvent.LONG_PRESS
     */
    LONG_PRESS: 'longPress',

    /**
     * Fires when a button is sustained-pressed.
     *
     * @event ButtonEvent.SUSTAINED_PRESS
     */
    SUSTAINED_PRESS: 'sustainedPress'
  };

  /**
   * The Button Class.
   *
   * @namespace webduino.module
   * @class Button
   * @constructor
   * @param {webduino.Board} board The board the button is attached to.
   * @param {webduino.pin} pin The pin the button is connected to.
   * @param {Number} [buttonMode] Type of resistor the button is connected to, either Button.PULL_DOWN or Button.PULL_UP.
   * @param {Number} [sustainedPressInterval] A period of time when the button is pressed and hold for that long, it would be considered a "sustained press." Measured in ms.
   * @extends webduino.Module
   */
  function Button(board, pin, buttonMode, sustainedPressInterval) {
    Module.call(this);

    this._board = board;
    this._pin = pin;
    this._repeatCount = 0;
    this._timer = null;
    this._timeout = null;

    this._buttonMode = buttonMode || Button.PULL_DOWN;
    this._sustainedPressInterval = sustainedPressInterval || 1000;
    this._debounceInterval = 20;
    this._pressHandler = onPress.bind(this);
    this._releaseHandler = onRelease.bind(this);
    this._sustainedPressHandler = onSustainedPress.bind(this);

    board.setDigitalPinMode(pin.number, Pin.DIN);

    if (this._buttonMode === Button.INTERNAL_PULL_UP) {
      board.enablePullUp(pin.number);
      this._pin.value = Pin.HIGH;
    } else if (this._buttonMode === Button.PULL_UP) {
      this._pin.value = Pin.HIGH;
    }

    this._pin.on(PinEvent.CHANGE, onPinChange.bind(this));
  }

  function onPinChange(pin) {
    var btnVal = pin.value;
    var stateHandler;

    if (this._buttonMode === Button.PULL_DOWN) {
      if (btnVal === 1) {
        stateHandler = this._pressHandler;
      } else {
        stateHandler = this._releaseHandler;
      }
    } else if (this._buttonMode === Button.PULL_UP || this._buttonMode === Button.INTERNAL_PULL_UP) {
      if (btnVal === 1) {
        stateHandler = this._releaseHandler;
      } else {
        stateHandler = this._pressHandler;
      }
    }

    if (this._timeout) {
      clearTimeout(this._timeout);
    }
    this._timeout = setTimeout(stateHandler, this._debounceInterval);
  }

  function onPress() {
    this.emit(ButtonEvent.PRESS);
    if (this._timer) {
      clearInterval(this._timer);
      delete this._timer;
    }
    this._timer = setInterval(this._sustainedPressHandler, this._sustainedPressInterval);
  }

  function onRelease() {
    this.emit(ButtonEvent.RELEASE);
    if (this._timer) {
      clearInterval(this._timer);
      delete this._timer;
    }
    this._repeatCount = 0;
  }

  function onSustainedPress() {
    if (this._repeatCount > 0) {
      this.emit(ButtonEvent.SUSTAINED_PRESS);
    } else {
      this.emit(ButtonEvent.LONG_PRESS);
    }
    this._repeatCount++;
  }

  Button.prototype = Object.create(Module.prototype, {

    constructor: {
      value: Button
    },

    /**
     * Return the button mode.
     *
     * @attribute buttonMode
     * @type {Number} buttonMode
     * @readOnly
     */
    buttonMode: {
      get: function () {
        return this._buttonMode;
      }
    },

    /**
     * Return the sustained-press interval.
     *
     * @attribute sustainedPressInterval
     * @type {Number}
     */
    sustainedPressInterval: {
      get: function () {
        return this._sustainedPressInterval;
      },
      set: function (intervalTime) {
        this._sustainedPressInterval = intervalTime;
      }
    }

  });

  /**
   * Indicates the pull-down resistor type.
   * 
   * @property PULL_DOWN
   * @type {Number}
   * @static
   * @final
   */
  Button.PULL_DOWN = 0;

  /**
   * Indicates the pull-up resistor type.
   * 
   * @property PULL_UP
   * @type {Number}
   * @static
   * @final
   */
  Button.PULL_UP = 1;

  /**
   * Indicates the internal-pull-up resistor type.
   * 
   * @property INTERNAL_PULL_UP
   * @type {Number}
   * @static
   * @final
   */
  Button.INTERNAL_PULL_UP = 2;

  scope.module.ButtonEvent = ButtonEvent;
  scope.module.Button = Button;
}));