GreyRook/gown.js

View on GitHub
src/interaction/ResizeManager.js

Summary

Maintainability
B
6 hrs
Test Coverage
var EventEmitter = require('eventemitter3');

/**
 * The resize manager deals with changes in the Application size
 * e.g. if the browser window has been resized because the keyboard is shown
 * or the device has been rotated on mobile or the user resized it on the
 * desktop with his mouse.
 *
 * @class
 * @extends EventEmitter
 * @memberof GOWN.interaction
 * @param renderer A reference to the current renderer {PIXI.CanvasRenderer|PIXI.WebGLRenderer}
 * @param [options] {object}
 * @param [options.autoPreventDefault=true] Should the manager automatically prevent default browser actions. {boolean}
 * @param [options.fullscreen=false] Should we use the whole browser width/height (window.innerHeight/Width). {boolean}
 */
 // TODO: orientation change for cordova and cocoon ('orientationchange' and Cocoon.Device.getOrientation())
 // TODO: take a look at phaser ScaleManager - see https://github.com/photonstorm/phaser/blob/v2.4.2/src/core/ScaleManager.js
 // TODO: check single Canvas-DOM-Element, not only whole window (see https://github.com/marcj/css-element-queries )
function ResizeManager(renderer, options) {
    EventEmitter.call(this);

    options = options || {};

    /**
     * The renderer this interaction manager works for.
     *
     * @type PIXI.SystemRenderer
     */
    this.renderer = renderer;

    /**
     * Should default browser actions automatically be prevented.
     *
     * @type bool
     * @default true
     */
    this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true;

    /**
     * Should we use the whole browser width/height (window.innerHeight/Width)
     *
     * @type bool
     * @default false
     */
    this.fullscreen = options.fullscreen || false;

    /**
     * TODO
     */
    this.element = null;

    /**
     * Time to wait after every resize event to prevent flickering
     *
     * @type Number
     * @default 0
     */
    this.resizeTimeout = 0;

    /**
     * Should the resize manager wait after every resize event
     *
     * @type bool
     * @default true
     */
    this.useResizeDoneTimeout = options.useResizeDoneTimeout !== undefined ? options.useResizeDoneTimeout : true;

    /**
     * An event data object to handle all the event tracking/dispatching
     *
     * @type Object
     */
    this.eventData = {
        stopped: false,
        target: null,
        type: null,
        data: {},
        stopPropagation:function(){
            this.stopped = true;
        }
    };

     this.onResize = this.onResize.bind(this);
     this.processResize = this.processResize.bind(this);

     this.addEvents();
}

ResizeManager.prototype = Object.create(EventEmitter.prototype);
ResizeManager.prototype.constructor = ResizeManager;
module.exports = ResizeManager;

/**
 * The waiting time after the resize event before updating
 * (prevent the canvas from flickering when resizing)
 *
 * @static
 * @final
 * @type String
 */
ResizeManager.RESIZE_DONE_TIMEOUT = 100;

 /**
 * Registers all the DOM events
 *
 * @private
 */
ResizeManager.prototype.addEvents = function () {
    window.addEventListener('resize', this.onResize, true);
    this.eventsAdded = true;
};

/**
 * Removes all the DOM events that were previously registered
 *
 * @private
 */
ResizeManager.prototype.removeEvents = function () {
    window.removeEventListener('resize', this.onResize, true);
    this.eventsAdded = false;
};

/**
 * Is called when the application/browser window gets resized
 *
 * @param _event The DOM event {Event}
 * @private
 */
ResizeManager.prototype.onResize = function (_event) {
    if (!this.element && this.fullscreen === false) {
        // we assume you have a fixed size?!
        // TODO: do not add resizeManager in the first place?
        return;
    }
    if (this.autoPreventDefault) {
        _event.preventDefault();
    }

    if (this.useResizeDoneTimeout) {
        var scope = this;
        if (this.resizeTimeout) {
            clearInterval(this.resizeTimeout);
        }
        this.resizeTimeout = setInterval(function () {
            clearInterval(scope.resizeTimeout);
            scope._resizeEvent(_event);
        }, ResizeManager.RESIZE_DONE_TIMEOUT);
    } else {
        this._resizeEvent(_event);
    }

};

/**
 * Handle the original resize event and forward it
 *
 * @param event The DOM event {Event}
 * @private
 */
ResizeManager.prototype._resizeEvent = function(event) {
    this.eventData.stopped = false;

    this.eventData.data = this.getSizeData(event);

    if (this.autoPreventDefault) {
        event.preventDefault();
    }

    this.processInteractive(this.renderer._lastObjectRendered, this.processResize);
    this.emit('resize', this.eventData);
};

/**
 * Grabs the size from the browser window
 *
 * @private
 */
ResizeManager.prototype.getSizeData = function (event) {
    var eventData = {
        originalEvent: event
    };
    if (this.fullscreen) {
        eventData.width = window.innerWidth;
        eventData.height = window.innerHeight;
    } else {
        eventData.width = this.element.clientWidth;
        eventData.height = this.element.clientHeight;
    }
    return eventData;
};

/**
 * Dispatch a resize event for a given display object
 *
 * @param displayObject {PIXI.DisplayObject}
 * @private
 */
ResizeManager.prototype.processResize = function(displayObject) {
    this.dispatchEvent( displayObject, 'resize', this.eventData );
};

/**
 * Dispatches an event on the display object that has resizable set to true
 *
 * @param displayObject The display object in question {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite}
 * @param eventString The name of the event (e.g, resize or orientation) {string}
 * @param eventData The event data object {object}
 * @private
 */
ResizeManager.prototype.dispatchEvent = function (displayObject, eventString, eventData) {
    if (!eventData.stopped) {
        eventData.target = displayObject;
        eventData.type = eventString;

        displayObject.emit(eventString, eventData);

        if (displayObject[eventString]) {
            displayObject[eventString](eventData);
        }
    }
};

/**
 * Traverse through the scene graph to call given function on all displayObjects
 * that are resizable
 *
 * @param displayObject the displayObject that will be resized (recursivly crawls its children)
 * {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite}
 * @param [func] the function that will be called on each resizable object. The displayObject will be passed to the function {Function}
 */
ResizeManager.prototype.processInteractive = function (displayObject, func)
{
    if(!displayObject || !displayObject.visible)
    {
        return false;
    }

    // resize parent first
    if (displayObject.resizable) {
        func(displayObject);
    }

    var children = displayObject.children;

    for (var i = children.length-1; i >= 0; i--) {
        // unlike the InteractionManager we iterate over ALL children
        // and check every one if it is resizable, because
        // we assume that resize is something that could affect every component
        // not only the one that has focus.
        var child = children[i];
        this.processInteractive(child, func);
    }
};

/**
 * Remove events and listener etc.
 */
ResizeManager.prototype.destroy = function(){
    this.removeEvents();
    this.removeAllListeners();
    this.renderer = null;
    this.eventData = null;
    this.onResize = null;
};

PIXI.WebGLRenderer.registerPlugin('resize', ResizeManager);
PIXI.CanvasRenderer.registerPlugin('resize', ResizeManager);