API Docs for: 1.4.0
Show:

File: src\core\Stage.ts

/**
* 
* @module Kiwi
* 
*/

module Kiwi {

	/**
	* Each game contains a single Stage which controls the creation and
	* management of main domElements required for a Kiwi game to work.
	* This includes the Canvas and the rendering contexts,
	* as well as the width/height of the game and the position it should be
	* on the screen.
	*
	* @class Stage
	* @namespace Kiwi
	* @constructor
	* @param game {Kiwi.Game} The game that this Stage belongs to. 
	* @param name {String} The name of the kiwi game.
	* @param width {Number} The initial width of the game. 
	* @param height {Number} The initial heihgt of the game.
	* @param scaleType {Number} The scale method that should be used for the game.
	* @return {Kiwi.Stage}  
	*
	*/
	export class Stage {

		constructor(game: Kiwi.Game, name: string, width: number, height: number, scaleType:number) {

			this._game = game;

			this.name = name;

			this.domReady = false;

			// Properties
			this._alpha = 1;

			this._x = 0;
			this._y = 0;

			this._width = width;
			this._height = height;
			this.color = "ffffff";
			// CocoonJS should be black instead
			if( this._game.deviceTargetOption === Kiwi.TARGET_COCOON) {
				this.color = "000000";
			}

			this._scale = new Kiwi.Geom.Point(1, 1);
			this._scaleType = scaleType;

			this.onResize = new Kiwi.Signal();
			this.onWindowResize = new Kiwi.Signal();

			this.onFocus = new Kiwi.Signal();
			this.onBlur = new Kiwi.Signal();
			this.onVisibilityChange = new Kiwi.Signal();

			this._renderer = null;
		}

		/**
		* Returns the type of this object.
		* @method objType
		* @return {string} "Stage"
		* @public
		*/
		public objType():string {
			return "Stage";
		}

		/**
		* The default width of the stage.
		* @property DEFAULT_WIDTH
		* @type number
		* @default 800
		* @public
		* @static
		*/
		public static DEFAULT_WIDTH: number = 800;

		/**
		* The default height of the stage.
		* @property DEFAULT_HEIGHT
		* @type number
		* @default 600
		* @public
		* @static
		*/
		public static DEFAULT_HEIGHT: number = 600;


		/**
		* The default scaling method used on Kiwi Games. 
		* This scaling method will set the container's width/height
		* to static values.
		*
		* @property SCALE_NONE
		* @type number
		* @default 0 
		* @public 
		* @static
		*/
		public static SCALE_NONE: number = 0;

		/**
		* Scale Fit will scale the stage's width to fit its parents width.
		* The height is then calculated to maintain the aspect ratio of the
		* width/height of the Stage.
		* @property SCALE_FIT
		* @type number
		* @default 1 
		* @public 
		* @static
		*/
		public static SCALE_FIT: number = 1;

		/**
		* Stretch will make the stage scale to fit its parents width/height
		* (by using max/min height of 100%).
		* If the parent doesn't have a height set then the height will be
		* the height of the stage. 
		* @property SCALE_STRETCH
		* @type number
		* @default 2 
		* @public 
		* @static
		*/
		public static SCALE_STRETCH: number = 2;

		/**
		* Private property that holds the scaling method that should be
		* applied to the container element. 
		* @property _scaleType
		* @type number
		* @default Kiwi.Stage.SCALE_NONE
		* @private
		*/
		private _scaleType: number = Kiwi.Stage.SCALE_NONE;

		/**
		* Holds type of scaling that should be applied the container element. 
		* @property scaleType
		* @type number
		* @default Kiwi.Stage.SCALE_NONE
		* @public
		*/
		public set scaleType(val: number) {
			this._scaleType = val;
			this._scaleContainer();
		}

		public get scaleType():number {
			return this._scaleType;
		}


		/**
		* The alpha of the stage. 
		* @property _alpha
		* @type number
		* @default 1
		* @private
		*/
		private _alpha: number;

