<commit_before>/**
 * Based on Pin.as originally written in as3.
 * Copyright (c) the Funnel development team
 * http://www.funnel.cc
 *
 * Ported to JavaScript by Jeff Hoefs
 * Copyright (c) 2011-2012 Jeff Hoefs <soundanalogous@gmail.com>
 *
 * Released under the MIT license. See LICENSE file for details.
 */

JSUTILS.namespace('BO.Pin');

BO.Pin = (function () {
    "use strict";

    var Pin;

    // dependencies
    var EventDispatcher = JSUTILS.EventDispatcher,
        PinEvent = BO.PinEvent;

    /**
     * Each analog and digital pin of the physical I/O board is
     * represented by a Pin object.
     * The Pin object is the foundation for many of the io objects and is also
     * very useful on its own. See the Using The Pin Object Guide on
     * [http://breakoutjs.com](http://breakoutjs.com) for a detailed overview.
     *
     * @class Pin
     * @constructor
     * @uses JSUTILS.EventDispatcher
     * @param {Number} number The pin number
     * @param {Number} type The type of pin
     */
    Pin = function (number, type) {

        this.name = "Pin";

        this._type = type;
        this._capabilities = {};
        this._number = number;
        this._analogNumber = undefined;
        this._maxPWMValue = 255;
        this._value = 0;
        this._lastValue = -1;
        this._preFilterValue = 0;
        this._average = 0;
        this._minimum = Math.pow(2, 16);
        this._maximum = 0;
        this._sum = 0;
        this._numSamples = 0;
        this._filters = null;
        this._generator = null;
        this._state = undefined;

        this._autoSetValueCallback = this.autoSetValue.bind(this);

        this._evtDispatcher = new EventDispatcher(this);

    };

    Pin.prototype = {

        constructor: Pin,

        /**
         * The analogNumber sould only be set internally.
         * @private
         */
        setAnalogNumber: function (num) {
            this._analogNumber = num;
        },

        /**
         * [read-only] The analog pin number used by the IOBoard (printed on
         * board or datasheet).
         * @property analogNumber
         * @type Number
         */
        get analogNumber() {
            return this._analogNumber;
        },

        /**
         * [read-only] The pin number corresponding to the Arduino documentation
         * for the type of board.
         * @property number
         * @type Number
         */
        get number() {
            return this._number;
        },

        /**
         * The maximum PWM value supported for this pin. This value should
         * normally be set internally.
         * @private
         */
        setMaxPWMValue: function (value) {
            this._maxPWMValue = value;
        },

        /**
         * Sets the state value. This is populated by the
         * processPinStateResponse method of the IOBoard object. It should not
         * be called manually.
         *
         * @param {Number} state The state of the pin. For output modes, the
         * state is any value that has been previously written to the pin. For
         * input modes, the state is typically zero, however for digital inputs
         * the state is the status of the pullup resistor.
         * @private
         */
        setState: function (state) {
            // convert PWM values to 0.0 - 1.0 range
            if (this._type === Pin.PWM) {
                state = state / this.maxPWMValue;
            }

            this._state = state;
        },

        /**
         * [read-only] The maximum PWM value supported for this pin.
         *
 This is the max PWM value supported by Arduino (currently 255)
         * rather than the max PWM value specified by the microcontroller
         * datasheet.

         *
         * @property maxPWMValue
         * @type Number
         */
        get maxPWMValue() {
            return this._maxPWMValue;
        },

        /**
         * [read-only] The average value of the pin over time. Call clear() to
         * reset.
         * @property average
         * @type Number
         */
        get average() {
            return this._average;
        },

        /**
         * [read-only] The minimum value of the pin over time. Call clear() to
         * reset.
         * @property minimum
         * @type Number
         */
        get minimum() {
            return this._minimum;
        },

        /**
         * [read-only] The maximum value of the pin over time. Call clear() to
         * reset.
         * @property maximum
         * @type Number
         */
        get maximum() {
            return this._maximum;
        },

        /**
         *
[read-only] The state of the pin. For output modes, the state is
         * any value that has been previously written to the pin. For input
         * modes, the state is typically zero, however for digital inputs the
         * state is the status of the pullup resistor.

         *
         *
This propery is populated by calling the queryPinState method of
         * the IOBoard object. This is useful if there are multiple client
         * applications connected to a single physical IOBoard and you want to
         * get the state of a pin that is set by another client application.

         *
         * @property state
         * @type Number
         */
        get state() {
            return this._state;
        },

        /**
         * The current digital or analog value of the pin.
         * @property value
         * @type Number
         */
        get value() {
            return this._value;
        },
        set value(val) {
            this._lastValue = this._value;
            this._preFilterValue = val;
            this._value = this.applyFilters(val);
            this.calculateMinMaxAndMean(this._value);
            this.detectChange(this._lastValue, this._value);
        },

        /**
         * [read-only] The last pin value.
         * @property lastValue
         * @type Number
         */
        get lastValue() {
            return this._lastValue;
        },

        /**
         * [read-only] The value before any filters were applied.
         * @property preFilterValue
         * @type Number
         */
        get preFilterValue() {
            return this._preFilterValue;
        },

        /**
         * Get and set filters for the Pin.
         * @property filters
         * @type FilterBase[]
         */
        get filters() {
            return this._filters;
        },
        set filters(filterArray) {
            this._filters = filterArray;
        },

        /**
         * [read-only] Get a reference to the current generator.
         * @property generator
         * @type GeneratorBase
         */
        get generator() {
            return this._generator;
        },

        /**
         * The type/mode of the pin (0: DIN, 1: DOUT, 2: AIN, 3: AOUT / PWM,
         * 4: SERVO, 5: SHIFT, 6: I2C). Use
         * IOBoard.setDigitalPinMode(pinNumber) to set the pin type.
         * @method getType
         * @return {Number} The pin type/mode
         */
        getType: function () {
            return this._type;
        },

        /**
         * Set the pin type. This method should only be used internally.
         * @private
         */
        setType: function (pinType) {
            // Ensure pin type is valid
            if (pinType >= 0 && pinType < Pin.TOTAL_PIN_MODES) {
                this._type = pinType;
            }
        },

        /**
         * An object storing the capabilities of the pin.
         * @method getCapabilities
         * @return {Object} An object describing the capabilities of this Pin.
         */
        getCapabilities: function () {
            return this._capabilities;
        },

        /**
         * This method should only be used internally.
         * @private
         */
        setCapabilities: function (pinCapabilities) {
            this._capabilities = pinCapabilities;
        },

        /**
         * Dispatch a Change event whenever a pin value changes
         * @private
         * @method detectChange
         */
        detectChange: function (oldValue, newValue) {
            if (oldValue === newValue) {
                return;
            }
            this.dispatchEvent(new PinEvent(PinEvent.CHANGE));

            if (oldValue <= 0 && newValue !== 0) {
                this.dispatchEvent(new PinEvent(PinEvent.RISING_EDGE));
            } else if (oldValue !== 0 && newValue <= 0) {
                this.dispatchEvent(new PinEvent(PinEvent.FALLING_EDGE));
            }
        },

        /**
         * From funnel Pin.as
         * @private
         * @method clearWeight
         */
        clearWeight: function () {
            this._sum = this._average;
            this._numSamples = 1;
        },

        /**
         * From funnel Pin.as
         * @private
         * @method calculateMinMaxAndMean
         */
        calculateMinMaxAndMean: function (val) {
            var MAX_SAMPLES = Number.MAX_VALUE;

            this._minimum = Math.min(val, this._minimum);
            this._maximum = Math.max(val, this._maximum);

            this._sum += val;
            this._average = this._sum / (++this._numSamples);
            if (this._numSamples >= MAX_SAMPLES) {
                this.clearWeight();
            }
        },

        /**
         * Resets the minimum, maximum, average and lastValue of the pin.
         * @method clear
         */
        clear: function () {
            this._minimum = this._maximum = this._average = this._lastValue = this._preFilterValue;
            this.clearWeight();
        },

        /**
         * Add a new filter to the Pin.
         * @method addFilter
         * @param {FilterBase} newFilter A filter object that extends
         * FilterBase.
         * @see BO.filters.Convolution
         * @see BO.filters.Scaler
         * @see BO.filters.TriggerPoint
         */
        addFilter: function (newFilter) {

            if (newFilter === null) {
                return;
            }

            if (this._filters === null) {
                this._filters = [];
            }

            this._filters.push(newFilter);
        },

        /**
         * Remove a specified filter from the Pin.
         * @method removeFilter
         * @param {FilterBase} filterToRemove The filter to remove.
         * @see BO.filters.Convolution
         * @see BO.filters.Scaler
         * @see BO.filters.TriggerPoint
         */
        removeFilter: function (filterToRemove) {
            var index;

            if (this._filters.length < 1) {
                return;
            }

            index = this._filters.indexOf(filterToRemove);

            if (index !== -1) {
                this._filters.splice(index, 1);
            }
        },

        /**
         * Add a new generator to the Pin. A pin can only have one generator
         * assigned.
         * Assigning a new generator will replace the previously assigned
         * generator.
         * @method addGenerator
         * @param {GeneratorBase} newGenerator A generator object that extends
         * GeneratorBase.
         * @see BO.generators.Oscillator
         */
        addGenerator: function (newGenerator) {
            this.removeGenerator();
            this._generator = newGenerator;
            this._generator.addEventListener(BO.generators.GeneratorEvent.UPDATE, this._autoSetValueCallback);
        },

        /**
         * Removes the generator from the pin.
         * @method removeGenerator
         */
        removeGenerator: function () {
            if (this._generator !== null) {
                this._generator.removeEventListener(BO.generators.GeneratorEvent.UPDATE, this._autoSetValueCallback);
            }
            this._generator = null;
        },

        /**
         * Removes all filters from the pin.
         * @method removeAllFilters
         */
        removeAllFilters: function () {
            this._filters = null;
        },

        /**
         * @private
         * @method autoSetValue
         */
        autoSetValue: function (event) {
            var val = this._generator.value;
            this.value = val;
        },

        /**
         * @private
         * @method applyFilters
         */
        applyFilters: function (val) {
            var result;

            if (this._filters === null) {
                return val;
            }

            result = val;
            var len = this._filters.length;
            for (var i = 0; i < len; i++) {
                result = this._filters[i].processSample(result);
            }

            return result;
        },

        // Implement EventDispatcher

        /**
         * @param {String} type The event type
         * @param {Function} listener The function to be called when the event
         * is fired
         */
        addEventListener: function (type, listener) {
            this._evtDispatcher.addEventListener(type, listener);
        },

        /**
         * @param {String} type The event type
         * @param {Function} listener The function to be called when the event
         * is fired
         */
        removeEventListener: function (type, listener) {
            this._evtDispatcher.removeEventListener(type, listener);
        },

        /**
         * @param {String} type The event type
         * return {boolean} True is listener exists for this type, false if not.
         */
        hasEventListener: function (type) {
            return this._evtDispatcher.hasEventListener(type);
        },

        /**
         * @param {PinEvent} type The Event object
         * @param {Object} optionalParams Optional parameters to assign to the
         * event object.
         * return {boolean} True if dispatch is successful, false if not.
         */
        dispatchEvent: function (event, optionalParams) {
            return this._evtDispatcher.dispatchEvent(event, optionalParams);
        }

    };

    /**
     * @property Pin.HIGH
     * @static
     */
    Pin.HIGH = 1;
    /**
     * @property Pin.LOW
     * @static
     */
    Pin.LOW = 0;
    /**
     * @property Pin.ON
     * @static
     */
    Pin.ON = 1;
    /**
     * @property Pin.OFF
     * @static
     */
    Pin.OFF = 0;

    // Pin modes
    /**
     * @property Pin.DIN
     * @static
     */
    Pin.DIN = 0x00;
    /**
     * @property Pin.DOUT
     * @static
     */
    Pin.DOUT = 0x01;
    /**
     * @property Pin.AIN
     * @static
     */
    Pin.AIN = 0x02;
    /**
     * @property Pin.AOUT
     * @static
     */
    Pin.AOUT = 0x03;
    /**
     * @property Pin.PWM
     * @static
     */
    Pin.PWM = 0x03;
    /**
     * @property Pin.SERVO
     * @static
     */
    Pin.SERVO = 0x04;
    /**
     * @property Pin.SHIFT
     * @static
     */
    Pin.SHIFT = 0x05;
    /**
     * @property Pin.I2C
     * @static
     */
    Pin.I2C = 0x06;
    /**
     * @property Pin.ONEWIRE
     * @static
     */
    Pin.ONEWIRE = 0x07;
    /**
     * @property Pin.STEPPER
     * @static
     */
    Pin.STEPPER = 0x08;
    /**
     * @property Pin.TOTAL_PIN_MODES
     * @static
     */
    Pin.TOTAL_PIN_MODES = 9;

    // Document events

    /**
     * The pinChange event is dispatched when the pin value changes.
     * @type BO.PinEvent.CHANGE
     * @event pinChange
     * @param {BO.Pin} target A reference to the Pin object.
     */

    /**
     * The risingEdge event is dispatched when the pin value increased
     * (from 0 to 1).
     * @type BO.PinEvent.RISING_EDGE
     * @event risingEdge
     * @param {BO.Pin} target A reference to the Pin object.
     */

    /**
     * The change event is dispatched when the pin value decreased
     * (from 1 to 0).
     * @type BO.PinEvent.FALLING_EDGE
     * @event fallingEdge
     * @param {BO.Pin} target A reference to the Pin object.
     */

    return Pin;

}());
<commit_msg>replacing GeneratorEvent.UPDATE in Pin.js with string value to eliminate dependency on GeneratorEvent in Pin object<commit_after>/**
 * Based on Pin.as originally written in as3.
 * Copyright (c) the Funnel development team
 * http://www.funnel.cc
 *
 * Ported to JavaScript by Jeff Hoefs
 * Copyright (c) 2011-2012 Jeff Hoefs <soundanalogous@gmail.com>
 *
 * Released under the MIT license. See LICENSE file for details.
 */

