Source: core/Address.js

/**
 * Address
 * 
 * @namespace core
 * @author Georgi Popov
 * @version 1.0.0
 * @license http://www.gnu.org/licenses/gpl-3.0.en.html GPLv3
 * @requires com/magadanski/core/core.js
 * @requires com/magadanski/core/EventDispatcher.js
 */

define('com.magadanski.core.Address', function () {
	var that;
	
	// private properties
	/**
	 * Root-relative address to the homepage
	 * 
	 * This variable holds the path to the root of the application. In case it's in the root of the domain the default value of '/' will be used.
	 * 
	 * In case the application is, however, in some subdirectory you'll need to overwrite this by passing an `aRoot` argument to the class constructor.
	 * 
	 * @access private
	 * @inner
	 * @memberOf Address
	 * @type {string}
	 * @default
	 * @see Address
	 */
	var addressRoot = '/';
	
	/**
	 * Copy of deep-linked address
	 * 
	 * The property is not to be mistaken with the `window.location.hash` object (although it can contain it).
	 * This will not always appear with "#" but can also be set as any path.
	 * 
	 * If you use `setHath('#hash')` this will essentially match the `window.location.hash` yet you can ommit the "#" symbol
	 * when calling the method.
	 * 
	 * It gets updated through the `updateHash()` private method each time a `setHash()` call occurs.
	 * 
	 * There is a `getHash()` getter and a `setHath()` setter to allow you to control the property.
	 * 
	 * @access private
	 * @inner
	 * @memberOf Address
	 * @type {string}
	 * @see {@link Address#getHash|getHash}
	 * @see {@link Address#setHash|setHash}
	 */
	var hash = '';
	
	/**
	 * Holder of a state property for HTML5 `history.pushState()`
	 * 
	 * This will be updated when the `setHash()` method is used.
	 * 
	 * You cannot directly set the value of `state` without calling `setHash()` but you can retrieve it with the `getState()` getter.
	 * 
	 * @access private
	 * @inner
	 * @readonly
	 * @memberOf Address
	 * @type {string}
	 * @see {@link Address#getState|getState}
	 */
	var state = '';
	
	// private methods
	/**
	 * Auto handler of the `window.popstate` event
	 * 
	 * @access private
	 * @inner
	 * @memberOf Address
	 * @param  {Event} e The `popstate` event
	 * @return {void}
	 */
	function popStateHandler(e) {
		updateHash();
		state = e.state;
	}
	
	/**
	 * Stores the current hash as a property each time `setHash()` is called.
	 * 
	 * @access private
	 * @inner
	 * @memberOf Address
	 * @return {void}
	 * @see {@link Address~hash|hash}
	 * @see {@link Address#setHash|setHash}
	 */
	function updateHash() {
		hash = '/' + window.location.pathname.replace(addressRoot, '').replace(/^\//, '');
	}
	
	/**
	 * A helper class for working with browser history and push state. This should be used for deep linking.
	 * 
	 * @class Address
	 * @since  1.0.0
	 * @extends {EventDispatcher}
	 * @param {string} aRoot The path to the main script on the server. Defult value is root: '/'.
	 * @fires {@link Address#change|change}
	 */
	var Address = function (aRoot) {
		that = this;
		if (typeof(aRoot) === 'string') {
			addressRoot = aRoot;
		}
		
		// priviledged properties
		
		// priviledged methods
		/**
		 * Getter for {@link Address~hash|hash}
		 * 
		 * @access public
		 * @instance
		 * @method getHash
		 * @memberof Address
		 * @return {string} The URL hash
		 * @see {@link Address~hash|hash}
		 * @see {@link Address#setHash|setHash}
		 */
		that.getHash = function () {
			return hash;
		}
		
		/**
		 * Sets the URL hash, the title and allows for a custom state to be saved.
		 * 
		 * This is an alias for `history.pushState` that also performs some internal class handling and event triggering.
		 * 
		 * @access public
		 * @instance
		 * @method setHash
		 * @memberof Address
		 * @param {string} hash The new value that would be set for the hash
		 * @param {string} title The title argument to be passed to the history.pushstate function. As of February 8th, 2016 this is still simply ignored by all browsers.
		 * @param {object} state An object storing additional information for the state itself
		 * @see {@link Address~hash|hash}
		 * @see {@link Address#getHash|getHash}
		 */
		that.setHash = function (hash, title, state) {
			var old = { hash: that.getHash(), state: that.getState() };
			
			if (old.hash != hash) {
				history.pushState(title, state, hash);
				updateHash();
				
				window.dispatchEvent(new PopStateEvent('popstate', { state: state }));
				/**
				 * Event dispatched when the URL of the page changes.
				 * 
				 * This is accompanied by a native window.popstate event.
				 * 
				 * @event Address#change
				 * @type {Object}
				 * @property {string} previousHash The previous page {@link Address~hash|hash}
				 * @property {Object} previousState The previous page {@link Address~state|state}
				 */
				that.dispatchEvent('change', { previousHash: old.hash, previousState: old.state });
			}
		}
		
		/**
		 * Getter for {@link Address~state|state}
		 * 
		 * @access public
		 * @instance
		 * @method getState
		 * @memberof Address
		 * @return {object} The state object
		 * @see {@link Address#state~state}
		 * @see {@link Address#setHash|setHash}
		 */
		that.getState = function () {
			return state;
		}
		
		// constructor
		window.addEventListener('popstate', popStateHandler);
	}
	Address.inherits(com.magadanski.core.EventDispatcher);
	com.magadanski.core.Address = Address;
	
	// public methods
});