Source: ui/base/abstract-radio-button.js

/*global require, exports, document, Error*/
var AbstractControl = require("./abstract-control").AbstractControl,
    PressComposer = require("../../composer/press-composer").PressComposer,
    KeyComposer = require("../../composer/key-composer").KeyComposer;

var CLASS_MONTAGE_DISABLED = "montage--disabled",
    CLASS_MONTAGE_ACTIVE = "montage--active",
    CLASS_MONTAGE_CHECKED = "montage-RadioButton--checked";

/**
 * @class AbstractRadioButton
 * @classdesc Provides common implementation details for radio buttons.
 * @extends AbstractControl
 */
var AbstractRadioButton = exports.AbstractRadioButton = AbstractControl.specialize(
    /** @lends AbstractRadioButton# */
{

    /**
     * Dispatched when the radio button is activated through a mouse click,
     * finger tap, or when focused and the spacebar is pressed.
     * @event action
     * @memberof AbstractRadioButton
     * @param {Event} event
     */

    constructor: {
        value: function AbstractRadioButton() {
            if(this.constructor === AbstractRadioButton) {
                throw new Error("AbstractRadioButton cannot be instantiated.");
            }
        }
    },

    /**
     * Whether the user is pressing the radio button.
     * @type {boolean}
     */
    _active: {
        value: false
    },

    active: {
        set: function (_active) {
            _active = !!_active;

            if (this._active !== _active) {
                this._active = _active;

                if (_active) {
                    this.classList.add(this.activeClass);
                } else {
                    this.classList.remove(this.activeClass);
                }
            }
        },
        get: function () {
            return this._active;
        }
    },

    _checked: {
        value: null
    },

    /**
     * Whether this radio button is checked.
     * @type {boolean}
     */
    checked: {
        set: function (_checked) {
            _checked = !!_checked;

            if (this._checked !== _checked) {
                this._checked = _checked;

                if (_checked) {
                    this.classList.add(this.checkedClass);
                } else {
                    this.classList.remove(this.checkedClass);
                }
            }
        },
        get: function () {
            return this._checked;
        }
    },

    _disabledClass: {
        value: null
    },

    disabledClass: {
        set: function (_disabledClass) {
            var previousClass = this.disabledClass; // need to call the getter

            if (_disabledClass && typeof _disabledClass === "string" && previousClass !== _disabledClass) {
                this._swapClassNameIfNeeded(previousClass, _disabledClass);
                this._disabledClass = _disabledClass;
            }
        },
        get: function () {
            return this._disabledClass || CLASS_MONTAGE_DISABLED;
        }
    },

    _activeClass: {
        value: null
    },

    activeClass: {
        set: function (_activeClass) {
            var previousClass = this.activeClass; // need to call the getter

            if (_activeClass && typeof _activeClass === "string" && previousClass !== _activeClass) {
                this._swapClassNameIfNeeded(previousClass, _activeClass);
                this._activeClass = _activeClass;
            }
        },
        get: function () {
            return this._activeClass || CLASS_MONTAGE_ACTIVE;
        }
    },

    _checkedClass: {
        value: null
    },

    checkedClass: {
        set: function (_checkedClass) {
            var previousClass = this.checkedClass; // need to call the getter

            if (_checkedClass && typeof _checkedClass === "string" && previousClass !== _checkedClass) {
                this._swapClassNameIfNeeded(previousClass, _checkedClass);
                this._checkedClass = _checkedClass;
            }
        },
        get: function () {
            return this._checkedClass || CLASS_MONTAGE_CHECKED;
        }
    },

    _swapClassNameIfNeeded: {
        value: function (_previous, _new) {
            if (_previous && _new && this.classList.has(_previous)) {
                this.classList.remove(_previous);
                this.classList.add(_new);
            }
        }
    },

    /**
     * Whether this radio button is enabled.
     * @type {boolean}
     */
    _enabled: {
        value: true
    },

    enabled: {
        set: function (_enabled) {
            _enabled = !!_enabled;

            if (this._enabled !== _enabled) {
                this._enabled = _enabled;

                if (!_enabled) {
                    this.classList.add(this.disabledClass);
                } else {
                    this.classList.remove(this.disabledClass);
                }
            }
        },
        get: function () {
            return this._enabled;
        }
    },

    __keyComposer: {
        value: null
    },

    _keyComposer: {
        get: function () {
            if (!this.__keyComposer) {
                this.__keyComposer = new KeyComposer();
                this.__keyComposer.keys = "space";
                this.addComposer(this.__keyComposer);
            }

            return this.__keyComposer;
        }
    },

    _radioButtonController: {
        value: null
    },

    /**
     * The radio button controller that ensures that only one radio button in
     * its `content` is `checked` at any time.
     * @type {RadioButtonController}
     */
    radioButtonController: {
        set: function (value) {
            if (this._radioButtonController) {
                this._radioButtonController.unregisterRadioButton(this);
            }
            this._radioButtonController = value;
            value.registerRadioButton(this);
        },
        get: function () {
            return this._radioButtonController;
        }
    },

    __pressComposer: {
        value: null
    },

    _pressComposer: {
        get: function () {
            if (!this.__pressComposer) {
                this.__pressComposer = new PressComposer();
                this.addComposer(this.__pressComposer);
            }

            return this.__pressComposer;
        }
    },

    enterDocument: {
        value: function (firstTime) {
            if (firstTime) {
                this.element.setAttribute("role", "radio");
            }
        }
    },

    draw: {
        value: function () {
            if (this.checked) {
                this.element.setAttribute("aria-checked", "true");
            } else {
                this.element.setAttribute("aria-checked", "false");
            }
        }
    },

    handlePressStart: {
        value: function (event) {
            this.active = true;

            if (event.touch) {
                // Prevent default on touchmove so that if we are inside a scroller,
                // it scrolls and not the webpage
                document.addEventListener("touchmove", this, false);
            }
        }
    },

    check: {
        value: function () {
            if (!this.enabled || this.checked) {
                return;
            }

            this.dispatchActionEvent();
            this.checked = true;
        }
    },

    /**
     Handle press event from press composer
     */
    handlePress: {
        value: function (/* event */) {
            this.active = false;
            this.check();
        }
    },

    /**
     Called when all interaction is over.
     @private
     */
    handlePressCancel: {
        value: function (/* event */) {
            this.active = false;
            document.removeEventListener("touchmove", this, false);
        }
    },

    handleKeyPress: {
        value: function () {
            this.active = true;
        }
    },

    handleKeyRelease: {
        value: function () {
            this.active = false;
            this.check();
        }
    },

    prepareForActivationEvents: {
        value: function () {
            this._keyComposer.addEventListener("keyPress", this, false);
            this._keyComposer.addEventListener("keyRelease", this, false);

            this._pressComposer.addEventListener("pressStart", this, false);
            this._pressComposer.addEventListener("press", this, false);
            this._pressComposer.addEventListener("pressCancel", this, false);
        }
    },

    activate: {
        value: function () {
            this.check();
        }
    }
});