JSUTILS.namespace('BO.Pin');

BO.Pin = (function () {
    "use strict";

    var Pin;

    // dependencies
    var EventDispatcher = JSUTILS.EventDispatcher,
        PinEvent = BO.PinEvent;

    /**
     * Each analog and digital pin of the physical I/O board is
     * represented by a Pin object.
     * The Pin object is the foundation for many of the io objects and is also
     * very useful on its own. See the Using The Pin Object Guide on
     * [http://breakoutjs.com](http://breakoutjs.com) for a detailed overview.
     *
     * @class Pin
     * @constructor
     * @uses JSUTILS.EventDispatcher
     * @param {Number} number The pin number
     * @param {Number} type The type of pin
     */
    Pin = function (number, type) {

        this.name = "Pin";

        this._type = type;
        this._capabilities = {};
        this._number = number;
        this._analogNumber = undefined;
        this._maxPWMValue = 255;
        this._value = 0;
        this._lastValue = -1;
        this._preFilterValue = 0;
        this._average = 0;
        this._minimum = Math.pow(2, 16);
        this._maximum = 0;
        this._sum = 0;
        this._numSamples = 0;
        this._filters = null;
        this._generator = null;
        this._state = undefined;

        this._autoSetValueCallback = this.autoSetValue.bind(this);

        this._evtDispatcher = new EventDispatcher(this);

    };

    Pin.prototype = {

        constructor: Pin,

        /**
         * The analogNumber sould only be set internally.
         * @private
         */
        setAnalogNumber: function (num) {
            this._analogNumber = num;
        },

        /**
         * [read-only] The analog pin number used by the IOBoard (printed on
         * board or datasheet).
         * @property analogNumber
         * @type Number
         */
        get analogNumber() {
            return this._analogNumber;
        },

        /**
         * [read-only] The pin number corresponding to the Arduino documentation
         * for the type of board.
         * @property number
         * @type Number
         */
        get number() {
            return this._number;
        },

        /**
         * The maximum PWM value supported for this pin. This value should
         * normally be set internally.
         * @private
         */
        setMaxPWMValue: function (value) {
            this._maxPWMValue = value;
        },

        /**
         * Sets the state value. This is populated by the
         * processPinStateResponse method of the IOBoard object. It should not
         * be called manually.
         *
         * @param {Number} state The state of the pin. For output modes, the
         * state is any value that has been previously written to the pin. For
         * input modes, the state is typically zero, however for digital inputs
         * the state is the status of the pullup resistor.
         * @private
         */
        setState: function (state) {
            // convert PWM values to 0.0 - 1.0 range
            if (this._type === Pin.PWM) {
                state = state / this.maxPWMValue;
            }

            this._state = state;
        },

        /**
         * [read-only] The maximum PWM value supported for this pin.
         *
 This is the max PWM value supported by Arduino (currently 255)
         * rather than the max PWM value specified by the microcontroller
         * datasheet.

         *
         * @property maxPWMValue
         * @type Number
         */
        get maxPWMValue() {
            return this._maxPWMValue;
        },

        /**
         * [read-only] The average value of the pin over time. Call clear() to
         * reset.
         * @property average
         * @type Number
         */
        get average() {
            return this._average;
        },

        /**
         * [read-only] The minimum value of the pin over time. Call clear() to
         * reset.
         * @property minimum
         * @type Number
         */
        get minimum() {
            return this._minimum;
        },

        /**
         * [read-only] The maximum value of the pin over time. Call clear() to
         * reset.
         * @property maximum
         * @type Number
         */
        get maximum() {
            return this._maximum;
        },

        /**
         *
[read-only] The state of the pin. For output modes, the state is
         * any value that has been previously written to the pin. For input
         * modes, the state is typically zero, however for digital inputs the
         * state is the status of the pullup resistor.

         *
         *
This propery is populated by calling the queryPinState method of
         * the IOBoard object. This is useful if there are multiple client
         * applications connected to a single physical IOBoard and you want to
         * get the state of a pin that is set by another client application.

         *
         * @property state
         * @type Number
         */
        get state() {
            return this._state;
        },

        /**
         * The current digital or analog value of the pin.
         * @property value
         * @type Number
         */
        get value() {
            return this._value;
        },
        set value(val) {
            this._lastValue = this._value;
            this._preFilterValue = val;
            this._value = this.applyFilters(val);
            this.calculateMinMaxAndMean(this._value);
            this.detectChange(this._lastValue, this._value);
        },

        /**
         * [read-only] The last pin value.
         * @property lastValue
         * @type Number
         */
        get lastValue() {
            return this._lastValue;
        },

        /**
         * [read-only] The value before any filters were applied.
         * @property preFilterValue
         * @type Number
         */
        get preFilterValue() {
            return this._preFilterValue;
        },

        /**
         * Get and set filters for the Pin.
         * @property filters
         * @type FilterBase[]
         */
        get filters() {
            return this._filters;
        },
        set filters(filterArray) {
            this._filters = filterArray;
        },

        /**
         * [read-only] Get a reference to the current generator.
         * @property generator
         * @type GeneratorBase
         */
        get generator() {
            return this._generator;
        },

        /**
         * The type/mode of the pin (0: DIN, 1: DOUT, 2: AIN, 3: AOUT / PWM,
         * 4: SERVO, 5: SHIFT, 6: I2C). Use
         * IOBoard.setDigitalPinMode(pinNumber) to set the pin type.
         * @method getType
         * @return {Number} The pin type/mode
         */
        getType: function () {
            return this._type;
        },

        /**
         * Set the pin type. This method should only be used internally.
         * @private
         */
        setType: function (pinType) {
            // Ensure pin type is valid
            if (pinType >= 0 && pinType < Pin.TOTAL_PIN_MODES) {
                this._type = pinType;
            }
        },

        /**
         * An object storing the capabilities of the pin.
         * @method getCapabilities
         * @return {Object} An object describing the capabilities of this Pin.
         */
        getCapabilities: function () {
            return this._capabilities;
        },

        /**
         * This method should only be used internally.
         * @private
         */
        setCapabilities: function (pinCapabilities) {
            this._capabilities = pinCapabilities;
        },

        /**
         * Dispatch a Change event whenever a pin value changes
         * @private
         * @method detectChange
         */
        detectChange: function (oldValue, newValue) {
            if (oldValue === newValue) {
                return;
            }
            this.dispatchEvent(new PinEvent(PinEvent.CHANGE));

            if (oldValue <= 0 && newValue !== 0) {
                this.dispatchEvent(new PinEvent(PinEvent.RISING_EDGE));
            } else if (oldValue !== 0 && newValue <= 0) {
                this.dispatchEvent(new PinEvent(PinEvent.FALLING_EDGE));
            }
        },

        /**
         * From funnel Pin.as
         * @private
         * @method clearWeight
         */
        clearWeight: function () {
            this._sum = this._average;
            this._numSamples = 1;
        },

        /**
         * From funnel Pin.as
         * @private
         * @method calculateMinMaxAndMean
         */
        calculateMinMaxAndMean: function (val) {
            var MAX_SAMPLES = Number.MAX_VALUE;

            this._minimum = Math.min(val, this._minimum);
            this._maximum = Math.max(val, this._maximum);

            this._sum += val;
            this._average = this._sum / (++this._numSamples);
            if (this._numSamples >= MAX_SAMPLES) {
                this.clearWeight();
            }
        },

        /**
         * Resets the minimum, maximum, average and lastValue of the pin.
         * @method clear
         */
        clear: function () {
            this._minimum = this._maximum = this._average = this._lastValue = this._preFilterValue;
            this.clearWeight();
        },

        /**
         * Add a new filter to the Pin.
         * @method addFilter
         * @param {FilterBase} newFilter A filter object that extends
         * FilterBase.
         * @see BO.filters.Convolution
         * @see BO.filters.Scaler
         * @see BO.filters.TriggerPoint
         */
        addFilter: function (newFilter) {

            if (newFilter === null) {
                return;
            }

            if (this._filters === null) {
                this._filters = [];
            }

            this._filters.push(newFilter);
        },

        /**
         * Remove a specified filter from the Pin.
         * @method removeFilter
         * @param {FilterBase} filterToRemove The filter to remove.
         * @see BO.filters.Convolution
         * @see BO.filters.Scaler
         * @see BO.filters.TriggerPoint
         */
        removeFilter: function (filterToRemove) {
            var index;

            if (this._filters.length < 1) {
                return;
            }

            index = this._filters.indexOf(filterToRemove);

            if (index !== -1) {
                this._filters.splice(index, 1);
            }
        },

        /**
         * Add a new generator to the Pin. A pin can only have one generator
         * assigned.
         * Assigning a new generator will replace the previously assigned
         * generator.
         * @method addGenerator
         * @param {GeneratorBase} newGenerator A generator object that extends
         * GeneratorBase.
         * @see BO.generators.Oscillator
         */
        addGenerator: function (newGenerator) {
            this.removeGenerator();
            this._generator = newGenerator;
            // BO.generators.GeneratorEvent.UPDATE = "update"
            this._generator.addEventListener("update", this._autoSetValueCallback);
        },

        /**
         * Removes the generator from the pin.
         * @method removeGenerator
         */
        removeGenerator: function () {
            if (this._generator !== null) {
                // BO.generators.GeneratorEvent.UPDATE = "update"
                this._generator.removeEventListener("update", this._autoSetValueCallback);
            }
            this._generator = null;
        },

        /**
         * Removes all filters from the pin.
         * @method removeAllFilters
         */
        removeAllFilters: function () {
            this._filters = null;
        },

        /**
         * @private
         * @method autoSetValue
         */
        autoSetValue: function (event) {
            var val = this._generator.value;
            this.value = val;
        },

        /**
         * @private
         * @method applyFilters
         */
        applyFilters: function (val) {
            var result;

            if (this._filters === null) {
                return val;
            }

            result = val;
            var len = this._filters.length;
            for (var i = 0; i < len; i++) {
                result = this._filters[i].processSample(result);
            }

            return result;
        },

        // Implement EventDispatcher

        /**
         * @param {String} type The event type
         * @param {Function} listener The function to be called when the event
         * is fired
         */
        addEventListener: function (type, listener) {
            this._evtDispatcher.addEventListener(type, listener);
        },

        /**
         * @param {String} type The event type
         * @param {Function} listener The function to be called when the event
         * is fired
         */
        removeEventListener: function (type, listener) {
            this._evtDispatcher.removeEventListener(type, listener);
        },

        /**
         * @param {String} type The event type
         * return {boolean} True is listener exists for this type, false if not.
         */
        hasEventListener: function (type) {
            return this._evtDispatcher.hasEventListener(type);
        },

        /**
         * @param {PinEvent} type The Event object
         * @param {Object} optionalParams Optional parameters to assign to the
         * event object.
         * return {boolean} True if dispatch is successful, false if not.
         */
        dispatchEvent: function (event, optionalParams) {
            return this._evtDispatcher.dispatchEvent(event, optionalParams);
        }

    };

    /**
     * @property Pin.HIGH
     * @static
     */
    Pin.HIGH = 1;
    /**
     * @property Pin.LOW
     * @static
     */
    Pin.LOW = 0;
    /**
     * @property Pin.ON
     * @static
     */
    Pin.ON = 1;
    /**
     * @property Pin.OFF
     * @static
     */
    Pin.OFF = 0;

    // Pin modes
    /**
     * @property Pin.DIN
     * @static
     */
    Pin.DIN = 0x00;
    /**
     * @property Pin.DOUT
     * @static
     */
    Pin.DOUT = 0x01;
    /**
     * @property Pin.AIN
     * @static
     */
    Pin.AIN = 0x02;
    /**
     * @property Pin.AOUT
     * @static
     */
    Pin.AOUT = 0x03;
    /**
     * @property Pin.PWM
     * @static
     */
    Pin.PWM = 0x03;
    /**
     * @property Pin.SERVO
     * @static
     */
    Pin.SERVO = 0x04;
    /**
     * @property Pin.SHIFT
     * @static
     */
    Pin.SHIFT = 0x05;
    /**
     * @property Pin.I2C
     * @static
     */
    Pin.I2C = 0x06;
    /**
     * @property Pin.ONEWIRE
     * @static
     */
    Pin.ONEWIRE = 0x07;
    /**
     * @property Pin.STEPPER
     * @static
     */
    Pin.STEPPER = 0x08;
    /**
     * @property Pin.TOTAL_PIN_MODES
     * @static
     */
    Pin.TOTAL_PIN_MODES = 9;

    // Document events

    /**
     * The pinChange event is dispatched when the pin value changes.
     * @type BO.PinEvent.CHANGE
     * @event pinChange
     * @param {BO.Pin} target A reference to the Pin object.
     */

    /**
     * The risingEdge event is dispatched when the pin value increased
     * (from 0 to 1).
     * @type BO.PinEvent.RISING_EDGE
     * @event risingEdge
     * @param {BO.Pin} target A reference to the Pin object.
     */

    /**
     * The change event is dispatched when the pin value decreased
     * (from 1 to 0).
     * @type BO.PinEvent.FALLING_EDGE
     * @event fallingEdge
     * @param {BO.Pin} target A reference to the Pin object.
     */

    return Pin;

}());
<|endoftext|>