		/**
		* Sets the alpha of the container element.
		* 0 = invisible, 1 = fully visible.
		* Note: Because the alpha value is applied to the container,
		* it will not work in CocoonJS.
		*
		* @property alpha
		* @type number
		* @default 1
		* @public
		*/
		public get alpha():number {
			return this._alpha;
		}
		public set alpha(value: number) {
			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {
				this.container.style.opacity = String(Kiwi.Utils.GameMath.clamp(value, 1, 0));
			}
			// Doesn't work in cocoon

			this._alpha = value;
		}

		/**
		* The X coordinate of the stage.
		* @property _x
		* @type number
		* @private 
		*/
		private _x: number;

		/**
		* The X coordinate of the stage. This number should be the same
		* as the stage's `left` property.
		* @property x
		* @type number
		* @public
		*/
		public get x(): number {
			return this._x;
		} 
		public set x(value: number) {
			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {
				this.container.style.left = String(value + "px");
			} else if (this._game.deviceTargetOption === Kiwi.TARGET_COCOON) {
				this.canvas.style.left = String(value + "px");
			}
			this._x = value;
		}

		/**
		* The Y coordinate of the stage.
		* @property _y
		* @type number
		* @private
		*/
		private _y: number;

		/**
		* Get the Y coordinate of the stage. This number should be the same
		* as the stage's `top` property.
		* @property y
		* @type number
		* @public
		*/
		public get y(): number {
			return this._y;
		}
		public set y(value: number) {
			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {
				this.container.style.top = String(value + "px");
			} else if (this._game.deviceTargetOption === Kiwi.TARGET_COCOON) {
				this.canvas.style.top = String(value + "px");
			}
			this._y = value;
		}

		/**
		* The width of the stage.
		* @property _width
		* @type number
		* @private
		*/
		private _width: number;

		/**
		* The width of the stage. This is READ ONLY.
		* See the "resize" method if you need to modify this value.
		* @property width
		* @type number
		* @public 
		* @readonly
		*/
		public get width(): number {
			return this._width;
		}

		/**
		* The height of the stage
		* @property _height
		* @type number
		* @private
		*/
		private _height: number;

		/**
		* The height of the stage. This is READ ONLY.
		* See the `resize` method if you need to modify this value.
		* @property height
		* @type number
		* @public
		* @readonly
		*/
		public get height(): number {
			return this._height;
		}

		/**
		* A `Signal` that dispatches an event when the stage gets resized.
		* @property onResize
		* @type Kiwi.Signal
		* @public
		*/
		public onResize: Kiwi.Signal;


		/**
		* A Signal which dispatches events when the window is resized. 
		* Useful to detect if the screen is now in a "landscape" or "portrait"
		* view on Mobile/Cocoon devices. 
		* @property onWindowResize
		* @type Kiwi.Signal
		* @public
		*/
		public onWindowResize: Kiwi.Signal;

		/**
		* Calculates and returns the amount that the container has been scaled
		* by. Mainly used for re-calculating input coordinates. 
		* Note: For COCOONJS this returns 1 since COCOONJS translates the
		* scale itself. This property is READ ONLY.
		* @property scale
		* @type Kiwi.Geom.Point
		* @default 1
		* @public
		*/
		private _scale: Kiwi.Geom.Point;

		public get scale(): Kiwi.Geom.Point {
			return this._scale;
		}

		/**
		* Calculates and returns the amount that the container has been scaled
		* by on the X axis.  
		* @property scaleX
		* @type Number
		* @default 1 
		* @public
		*/
		public get scaleX(): number {
			return this._scale.x;
		}

		/**
		* Calculates and returns the amount that the container has been scaled
		* by on the Y axis.
		* @property scaleY
		* @type Number
		* @default 1
		* @public
		*/
		public get scaleY(): number {
			return this._scale.y;
		}

