text: var Board = require("../lib/board.js"),
    __ = require("../lib/fn.js"),
    events = require("events"),
    util = require("util");
  
  // Button instance private data
  var priv = new WeakMap(),
    aliases = {
      down: ["down", "press", "tap", "impact", "hit"],
      up: ["up", "release"]
    },
    // Create a 5 ms debounce boundary on event triggers
    // this avoids button events firing on
    // press noise and false positives
    trigger = __.debounce(function(key) {
      aliases[key].forEach(function(type) {
        this.emit(type, null);
      }, this);
    }, 7);
  
  
  /**
   * Button
   * @constructor
   *
   * five.Button();
   *
   * five.Button({
   *   pin: 10
   * });
   *
   *
   * @param {Object} opts [description]
   *
   */
  
  function Button(opts) {
  
    if (!(this instanceof Button)) {
      return new Button(opts);
    }
  
    var timeout;
  
    // Initialize a Device instance on a Board
    Board.Device.call(
      this, opts = Board.Options(opts)
    );
  
    // Set the pin to INPUT mode
    this.mode = this.io.MODES.INPUT;
  
    // Option to enable the built-in pullup resistor
    this.isPullup = opts.isPullup || false;
  
    this.io.pinMode(this.pin, this.mode);
  
    // Enable the pullup resistor after setting pin mode
    if (this.isPullup) {
      this.io.digitalWrite(this.pin, this.io.HIGH);
    }
  
    // Turns out some button circuits will send
    // 0 for up and 1 for down, and some the inverse,
    // so we can invert our function with this option.
    // Default to invert in pullup mode, but use opts.invert
    // if explicitly defined (even if false)
    this.invert = typeof opts.invert !== "undefined" ?
      opts.invert : (this.isPullup || false);
  
    this.downValue = this.invert ? 0 : 1;
    this.upValue = this.invert ? 1 : 0;
  
    // Button instance properties
    this.holdtime = opts && opts.holdtime || 500;
  
    // Create a "state" entry for privately
    // storing the state of the button
    priv.set(this, {
      isDown: false
    });
  
    // Analog Read event loop
    this.io.digitalRead(this.pin, function(data) {
      var err = null;
  
      // data = upValue, this.isDown = true
      // indicates that the button has been released
      // after previously being pressed
      if (data === this.upValue && this.isDown) {
        if (timeout) {
          clearTimeout(timeout);
        }
        priv.get(this).isDown = false;
  
        trigger.call(this, "up");
      }
  
      // data = downValue, this.isDown = false
      // indicates that the button has been pressed
      // after previously being released
      if (data === this.downValue && !this.isDown) {
  
        // Update private data
        priv.get(this).isDown = true;
  
        // Call debounced event trigger for given "key"
        // This will trigger all event aliases assigned
        // to "key"
        trigger.call(this, "down" /* key */ );
  
        timeout = setTimeout(function() {
          if (this.isDown) {
            this.emit("hold", err);
          }
        }.bind(this), this.holdtime);
      }
    }.bind(this));
  
    Object.defineProperties(this, {
      isDown: {
        get: function() {
          return priv.get(this).isDown;
        }
      }
    });
  }
  
  util.inherits(Button, events.EventEmitter);
  
  
  /**
   * Fired when the button is pressed down
   *
   * @event
   * @name down
   * @memberOf Button
   */
  
  /**
   * Fired when the button is held
   *
   * @event
   * @name hold
   * @memberOf Button
   */
  
  /**
   * Fired when the button is released
   *
   * @event
   * @name up
   * @memberOf Button
   */
  
  
  module.exports = Button;
  