<commit_before>const utils = require('./utils.js')
var fs = require('fs');

var prompt = require('prompt');
var colors = require("colors/safe")

var ar = __dirname.split("/");
var rootPath = ar.splice(0, ar.length-1).join("/");

var contPath = '/app/controllers';
const viewsPath = '/app/views';

var logDetail = (str) => {
	console.log("    - "+str);
}

prompt.start();
prompt.message = colors.rainbow(" >> ");

var path = "";

module.exports = {
	generateLogin: function(cb) {
		createControllerFile((err) => {
			if(err){
				cb(true);
				return;
			}
			//create view file
			createViewFile((err) =>{
				if(err){
					cb(true);
					return;
				}
				changeAuthHelper((err) => {
					if(err){
						cb(true);
						return;
					}
					cb(false);
				});
			});
		});
	}
}

var createControllerFile = (cb) => {
	var controllerName = "Login";
	var tempConFile = rootPath+'/template/login/controller.ejs';
	var txtData = fs.readFileSync(tempConFile, 'utf-8');
	var cmdPath = process.cwd();
	utils.checkPath(cmdPath, contPath, (err, pathChk) => {
		if(err){
			logDetail('connot found folder controller');
			cb(true);
			return;
		}
		path = pathChk;
		//write file
		var targetFile = path+contPath+"/"+utils.titleFormatName(controllerName)+"Controller.js";
		fs.writeFile(targetFile, txtData, (err) =>{
			if(err){
				logDetail(err);
				cb(true);
			}else{
				logDetail('create file login controller completed');
				cb(false);
			}
		});
	});
}

var createViewFile = (cb) => {
	var tempConFile = rootPath+'/template/login/login.ejs';
	var txtData = fs.readFileSync(tempConFile, 'utf-8');

	logDetail('create login view controller folder');
	var viewFolder = path + viewsPath+"/login";
	// fs.mkdirSync(viewFolder);
	fs.mkdir(viewFolder, function(err){
		if(err && err.code != "EEXIST"){
			logDetail(err);
		}
		logDetail('create file view');
		var targetFile = viewFolder+"/index.html";
		fs.writeFile(targetFile, txtData, (err) => {
			if(err){
				logDetail(err);
				cb(true);
			}else{
				logDetail('create file login controller completed');
				cb(false);
			}
		});
	});
}

var changeAuthHelper = (cb) => {
	var schemaOverride = {
		properties:{
			confirmOverride:{
				message:'are you want redirect to login when not login (Y/N)'
			}
		}
	}

	prompt.get([schemaOverride], function(err, result){
		if(result.confirmOverride.toLowerCase()=="y"){
			var txt = `module.exports = {
	checkAuth: function(req, res, next){
		if(req.cookies == undefined || req.cookies.logined == undefined || req.cookies.logined == false){
			res.redirect("/login/index");
			return;
		}
		next();
	}
}`;
		var authFile = path+'/app/helpers/auth.js';
		fs.writeFile(authFile, txt, (err) => {
			if(err){
				cb(true);
				return;
			}
			cb(false);
		})
		}else{
			cb(false);
		}
	});
}<commit_msg>insert indent<commit_after>const utils = require('./utils.js')
var fs = require('fs');

var prompt = require('prompt');
var colors = require("colors/safe")

var ar = __dirname.split("/");
var rootPath = ar.splice(0, ar.length-1).join("/");

var contPath = '/app/controllers';
const viewsPath = '/app/views';

var logDetail = (str) => {
	console.log("    - "+str);
}

prompt.start();
prompt.message = colors.rainbow(" >> ");

var path = "";