		/**
		* A point which determines the offset of this Stage
		* @property offset
		* @type Kiwi.Geom.Point
		* @public
		*/
		public offset: Kiwi.Geom.Point = new Kiwi.Geom.Point();

		/**
		* The game this Stage belongs to
		* @property _game
		* @type Kiwi.Game
		* @private 
		*/
		private _game: Kiwi.Game;

		/**
		* The title of your stage
		* @property name
		* @type string
		* @public
		*/
		public name: string;

		/**
		* Whether or not this Stage is DOM ready.
		* @property domReady
		* @type boolean
		* @public
		*/
		public domReady: boolean;

		/**
		* The background color of the stage. 
		*
		* @property _color
		* @type Kiwi.Utils.Color
		* @public
		*/
		public _color: Kiwi.Utils.Color = new Kiwi.Utils.Color();

		/**
		* Sets the background color of the stage.
		*
		* This can be any valid parameter for Kiwi.Utils.Color.
		* If passing multiple parameters, do so in a single array.
		*
		* The default value is "ffffff" or pure white.
		*
		* Note for users of CocoonJS: When using the WebGL renderer,
		* the stage color will fill all parts of the screen outside the canvas.
		* Kiwi.js will automatically set the color to "000000" or pure black
		* when using CocoonJS. If you change it, and your game does not fill
		* the entire screen, the empty portions of the screen will also change
		* color.
		*
		* @property color
		* @type string
		* @public
		*/
		public get color(): any {
			return this._color.getHex();
		}

		public set color( val: any ) {
			if ( !Kiwi.Utils.Common.isArray( val ) ) {
				val = [ val ];
			}
			this._color.set.apply( this._color, val );
		}

		/**
		* Allows the setting of the background color of the stage through
		* component RGB colour values.
		*
		* This property is an Object Literal with "r", "g", "b" colour streams
		* of values between 0 and 255.
		*
		* @property rgbColor
		* @type Object
		* @public
		*/
		public get rgbColor():any {
			return { r: this._color.r * 255, g: this._color.g * 255, b: this._color.b * 255 };
		}

		public set rgbColor(val: any) {
			this._color.r255 = val.r;
			this._color.g255 = val.g;
			this._color.b255 = val.b;
		}

		/**
		* Allows the setting of the background color of the stage through
		* component RGBA colour values.
		*
		* This property is an Object Literal with "r", "g", "b", "a" colour
		* streams of values between 0 and 255.
		* 
		* Note that the alpha value is from 0-255, not 0-1. This is to
		* preserve compatibility with hex-style color values, e.g. "ff0000ff".
		*
		* @property rgbaColor
		* @type Object
		* @public
		* @since 1.1.0
		*/
		public get rgbaColor():any {
			return { r: this._color.r * 255, g: this._color.g * 255, b: this._color.b * 255, a: this._color.a * 255 };
		}

		public set rgbaColor(val: any) {
			this._color.r255 = val.r;
			this._color.g255 = val.g;
			this._color.b255 = val.b;
			this._color.a255 = val.a;
		}

		/**
		* Get the normalized background color of the stage.
		* Returns an object with rgba values, each being between 0 and 1.
		* This is READ ONLY.
		* @property normalizedColor
		* @type string
		* @public
		*/
		public get normalizedColor(): any {
			return {
				r: this._color.r,
				g: this._color.g,
				b: this._color.b,
				a: this._color.a };
			}

		/**
		* The webgl rendering context.
		* @property gl
		* @type WebGLRenderingContext
		* @public
		*/
		public gl: WebGLRenderingContext;


		/**
		* The canvas rendering context.
		* @property ctx
		* @type CanvasRenderingContext2D
		* @public
		*/
		public ctx: CanvasRenderingContext2D;


		/**
		* The canvas element that is being rendered on.
		* @property canvas
		* @type HTMLCanvasElement
		* @public
		*/
		public canvas: HTMLCanvasElement;


