/*
* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome.
* @version 3.1.4
* @link http://fooplugins.com
* @copyright Steven Usher & Brad Vincent 2015
* @license Released under the GPLv3 license.
*/
(function($, F){

	// global int to use if the table has no ID
	var _uid = 0,
	// a hash value for the current url
		_url_hash = (function(str){
			var i, l, hval = 0x811c9dc5;
			for (i = 0, l = str.length; i < l; i++) {
				hval ^= str.charCodeAt(i);
				hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
			}
			return hval >>> 0;
		})(location.origin + location.pathname);

	F.State = F.Component.extend(/** @lends FooTable.State */{
		/**
		 * The state component adds the ability for the table to remember its basic state for filtering, paging and sorting.
		 * @constructs
		 * @extends FooTable.Component
		 * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component.
		 * @returns {FooTable.State}
		 */
		construct: function(table){
			// call the constructor of the base class
			this._super(table, table.o.state.enabled);
			// Change this value if an update to this component requires any stored data to be reset
			this._key = '1';
			/**
			 * The key to use to store the state for this table.
			 * @type {(null|string)}
			 */
			this.key = this._key + (F.is.string(table.o.state.key) ? table.o.state.key : this._uid());
			/**
			 * Whether or not to allow the filtering component to store it's state.
			 * @type {boolean}
			 */
			this.filtering = F.is.boolean(table.o.state.filtering) ? table.o.state.filtering : true;
			/**
			 * Whether or not to allow the paging component to store it's state.
			 * @type {boolean}
			 */
			this.paging = F.is.boolean(table.o.state.paging) ? table.o.state.paging : true;
			/**
			 * Whether or not to allow the sorting component to store it's state.
			 * @type {boolean}
			 */
			this.sorting = F.is.boolean(table.o.state.sorting) ? table.o.state.sorting : true;
		},
		/* PROTECTED */
		/**
		 * Checks the supplied data and options for the state component.
		 * @instance
		 * @protected
		 * @param {object} data - The jQuery data object from the parent table.
		 * @fires FooTable.State#"preinit.ft.state"
		 * @this FooTable.State
		 */
		preinit: function(data){
			var self = this;
			/**
			 * The preinit.ft.state event is raised before the UI is created and provides the tables jQuery data object for additional options parsing.
			 * Calling preventDefault on this event will disable the component.
			 * @event FooTable.State#"preinit.ft.state"
			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
			 * @param {object} data - The jQuery data object of the table raising the event.
			 */
			this.ft.raise('preinit.ft.state', [data]).then(function(){

				self.enabled = F.is.boolean(data.state)
					? data.state
					: self.enabled;

				if (!self.enabled) return;

				self.key = self._key + (F.is.string(data.stateKey) ? data.stateKey : self.key);

				self.filtering = F.is.boolean(data.stateFiltering) ? data.stateFiltering : self.filtering;

				self.paging = F.is.boolean(data.statePaging) ? data.statePaging : self.paging;

				self.sorting = F.is.boolean(data.stateSorting) ? data.stateSorting : self.sorting;

			}, function(){
				self.enabled = false;
			});
		},
		/**
		 * Gets the state value for the specified key for this table.
		 * @instance
		 * @param {string} key - The key to get the value for.
		 * @returns {(*|null)}
		 */
		get: function(key){
			return JSON.parse(localStorage.getItem(this.key + ':' + key));
		},
		/**
		 * Sets the state value for the specified key for this table.
		 * @instance
		 * @param {string} key - The key to set the value for.
		 * @param {*} data - The value to store for the key. This value must be JSON.stringify friendly.
		 */
		set: function(key, data){
			localStorage.setItem(this.key + ':' + key, JSON.stringify(data));
		},
		/**
		 * Clears the state value for the specified key for this table.
		 * @instance
		 * @param {string} key - The key to clear the value for.
		 */
		remove: function(key){
			localStorage.removeItem(this.key + ':' + key);
		},
		/**
		 * Executes the {@link FooTable.Component#readState} function on all components.
		 * @instance
		 */
		read: function(){
			this.ft.execute(false, true, 'readState');
		},
		/**
		 * Executes the {@link FooTable.Component#writeState} function on all components.
		 * @instance
		 */
		write: function(){
			this.ft.execute(false, true, 'writeState');
		},
		/**
		 * Executes the {@link FooTable.Component#clearState} function on all components.
		 * @instance
		 */
		clear: function(){
			this.ft.execute(false, true, 'clearState');
		},
		/**
		 * Generates a unique identifier for the current {@link FooTable.Table} if one is not supplied through the options.
		 * This value is a combination of the url hash and either the element ID or an incremented global int value.
		 * @instance
		 * @returns {*}
		 * @private
		 */
		_uid: function(){
			var id = this.ft.$el.attr('id');
			return _url_hash + '_' + (F.is.string(id) ? id : ++_uid);
		}
	});

	F.components.register('state', F.State, 700);

})(jQuery, FooTable);
(function(F){

	/**
	 * This method is called from the {@link FooTable.State#read} method and allows a component to retrieve its' stored state.
	 * @instance
	 * @protected
	 * @function
	 */
	F.Component.prototype.readState = function(){};

	/**
	 * This method is called from the {@link FooTable.State#write} method and allows a component to write its' current state to the store.
	 * @instance
	 * @protected
	 * @function
	 */
	F.Component.prototype.writeState = function(){};

	/**
	 * This method is called from the {@link FooTable.State#clear} method and allows a component to clear any stored state.
	 * @instance
	 * @protected
	 * @function
	 */
	F.Component.prototype.clearState = function(){};

})(FooTable);
(function(F){

	/**
	 * An object containing the state options for the plugin. Added by the {@link FooTable.State} component.
	 * @type {object}
	 * @prop {boolean} enabled=false - Whether or not to allow state to be stored for the table. This overrides the individual component enable options.
	 * @prop {boolean} filtering=true - Whether or not to allow the filtering state to be stored.
	 * @prop {boolean} paging=true - Whether or not to allow the filtering state to be stored.
	 * @prop {boolean} sorting=true - Whether or not to allow the filtering state to be stored.
	 * @prop {string} key=null - The unique key to use to store the table's data.
	 */
	F.Defaults.prototype.state = {
		enabled: false,
		filtering: true,
		paging: true,
		sorting: true,
		key: null
	};

})(FooTable);
(function(F){

	if (!F.Filtering) return;

	/**
	 * Allows the filtering component to retrieve its' stored state.
	 */
	F.Filtering.prototype.readState = function(){
		if (this.ft.state.filtering){
			var state = this.ft.state.get('filtering');
			if (F.is.hash(state) && !F.is.emptyArray(state.filters)){
				this.filters = this.ensure(state.filters);
			}
		}
	};

	/**
	 * Allows the filtering component to write its' current state to the store.
	 */
	F.Filtering.prototype.writeState = function(){
		if (this.ft.state.filtering) {
			var filters = F.arr.map(this.filters, function (f) {
				return {
					name: f.name,
					query: f.query instanceof F.Query ? f.query.val() : f.query,
					columns: F.arr.map(f.columns, function (c) {
						return c.name;
					}),
					hidden: f.hidden,
					space: f.space,
					connectors: f.connectors,
					ignoreCase: f.ignoreCase
				};
			});
			this.ft.state.set('filtering', {filters: filters});
		}
	};

	/**
	 * Allows the filtering component to clear any stored state.
	 */
	F.Filtering.prototype.clearState = function(){
		if (this.ft.state.filtering) {
			this.ft.state.remove('filtering');
		}
	};

})(FooTable);
(function(F){

	if (!F.Paging) return;

	/**
	 * Allows the paging component to retrieve its' stored state.
	 */
	F.Paging.prototype.readState = function(){
		if (this.ft.state.paging) {
			var state = this.ft.state.get('paging');
			if (F.is.hash(state)) {
				this.current = state.current;
				this.size = state.size;
			}
		}
	};

	/**
	 * Allows the paging component to write its' current state to the store.
	 */
	F.Paging.prototype.writeState = function(){
		if (this.ft.state.paging) {
			this.ft.state.set('paging', {
				current: this.current,
				size: this.size
			});
		}
	};

	/**
	 * Allows the paging component to clear any stored state.
	 */
	F.Paging.prototype.clearState = function(){
		if (this.ft.state.paging) {
			this.ft.state.remove('paging');
		}
	};

})(FooTable);
(function(F){

	if (!F.Sorting) return;

	/**
	 * Allows the sorting component to retrieve its' stored state.
	 */
	F.Sorting.prototype.readState = function(){
		if (this.ft.state.sorting) {
			var state = this.ft.state.get('sorting');
			if (F.is.hash(state)) {
				var column = this.ft.columns.get(state.column);
				if (column instanceof F.Column) {
					this.column = column;
					this.column.direction = state.direction;
				}
			}
		}
	};

	/**
	 * Allows the sorting component to write its' current state to the store.
	 */
	F.Sorting.prototype.writeState = function(){
		if (this.ft.state.sorting && this.column instanceof F.Column){
			this.ft.state.set('sorting', {
				column: this.column.name,
				direction: this.column.direction
			});
		}
	};

	/**
	 * Allows the sorting component to clear any stored state.
	 */
	F.Sorting.prototype.clearState = function(){
		if (this.ft.state.sorting) {
			this.ft.state.remove('sorting');
		}
	};

})(FooTable);
(function(F){

	// hook into the _construct method so we can add the state property to the table.
	F.Table.extend('_construct', function(ready){
		this.state = this.use(FooTable.State);
		this._super(ready);
	});

	// hook into the _preinit method so we can trigger a plugin wide read state operation.
	F.Table.extend('_preinit', function(){
		var self = this;
		return self._super().then(function(){
			if (self.state.enabled){
				self.state.read();
			}
		});
	});

	// hook into the draw method so we can trigger a plugin wide write state operation.
	F.Table.extend('draw', function(){
		var self = this;
		return self._super().then(function(){
			if (self.state.enabled){
				self.state.write();
			}
		});
	});

})(FooTable);