module.exports = {
	generateLogin: function(cb) {
		createControllerFile((err) => {
			if(err){
				cb(true);
				return;
			}
			//create view file
			createViewFile((err) =>{
				if(err){
					cb(true);
					return;
				}
				changeAuthHelper((err) => {
					if(err){
						cb(true);
						return;
					}
					cb(false);
				});
			});
		});
	}
}

var createControllerFile = (cb) => {
	var controllerName = "Login";
	var tempConFile = rootPath+'/template/login/controller.ejs';
	var txtData = fs.readFileSync(tempConFile, 'utf-8');
	var cmdPath = process.cwd();
	utils.checkPath(cmdPath, contPath, (err, pathChk) => {
		if(err){
			logDetail('connot found folder controller');
			cb(true);
			return;
		}
		path = pathChk;
		//write file
		var targetFile = path+contPath+"/"+utils.titleFormatName(controllerName)+"Controller.js";
		fs.writeFile(targetFile, txtData, (err) =>{
			if(err){
				logDetail(err);
				cb(true);
			}else{
				logDetail('create file login controller completed');
				cb(false);
			}
		});
	});
}

var createViewFile = (cb) => {
	var tempConFile = rootPath+'/template/login/login.ejs';
	var txtData = fs.readFileSync(tempConFile, 'utf-8');

	logDetail('create login view controller folder');
	var viewFolder = path + viewsPath+"/login";
	// fs.mkdirSync(viewFolder);
	fs.mkdir(viewFolder, function(err){
		if(err && err.code != "EEXIST"){
			logDetail(err);
		}
		logDetail('create file view');
		var targetFile = viewFolder+"/index.html";
		fs.writeFile(targetFile, txtData, (err) => {
			if(err){
				logDetail(err);
				cb(true);
			}else{
				logDetail('create file login controller completed');
				cb(false);
			}
		});
	});
}

var changeAuthHelper = (cb) => {
	var schemaOverride = {
		properties:{
			confirmOverride:{
				message:'are you want redirect to login when not login (Y/N)'
			}
		}
	}

	prompt.get([schemaOverride], function(err, result){
		if(result.confirmOverride.toLowerCase()=="y"){
			var txt = `module.exports = {
	checkAuth: function(req, res, next){
		if(req.cookies == undefined || req.cookies.logined == undefined || req.cookies.logined == false){
			res.redirect("/login/index");
			return;
		}
		next();
	}
}`;
			var authFile = path+'/app/helpers/auth.js';
			fs.writeFile(authFile, txt, (err) => {
				if(err){
					cb(true);
					return;
				}
				cb(false);
			})
		}else{
			cb(false);
		}
	});
}<|endoftext|>