		/**
		* The debugging canvas.
		* @property debugCanvas
		* @type HTMLCanvasElement
		* @public
		*/
		public debugCanvas: HTMLCanvasElement;


		/**
		* The debug canvas rendering context.
		* @property dctx
		* @type CanvasRenderingContext2D
		* @public
		*/
		public dctx: CanvasRenderingContext2D;


		/**
		* The parent div in which the layers and input live
		* @property container
		* @type HTMLDivElement
		* @public
		*/
		public container:HTMLDivElement = null;


		/**
		* Stores the renderer created after context detection.
		* @property _renderer
		* @type any
		* @private
		* @since 1.1.0
		*/
		private _renderer: any;

		/**
		* Get the renderer associated with the canvas context.
		* This is either a GLRenderManager or a CanvasRenderer.
		* If the Kiwi.RENDERER_WEBGL renderer was requested
		* but could not be created, it will fall back to CanvasRenderer.
		* This is READ ONLY.
		* @property renderer
		* @type number
		* @public
		* @since 1.1.0
		*/
		public get renderer(): any {
			return this._renderer;
		}


		/**
		* Is executed when the DOM has loaded and the game is just starting. 
		* This is a internal method used by the core of Kiwi itself. 
		* @method boot
		* @param dom {HTMLElement} The
		* @public
		*/
		public boot(dom: Kiwi.System.Bootstrap) {
			var self = this;

			this.domReady = true;
			this.container = dom.container;

			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {

				this.offset = this.getOffsetPoint( this.container );

				this._x = this.offset.x;
				this._y = this.offset.y;

				window.addEventListener("resize",(event: UIEvent) => this._windowResized(event), true);

				this._createFocusEvents();
			}

			this._createCompositeCanvas();

			if (this._game.deviceTargetOption === Kiwi.TARGET_COCOON) {
				this._scaleContainer();
				// Detect reorientation/resize
				window.addEventListener("orientationchange", function( event:UIEvent ) {
					return self._orientationChanged(event);
				}, true);
			} else {
				this._calculateContainerScale();
			}
		}

		/**
		* Gets the x/y coordinate offset of any given valid DOM Element
		* from the top/left position of the browser
		* Based on jQuery offset https://github.com/jquery/jquery/blob/master/src/offset.js 
		* @method getOffsetPoint
		* @param {Any} element
		* @param {Kiwi.Geom.Point} output
		* @return {Kiwi.Geom.Point}
		* @public
		*/
		public getOffsetPoint(element, output: Kiwi.Geom.Point = new Kiwi.Geom.Point): Kiwi.Geom.Point {

			var box = element.getBoundingClientRect();

			var clientTop = element.clientTop || document.body.clientTop || 0;
			var clientLeft = element.clientLeft || document.body.clientLeft || 0;
			var scrollTop = window.pageYOffset || element.scrollTop || document.body.scrollTop;
			var scrollLeft = window.pageXOffset || element.scrollLeft || document.body.scrollLeft;

			return output.setTo(box.left + scrollLeft - clientLeft, box.top + scrollTop - clientTop);

		}

		/**
		* Method that is fired when the window is resized. 
		* @method _windowResized
		* @param event {UIEvent}
		* @private
		*/
		private _windowResized(event:UIEvent) {
			this._calculateContainerScale();

			// Dispatch window resize event
			this.onWindowResize.dispatch();
		}

		/**
		* Method that is fired when the device is reoriented.
		* @method _orientationChanged
		* @param event {UIEvent}
		* @private
		* @since 1.1.1
		*/
		private _orientationChanged(event:UIEvent) {
			this.onResize.dispatch(window.innerWidth, window.innerHeight);
		}

