API Docs for: 1.0.0
Show:

File: src/gallery-datatable-state/js/state.js

"use strict";

/**
 * @module gallery-datatable-state
 */

/**********************************************************************
 * <p>Plugin for DataTable to preserve state, either on a single page or
 * across pages.</p>
 *
 * @main gallery-datatable-state
 * @class DataTableState
 * @namespace Plugin
 * @extends Plugin.Base
 * @constructor
 * @param config {Object} configuration
 */
function State(
	/* object */ config)
{
	State.superclass.constructor.call(this, config);
}

State.NAME = "DataTableStatePlugin";
State.NS   = "state";

State.ATTRS =
{
	/**
	 * Id of a column (usually not displayed) that yields a unique value
	 * for each record.  The saved state is indexed by the value of this
	 * column.
	 *
	 * @attribute uniqueIdKey
	 * @type {String}
	 * @required
	 */
	uniqueIdKey:
	{
		validator: Y.Lang.isString
	},

	/**
	 * List of objects specifying the values to be saved before
	 * the table is re-rendered.  Each object must define:
	 * <dl>
	 * <dt>column</dt>
	 * <dd>the column key</dd>
	 * <dt>node or widget</dt>
	 * <dd>CSS selector to find either the node or the widget container inside a cell</dd>
	 * <dt>key</dt>
	 * <dd>the value to pass to get/set</dd>
	 * <dt>temp</dt>
	 * <dd>true if the state should be cleared when paginating</dd>
	 * </dl>
	 * If a value should not be maintained when paginating, specify temp:true.
	 *
	 * @attribute save
	 * @type {Array}
	 * @required
	 */
	save:
	{
		value:     [],
		validator: Y.Lang.isArray
	},

	/**
	 * Paginator that triggers clearing of temporary state.  If
	 * this is not specified, temp:true will have no effect in the "save"
	 * configuration.
	 * 
	 * @attribute paginator
	 * @type {Paginator}
	 */
	paginator:
	{
		validator: function(value)
		{
			return (!value || Y.Lang.isObject(value));
		}
	}
};

function removeKey(key, obj)
{
	delete obj[key];
}

function clearState(key)
{
	Y.each(this.state, Y.bind(removeKey, null, key));
}

function analyzeColumns()
{
	var list = this.get('host')._displayColumns;

	Y.each(this.get('save'), function(item)
	{
		item.column_index = Y.Array.findIndexOf(list, function(c)
		{
			return c.key === item.column;
		});

		if (item.column_index < 0)
		{
			clearState.call(this, item.column);
		}
	},
	this);
}

function saveState()
{
	var host   = this.get('host');
	var count  = host.data.size();
	var id_key = this.get('uniqueIdKey');
	Y.each(this.get('save'), function(item)
	{
		if (item.column_index < 0)
		{
			return;
		}

		for (var i=0; i<count; i++)
		{
			var value = null;

			var cell = host.getCell([i, item.column_index]);
			if (cell)
			{
				if (item.node)
				{
					var node = cell.one(item.node);
					if (node)
					{
						value = node.get(item.key);
					}
				}
				else if (item.widget)
				{
					var widget = Y.Widget.getByNode(cell.one(item.widget));
					if (widget)
					{
						value = widget.get(item.key);
					}
				}
			}

			var rec = host.data.item(i);
			var id  = rec.get(id_key);
			if (!this.state[id])
			{
				this.state[id] = {};
			}
			this.state[ id ][ item.column ] = value;
		}
	},
	this);
}

function restoreState()
{
	var host   = this.get('host');
	var count  = host.data.size();
	var id_key = this.get('uniqueIdKey');
	Y.each(this.get('save'), function(item)
	{
		if (item.column_index < 0)
		{
			return;
		}

		for (var i=0; i<count; i++)
		{
			var rec   = host.data.item(i);
			var state = this.state[ rec.get(id_key) ];
			if (state)
			{
				var value = state[ item.column ];
				var cell  = host.getCell([i, item.column_index]);
				if (cell)
				{
					if (item.node)
					{
						var node = cell.one(item.node);
						if (node)
						{
							node.set(item.key, value);
						}
					}
					else if (item.widget)
					{
						var widget = Y.Widget.getByNode(cell.one(item.widget));
						if (widget)
						{
							widget.set(item.key, value);
						}
					}
				}
			}
		}
	},
	this);
}

function clearTempState()
{
	Y.each(this.get('save'), function(item)
	{
		if (item.column_index < 0 || item.temp)
		{
			clearState.call(this, item.column);
		}
	},
	this);
}

function listenToPaginator(pg)
{
	this.pg_event_handler = pg.on('changeRequest', clearTempState, this);
}

Y.extend(State, Y.Plugin.Base,
{
	initializer: function(config)
	{
		this.state = {};
		this.on('uniqueIdKeyChange', function()
		{
			this.state = {};
		});

		if (config.paginator)
		{
			listenToPaginator.call(this, config.paginator)
		}

		this.on('paginatorChange', function(e)
		{
			if (this.pg_event_handler)
			{
				this.pg_event_handler.detach();
				this.pg_event_handler = null;
			}

			if (e.newVal)
			{
				listenToPaginator.call(this, e.newVal);
			}
		});

		analyzeColumns.call(this);
		this.after('saveChange', analyzeColumns);
		this.afterHostEvent('columnsChange', analyzeColumns);

		var host        = this.get('host');
		var self        = this;
		var orig_syncUI = this.orig_syncUI = host.syncUI;
		host.syncUI = function()
		{
			saveState.call(self);
			orig_syncUI.apply(host, arguments);
			restoreState.call(self);
		}

		this.onHostEvent('dataChange', saveState);
		this.afterHostEvent('dataChange', function()
		{
			Y.later(0, this, restoreState);
		});
	},

	destructor: function()
	{
		this.get('host').syncUI = this.orig_syncUI;

		if (this.pg_event_handler)
		{
			this.pg_event_handler.detach();
		}
	},

	/**
	 * @method getState
	 * @return {Object} state for each row, indexed by uniqueIdKey and column key
	 */
	getState: function()
	{
		saveState.call(this);
		return this.state;
	}
});

Y.namespace("Plugin");
Y.Plugin.DataTableState = State;