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

  var Pin = scope.Pin,
    Module = scope.Module,
    BoardEvent = scope.BoardEvent,
    proto;

  /**
   * The Led class.
   *
   * @namespace webduino.module
   * @class Led
   * @constructor
   * @param {webduino.Board} board The board the LED is attached to.
   * @param {webduino.Pin} pin The pin the LED is connected to.
   * @param {Number} [driveMode] Drive mode the LED is operating at, either Led.SOURCE_DRIVE or Led.SYNC_DRIVE.
   * @extends webduino.Module
   */
  function Led(board, pin, driveMode) {
    Module.call(this);

    this._board = board;
    this._pin = pin;
    this._driveMode = driveMode || Led.SOURCE_DRIVE;
    this._supportsPWM = undefined;
    this._blinkTimer = null;

    this._board.on(BoardEvent.BEFOREDISCONNECT, this._clearBlinkTimer.bind(this));
    this._board.on(BoardEvent.ERROR, this._clearBlinkTimer.bind(this));

    if (this._driveMode === Led.SOURCE_DRIVE) {
      this._onValue = 1;
      this._offValue = 0;
    } else if (this._driveMode === Led.SYNC_DRIVE) {
      this._onValue = 0;
      this._offValue = 1;
    } else {
      throw new Error('driveMode should be Led.SOURCE_DRIVE or Led.SYNC_DRIVE');
    }

    if (pin.capabilities[Pin.PWM]) {
      board.setDigitalPinMode(pin.number, Pin.PWM);
      this._supportsPWM = true;
    } else {
      board.setDigitalPinMode(pin.number, Pin.DOUT);
      this._supportsPWM = false;
    }
  }

  function checkPinState(self, pin, state, callback) {
    self._board.queryPinState(pin, function (pin) {
      if (pin.state === state) {
        callback.call(self);
      }
    });
  }

  Led.prototype = proto = Object.create(Module.prototype, {

    constructor: {
      value: Led
    },

    /**
     * Intensity of the LED.
     *
     * @attribute intensity
     * @type {Number}
     */
    intensity: {
      get: function () {
        return this._pin.value;
      },
      set: function (val) {
        if (!this._supportsPWM) {
          if (val < 0.5) {
            val = 0;
          } else {
            val = 1;
          }
        }

        if (this._driveMode === Led.SOURCE_DRIVE) {
          this._pin.value = val;
        } else if (this._driveMode === Led.SYNC_DRIVE) {
          this._pin.value = 1 - val;
        }
      }
    }

  });

  /**
   * Light up the LED.
   *
   * @method on
   * @param {Function} [callback] LED state changed callback.
   */
  proto.on = function (callback) {
    this._clearBlinkTimer();
    this._pin.value = this._onValue;
    if (typeof callback === 'function') {
      checkPinState(this, this._pin, this._pin.value, callback);
    }
  };

  /**
   * Dim the LED.
   *
   * @method off
   * @param {Function} [callback] LED state changed callback.
   */
  proto.off = function (callback) {
    this._clearBlinkTimer();
    this._pin.value = this._offValue;
    if (typeof callback === 'function') {
      checkPinState(this, this._pin, this._pin.value, callback);
    }
  };

  /**
   * Toggle LED state between on/off.
   *
   * @method toggle
   * @param {Function} [callback] State changed callback.
   */
  proto.toggle = function (callback) {
    if (this._blinkTimer) {
      this.off();
    } else {
      this._pin.value = 1 - this._pin.value;
    }
    if (typeof callback === 'function') {
      checkPinState(this, this._pin, this._pin.value, callback);
    }
  };

  /**
   * Blink the LED.
   *
   * @method blink
   * @param {Number} [interval=1000] Led blinking interval.
   * @param {Function} [callback] Led state changed callback.
   */
  proto.blink = function (interval, callback) {
    if (arguments.length === 1 && typeof arguments[0] === 'function') {
      callback = arguments[0];
    }
    interval = parseInt(interval);
    interval = isNaN(interval) || interval <= 0 ? 1000 : interval;

    this._clearBlinkTimer();
    this._blinkTimer = this._blink(interval, callback);
  };

  proto._blink = function (interval, callback) {
    var self = this;
    return setTimeout(function () {
      self._pin.value = 1 - self._pin.value;
      if (typeof callback === 'function') {
        checkPinState(self, self._pin, self._pin.value, callback);
      }
      self._blinkTimer = self._blink(interval, callback);
    }, interval);
  };

  proto._clearBlinkTimer = function () {
    if (this._blinkTimer) {
      clearTimeout(this._blinkTimer);
      this._blinkTimer = null;
    }
  };

  /**
   * Indicates the source LED drive mode.
   *
   * @property SOURCE_DRIVE
   * @type Number
   * @static
   * @final
   */
  Led.SOURCE_DRIVE = 0;

  /**
   * Indicates the synchronous LED drive mode.
   *
   * @property SYNC_DRIVE
   * @type Number
   * @static
   * @final
   */
  Led.SYNC_DRIVE = 1;

  scope.module.Led = Led;
}));