		/**
		* Used to calculate new offset and scale for the stage.
		* @method _calculateContainerScale
		* @private
		*/
		private _calculateContainerScale() {

			// Calculate the scale twice
			// This will give the correct scale upon completion the second run
			// Temporary fix until the Scale Manager is implemented
			this._scaleContainer();
			this._scaleContainer();

			this.offset = this.getOffsetPoint(this.container);

			this._scale.x = this._width / this.container.clientWidth;
			this._scale.y = this._height / this.container.clientHeight;

		}

		/**
		* Handles the creation of the canvas that the game will use and
		* retrieves the context for the renderer. 
		*
		* @method _createCompositeCanvas
		* @private
		*/
		private _createCompositeCanvas() {

			// If we are using cocoon then create a accelerated screen canvas
			if (this._game.deviceTargetOption == Kiwi.TARGET_COCOON) {
				this.canvas = <HTMLCanvasElement>document.createElement(navigator["isCocoonJS"] ? "screencanvas" : "canvas");
			
			// Otherwise default to normal canvas
			} else {
				this.canvas = <HTMLCanvasElement>document.createElement("canvas");
				this.canvas.style.width = "100%";
				this.canvas.style.height = "100%";

			}

			this.canvas.id = this._game.id + "compositeCanvas";
			this.canvas.style.position = "absolute";
			this.canvas.width = this.width;
			this.canvas.height = this.height;
			

			// Get 2D or GL Context; do error detection
			// and fallback to valid rendering context
			if (this._game.renderOption === Kiwi.RENDERER_CANVAS) {
				this.ctx = this.canvas.getContext("2d");
				this.ctx.fillStyle = "#fff";
				this.gl = null;
			} else if (this._game.renderOption === Kiwi.RENDERER_WEBGL) {
				this.gl = this.canvas.getContext("webgl");
				if (!this.gl) {
					this.gl = this.canvas.getContext("experimental-webgl");
					if (!this.gl) {
						Kiwi.Log.warn("Kiwi.Stage: WebGL rendering is not available despite the device apparently supporting it. Reverting to CANVAS.", "#renderer");
						// Reset to canvas mode
						this.ctx = this.canvas.getContext("2d");
						this.ctx.fillStyle = "#fff";
						this.gl = null;
					} else {
						Kiwi.Log.warn("Kiwi.Stage: 'webgl' context is not available. Using 'experimental-webgl'", "#renderer");
					}
				}
				if ( this.gl ) {

					// That is, WebGL was properly supported and created
					this.gl.clearColor(1, 1, 1, 1);
					this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
					this.ctx = null;
				}
			}

			// Create render manager
			// This is reported back to the Kiwi.Game that created the Stage.
			if(this.ctx) {
				this._renderer = new Kiwi.Renderers.CanvasRenderer(this._game);
			} else if(this.gl) {
				this._renderer = new Kiwi.Renderers.GLRenderManager(this._game);
			}


			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {
				this.container.appendChild(this.canvas);
			} else {
				document.body.appendChild(this.canvas);
			}
		}

		/**
		* Set the stage width and height for rendering purposes.
		* This will not effect that "scaleType" that it has been set to.
		*
		* @method resize
		* @param width {number} The new Stage width.
		* @param height {number} The new Stage height.
		* @public
		*/
		public resize(width: number, height: number) {

			this.canvas.height = height;
			this.canvas.width = width;
			this._height = height;
			this._width = width;

			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {
				this._calculateContainerScale();
			}

			this.onResize.dispatch(this._width, this._height);
		}

		/**
		* Sets the background color of the stage through component
		* RGB colour values. Each parameter is a number between 0 and 255.
		* This method also returns an Object Literal with "r", "g", "b"
		* properties.
		*
		* @method setRGBColor
		* @param r {Number} The red component. A value between 0 and 255.
		* @param g {Number} The green component. A value between 0 and 255.
		* @param b {Number} The blue component. A value between 0 and 255.
		* @return {Object} A Object literal containing the r,g,b properties. 
		* @public
		*/
		public setRGBColor(r: number, g: number, b: number):any {
			this.rgbColor = { r: r, g: g, b: b };
			return this.rgbColor;
		}