<commit_before>// DOM utility methods used by Rangy
/* build:replaceWith(api) */rangy/* build:replaceEnd */.createCoreModule("DomUtil", [], function(api, module) {
    var log = log4javascript.getLogger("rangy.dom");
    var UNDEF = "undefined";
    var util = api.util;

    // Perform feature tests
    if (!util.areHostMethods(document, ["createDocumentFragment", "createElement", "createTextNode"])) {
        module.fail("document missing a Node creation method");
    }

    if (!util.isHostMethod(document, "getElementsByTagName")) {
        module.fail("document missing getElementsByTagName method");
    }

    var el = document.createElement("div");
    if (!util.areHostMethods(el, ["insertBefore", "appendChild", "cloneNode"] ||
            !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]))) {
        module.fail("Incomplete Element implementation");
    }

    // innerHTML is required for Range's createContextualFragment method
    if (!util.isHostProperty(el, "innerHTML")) {
        module.fail("Element is missing innerHTML property");
    }

    var textNode = document.createTextNode("test");
    if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
            !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
            !util.areHostProperties(textNode, ["data"]))) {
        module.fail("Incomplete Text Node implementation");
    }

    /*----------------------------------------------------------------------------------------------------------------*/

    // Removed use of indexOf because of a bizarre bug in Opera that is thrown in one of the Acid3 tests. I haven't been
    // able to replicate it outside of the test. The bug is that indexOf returns -1 when called on an Array that
    // contains just the document as a single element and the value searched for is the document.
    var arrayContains = /*Array.prototype.indexOf ?
        function(arr, val) {
            return arr.indexOf(val) > -1;
        }:*/

        function(arr, val) {
            var i = arr.length;
            while (i--) {
                if (arr[i] === val) {
                    return true;
                }
            }
            return false;
        };

    // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
    function isHtmlNamespace(node) {
        var ns;
        return typeof node.namespaceURI == UNDEF || ((ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml");
    }

    function parentElement(node) {
        var parent = node.parentNode;
        return (parent.nodeType == 1) ? parent : null;
    }

    function getNodeIndex(node) {
        var i = 0;
        while( (node = node.previousSibling) ) {
            ++i;
        }
        return i;
    }

    function getNodeLength(node) {
        switch (node.nodeType) {
            case 7:
            case 10:
                return 0;
            case 3:
            case 8:
                return node.length;
            default:
                return node.childNodes.length;
        }
    }

    function getCommonAncestor(node1, node2) {
        var ancestors = [], n;
        for (n = node1; n; n = n.parentNode) {
            ancestors.push(n);
        }

        for (n = node2; n; n = n.parentNode) {
            if (arrayContains(ancestors, n)) {
                return n;
            }
        }

        return null;
    }

    function isAncestorOf(ancestor, descendant, selfIsAncestor) {
        var n = selfIsAncestor ? descendant : descendant.parentNode;
        while (n) {
            if (n === ancestor) {
                return true;
            } else {
                n = n.parentNode;
            }
        }
        return false;
    }

    function isOrIsAncestorOf(ancestor, descendant) {
        return isAncestorOf(ancestor, descendant, true);
    }

    function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
        var p, n = selfIsAncestor ? node : node.parentNode;
        while (n) {
            p = n.parentNode;
            if (p === ancestor) {
                return n;
            }
            n = p;
        }
        return null;
    }

    function isCharacterDataNode(node) {
        var t = node.nodeType;
        return t == 3 || t == 4 || t == 8 ; // Text, CDataSection or Comment
    }

    function isTextOrCommentNode(node) {
        if (!node) {
            return false;
        }
        var t = node.nodeType;
        return t == 3 || t == 8 ; // Text or Comment
    }

    function insertAfter(node, precedingNode) {
        var nextNode = precedingNode.nextSibling, parent = precedingNode.parentNode;
        if (nextNode) {
            parent.insertBefore(node, nextNode);
        } else {
            parent.appendChild(node);
        }
        return node;
    }

    // Note that we cannot use splitText() because it is bugridden in IE 9.
    function splitDataNode(node, index, positionsToPreserve) {
        log.debug("splitDataNode called at index " + index + " in node " + inspectNode(node));
        var newNode = node.cloneNode(false);
        newNode.deleteData(0, index);
        node.deleteData(index, node.length - index);
        insertAfter(newNode, node);

        // Preserve positions
        if (positionsToPreserve) {
            for (var i = 0, position; position = positionsToPreserve[i++]; ) {
                // Handle case where position was inside the portion of node after the split point
                if (position.node == node && position.offset > index) {
                    position.node = newNode;
                    position.offset -= index;
                }
                // Handle the case where the position is a node offset within node's parent
                else if (position.node == node.parentNode && position.offset > getNodeIndex(node)) {
                    ++position.offset;
                }
            }
        }
        return newNode;
    }

    function getDocument(node) {
        if (node.nodeType == 9) {
            return node;
        } else if (typeof node.ownerDocument != UNDEF) {
            return node.ownerDocument;
        } else if (typeof node.document != UNDEF) {
            return node.document;
        } else if (node.parentNode) {
            return getDocument(node.parentNode);
        } else {
            throw module.createError("getDocument: no document found for node");
        }
    }

    function getWindow(node) {
        var doc = getDocument(node);
        if (typeof doc.defaultView != UNDEF) {
            return doc.defaultView;
        } else if (typeof doc.parentWindow != UNDEF) {
            return doc.parentWindow;
        } else {
            throw module.createError("Cannot get a window object for node");
        }
    }

    function getIframeDocument(iframeEl) {
        if (typeof iframeEl.contentDocument != UNDEF) {
            return iframeEl.contentDocument;
        } else if (typeof iframeEl.contentWindow != UNDEF) {
            return iframeEl.contentWindow.document;
        } else {
            throw module.createError("getIframeDocument: No Document object found for iframe element");
        }
    }

    function getIframeWindow(iframeEl) {
        if (typeof iframeEl.contentWindow != UNDEF) {
            return iframeEl.contentWindow;
        } else if (typeof iframeEl.contentDocument != UNDEF) {
            return iframeEl.contentDocument.defaultView;
        } else {
            throw module.createError("getIframeWindow: No Window object found for iframe element");
        }
    }

    // This looks bad. Is it worth it?
    function isWindow(obj) {
        return obj && util.isHostMethod(obj, "setTimeout") && util.isHostObject(obj, "document");
    }

    function getContentDocument(obj, module, methodName) {
        var doc;

        if (!obj) {
            doc = document;
        }

        // Test if a DOM node has been passed and obtain a document object for it if so
        else if (util.isHostProperty(obj, "nodeType")) {
            doc = (obj.nodeType == 1 && obj.tagName.toLowerCase() == "iframe") ?
                getIframeDocument(obj) : getDocument(obj);
        }

        // Test if the doc parameter appears to be a Window object
        else if (isWindow(obj)) {
            doc = obj.document;
        }

        if (!doc) {
            throw module.createError(methodName + "(): Parameter must be a Window object or DOM node");
        }

        return doc;
    }

    function getRootContainer(node) {
        var parent;
        while ( (parent = node.parentNode) ) {
            node = parent;
        }
        return node;
    }

    function comparePoints(nodeA, offsetA, nodeB, offsetB) {
        // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
        var nodeC, root, childA, childB, n;
        if (nodeA == nodeB) {
            log.debug("case 1");
            // Case 1: nodes are the same
            return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
        } else if ( (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) ) {
            log.debug("case 2", inspectNode(nodeC), getNodeIndex(nodeC));
            // Case 2: node C (container B or an ancestor) is a child node of A
            return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
        } else if ( (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) ) {
            log.debug("case 3");
            // Case 3: node C (container A or an ancestor) is a child node of B
            return getNodeIndex(nodeC) < offsetB  ? -1 : 1;
        } else {
            root = getCommonAncestor(nodeA, nodeB);
            if (!root) {
                throw new Error("comparePoints error: nodes have no common ancestor");
            }

            // Case 4: containers are siblings or descendants of siblings
            log.debug("case 4");
            childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
            childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);

            if (childA === childB) {
                // This shouldn't be possible
                log.warn("comparePoints got to case 4 and childA and childB are the same!", nodeA, offsetA, nodeB, offsetB);
                throw module.createError("comparePoints got to case 4 and childA and childB are the same!");
            } else {
                n = root.firstChild;
                while (n) {
                    if (n === childA) {
                        return -1;
                    } else if (n === childB) {
                        return 1;
                    }
                    n = n.nextSibling;
                }
            }
        }
    }

    /*----------------------------------------------------------------------------------------------------------------*/

    // Test for IE's crash (IE 6/7) or exception (IE >= 8) when a reference to garbage-collected text node is queried
    var crashyTextNodes = false;

    function isBrokenNode(node) {
        var n;
        try {
            n = node.parentNode;
            return false;
        } catch (e) {
            return true;
        }
    }

    (function() {
        var el = document.createElement("b");
        el.innerHTML = "1";
        var textNode = el.firstChild;
        el.innerHTML = "<br>";
        crashyTextNodes = isBrokenNode(textNode);

        api.features.crashyTextNodes = crashyTextNodes;
    })();

    /*----------------------------------------------------------------------------------------------------------------*/

    function inspectNode(node) {
        if (!node) {
            return "[No node]";
        }
        if (crashyTextNodes && isBrokenNode(node)) {
            return "[Broken node]";
        }
        if (isCharacterDataNode(node)) {
            return '"' + node.data + '"';
        }
        if (node.nodeType == 1) {
            var idAttr = node.id ? ' id="' + node.id + '"' : "";
            return "<" + node.nodeName + idAttr + ">[index:" + getNodeIndex(node) + ",length:" + node.childNodes.length + "][" + (node.innerHTML || "[innerHTML not supported]").slice(0, 25) + "]";
        }
        return node.nodeName;
    }

    function fragmentFromNodeChildren(node) {
        var fragment = getDocument(node).createDocumentFragment(), child;
        while ( (child = node.firstChild) ) {
            fragment.appendChild(child);
        }
        return fragment;
    }

    var getComputedStyleProperty;
    if (typeof window.getComputedStyle != UNDEF) {
        getComputedStyleProperty = function(el, propName) {
            return getWindow(el).getComputedStyle(el, null)[propName];
        };
    } else if (typeof document.documentElement.currentStyle != UNDEF) {
        getComputedStyleProperty = function(el, propName) {
            return el.currentStyle[propName];
        };
    } else {
        module.fail("No means of obtaining computed style properties found");
    }

    function NodeIterator(root) {
        this.root = root;
        this._next = root;
    }

    NodeIterator.prototype = {
        _current: null,

        hasNext: function() {
            return !!this._next;
        },

        next: function() {
            var n = this._current = this._next;
            var child, next;
            if (this._current) {
                child = n.firstChild;
                if (child) {
                    this._next = child;
                } else {
                    next = null;
                    while ((n !== this.root) && !(next = n.nextSibling)) {
                        n = n.parentNode;
                    }
                    this._next = next;
                }
            }
            return this._current;
        },

        detach: function() {
            this._current = this._next = this.root = null;
        }
    };

    function createIterator(root) {
        return new NodeIterator(root);
    }

    function DomPosition(node, offset) {
        this.node = node;
        this.offset = offset;
    }

    DomPosition.prototype = {
        equals: function(pos) {
            return !!pos && this.node === pos.node && this.offset == pos.offset;
        },

        inspect: function() {
            return "[DomPosition(" + inspectNode(this.node) + ":" + this.offset + ")]";
        },

        toString: function() {
            return this.inspect();
        }
    };

    function DOMException(codeName) {
        this.code = this[codeName];
        this.codeName = codeName;
        this.message = "DOMException: " + this.codeName;
    }

    DOMException.prototype = {
        INDEX_SIZE_ERR: 1,
        HIERARCHY_REQUEST_ERR: 3,
        WRONG_DOCUMENT_ERR: 4,
        NO_MODIFICATION_ALLOWED_ERR: 7,
        NOT_FOUND_ERR: 8,
        NOT_SUPPORTED_ERR: 9,
        INVALID_STATE_ERR: 11,
        INVALID_NODE_TYPE_ERR: 24
    };

    DOMException.prototype.toString = function() {
        return this.message;
    };

    api.dom = {
        arrayContains: arrayContains,
        isHtmlNamespace: isHtmlNamespace,
        parentElement: parentElement,
        getNodeIndex: getNodeIndex,
        getNodeLength: getNodeLength,
        getCommonAncestor: getCommonAncestor,
        isAncestorOf: isAncestorOf,
        isOrIsAncestorOf: isOrIsAncestorOf,
        getClosestAncestorIn: getClosestAncestorIn,
        isCharacterDataNode: isCharacterDataNode,
        isTextOrCommentNode: isTextOrCommentNode,
        insertAfter: insertAfter,
        splitDataNode: splitDataNode,
        getDocument: getDocument,
        getWindow: getWindow,
        getIframeWindow: getIframeWindow,
        getIframeDocument: getIframeDocument,
        getBody: util.getBody,
        isWindow: isWindow,
        getContentDocument: getContentDocument,
        getRootContainer: getRootContainer,
        comparePoints: comparePoints,
        isBrokenNode: isBrokenNode,
        inspectNode: inspectNode,
        getComputedStyleProperty: getComputedStyleProperty,
        fragmentFromNodeChildren: fragmentFromNodeChildren,
        createIterator: createIterator,
        DomPosition: DomPosition
    };

    api.DOMException = DOMException;
});<commit_msg>make xhtml compliant<commit_after>// DOM utility methods used by Rangy
/* build:replaceWith(api) */rangy/* build:replaceEnd */.createCoreModule("DomUtil", [], function(api, module) {
    var log = log4javascript.getLogger("rangy.dom");
    var UNDEF = "undefined";
    var util = api.util;

    // Perform feature tests
    if (!util.areHostMethods(document, ["createDocumentFragment", "createElement", "createTextNode"])) {
        module.fail("document missing a Node creation method");
    }

    if (!util.isHostMethod(document, "getElementsByTagName")) {
        module.fail("document missing getElementsByTagName method");
    }

    var el = document.createElement("div");
    if (!util.areHostMethods(el, ["insertBefore", "appendChild", "cloneNode"] ||
            !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]))) {
        module.fail("Incomplete Element implementation");
    }

    // innerHTML is required for Range's createContextualFragment method
    if (!util.isHostProperty(el, "innerHTML")) {
        module.fail("Element is missing innerHTML property");
    }

    var textNode = document.createTextNode("test");
    if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
            !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
            !util.areHostProperties(textNode, ["data"]))) {
        module.fail("Incomplete Text Node implementation");
    }

    /*----------------------------------------------------------------------------------------------------------------*/

    // Removed use of indexOf because of a bizarre bug in Opera that is thrown in one of the Acid3 tests. I haven't been
    // able to replicate it outside of the test. The bug is that indexOf returns -1 when called on an Array that
    // contains just the document as a single element and the value searched for is the document.
    var arrayContains = /*Array.prototype.indexOf ?
        function(arr, val) {
            return arr.indexOf(val) > -1;
        }:*/

        function(arr, val) {
            var i = arr.length;
            while (i--) {
                if (arr[i] === val) {
                    return true;
                }
            }
            return false;
        };

    // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
    function isHtmlNamespace(node) {
        var ns;
        return typeof node.namespaceURI == UNDEF || ((ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml");
    }

    function parentElement(node) {
        var parent = node.parentNode;
        return (parent.nodeType == 1) ? parent : null;
    }

    function getNodeIndex(node) {
        var i = 0;
        while( (node = node.previousSibling) ) {
            ++i;
        }
        return i;
    }

    function getNodeLength(node) {
        switch (node.nodeType) {
            case 7:
            case 10:
                return 0;
            case 3:
            case 8:
                return node.length;
            default:
                return node.childNodes.length;
        }
    }

    function getCommonAncestor(node1, node2) {
        var ancestors = [], n;
        for (n = node1; n; n = n.parentNode) {
            ancestors.push(n);
        }

        for (n = node2; n; n = n.parentNode) {
            if (arrayContains(ancestors, n)) {
                return n;
            }
        }

        return null;
    }

    function isAncestorOf(ancestor, descendant, selfIsAncestor) {
        var n = selfIsAncestor ? descendant : descendant.parentNode;
        while (n) {
            if (n === ancestor) {
                return true;
            } else {
                n = n.parentNode;
            }
        }
        return false;
    }

    function isOrIsAncestorOf(ancestor, descendant) {
        return isAncestorOf(ancestor, descendant, true);
    }

    function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
        var p, n = selfIsAncestor ? node : node.parentNode;
        while (n) {
            p = n.parentNode;
            if (p === ancestor) {
                return n;
            }
            n = p;
        }
        return null;
    }

    function isCharacterDataNode(node) {
        var t = node.nodeType;
        return t == 3 || t == 4 || t == 8 ; // Text, CDataSection or Comment
    }

    function isTextOrCommentNode(node) {
        if (!node) {
            return false;
        }
        var t = node.nodeType;
        return t == 3 || t == 8 ; // Text or Comment
    }

    function insertAfter(node, precedingNode) {
        var nextNode = precedingNode.nextSibling, parent = precedingNode.parentNode;
        if (nextNode) {
            parent.insertBefore(node, nextNode);
        } else {
            parent.appendChild(node);
        }
        return node;
    }

    // Note that we cannot use splitText() because it is bugridden in IE 9.
    function splitDataNode(node, index, positionsToPreserve) {
        log.debug("splitDataNode called at index " + index + " in node " + inspectNode(node));
        var newNode = node.cloneNode(false);
        newNode.deleteData(0, index);
        node.deleteData(index, node.length - index);
        insertAfter(newNode, node);

        // Preserve positions
        if (positionsToPreserve) {
            for (var i = 0, position; position = positionsToPreserve[i++]; ) {
                // Handle case where position was inside the portion of node after the split point
                if (position.node == node && position.offset > index) {
                    position.node = newNode;
                    position.offset -= index;
                }
                // Handle the case where the position is a node offset within node's parent
                else if (position.node == node.parentNode && position.offset > getNodeIndex(node)) {
                    ++position.offset;
                }
            }
        }
        return newNode;
    }

    function getDocument(node) {
        if (node.nodeType == 9) {
            return node;
        } else if (typeof node.ownerDocument != UNDEF) {
            return node.ownerDocument;
        } else if (typeof node.document != UNDEF) {
            return node.document;
        } else if (node.parentNode) {
            return getDocument(node.parentNode);
        } else {
            throw module.createError("getDocument: no document found for node");
        }
    }

    function getWindow(node) {
        var doc = getDocument(node);
        if (typeof doc.defaultView != UNDEF) {
            return doc.defaultView;
        } else if (typeof doc.parentWindow != UNDEF) {
            return doc.parentWindow;
        } else {
            throw module.createError("Cannot get a window object for node");
        }
    }

    function getIframeDocument(iframeEl) {
        if (typeof iframeEl.contentDocument != UNDEF) {
            return iframeEl.contentDocument;
        } else if (typeof iframeEl.contentWindow != UNDEF) {
            return iframeEl.contentWindow.document;
        } else {
            throw module.createError("getIframeDocument: No Document object found for iframe element");
        }
    }

    function getIframeWindow(iframeEl) {
        if (typeof iframeEl.contentWindow != UNDEF) {
            return iframeEl.contentWindow;
        } else if (typeof iframeEl.contentDocument != UNDEF) {
            return iframeEl.contentDocument.defaultView;
        } else {
            throw module.createError("getIframeWindow: No Window object found for iframe element");
        }
    }

    // This looks bad. Is it worth it?
    function isWindow(obj) {
        return obj && util.isHostMethod(obj, "setTimeout") && util.isHostObject(obj, "document");
    }

    function getContentDocument(obj, module, methodName) {
        var doc;

        if (!obj) {
            doc = document;
        }

        // Test if a DOM node has been passed and obtain a document object for it if so
        else if (util.isHostProperty(obj, "nodeType")) {
            doc = (obj.nodeType == 1 && obj.tagName.toLowerCase() == "iframe") ?
                getIframeDocument(obj) : getDocument(obj);
        }

        // Test if the doc parameter appears to be a Window object
        else if (isWindow(obj)) {
            doc = obj.document;
        }

        if (!doc) {
            throw module.createError(methodName + "(): Parameter must be a Window object or DOM node");
        }

        return doc;
    }

    function getRootContainer(node) {
        var parent;
        while ( (parent = node.parentNode) ) {
            node = parent;
        }
        return node;
    }

    function comparePoints(nodeA, offsetA, nodeB, offsetB) {
        // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing
        var nodeC, root, childA, childB, n;
        if (nodeA == nodeB) {
            log.debug("case 1");
            // Case 1: nodes are the same
            return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
        } else if ( (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) ) {
            log.debug("case 2", inspectNode(nodeC), getNodeIndex(nodeC));
            // Case 2: node C (container B or an ancestor) is a child node of A
            return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
        } else if ( (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) ) {
            log.debug("case 3");
            // Case 3: node C (container A or an ancestor) is a child node of B
            return getNodeIndex(nodeC) < offsetB  ? -1 : 1;
        } else {
            root = getCommonAncestor(nodeA, nodeB);
            if (!root) {
                throw new Error("comparePoints error: nodes have no common ancestor");
            }

            // Case 4: containers are siblings or descendants of siblings
            log.debug("case 4");
            childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
            childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);

            if (childA === childB) {
                // This shouldn't be possible
                log.warn("comparePoints got to case 4 and childA and childB are the same!", nodeA, offsetA, nodeB, offsetB);
                throw module.createError("comparePoints got to case 4 and childA and childB are the same!");
            } else {
                n = root.firstChild;
                while (n) {
                    if (n === childA) {
                        return -1;
                    } else if (n === childB) {
                        return 1;
                    }
                    n = n.nextSibling;
                }
            }
        }
    }

    /*----------------------------------------------------------------------------------------------------------------*/

    // Test for IE's crash (IE 6/7) or exception (IE >= 8) when a reference to garbage-collected text node is queried
    var crashyTextNodes = false;

    function isBrokenNode(node) {
        var n;
        try {
            n = node.parentNode;
            return false;
        } catch (e) {
            return true;
        }
    }

    (function() {
        var el = document.createElement("b");
        el.innerHTML = "1";
        var textNode = el.firstChild;
        el.innerHTML = "<br />";
        crashyTextNodes = isBrokenNode(textNode);

        api.features.crashyTextNodes = crashyTextNodes;
    })();

    /*----------------------------------------------------------------------------------------------------------------*/

    function inspectNode(node) {
        if (!node) {
            return "[No node]";
        }
        if (crashyTextNodes && isBrokenNode(node)) {
            return "[Broken node]";
        }
        if (isCharacterDataNode(node)) {
            return '"' + node.data + '"';
        }
        if (node.nodeType == 1) {
            var idAttr = node.id ? ' id="' + node.id + '"' : "";
            return "<" + node.nodeName + idAttr + ">[index:" + getNodeIndex(node) + ",length:" + node.childNodes.length + "][" + (node.innerHTML || "[innerHTML not supported]").slice(0, 25) + "]";
        }
        return node.nodeName;
    }

    function fragmentFromNodeChildren(node) {
        var fragment = getDocument(node).createDocumentFragment(), child;
        while ( (child = node.firstChild) ) {
            fragment.appendChild(child);
        }
        return fragment;
    }

    var getComputedStyleProperty;
    if (typeof window.getComputedStyle != UNDEF) {
        getComputedStyleProperty = function(el, propName) {
            return getWindow(el).getComputedStyle(el, null)[propName];
        };
    } else if (typeof document.documentElement.currentStyle != UNDEF) {
        getComputedStyleProperty = function(el, propName) {
            return el.currentStyle[propName];
        };
    } else {
        module.fail("No means of obtaining computed style properties found");
    }

    function NodeIterator(root) {
        this.root = root;
        this._next = root;
    }

    NodeIterator.prototype = {
        _current: null,

        hasNext: function() {
            return !!this._next;
        },

        next: function() {
            var n = this._current = this._next;
            var child, next;
            if (this._current) {
                child = n.firstChild;
                if (child) {
                    this._next = child;
                } else {
                    next = null;
                    while ((n !== this.root) && !(next = n.nextSibling)) {
                        n = n.parentNode;
                    }
                    this._next = next;
                }
            }
            return this._current;
        },

        detach: function() {
            this._current = this._next = this.root = null;
        }
    };

    function createIterator(root) {
        return new NodeIterator(root);
    }

    function DomPosition(node, offset) {
        this.node = node;
        this.offset = offset;
    }

    DomPosition.prototype = {
        equals: function(pos) {
            return !!pos && this.node === pos.node && this.offset == pos.offset;
        },

        inspect: function() {
            return "[DomPosition(" + inspectNode(this.node) + ":" + this.offset + ")]";
        },

        toString: function() {
            return this.inspect();
        }
    };

    function DOMException(codeName) {
        this.code = this[codeName];
        this.codeName = codeName;
        this.message = "DOMException: " + this.codeName;
    }

    DOMException.prototype = {
        INDEX_SIZE_ERR: 1,
        HIERARCHY_REQUEST_ERR: 3,
        WRONG_DOCUMENT_ERR: 4,
        NO_MODIFICATION_ALLOWED_ERR: 7,
        NOT_FOUND_ERR: 8,
        NOT_SUPPORTED_ERR: 9,
        INVALID_STATE_ERR: 11,
        INVALID_NODE_TYPE_ERR: 24
    };

    DOMException.prototype.toString = function() {
        return this.message;
    };

    api.dom = {
        arrayContains: arrayContains,
        isHtmlNamespace: isHtmlNamespace,
        parentElement: parentElement,
        getNodeIndex: getNodeIndex,
        getNodeLength: getNodeLength,
        getCommonAncestor: getCommonAncestor,
        isAncestorOf: isAncestorOf,
        isOrIsAncestorOf: isOrIsAncestorOf,
        getClosestAncestorIn: getClosestAncestorIn,
        isCharacterDataNode: isCharacterDataNode,
        isTextOrCommentNode: isTextOrCommentNode,
        insertAfter: insertAfter,
        splitDataNode: splitDataNode,
        getDocument: getDocument,
        getWindow: getWindow,
        getIframeWindow: getIframeWindow,
        getIframeDocument: getIframeDocument,
        getBody: util.getBody,
        isWindow: isWindow,
        getContentDocument: getContentDocument,
        getRootContainer: getRootContainer,
        comparePoints: comparePoints,
        isBrokenNode: isBrokenNode,
        inspectNode: inspectNode,
        getComputedStyleProperty: getComputedStyleProperty,
        fragmentFromNodeChildren: fragmentFromNodeChildren,
        createIterator: createIterator,
        DomPosition: DomPosition
    };

    api.DOMException = DOMException;
});<|endoftext|>


