Source: core/event/mutable-event.js

/**
 @module montage/core/event/mutable-event
 @requires montage
 */
var Montage = require("../core").Montage;
var wrapPropertyGetter = function (key, storageKey) {
        return function () {
            return this.hasOwnProperty(storageKey) ? this[storageKey] : this._event[key];
        };
    },
    wrapPropertySetter = function (storageKey) {
        return function (value) {
            this[storageKey] = value;
        };
    };


// XXX Does not presently function server-side
if (typeof window !== "undefined") {

    var _eventConstructorsByType = {};

    var wrapProperty = function (obj, key) {
        var storageKey = "_" + key;

        Montage.defineProperty(obj, storageKey, {value: undefined});

        Montage.defineProperty(obj, key, {
            get: wrapPropertyGetter(key, storageKey),
            set: wrapPropertySetter(storageKey)
        });
    };
    /**
        @class MutableEvent
    */
    var MutableEvent = exports.MutableEvent = Montage.specialize(/** @lends MutableEvent# */ {

    /**
      @private
    */
        _initPrototypeWithEvent: {
            value: function (event) {
                var key, proto = this.__proto__ || Object.getPrototypeOf(this);

                /* jshint forin: true */
                for (key in event) {
                /* jshint forin: false */

                    //  Don't overwrite keys we have installed
                    if (key in this || Object.getOwnPropertyDescriptor(proto,key)) {
                        continue;
                    }

                    // Skip methods, the ones we care about have been wrapped
                    // already.
                    // TODO actually wrap all known functions generically
                    //if (typeof this[key] === "function") {
                    // continue;
                    //}

                    // TODO ok, maybe it would be quicker to not make this a
                    // function, but I really hate duplicated code.
                    wrapProperty(this, key);
                }

                wrapProperty(this, "replayed");

                return this;
            }
        },

        _initWithEvent: {
            value: function (event) {
                this._event = event;
                return this;
            }
        },

        /**
         * @function
         */
        preventDefault: {
            value: function () {
                this._event.preventDefault();
            }
        },

        /**
         * @function
         */
        getPreventDefault: {
            value: function () {
                if (this._event.getPreventDefault) {
                    return this._event.getPreventDefault();
                }
                return this._event.defaultPrevented;
            }
        },

        /**
         * @function
         */
        stopImmediatePropagation: {
            value: function () {
                this._event.stopImmediatePropagation();
                // TODO only if the event is cancellable?
                this.propagationStopped = true;
                this.immediatePropagationStopped = true;
            }
        },

        /**
         * @type {Property}
         * @default {boolean} false
         */
        propagationStopped: {
            value: false
        },

        /**
         * @type {Property}
         * @default {boolean} false
         */
        immediatePropagationStopped: {
            value: false
        },

        /**
         * @type {Property}
         * @default {boolean} true
        */
        mutable: {
            value: true
        },

        /**
         * @function
         */
        stopPropagation: {
            value: function () {
                this._event.stopPropagation();
                // TODO only if the event is cancellable?
                this.propagationStopped = true;
            }
        },

        /**
         * @function
         */
        stop: {
            value: function () {
                this.preventDefault();
                this.stopPropagation();
            }
        },

        _eventPhase: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        eventPhase: {
            get: function () {
                return (this._eventPhase !== void 0) ? this._eventPhase : this._event.eventPhase;
            },
            set: function (value) {
                this._eventPhase = value;
            }
        },
        _target: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        target: {
            get: function () {
                return (this._target !== void 0) ? this._target : this._event.target;
            },
            set: function (value) {
                this._target = value;
            }
        },
        _currentTarget: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        currentTarget: {
            get: function () {
                return (this._currentTarget !== void 0) ? this._currentTarget : this._event.currentTarget;
            },
            set: function (value) {
                this._currentTarget = value;
            }
        },
        _type: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        type: {
            get: function () {
                return (this._type !== void 0) ? this._type : this._event.type;
            },
            set: function (value) {
                this._type = value;
            }
        },
        _bubbles: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        bubbles: {
            get: function () {
                return (this._bubbles !== void 0) ? this._bubbles : this._event.bubbles;
            },
            set: function (value) {
                this._bubbles = value;
            }
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        touches: {
            get: function () {
                return this._event.touches;
            }
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        changedTouches: {
            get: function () {
                return this._event.changedTouches;
            }
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        targetTouches: {
            get: function () {
                return this._event.targetTouches;
            }
        },
        _defaultPrevented: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        defaultPrevented: {
            get: function () {
                return (this._defaultPrevented !== void 0) ? this._defaultPrevented : this._event.defaultPrevented;
            },
            set: function (value) {
                this._defaultPrevented = value;
            }
        },
        _timeStamp: {
            value: void 0
        },
        /**
         * @type {Property}
         * @default {Element} null
         */
        timeStamp: {
            get: function () {
                return (this._timeStamp !== void 0) ? this._timeStamp : this._event.timeStamp;
            },
            set: function (value) {
                this._timeStamp = value;
            }
        }

    }, {

        /**
         * @function
         * @param {Event} event The original event.
         * @returns newEvent
         */
        fromEvent: {
            value: function (event) {
                var type = event.type,
                    constructor = _eventConstructorsByType[type],
                    newEvent;
                if (!constructor) {
                    constructor = function MutableEvent() {
                    };
                    constructor.prototype = new exports.MutableEvent()._initPrototypeWithEvent(event);
                    _eventConstructorsByType[type] = constructor;
                }
                newEvent = new constructor();
                newEvent._initWithEvent(event);
                return newEvent;
            }
        },

        //    Same arguments as initEvent & initCustomEvent

        /**
         * @function
         * @param {Event} type TODO
         * @param {Event} canBubbleArg TODO
         * @param {Event} cancelableArg TODO
         * @param {Event} data TODO
         * @returns this.fromEvent(anEvent)
         */
        fromType: {
            value: function (type, canBubbleArg, cancelableArg, detail) {
                return this.fromEvent(new CustomEvent(type, {bubbles: canBubbleArg, cancelable:cancelableArg, detail:detail}));
            }
        }

    });

} // client-side