		/**
		* Creates a debug canvas and adds it above the regular game canvas.
		* The debug canvas is not created by default (even with debugging on)
		* and rendering/clearing of the canvas is upto the developer. 
		* The context for rendering can be access via the "dctx" property and
		* you can use the "clearDebugCanvas" method to clear the canvas.
		*
		* @method createDebugCanvas
		* @public
		*/
		public createDebugCanvas() {

			if( Kiwi.Utils.Common.isUndefined(this.debugCanvas) == false ) return;
			
			if (this._game.deviceTargetOption === Kiwi.TARGET_COCOON) {
				// Not supported in CocoonJS only because we cannot add it to
				// the container (as a container does not exist) and position
				// will be hard.
				Kiwi.Log.log("Debug canvas not supported in cocoon, creating canvas and context anyway", "#debug-canvas");
			}

			this.debugCanvas = <HTMLCanvasElement>document.createElement("canvas");
			this.debugCanvas.id = this._game.id + "debugCanvas";
			this.debugCanvas.style.position = "absolute";
			this.debugCanvas.width = this.width;
			this.debugCanvas.height = this.height;
			this.debugCanvas.style.width = "100%";
			this.debugCanvas.style.height = "100%";
			this.dctx = this.debugCanvas.getContext("2d");
			this.clearDebugCanvas();

			if (this._game.deviceTargetOption === Kiwi.TARGET_BROWSER) {
				this.container.appendChild(this.debugCanvas);
			}

		}

		/**
		* Clears the debug canvas and fills with either the color passed. 
		* If not colour is passed then Red at 20% opacity is used.
		* 
		* @method clearDebugCanvas
		* @param [color="rgba(255,0,0,0.2)"] {string} The debug color to rendering on the debug canvas.
		* @public
		*/
		public clearDebugCanvas( color?:string ) {
			this.dctx.fillStyle = color || "rgba(255,0,0,.2)";
			this.dctx.clearRect(0, 0, this.width, this.height);
			this.dctx.fillRect(0, 0, this.width, this.height)
		}

		/**
		* Toggles the visibility of the debug canvas. 
		* @method toggleDebugCanvas
		* @public
		*/
		public toggleDebugCanvas() {
			this.debugCanvas.style.display = (this.debugCanvas.style.display === "none") ? "block" : "none";
		}

		/**
		* Handles the scaling/sizing based upon the scaleType property.
		* @method _scaleContainer
		* @private
		*/
		private _scaleContainer() {

			if (this._game.deviceTargetOption == Kiwi.TARGET_BROWSER) {

				var clientWidth = this.container.clientWidth;

				this.container.style.width = String(this._width + "px");
				this.container.style.height = String(this._height + "px");

				if (this._scaleType == Kiwi.Stage.SCALE_NONE) {
					this.container.style.maxWidth = "";
					this.container.style.minWidth = "";
				}

				// To Fit or STRETCH 
				if (this._scaleType == Kiwi.Stage.SCALE_STRETCH || this._scaleType == Kiwi.Stage.SCALE_FIT) {
					this.container.style.minWidth = "100%";
					this.container.style.maxWidth = "100%";
				}

				// If scale stretched then scale the containers height to 100%
				// of its parent's.
				if (this._scaleType == Kiwi.Stage.SCALE_STRETCH) {
					this.container.style.minHeight = "100%";
					this.container.style.maxHeight = "100%";
				} else {
					this.container.style.minHeight = "";
					this.container.style.maxHeight = "";
				}

				// If it is SCALE to FIT then scale the containers height in
				// ratio with the containers width.
				if (this._scaleType == Kiwi.Stage.SCALE_FIT) {
					this.container.style.height = String((clientWidth / this._width) * this._height) + "px";
				}

			}

			if (this._game.deviceTargetOption == Kiwi.TARGET_COCOON) {
				// This has no effect in WebGL, and is thus handled separately.
				switch (this._scaleType) {
					case Kiwi.Stage.SCALE_FIT:
						this.canvas.style.cssText = "idtkscale:ScaleAspectFit";
						break;

					case Kiwi.Stage.SCALE_STRETCH:
						this.canvas.style.cssText = "idtkscale:ScaleToFill";
						break;

					case Kiwi.Stage.SCALE_NONE:
						this.canvas.style.cssText = "";
						break;
				}

			}

		}
	