<commit_before>// Processing is done based on the list found at
// https://wiki.mozilla.org/WebExtensions#List_of_supported_APIs

// Since support of an API is not binary, there is more logic here
// than on chrome APIs detection

function SupportStatus (type, msg) {
    this.type = type;
    this.msg = msg;
}

function FutureSupport () {
    return function (propsChain) {
        return new SupportStatus("FUTURE_SUPPORT");
    };
}

function FullSupport () {
    return function (propsChain) {
        return new SupportStatus("FULL_SUPPORT");
    };
}

function ExtensionSupport (propsChain) {
    var support = [
        "getURL",
        "inIncognitoContex",
        "getBackgroundPage",
        "getViews",
    ].join('|');

    var r = new RegExp("^(" + support + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("SUPPORT");
    }

    var futureSupport = [
        "isAllowedIncognitoAccess",
        "isAllowedFileSchemeAccess",
        "setUpdateUrlData",
        "lastError",
    ].join('|');

    var r2 = new RegExp("^(" + futureSupport + ")");

    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("FUTURE_SUPPORT");
    }

    return new SupportStatus("NO_SUPPORT");
}

function i18nSupport (propsChain) {
    if (propsChain.indexOf("getMessage") === -1) {
        return new SupportStatus("NO_SUPPORT");
    }

    return new SupportStatus("SUPPORT");
}

