File:ScaleItem.js

/**
 * @module UI
 * @namespace springroll
 * @requires Core
 */
(function()
{
	// Class imports
	var ScaleManager;

	/**
	 * A single UI item that needs to be resized,
	 * this is an internal class that you would not need to interact with.
	 *
	 * @class ScaleItem
	 * @private
	 * @param {PIXI.DisplayObject|createjs.DisplayObject} display The item to affect
	 * @param {String} align The vertical-horizontal alignment shorthand
	 * @param {Object} size The original screen the item was designed for
	 * @param {DisplayAdapter} adapter The display adapter
	 */
	var ScaleItem = function(display, align, size, adapter)
	{
		if (!ScaleManager)
		{
			ScaleManager = include('springroll.ScaleManager');
		}

		// Break align into parts
		align = align.split('-');

		/**
		 * What vertical screen location the item should be aligned to: "top", "center", "bottom"
		 * @property {String} vertAlign
		 */
		this.vertAlign = align[0];

		/**
		 * What horizontal screen location the item should be aligned to: "left", "center", "right"
		 * @property {String} horiAlign
		 */
		this.horiAlign = align[1];

		/**
		 * If this element should be aligned to the title safe area, not the actual screen.
		 * Values of "horizontal" and "vertical" make the title safe calculations take place only
		 * for one direction.
		 * @property {Boolean|String} titleSafe
		 * @default false
		 */
		this.titleSafe = false;

		/**
		 * Maximum scale allowed in physical size
		 * @property {Number} maxScale
		 * @default 1
		 */
		this.maxScale = 1;

		/**
		 * Minimum scale allowed in physical size
		 * @property {Number} minScale
		 * @default 1
		 */
		this.minScale = 1;

		/**
		 * If the UI element is centered horizontally
		 * @property {Boolean} centeredHorizontally
		 * @default false
		 */
		this.centeredHorizontally = false;

		/**
		 * The reference to the interface item we're scaling
		 * @private
		 * @property {PIXI.DisplayObject|createjs.DisplayObject} _display
		 */
		this._display = display;

		/**
		 * The original screen the item was designed for
		 * @private
		 * @property {Object} _size
		 */
		this._size = size;

		/**
		 * The adapter for universal scale, rotation size access
		 * @property {Object} _adapter
		 * @private
		 */
		this._adapter = adapter;

		var scale = adapter.getScale(display);
		var position = adapter.getPosition(display);

		/**
		 * Original X scale of the item
		 * @property {Number} origScaleX
		 * @default 0
		 */
		var origScaleX = this.origScaleX = scale.x || 1;

		/**
		 * The original Y scale of the item
		 * @property {Number} origScaleY
		 * @default 0
		 */
		var origScaleY = this.origScaleY = scale.y || 1;

		/**
		 * The original bounds of the item with x, y, right, bottom, width,
		 * height properties. This is converted from local bounds to scaled bounds.
		 * @property {Object} origBounds
		 */
		this.origBounds = adapter.getLocalBounds(display);
		//convert bounds to something more usable
		var temp, bounds = this.origBounds;
		if (this.origScaleX < 0)
		{
			temp = bounds.x;
			bounds.x = bounds.right * origScaleX;
			bounds.right = temp * origScaleX;
			bounds.width *= Math.abs(origScaleX);
		}
		else
		{
			bounds.x *= origScaleX;
			bounds.right *= origScaleX;
			bounds.width *= origScaleX;
		}
		if (this.origScaleY < 0)
		{
			temp = bounds.y;
			bounds.y = bounds.bottom * origScaleY;
			bounds.bottom = temp * origScaleY;
			bounds.height *= Math.abs(origScaleY);
		}
		else
		{
			bounds.y *= origScaleY;
			bounds.bottom *= origScaleY;
			bounds.height *= origScaleY;
		}

		/**
		 * Original horizontal margin in pixels
		 * @property {Number} origMarginHori
		 * @default 0
		 */
		this.origMarginHori = 0;

		/**
		 * Original vertical margin in pixels
		 * @property {Number} origMarginVert
		 * @default 0
		 */
		this.origMarginVert = 0;

		switch (this.vertAlign)
		{
			case ScaleManager.ALIGN_TOP:
				{
					this.origMarginVert = position.y + this.origBounds.y;
					break;
				}
			case ScaleManager.ALIGN_CENTER:
				{
					this.origMarginVert = size.height * 0.5 - position.y;
					break;
				}
			case ScaleManager.ALIGN_BOTTOM:
				{
					this.origMarginVert = size.height - (position.y + this.origBounds.bottom);
					break;
				}
		}

		switch (this.horiAlign)
		{
			case ScaleManager.ALIGN_LEFT:
				{
					this.origMarginHori = position.x + this.origBounds.x;
					break;
				}
			case ScaleManager.ALIGN_CENTER:
				{
					this.origMarginHori = size.width * 0.5 - position.x;
					break;
				}
			case ScaleManager.ALIGN_RIGHT:
				{
					this.origMarginHori = size.width - (position.x + this.origBounds.right);
					break;
				}
		}
	};

	// Reference to the prototype
	var p = extend(ScaleItem);

	if (DEBUG)
	{
		p.toString = function()
		{
			return "[ScaleItem (vertAlign='" + this.vertAlign + "', horiAlign='" + this.horiAlign + "')]";
		};
	}

	/**
	 * Get the current display item
	 * @property {PIXI.DisplayObject|createjs.DisplayObject} display
	 * @readOnly
	 */
	Object.defineProperty(p, 'display',
	{
		get: function()
		{
			return this._display;
		}
	});

	/**
	 * Adjust the item scale and position, to reflect new screen
	 * @method resize
	 * @param {Number} displayWidth The current screen width
	 * @param {Number} displayHeight The current screen height
	 */
	p.resize = function(displayWidth, displayHeight)
	{
		var adapter = this._adapter;
		var _display = this._display;
		var _size = this._size;
		var origBounds = this.origBounds;
		var origScaleX = this.origScaleX;
		var origScaleY = this.origScaleY;
		var defaultRatio = _size.width / _size.height;
		var currentRatio = displayWidth / displayHeight;
		var overallScale = currentRatio >= defaultRatio ?
			displayHeight / _size.height :
			displayWidth / _size.width;
		var scaleToHeight = currentRatio >= defaultRatio;
		var letterBoxWidth = 0;
		var letterBoxHeight = 0;

		if (scaleToHeight)
		{
			letterBoxWidth = (displayWidth - _size.width * overallScale) / 2;
		}
		else
		{
			letterBoxHeight = (displayHeight - _size.height * overallScale) / 2;
		}

		// Optional clamps on the min and max scale of the item
		var itemScale = overallScale;
		if (this.minScale && itemScale < this.minScale)
		{
			itemScale = this.minScale;
		}
		else if (this.maxScale && itemScale > this.maxScale)
		{
			itemScale = this.maxScale;
		}

		adapter.setScale(_display, origScaleX * itemScale, "x");
		adapter.setScale(_display, origScaleY * itemScale, "y");

		// Positioning
		var m;
		var x;
		var y;

		// Vertical margin
		m = this.origMarginVert * overallScale;

		// Determine if vertical alignment should be title safe
		var titleSafe = this.titleSafe === true || this.titleSafe === "vertical";

		switch (this.vertAlign)
		{
			case ScaleManager.ALIGN_TOP:
				{
					if (titleSafe)
					{
						y = letterBoxHeight + m - origBounds.y * itemScale;
					}
					else
					{
						y = m - origBounds.y * itemScale;
					}
					break;
				}
			case ScaleManager.ALIGN_CENTER:
				{
					y = displayHeight * 0.5 - m;
					break;
				}
			case ScaleManager.ALIGN_BOTTOM:
				{
					if (titleSafe)
					{
						y = displayHeight - letterBoxHeight - m - origBounds.bottom * itemScale;
					}
					else
					{
						y = displayHeight - m - origBounds.bottom * itemScale;
					}
					break;
				}
		}

		// Set the position
		if (y !== null)
		{
			adapter.setPosition(_display, y, "y");
		}

		// Horizontal margin
		m = this.origMarginHori * overallScale;

		// Determine if horizontal alignment should be title safe
		titleSafe = this.titleSafe === true || this.titleSafe === "horizontal";

		switch (this.horiAlign)
		{
			case ScaleManager.ALIGN_LEFT:
				{
					if (titleSafe)
					{
						x = letterBoxWidth + m - origBounds.x * itemScale;
					}
					else
					{
						x = m - origBounds.x * itemScale;
					}
					break;
				}
			case ScaleManager.ALIGN_CENTER:
				{
					if (this.centeredHorizontally)
					{
						x = (displayWidth - _display.width) * 0.5;
					}
					else
					{
						x = displayWidth * 0.5 - m;
					}
					break;
				}
			case ScaleManager.ALIGN_RIGHT:
				{
					if (titleSafe)
					{
						x = displayWidth - letterBoxWidth - m - origBounds.right * itemScale;
					}
					else
					{
						x = displayWidth - m - origBounds.right * itemScale;
					}
					break;
				}
		}

		// Set the position
		if (x !== null)
		{
			adapter.setPosition(_display, x, "x");
		}
	};

	/**
	 * Destroy this item, don't use after this
	 * @method destroy
	 */
	p.destroy = function()
	{
		this._adapter = null;
		this.origBounds = null;
		this._display = null;
		this._size = null;
	};

	// Assign to namespace
	namespace('springroll').ScaleItem = ScaleItem;

}());