		/**
		* Dispatches callbacks when the page containing this game gains focus.
		* 
		* @property onFocus
		* @type Kiwi.Signal
		* @since 1.3.0
		* @public
		*/
		public onFocus: Kiwi.Signal;

		/**
		* Dispatches callbacks when this page containing this game loses focus.
		* 
		* @property onBlur
		* @type Kiwi.Signal
		* @since 1.3.0
		* @public
		*/
		public onBlur: Kiwi.Signal;

		/**
		* Dispatches callbacks when the visiblity of the page changes.
		* 
		* @property onVisibilityChange
		* @type Kiwi.Signal
		* @since 1.3.0
		* @public
		*/
		public onVisibilityChange: Kiwi.Signal;

		/**
		* A flag indicating if the page is currently visible
		* (using the Visiblity API).
		* If the Visiblity API is unsupported this will remain set to true
		* regardless of focus / blur events.
		*
		* @property visibility
		* @type boolean 
		* @default true
		* @readOnly
		* @since 1.3.0
		* @public
		*/
		public get visibility():boolean {

			if (this._visibility) {
				return !document[this._visibility];
			}

			return true;
		}

		/**
		* Contains string used to access the `hidden` property on the document.
		* 
		* @property _visibility
		* @type String
		* @default 'hidden'
		* @since 1.3.0
		* @private
		*/
		private _visibility: string;

		/**
		* Contains the bound version of the `_checkVisibility` method.
		* 
		* @property _visibilityChange
		* @type any
		* @since 1.3.0
		* @private
		*/
		private _visibilityChange: any;

		/**
		* Fired when the page visibility changes, or the page focus/blur
		* events fire. In charge of firing the appropriate signals. 
		*
		* @method _checkVisibility
		* @param event {Any}
		* @since 1.3.0
		* @private
		*/
		private _checkVisibility(event) {

			if (event.type === "focus" || event.type === "pageshow") {
				this.onFocus.dispatch(event);
				return;

			} else if (event.type === "pagehide" || event.type === "blur") {
				this.onBlur.dispatch(event);
				return;

			}

			if (event.type === "visibilitychange" || event.type === "mozvisibilitychange" || event.type === "webkitvisibilitychange" || event.type === "msvisibilitychange") {
				this.onVisibilityChange.dispatch();
				return;
			}
			   
		}

		/**
		* Adds the focus, blur, and visibility events to the document.
		* 
		* @method _createFocusEvents
		* @since 1.3.0
		* @private
		*/
		private _createFocusEvents() {

			this._visibility = "hidden";
			this._visibilityChange = this._checkVisibility.bind(this);

			if ("hidden" in document) {
				document.addEventListener("visibilitychange", this._visibilityChange);

			} else if ((this._visibility = "mozHidden") in document) {
				document.addEventListener("mozvisibilitychange", this._visibilityChange);

			} else if ((this._visibility = "webkitHidden") in document) {
				document.addEventListener("webkitvisibilitychange", this._visibilityChange);

			} else if ((this._visibility = "msHidden") in document) {
				document.addEventListener("msvisibilitychange", this._visibilityChange);

			} else {
				// Not supported. 
				this._visibility = null;
			}

			window.addEventListener('pageshow', this._visibilityChange);
			window.addEventListener('pagehide', this._visibilityChange);
			window.addEventListener('focus', this._visibilityChange);
			window.addEventListener('blur', this._visibilityChange);

		}

	}

}