function NotificationsSupport (propsChain) {
    if (propsChain.indexOf("create") === 0) {
        return new SupportStatus("WARN", "The only supported notification options are iconUrl, title, and message.");
    }

    if (propsChain.indexOf("onClosed") === 0) {
        return new SupportStatus("WARN", "The byUser data is not provided.");
    }

    if (propsChain.match(/^(getAll|clear)/) === null) {
        return new SupportStatus("NO_SUPPORT");
    }

    return new SupportStatus("SUPPORT");
}

function RuntimeSupport (propsChain) {
    var support = [
        "onStartup",
        "getManifest",
        "getURL",
        "id",
        "sendMessage",
        "onMessage",
        "onConnect",
        "connect",
        "getPlatformInfo",
    ].join('|');

    var r = new RegExp("^(" + support + ")");
    if (r.exec(propsChain) !== null) {
        return new SupportStatus("SUPPORT");
    }

    var futureSupport = [
        "lastError",
        "getBackgroundPage",
        "openOptionsPage",
        "setUninstallURL",
        "reload",
        "requestUpdateCheck",
        "restart",
        "connectNative",
        "sendNativeMessage",
        "getPackageDirectoryEntry",
        "onInstalled",
        "onSuspend",
        "onSuspendCanceled",
        "onUpdateAvailable",
        "onBrowserUpdateAvailable",
        "onConnectExternal",
        "onMessageExternal",
        "onRestartRequired",
    ].join('|');

    var r2 = new RegExp("^(" + futureSupport + ")");
    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("FUTURE_SUPPORT");
    }

    return new SupportStatus("NO_SUPPORT");
}

function StorageSupport (propsChain) {
    if (propsChain.match(/^sync/) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.match(/(getBytesInUse)/) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    return new SupportStatus("SUPPORT");
}

function TabsSupport (propsChain) {
    var noSupport = [
        "sendRequest",
        "getSelected",
        "duplicate",
        "highlight",
        "move",
        "detectLanguage",
        "captureVisibleTab",
        "getZoom",
        "setZoom",
        "getZoomSettings",
        "setZoomSettings"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    var highlightActive = [
        "highlight",
        "update",
        "onActiveChanged",
        "onActivated",
        "onHighlightChanged",
        "onHighlighted"
    ].join('|');

    var r2 = new RegExp("^(" + highlightActive + ")");

    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("WARN", "Highlighted and active are treated as the same since Firefox cannot select multiple tabs.");
    }

    if (propsChain.indexOf("executeScript") === 0) {
        return [
            new SupportStatus("WARN", "The callback argument is not supported yet."),
            new SupportStatus("WARN", "Host permissions are not enforced yet.")
        ];
    }

    return new SupportStatus("SUPPORT");
}

function WebNavigationSupport (propsChain) {
    var noSupport = [
        "getFrame",
        "getAllFrames",
        "onCreatedNavigationTarget",
        "onHistoryStateUpdated"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.indexOf("onReferenceFragmentUpdated") === 0) {
        return [
            new SupportStatus("WARN", "This method also triggers for pushState."),
            new SupportStatus("WARN", "Filtering, transition types and qualifiers are unsupported.")
        ];
    }

    return new SupportStatus("WARN", "Filtering, transition types and qualifiers are unsupported.");
}

function WebRequestSupport (propsChain) {
    var noSupport = [
        "handlerBehaviorChanged",
        "onAuthRequired",
        "onBeforeRedirect",
        "onErrorOccurred"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.indexOf("onBeforeRequest") === 0) {
        return new SupportStatus("WARN", "Requests can not be modified/redirected on this method.");
    }

    if (propsChain.indexOf("onBeforeSendHeaders") === 0) {
        return new SupportStatus("WARN", "Requests can not be canceled on this method.");
    }

    if (propsChain.indexOf("") === 0) {
        return new SupportStatus("WARN", "Requests can not be redirected or canceled on this method.");
    }

    return new SupportStatus("WARN", "Requests can not be canceled, modified or redirected on this method.");
}

function WindowsSupport (propsChain) {
    if (propsChain.indexOf("onFocusChanged") === 0) {
        return new SupportStatus("WARN", "This method will trigger multiple times for a given focus change.");
    }

    if (propsChain.indexOf("create") === 0) {
        return new SupportStatus("WARN", "This method does not support the focused, type, or state options.");
    }

    if (propsChain.indexOf("update") === 0) {
        return new SupportStatus("WARN", "This method only supports the focused option.");
    }

    return new SupportStatus("SUPPORT");
}

function BookmarksSupport (propsChain) {
    var noSupport = [
        "getRecent",
        "search",
        "removeTree",
        "onCreated",
        "onRemoved",
        "onChanged",
        "onMoved",
        "onChildrenReordered",
        "onImportBegan",
        "onImportEnded",
        "BookmarkTreeNodeUnmodifiable"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.indexOf("removes") === 0) {
        return new SupportStatus("WARN", "This method also removes non empty folders.");
    }

    return new SupportStatus("SUPPORT");
}

function CookiesSupport (propsChain) {
    if (propsChain.indexOf("onChanged") === 0) {
        return new SupportStatus("WARN", "Events might be subtely different.");
    }

    if (propsChain.indexOf("set") === 0) {
        return new SupportStatus("WARN", "Creating session cookies with set might fail.");
    }

    if (propsChain.indexOf("getAllCookieStores") === 0) {
        return new SupportStatus("WARN", "This method always just returns one default store and no tabs.");
    }

    return [
        new SupportStatus("WARN", "Accessing cookies from private tabs is impossible."),
        new SupportStatus("WARN", "Host permissions are not currently enforced.")
    ];
}

module.exports = {
    // Fully supported APIs
    "alarms": FullSupport(),
    "contextMenus": FullSupport(),
    "browserAction": FullSupport(),
    "pageAction": FullSupport(),

    // Partially supported APIs
    "bookmarks": BookmarksSupport,
    "cookies": CookiesSupport,
    "extension": ExtensionSupport,
    "i18n": i18nSupport,
    "notifications": NotificationsSupport,
    "runtime": RuntimeSupport,
    "storage": StorageSupport,
    "tabs": TabsSupport,
    "webNavigation": WebNavigationSupport,
    "webRequest": WebRequestSupport,
    "windows": WindowsSupport,

    // Future APIs
    "commands": FutureSupport(),
    "downloads": FutureSupport(),
    "history": FutureSupport(),
    "idle": FutureSupport(),
    "omnibox": FutureSupport(),
    "permissions": FutureSupport(),
    "devtools.panels": FutureSupport()
};
<commit_msg>update i18nSupport<commit_after>// Processing is done based on the list found at
// https://wiki.mozilla.org/WebExtensions#List_of_supported_APIs

// Since support of an API is not binary, there is more logic here
// than on chrome APIs detection

function SupportStatus (type, msg) {
    this.type = type;
    this.msg = msg;
}

function FutureSupport () {
    return function (propsChain) {
        return new SupportStatus("FUTURE_SUPPORT");
    };
}

function FullSupport () {
    return function (propsChain) {
        return new SupportStatus("FULL_SUPPORT");
    };
}

function ExtensionSupport (propsChain) {
    var support = [
        "getURL",
        "inIncognitoContex",
        "getBackgroundPage",
        "getViews",
    ].join('|');

    var r = new RegExp("^(" + support + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("SUPPORT");
    }

    var futureSupport = [
        "isAllowedIncognitoAccess",
        "isAllowedFileSchemeAccess",
        "setUpdateUrlData",
        "lastError",
    ].join('|');

    var r2 = new RegExp("^(" + futureSupport + ")");

    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("FUTURE_SUPPORT");
    }

    return new SupportStatus("NO_SUPPORT");
}

function i18nSupport (propsChain) {
    var support = [
        "getMessage",
    ].join('|');

    var r = new RegExp("^(" + support + ")");
    if (r.exec(propsChain) !== null) {
        return new SupportStatus("SUPPORT");
    }

    var futureSupport = [
        "getAcceptLanguages",
        "getUILanguage",
        "detectLanguage",
    ].join('|');

    var r2 = new RegExp("^(" + futureSupport + ")");
    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("FUTURE_SUPPORT");
    }

    return new SupportStatus("NO_SUPPORT");
}

function NotificationsSupport (propsChain) {
    if (propsChain.indexOf("create") === 0) {
        return new SupportStatus("WARN", "The only supported notification options are iconUrl, title, and message.");
    }

    if (propsChain.indexOf("onClosed") === 0) {
        return new SupportStatus("WARN", "The byUser data is not provided.");
    }

    if (propsChain.match(/^(getAll|clear)/) === null) {
        return new SupportStatus("NO_SUPPORT");
    }

    return new SupportStatus("SUPPORT");
}

function RuntimeSupport (propsChain) {
    var support = [
        "onStartup",
        "getManifest",
        "getURL",
        "id",
        "sendMessage",
        "onMessage",
        "onConnect",
        "connect",
        "getPlatformInfo",
    ].join('|');

    var r = new RegExp("^(" + support + ")");
    if (r.exec(propsChain) !== null) {
        return new SupportStatus("SUPPORT");
    }

    var futureSupport = [
        "lastError",
        "getBackgroundPage",
        "openOptionsPage",
        "setUninstallURL",
        "reload",
        "requestUpdateCheck",
        "restart",
        "connectNative",
        "sendNativeMessage",
        "getPackageDirectoryEntry",
        "onInstalled",
        "onSuspend",
        "onSuspendCanceled",
        "onUpdateAvailable",
        "onBrowserUpdateAvailable",
        "onConnectExternal",
        "onMessageExternal",
        "onRestartRequired",
    ].join('|');

    var r2 = new RegExp("^(" + futureSupport + ")");
    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("FUTURE_SUPPORT");
    }

    return new SupportStatus("NO_SUPPORT");
}

function StorageSupport (propsChain) {
    if (propsChain.match(/^sync/) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.match(/(getBytesInUse)/) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    return new SupportStatus("SUPPORT");
}

function TabsSupport (propsChain) {
    var noSupport = [
        "sendRequest",
        "getSelected",
        "duplicate",
        "highlight",
        "move",
        "detectLanguage",
        "captureVisibleTab",
        "getZoom",
        "setZoom",
        "getZoomSettings",
        "setZoomSettings"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    var highlightActive = [
        "highlight",
        "update",
        "onActiveChanged",
        "onActivated",
        "onHighlightChanged",
        "onHighlighted"
    ].join('|');

    var r2 = new RegExp("^(" + highlightActive + ")");

    if (r2.exec(propsChain) !== null) {
        return new SupportStatus("WARN", "Highlighted and active are treated as the same since Firefox cannot select multiple tabs.");
    }

    if (propsChain.indexOf("executeScript") === 0) {
        return [
            new SupportStatus("WARN", "The callback argument is not supported yet."),
            new SupportStatus("WARN", "Host permissions are not enforced yet.")
        ];
    }

    return new SupportStatus("SUPPORT");
}

function WebNavigationSupport (propsChain) {
    var noSupport = [
        "getFrame",
        "getAllFrames",
        "onCreatedNavigationTarget",
        "onHistoryStateUpdated"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.indexOf("onReferenceFragmentUpdated") === 0) {
        return [
            new SupportStatus("WARN", "This method also triggers for pushState."),
            new SupportStatus("WARN", "Filtering, transition types and qualifiers are unsupported.")
        ];
    }

    return new SupportStatus("WARN", "Filtering, transition types and qualifiers are unsupported.");
}

function WebRequestSupport (propsChain) {
    var noSupport = [
        "handlerBehaviorChanged",
        "onAuthRequired",
        "onBeforeRedirect",
        "onErrorOccurred"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.indexOf("onBeforeRequest") === 0) {
        return new SupportStatus("WARN", "Requests can not be modified/redirected on this method.");
    }

    if (propsChain.indexOf("onBeforeSendHeaders") === 0) {
        return new SupportStatus("WARN", "Requests can not be canceled on this method.");
    }

    if (propsChain.indexOf("") === 0) {
        return new SupportStatus("WARN", "Requests can not be redirected or canceled on this method.");
    }

    return new SupportStatus("WARN", "Requests can not be canceled, modified or redirected on this method.");
}

function WindowsSupport (propsChain) {
    if (propsChain.indexOf("onFocusChanged") === 0) {
        return new SupportStatus("WARN", "This method will trigger multiple times for a given focus change.");
    }

    if (propsChain.indexOf("create") === 0) {
        return new SupportStatus("WARN", "This method does not support the focused, type, or state options.");
    }

    if (propsChain.indexOf("update") === 0) {
        return new SupportStatus("WARN", "This method only supports the focused option.");
    }

    return new SupportStatus("SUPPORT");
}

function BookmarksSupport (propsChain) {
    var noSupport = [
        "getRecent",
        "search",
        "removeTree",
        "onCreated",
        "onRemoved",
        "onChanged",
        "onMoved",
        "onChildrenReordered",
        "onImportBegan",
        "onImportEnded",
        "BookmarkTreeNodeUnmodifiable"
    ].join('|');

    var r = new RegExp("^(" + noSupport + ")");

    if (r.exec(propsChain) !== null) {
        return new SupportStatus("NO_SUPPORT");
    }

    if (propsChain.indexOf("removes") === 0) {
        return new SupportStatus("WARN", "This method also removes non empty folders.");
    }

    return new SupportStatus("SUPPORT");
}

function CookiesSupport (propsChain) {
    if (propsChain.indexOf("onChanged") === 0) {
        return new SupportStatus("WARN", "Events might be subtely different.");
    }

    if (propsChain.indexOf("set") === 0) {
        return new SupportStatus("WARN", "Creating session cookies with set might fail.");
    }

    if (propsChain.indexOf("getAllCookieStores") === 0) {
        return new SupportStatus("WARN", "This method always just returns one default store and no tabs.");
    }

    return [
        new SupportStatus("WARN", "Accessing cookies from private tabs is impossible."),
        new SupportStatus("WARN", "Host permissions are not currently enforced.")
    ];
}

module.exports = {
    // Fully supported APIs
    "alarms": FullSupport(),
    "contextMenus": FullSupport(),
    "browserAction": FullSupport(),
    "pageAction": FullSupport(),

    // Partially supported APIs
    "bookmarks": BookmarksSupport,
    "cookies": CookiesSupport,
    "extension": ExtensionSupport,
    "i18n": i18nSupport,
    "notifications": NotificationsSupport,
    "runtime": RuntimeSupport,
    "storage": StorageSupport,
    "tabs": TabsSupport,
    "webNavigation": WebNavigationSupport,
    "webRequest": WebRequestSupport,
    "windows": WindowsSupport,

    // Future APIs
    "commands": FutureSupport(),
    "downloads": FutureSupport(),
    "history": FutureSupport(),
    "idle": FutureSupport(),
    "omnibox": FutureSupport(),
    "permissions": FutureSupport(),
    "devtools.panels": FutureSupport()
};
<|endoftext|>


<commit_before>var JSM = function ()
{
	this.mainVersion = 0;
	this.subVersion = 41;
};

/**
* Function: RandomNumber
* Description: Generates a random number between two numbers.
* Parameters:
*	from {number} lowest random result
*	to {number} highest random result
* Returns:
*	{number} the result
*/
JSM.RandomNumber = function (from, to)
{
	return Math.random () * (to - from) + from;
};

/**
* Function: RandomInt
* Description: Generates a random integer between two integers.
* Parameters:
*	from {integer} lowest random result
*	to {integer} highest random result
* Returns:
*	{integer} the result
*/
JSM.RandomInt = function (from, to)
{
	return Math.floor ((Math.random () * (to - from + 1)) + from);
};

/**
* Function: SeededRandomInt
* Description: Generates a random integer between two integers. A seed number can be specified.
* Parameters:
*	from {integer} lowest random result
*	to {integer} highest random result
*	seed {integer} seed value
* Returns:
*	{integer} the result
*/
JSM.SeededRandomInt = function (from, to, seed)
{
    var random = ((seed * 9301 + 49297) % 233280) / 233280;
	return Math.floor ((random * (to - from + 1)) + from);
};

/**
* Function: ValueOrDefault
* Description: Returns the given value, or a default if it is undefined.
* Parameters:
*	val {anything} new value
*	def {anything} default value
* Returns:
*	{anything} the result
*/
JSM.ValueOrDefault = function (val, def)
{
	if (val === undefined || val === null) {
		return def;
	}
	return val;
};

/**
* Function: PrevIndex
* Description: Returns the circular previous index for an array with the given length.
* Parameters:
*	index {integer} the index
*	length {integer} the number of indices
*/
JSM.PrevIndex = function (index, length)
{
	return index > 0 ? index - 1 : length - 1;
};

/**
* Function: NextIndex
* Description: Returns the circular next index for an array with the given length.
* Parameters:
*	index {integer} the index
*	length {integer} the number of indices
*/
JSM.NextIndex = function (index, length)
{
	return index < length - 1 ? index + 1 : 0;
};

/**
* Function: CopyObjectProperties
* Description: Copies one object properties to another object.
* Parameters:
*	source {anything} source object
*	target {anything} target object
*	overwrite {boolean} overwrite existing properties
*/
JSM.CopyObjectProperties = function (source, target, overwrite)
{
	if (source === undefined || source === null ||
		target === undefined || target === null)
	{
		return;
	}

	var property;
	for (property in source) {
		if (source.hasOwnProperty (property)) {
			if (overwrite || target[property] === undefined || target[property] === null) {
				target[property] = source[property];
			}
		}
	}
};

/**
* Function: Message
* Description: Writes a message to the console.
* Parameters:
*	message {string} the message
*/
JSM.Message = function (message)
{
	console.log ('JSModeler: ' + message);
};
<commit_msg>Update version in development branch.<commit_after>var JSM = function ()
{
	this.mainVersion = 0;
	this.subVersion = 42;
};

/**
* Function: RandomNumber
* Description: Generates a random number between two numbers.
* Parameters:
*	from {number} lowest random result
*	to {number} highest random result
* Returns:
*	{number} the result
*/
JSM.RandomNumber = function (from, to)
{
	return Math.random () * (to - from) + from;
};

/**
* Function: RandomInt
* Description: Generates a random integer between two integers.
* Parameters:
*	from {integer} lowest random result
*	to {integer} highest random result
* Returns:
*	{integer} the result
*/
JSM.RandomInt = function (from, to)
{
	return Math.floor ((Math.random () * (to - from + 1)) + from);
};

/**
* Function: SeededRandomInt
* Description: Generates a random integer between two integers. A seed number can be specified.
* Parameters:
*	from {integer} lowest random result
*	to {integer} highest random result
*	seed {integer} seed value
* Returns:
*	{integer} the result
*/
JSM.SeededRandomInt = function (from, to, seed)
{
    var random = ((seed * 9301 + 49297) % 233280) / 233280;
	return Math.floor ((random * (to - from + 1)) + from);
};

/**
* Function: ValueOrDefault
* Description: Returns the given value, or a default if it is undefined.
* Parameters:
*	val {anything} new value
*	def {anything} default value
* Returns:
*	{anything} the result
*/
JSM.ValueOrDefault = function (val, def)
{
	if (val === undefined || val === null) {
		return def;
	}
	return val;
};

/**
* Function: PrevIndex
* Description: Returns the circular previous index for an array with the given length.
* Parameters:
*	index {integer} the index
*	length {integer} the number of indices
*/
JSM.PrevIndex = function (index, length)
{
	return index > 0 ? index - 1 : length - 1;
};

/**
* Function: NextIndex
* Description: Returns the circular next index for an array with the given length.
* Parameters:
*	index {integer} the index
*	length {integer} the number of indices
*/
JSM.NextIndex = function (index, length)
{
	return index < length - 1 ? index + 1 : 0;
};

/**
* Function: CopyObjectProperties
* Description: Copies one object properties to another object.
* Parameters:
*	source {anything} source object
*	target {anything} target object
*	overwrite {boolean} overwrite existing properties
*/
JSM.CopyObjectProperties = function (source, target, overwrite)
{
	if (source === undefined || source === null ||
		target === undefined || target === null)
	{
		return;
	}

	var property;
	for (property in source) {
		if (source.hasOwnProperty (property)) {
			if (overwrite || target[property] === undefined || target[property] === null) {
				target[property] = source[property];
			}
		}
	}
};

/**
* Function: Message
* Description: Writes a message to the console.
* Parameters:
*	message {string} the message
*/
JSM.Message = function (message)
{
	console.log ('JSModeler: ' + message);
};
<|endoftext|>