PageSourceSearch

https://kentasso.ru/local/templates/adapt/lib/footable/footable.js

js kentasso.ru collected 2026-10-05 00:13:42 UTC 268,317 bytes, 7,375 lines download raw bytes

1/*
2* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome.
3* @version 3.1.6
4* @link http://fooplugins.com
5* @copyright Steven Usher & Brad Vincent 2015
6* @license Released under the GPLv3 license.
7*/
8(function($, F){
9	// add in console we use in case it's missing
10	window.console = window.console || { log:function(){}, error:function(){} };
11
12	/**
13	 * The jQuery plugin initializer.
14	 * @function jQuery.fn.footable
15	 * @param {(object|FooTable.Defaults)} [options] - The options to initialize the plugin with.
16	 * @param {function} [ready] - A callback function to execute for each initialized plugin.
17	 * @returns {jQuery}
18	 */
19	$.fn.footable = function (options, ready) {
20		options = options || {};
21		// make sure we only work with tables
22		return this.filter('table').each(function (i, tbl) {
23			F.init(tbl, options, ready);
24		});
25	};
26
27	var debug_defaults = {
28		events: []
29	};
30	F.__debug__ = JSON.parse(localStorage.getItem('footable_debug')) || false;
31	F.__debug_options__ = JSON.parse(localStorage.getItem('footable_debug_options')) || debug_defaults;
32
33	/**
34	 * Gets or sets the internal debug variable which enables some additional logging to the console.
35	 * When enabled this value is stored in the localStorage so it can persist across page reloads.
36	 * @param {boolean} value - Whether or not to enable additional logging.
37	 * @param {object} [options] - Any debug specific options.
38	 * @returns {(boolean|undefined)}
39	 */
40	F.debug = function(value, options){
41		if (!F.is.boolean(value)) return F.__debug__;
42		F.__debug__ = value;
43		if (F.__debug__){
44			localStorage.setItem('footable_debug', JSON.stringify(F.__debug__));
45			F.__debug_options__ = $.extend(true, {}, debug_defaults, options || {});
46			if (F.is.hash(options)){
47				localStorage.setItem('footable_debug_options', JSON.stringify(F.__debug_options__));
48			}
49		} else {
50			localStorage.removeItem('footable_debug');
51			localStorage.removeItem('footable_debug_options');
52		}
53	};
54
55	/**
56	 * Gets the FooTable instance of the supplied table if one exists.
57	 * @param {(jQuery|jQuery.selector|HTMLTableElement)} table - The jQuery table object, selector or the HTMLTableElement to retrieve FooTable from.
58	 * @returns {(FooTable.Table|undefined)}
59	 */
60	F.get = function(table){
61		return $(table).first().data('__FooTable__');
62	};
63
64	/**
65	 * Initializes a new instance of FooTable on the supplied table.
66	 * @param {(jQuery|jQuery.selector|HTMLTableElement)} table - The jQuery table object, selector or the HTMLTableElement to initialize FooTable on.
67	 * @param {object} options - The options to initialize FooTable with.
68	 * @param {function} [ready] - A callback function to execute once the plugin is initialized.
69	 * @returns {FooTable.Table}
70	 */
71	F.init = function(table, options, ready){
72		var ft = F.get(table);
73		if (ft instanceof F.Table) ft.destroy();
74		return new F.Table(table, options, ready);
75	};
76
77	/**
78	 * Gets the FooTable.Row instance for the supplied element.
79	 * @param {(jQuery|jQuery.selector|HTMLTableElement)} element - A jQuery object, selector or the HTMLElement of an element to retrieve the FooTable.Row for.
80	 * @returns {FooTable.Row}
81	 */
82	F.getRow = function(element){
83		// to get the FooTable.Row object simply walk up the DOM, find the TR and grab the __FooTableRow__ data value
84		var $row = $(element).closest('tr');
85		// if this is a detail row get the previous row in the table to get the main TR element
86		if ($row.hasClass('footable-detail-row')){
87			$row = $row.prev();
88		}
89		// grab the row object
90		return $row.data('__FooTableRow__');
91	};
92
93	// The below are external type definitions mainly used as pointers to jQuery docs for important information
94	/**
95	 * jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API
96	 * that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript.
97	 * @name jQuery
98	 * @constructor
99	 * @returns {jQuery}
100	 * @see {@link http://api.jquery.com/}
101	 */
102
103	/**
104	 * This object provides a subset of the methods of the Deferred object (then, done, fail, always, pipe, and state) to prevent users from changing the state of the Deferred.
105	 * @typedef {object} jQuery.Promise
106	 * @see {@link http://api.jquery.com/Types/#Promise}
107	 */
108
109	/**
110	 * As of jQuery 1.5, the Deferred object provides a way to register multiple callbacks into self-managed callback queues, invoke callback queues as appropriate,
111	 * and relay the success or failure state of any synchronous or asynchronous function.
112	 * @typedef {object} jQuery.Deferred
113	 * @see {@link http://api.jquery.com/Types/#Deferred}
114	 */
115
116	/**
117	 * jQuery's event system normalizes the event object according to W3C standards. The event object is guaranteed to be passed to the event handler. Most properties from
118	 * the original event are copied over and normalized to the new event object.
119	 * @typedef {object} jQuery.Event
120	 * @see {@link http://api.jquery.com/category/events/event-object/}
121	 */
122
123	/**
124	 * Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events.
125	 * @memberof jQuery
126	 * @function when
127	 * @param {...jQuery.Deferred} deferreds - Any number of deferred objects to wait for.
128	 * @returns {jQuery.Promise}
129	 * @see {@link http://api.jquery.com/jQuery.when/}
130	 */
131
132	/**
133	 * The jQuery.fn namespace used to register plugins with jQuery.
134	 * @memberof jQuery
135	 * @namespace fn
136	 * @see {@link http://learn.jquery.com/plugins/basic-plugin-creation/}
137	 */
138})(
139	jQuery,
140	/**
141	 * The core FooTable namespace containing all the plugin code.
142	 * @namespace
143	 */
144	FooTable = window.FooTable || {}
145);
146(function(F){
147	var returnTrue = function(){ return true; };
148
149	/**
150	 * This namespace contains commonly used array utility methods.
151	 * @namespace {object} FooTable.arr
152	 */
153	F.arr = {};
154
155	/**
156	 * Iterates over each item in the supplied array and performs the supplied function passing in the current item as the first argument.
157	 * @memberof FooTable.arr
158	 * @function each
159	 * @param {Array} array - The array to iterate
160	 * @param {function} func - The function to execute for each item. The first argument supplied to this function is the current item and the second is the current index.
161	 */
162	F.arr.each = function (array, func) {
163		if (!F.is.array(array) || !F.is.fn(func)) return;
164		for (var i = 0, len = array.length; i < len; i++) {
165			if (func(array[i], i) === false) break;
166		}
167	};
168
169	/**
170	 * Get all items in the supplied array that optionally matches the supplied where function. If no items are found an empty array is returned.
171	 * @memberof FooTable.arr
172	 * @function get
173	 * @param {Array} array - The array to get items from.
174	 * @param {function} where - This function must return a boolean value, true includes the item in the result array.
175	 * @returns {Array}
176	 */
177	F.arr.get = function (array, where) {
178		var result = [];
179		if (!F.is.array(array)) return result;
180		if (!F.is.fn(where)) return array;
181		for (var i = 0, len = array.length; i < len; i++) {
182			if (where(array[i], i)) result.push(array[i]);
183		}
184		return result;
185	};
186
187	/**
188	 * Get a boolean value indicating if any item exists in the supplied array that optionally matches the supplied where function.
189	 * @memberof FooTable.arr
190	 * @function any
191	 * @param {Array} array - The array to check.
192	 * @param {function} [where] - [Optional] This function must return a boolean value, true indicates that the current item is a valid match.
193	 * @returns {boolean}
194	 */
195	F.arr.any = function (array, where) {
196		if (!F.is.array(array)) return false;
197		where = F.is.fn(where) ? where : returnTrue;
198		for (var i = 0, len = array.length; i < len; i++) {
199			if (where(array[i], i)) return true;
200		}
201		return false;
202	};
203
204	/**
205	 * Checks if the supplied value exists in the array.
206	 * @memberof FooTable.arr
207	 * @function contains
208	 * @param {Array} array - The array to check.
209	 * @param {*} value - The value to check for.
210	 * @returns {boolean}
211	 */
212	F.arr.contains = function(array, value){
213		if (!F.is.array(array) || F.is.undef(value)) return false;
214		for (var i = 0, len = array.length; i < len; i++) {
215			if (array[i] == value) return true;
216		}
217		return false;
218	};
219
220	/**
221	 * Get the first item in the supplied array that optionally matches the supplied where function. If no item is found null is returned.
222	 * @memberof FooTable.arr
223	 * @function first
224	 * @param {Array} array - The array to get the item from.
225	 * @param {function} [where] - [Optional] This function must return a boolean value, true indicates that the current item can be returned.
226	 * @returns {(*|null)}
227	 */
228	F.arr.first = function (array, where) {
229		if (!F.is.array(array)) return null;
230		where = F.is.fn(where) ? where : returnTrue;
231		for (var i = 0, len = array.length; i < len; i++) {
232			if (where(array[i], i)) return array[i];
233		}
234		return null;
235	};
236
237	/**
238	 * Creates a new array from the results of the supplied getter function. If no items are found an empty array is returned, to exclude an item from the results return null.
239	 * @memberof FooTable.arr
240	 * @function map
241	 * @param {Array} array - The array to iterate.
242	 * @param {function} getter - This function must return either a new value or null.
243	 * The first argument is the result being returned at this point in the iteration. The second argument is the current item being iterated.
244	 * @returns {(*|null)}
245	 */
246	F.arr.map = function (array, getter) {
247		var result = [], returned = null;
248		if (!F.is.array(array) || !F.is.fn(getter)) return result;
249		for (var i = 0, len = array.length; i < len; i++) {
250			if ((returned = getter(array[i], i)) != null) result.push(returned);
251		}
252		return result;
253	};
254
255	/**
256	 * Removes items from the array matching the supplied where function. All removed items are returned in a new array.
257	 * @memberof FooTable.arr
258	 * @function remove
259	 * @param {Array} array - The array to iterate and remove items from.
260	 * @param {function} where - This function must return a boolean value, true includes the item in the result array.
261	 * @returns {*}
262	 */
263	F.arr.remove = function (array, where) {
264		var remove = [], removed = [];
265		if (!F.is.array(array) || !F.is.fn(where)) return removed;
266		var i = 0, len = array.length;
267		for (; i < len; i++) {
268			if (where(array[i], i, removed)){
269				remove.push(i);
270				removed.push(array[i]);
271			}
272		}
273		// sort the indexes to be removed from largest to smallest
274		remove.sort(function(a, b){ return b - a; });
275		i = 0; len = remove.length;
276		for(; i < len; i++){
277			var index = remove[i] - i;
278			array.splice(index, 1);
279		}
280		return removed;
281	};
282
283	/**
284	 * Deletes a single item from the array. The item if removed is returned.
285	 * @memberof FooTable.arr
286	 * @function delete
287	 * @param {Array} array - The array to iterate and delete the item from.
288	 * @param {*} item - The item to find and delete.
289	 * @returns {(*|null)}
290	 */
291	F.arr.delete = function(array, item){
292		var remove = -1, removed = null;
293		if (!F.is.array(array) || F.is.undef(item)) return removed;
294		var i = 0, len = array.length;
295		for (; i < len; i++) {
296			if (array[i] == item){
297				remove = i;
298				removed = array[i];
299				break;
300			}
301		}
302		if (remove != -1) array.splice(remove, 1);
303		return removed;
304	};
305
306	/**
307	 * Replaces a single item in the array with a new one.
308	 * @memberof FooTable.arr
309	 * @function replace
310	 * @param {Array} array - The array to iterate and replace the item in.
311	 * @param {*} oldItem - The item to be replaced.
312	 * @param {*} newItem - The item to be inserted.
313	 */
314	F.arr.replace = function(array, oldItem, newItem){
315		var index = array.indexOf(oldItem);
316		if (index !== -1) array[index] = newItem;
317	};
318
319})(FooTable);
320(function (F) {
321
322	/**
323	 * This namespace contains commonly used 'is' type methods that return boolean values.
324	 * @namespace FooTable.is
325	 */
326	F.is = {};
327
328	/**
329	 * Checks if the type of the value is the same as that supplied.
330	 * @memberof FooTable.is
331	 * @function type
332	 * @param {*} value - The value to check the type of.
333	 * @param {string} type - The type to check for.
334	 * @returns {boolean}
335	 */
336	F.is.type = function (value, type) {
337		return typeof value === type;
338	};
339
340	/**
341	 * Checks if the value is defined.
342	 * @memberof FooTable.is
343	 * @function defined
344	 * @param {*} value - The value to check is defined.
345	 * @returns {boolean}
346	 */
347	F.is.defined = function (value) {
348		return typeof value !== 'undefined';
349	};
350
351	/**
352	 * Checks if the value is undefined.
353	 * @memberof FooTable.is
354	 * @function undef
355	 * @param {*} value - The value to check is undefined.
356	 * @returns {boolean}
357	 */
358	F.is.undef = function (value) {
359		return typeof value === 'undefined';
360	};
361
362	/**
363	 * Checks if the value is an array.
364	 * @memberof FooTable.is
365	 * @function array
366	 * @param {*} value - The value to check.
367	 * @returns {boolean}
368	 */
369	F.is.array = function (value) {
370		return '[object Array]' === Object.prototype.toString.call(value);
371	};
372
373	/**
374	 * Checks if the value is a date.
375	 * @memberof FooTable.is
376	 * @function date
377	 * @param {*} value - The value to check.
378	 * @returns {boolean}
379	 */
380	F.is.date = function (value) {
381		return '[object Date]' === Object.prototype.toString.call(value) && !isNaN(value.getTime());
382	};
383
384	/**
385	 * Checks if the value is a boolean.
386	 * @memberof FooTable.is
387	 * @function boolean
388	 * @param {*} value - The value to check.
389	 * @returns {boolean}
390	 */
391	F.is.boolean = function (value) {
392		return '[object Boolean]' === Object.prototype.toString.call(value);
393	};
394
395	/**
396	 * Checks if the value is a string.
397	 * @memberof FooTable.is
398	 * @function string
399	 * @param {*} value - The value to check.
400	 * @returns {boolean}
401	 */
402	F.is.string = function (value) {
403		return '[object String]' === Object.prototype.toString.call(value);
404	};
405
406	/**
407	 * Checks if the value is a number.
408	 * @memberof FooTable.is
409	 * @function number
410	 * @param {*} value - The value to check.
411	 * @returns {boolean}
412	 */
413	F.is.number = function (value) {
414		return '[object Number]' === Object.prototype.toString.call(value) && !isNaN(value);
415	};
416
417	/**
418	 * Checks if the value is a function.
419	 * @memberof FooTable.is
420	 * @function fn
421	 * @param {*} value - The value to check.
422	 * @returns {boolean}
423	 */
424	F.is.fn = function (value) {
425		return (F.is.defined(window) && value === window.alert) || '[object Function]' === Object.prototype.toString.call(value);
426	};
427
428	/**
429	 * Checks if the value is an error.
430	 * @memberof FooTable.is
431	 * @function error
432	 * @param {*} value - The value to check.
433	 * @returns {boolean}
434	 */
435	F.is.error = function (value) {
436		return '[object Error]' === Object.prototype.toString.call(value);
437	};
438
439	/**
440	 * Checks if the value is an object.
441	 * @memberof FooTable.is
442	 * @function object
443	 * @param {*} value - The value to check.
444	 * @returns {boolean}
445	 */
446	F.is.object = function (value) {
447		return '[object Object]' === Object.prototype.toString.call(value);
448	};
449
450	/**
451	 * Checks if the value is a hash.
452	 * @memberof FooTable.is
453	 * @function hash
454	 * @param {*} value - The value to check.
455	 * @returns {boolean}
456	 */
457	F.is.hash = function (value) {
458		return F.is.object(value) && value.constructor === Object && !value.nodeType && !value.setInterval;
459	};
460
461	/**
462	 * Checks if the supplied object is an HTMLElement
463	 * @memberof FooTable.is
464	 * @function element
465	 * @param {object} obj - The object to check.
466	 * @returns {boolean}
467	 */
468	F.is.element = function (obj) {
469		return typeof HTMLElement === 'object'
470			? obj instanceof HTMLElement
471			: obj && typeof obj === 'object' && obj !== null && obj.nodeType === 1 && typeof obj.nodeName === 'string';
472	};
473
474	/**
475	 * This is a simple check to determine if an object is a jQuery promise object. It simply checks the object has a "then" and "promise" function defined.
476	 * The promise object is created as an object literal inside of jQuery.Deferred.
477	 * It has no prototype, nor any other truly unique properties that could be used to distinguish it.
478	 * This method should be a little more accurate than the internal jQuery one that simply checks for a "promise" method.
479	 * @memberof FooTable.is
480	 * @function promise
481	 * @param {object} obj - The object to check.
482	 * @returns {boolean}
483	 */
484	F.is.promise = function(obj){
485		return F.is.object(obj) && F.is.fn(obj.then) && F.is.fn(obj.promise);
486	};
487
488	/**
489	 * Checks if the supplied object is an instance of a jQuery object.
490	 * @memberof FooTable.is
491	 * @function jq
492	 * @param {object} obj - The object to check.
493	 * @returns {boolean}
494	 */
495	F.is.jq = function(obj){
496		return F.is.defined(window.jQuery) && obj instanceof jQuery && obj.length > 0;
497	};
498
499	/**
500	 * Checks if the supplied object is a moment.js date object.
501	 * @memberof FooTable.is
502	 * @function moment
503	 * @param {object} obj - The object to check.
504	 * @returns {boolean}
505	 */
506	F.is.moment = function(obj){
507		return F.is.defined(window.moment) && F.is.object(obj) && F.is.boolean(obj._isAMomentObject)
508	};
509
510	/**
511	 * Checks if the supplied value is an object and if it is empty.
512	 * @memberof FooTable.is
513	 * @function emptyObject
514	 * @param {*} value - The value to check.
515	 * @returns {boolean}
516	 */
517	F.is.emptyObject = function(value){
518		if (!F.is.hash(value)) return false;
519		for(var prop in value) {
520			if(value.hasOwnProperty(prop))
521				return false;
522		}
523		return true;
524	};
525
526	/**
527	 * Checks if the supplied value is an array and if it is empty.
528	 * @memberof FooTable.is
529	 * @function emptyArray
530	 * @param {*} value - The value to check.
531	 * @returns {boolean}
532	 */
533	F.is.emptyArray = function(value){
534		return F.is.array(value) ? value.length === 0 : true;
535	};
536
537	/**
538	 * Checks if the supplied value is a string and if it is empty.
539	 * @memberof FooTable.is
540	 * @function emptyString
541	 * @param {*} value - The value to check.
542	 * @returns {boolean}
543	 */
544	F.is.emptyString = function(value){
545		return F.is.string(value) ? value.length === 0 : true;
546	};
547
548})(FooTable);
549(function (F) {
550	/**
551	 * This namespace contains commonly used string utility methods.
552	 * @namespace FooTable.str
553	 */
554	F.str = {};
555
556	/**
557	 * Checks if the supplied string contains the given substring.
558	 * @memberof FooTable.str
559	 * @function contains
560	 * @param {string} str - The string to check.
561	 * @param {string} contains - The string to check for.
562	 * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check.
563	 * @returns {boolean}
564	 */
565	F.str.contains = function (str, contains, ignoreCase) {
566		if (F.is.emptyString(str) || F.is.emptyString(contains)) return false;
567		return contains.length <= str.length
568			&& (ignoreCase ? str.toUpperCase().indexOf(contains.toUpperCase()) : str.indexOf(contains)) !== -1;
569	};
570
571	/**
572	 * Checks if the supplied string contains the exact given substring.
573	 * @memberof FooTable.str
574	 * @function contains
575	 * @param {string} str - The string to check.
576	 * @param {string} contains - The string to check for.
577	 * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check.
578	 * @returns {boolean}
579	 */
580	F.str.containsExact = function (str, contains, ignoreCase) {
581		if (F.is.emptyString(str) || F.is.emptyString(contains) || contains.length > str.length) return false;
582		return new RegExp('\\b'+ F.str.escapeRegExp(contains)+'\\b', ignoreCase ? 'i' : '').test(str);
583	};
584
585	/**
586	 * Checks if the supplied string contains the given word.
587	 * @memberof FooTable.str
588	 * @function containsWord
589	 * @param {string} str - The string to check.
590	 * @param {string} word - The word to check for.
591	 * @param {boolean} [ignoreCase=false] - Whether or not to ignore casing when performing the check.
592	 * @returns {boolean}
593	 */
594	F.str.containsWord = function(str, word, ignoreCase){
595		if (F.is.emptyString(str) || F.is.emptyString(word) || str.length < word.length)
596			return false;
597		var parts = str.split(/\W/);
598		for (var i = 0, len = parts.length; i < len; i++){
599			if (ignoreCase ? parts[i].toUpperCase() == word.toUpperCase() : parts[i] == word) return true;
600		}
601		return false;
602	};
603
604	/**
605	 * Returns the remainder of a string split on the first index of the given substring.
606	 * @memberof FooTable.str
607	 * @function from
608	 * @param {string} str - The string to split.
609	 * @param {string} from - The substring to split on.
610	 * @returns {string}
611	 */
612	F.str.from = function (str, from) {
613		if (F.is.emptyString(str)) return str;
614		return F.str.contains(str, from) ? str.substring(str.indexOf(from) + 1) : str;
615	};
616
617	/**
618	 * Checks if a string starts with the supplied prefix.
619	 * @memberof FooTable.str
620	 * @function startsWith
621	 * @param {string} str - The string to check.
622	 * @param {string} prefix - The prefix to check for.
623	 * @returns {boolean}
624	 */
625	F.str.startsWith = function (str, prefix) {
626		if (F.is.emptyString(str)) return str == prefix;
627		return str.slice(0, prefix.length) == prefix;
628	};
629
630	/**
631	 * Takes the supplied string and converts it to camel case.
632	 * @memberof FooTable.str
633	 * @function toCamelCase
634	 * @param {string} str - The string to camel case.
635	 * @returns {string}
636	 */
637	F.str.toCamelCase = function (str) {
638		if (F.is.emptyString(str)) return str;
639		if (str.toUpperCase() === str) return str.toLowerCase();
640		return str.replace(/^([A-Z])|[-\s_](\w)/g, function (match, p1, p2) {
641			if (F.is.string(p2)) return p2.toUpperCase();
642			return p1.toLowerCase();
643		});
644	};
645
646	/**
647	 * Generates a random string 9 characters long using the optional prefix if supplied.
648	 * @memberof FooTable.str
649	 * @function random
650	 * @param {string} [prefix] - The prefix to append to the 9 random characters.
651	 * @returns {string}
652	 */
653	F.str.random = function(prefix){
654		prefix = F.is.emptyString(prefix) ? '' : prefix;
655		return prefix + Math.random().toString(36).substr(2, 9);
656	};
657
658	/**
659	 * Escapes a string for use in a regular expression.
660	 * @memberof FooTable.str
661	 * @function escapeRegExp
662	 * @param {string} str - The string to escape.
663	 * @returns {string}
664	 */
665	F.str.escapeRegExp = function(str){
666		if (F.is.emptyString(str)) return str;
667		return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
668	};
669
670})(FooTable);
671(function (F) {
672	"use strict";
673
674	if (!Object.create) {
675		Object.create = (function () {
676			var Object = function () {};
677			return function (prototype) {
678				if (arguments.length > 1)
679					throw Error('Second argument not supported');
680
681				if (!F.is.object(prototype))
682					throw TypeError('Argument must be an object');
683
684				Object.prototype = prototype;
685				var result = new Object();
686				Object.prototype = null;
687				return result;
688			};
689		})();
690	}
691
692	/**
693	 * This base implementation does nothing except provide access to the {@link FooTable.Class#extend} method.
694	 * @constructs FooTable.Class
695	 * @classdesc This class is based off of John Resig's [Simple JavaScript Inheritance]{@link http://ejohn.org/blog/simple-javascript-inheritance} but it has been updated to be ES 5.1
696	 * compatible by implementing an [Object.create polyfill]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill}
697	 * for older browsers.
698	 * @see {@link http://ejohn.org/blog/simple-javascript-inheritance}
699	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill}
700	 * @returns {FooTable.Class}
701	 */
702	function Class() {}
703
704	var __extendable__ = /xyz/.test(function () {xyz;}) ? /\b_super\b/ : /.*/;
705
706	// this._super() within the context of the new function is a pointer to the original function
707	// except if the hook param is specified then the this._super variable is the result of the original function
708	Class.__extend__ = function(proto, name, func, original){
709		// to all who venture here, here be dragons!
710		proto[name] = F.is.fn(original) && __extendable__.test(func) ?
711			(function (name, fn) {
712				return function () {
713					var tmp, ret;
714					tmp = this._super;
715					this._super = original;
716					ret = fn.apply(this, arguments);
717					this._super = tmp;
718					return ret;
719				};
720			})(name, func) : func;
721	};
722
723	/**
724	 * Creates a new class that inherits from this class which in turn allows itself to be extended or if a name and function is supplied extends only that specific function on the class.
725	 * @param {(object|string)} arg1 - An object containing any new methods/members to implement or the name of the method to extend.
726	 * @param {function} arg2 - If the first argument is a method name then this is the new function to replace it with.
727	 * @returns {FooTable.Class} A new class that inherits from the base class.
728	 * @example <caption>The below shows an example of how to implement inheritance using this method.</caption>
729	 * var Person = FooTable.Class.extend({
730	 *   construct: function(isDancing){
731	 *     this.dancing = isDancing;
732	 *   },
733	 *   dance: function(){
734	 *     return this.dancing;
735	 *   }
736	 * });
737	 *
738	 * var Ninja = Person.extend({
739	 *   construct: function(){
740	 *     this._super( false );
741	 *   },
742	 *   dance: function(){
743	 *     // Call the inherited version of dance()
744	 *     return this._super();
745	 *   },
746	 *   swingSword: function(){
747	 *     return true;
748	 *   }
749	 * });
750	 *
751	 * var p = new Person(true);
752	 * p.dance(); // => true
753	 *
754	 * var n = new Ninja();
755	 * n.dance(); // => false
756	 * n.swingSword(); // => true
757	 *
758	 * // Should all be true
759	 * p instanceof Person && p instanceof FooTable.Class &&
760	 * n instanceof Ninja && n instanceof Person && n instanceof FooTable.Class
761	 */
762	Class.extend = function (arg1 , arg2) {
763		var args = Array.prototype.slice.call(arguments);
764		arg1 = args.shift();
765		arg2 = args.shift();
766
767		function __extend__(proto, name, func, original){
768			// to all who venture here, here be dragons!
769			proto[name] = F.is.fn(original) && __extendable__.test(func) ?
770				(function (name, fn, ofn) {
771					return function () {
772						var tmp, ret;
773						tmp = this._super;
774						this._super = ofn;
775						ret = fn.apply(this, arguments);
776						this._super = tmp;
777						return ret;
778					};
779				})(name, func, original) : func;
780		}
781
782		if (F.is.hash(arg1)){
783			var proto = Object.create(this.prototype),
784				_super = this.prototype;
785			for (var name in arg1) {
786				if (name === '__ctor__') continue;
787				__extend__(proto, name, arg1[name], _super[name]);
788			}
789			var obj = F.is.fn(proto.__ctor__) ? proto.__ctor__ : function () {
790				if (!F.is.fn(this.construct))
791					throw new SyntaxError('FooTable class objects must be constructed with the "new" keyword.');
792				this.construct.apply(this, arguments);
793			};
794			proto.construct = F.is.fn(proto.construct) ? proto.construct : function(){};
795			obj.prototype = proto;
796			proto.constructor = obj;
797			obj.extend = Class.extend;
798			return obj;
799		} else if (F.is.string(arg1) && F.is.fn(arg2)) {
800			__extend__(this.prototype, arg1, arg2, this.prototype[arg1]);
801		}
802	};
803
804	F.Class = Class;
805
806	F.ClassFactory = F.Class.extend(/** @lends FooTable.ClassFactory */{
807		/**
808		 * This is a simple factory for {@link FooTable.Class} objects allowing them to be registered using a friendly name
809		 * and then new instances can be created using this friendly name.
810		 * @constructs
811		 * @extends FooTable.Class
812		 * @returns {FooTable.ClassFactory}
813		 * @this FooTable.ClassFactory
814		 */
815		construct: function(){
816			/**
817			 * An object containing all registered classes.
818			 * @type {{}}
819			 */
820			this.registered = {};
821		},
822		/**
823		 * Checks if the factory contains a class registered using the supplied name.
824		 * @instance
825		 * @param {string} name - The name of the class to check.
826		 * @returns {boolean}
827		 * @this FooTable.ClassFactory
828		 */
829		contains: function(name){
830			return F.is.defined(this.registered[name]);
831		},
832		/**
833		 * Gets an array of all registered names.
834		 * @instance
835		 * @returns {Array.<string>}
836		 * @this FooTable.ClassFactory
837		 */
838		names: function(){
839			var names = [], name;
840			for (name in this.registered){
841				if (!this.registered.hasOwnProperty(name)) continue;
842				names.push(name);
843			}
844			return names;
845		},
846		/**
847		 * Registers a class object using the supplied friendly name and priority. The priority is only taken into account when loading all registered classes
848		 * using the {@link FooTable.ClassFactory#load} method.
849		 * @instance
850		 * @param {string} name - The friendly name of the class.
851		 * @param {function} klass - The class to register.
852		 * @param {number} priority - This determines the order that the class is created when using the {@link FooTable.ClassFactory#load} method, higher values are loaded first.
853		 * @this FooTable.ClassFactory
854		 */
855		register: function(name, klass, priority){
856			if (!F.is.string(name) || !F.is.fn(klass)) return;
857			var current = this.registered[name];
858			this.registered[name] = {
859				name: name,
860				klass: klass,
861				priority: F.is.number(priority) ? priority : (F.is.defined(current) ? current.priority : 0)
862			};
863		},
864		/**
865		 * Creates new instances of all registered classes using there priority and the supplied arguments to return them in an array.
866		 * @instance
867		 * @param {object} subs - An object containing classes to substitute on load.
868		 * @param {*} arg1 - The first argument to supply when creating new instances of all registered classes.
869		 * @param {*} [argN...] - Any number of additional arguments to supply when creating new instances of all registered classes.
870		 * @returns {Array.<FooTable.Class>}
871		 * @this FooTable.ClassFactory
872		 */
873		load: function(subs, arg1, argN){
874			var self = this, args = Array.prototype.slice.call(arguments), reg = [], loaded = [], name, klass;
875			subs = args.shift() || {};
876			for (name in self.registered){
877				if (!self.registered.hasOwnProperty(name)) continue;
878				var component = self.registered[name];
879				if (subs.hasOwnProperty(name)){
880					klass = subs[name];
881					if (F.is.string(klass)) klass = F.getFnPointer(subs[name]);
882					if (F.is.fn(klass)){
883						component = {name: name, klass: klass, priority: self.registered[name].priority};
884					}
885				}
886				reg.push(component);
887			}
888			for (name in subs){
889				if (!subs.hasOwnProperty(name) || self.registered.hasOwnProperty(name)) continue;
890				klass = subs[name];
891				if (F.is.string(klass)) klass = F.getFnPointer(subs[name]);
892				if (F.is.fn(klass)){
893					reg.push({name: name, klass: klass, priority: 0});
894				}
895			}
896			reg.sort(function(a, b){ return b.priority - a.priority; });
897			F.arr.each(reg, function(r){
898				if (F.is.fn(r.klass)){
899					loaded.push(self._make(r.klass, args));
900				}
901			});
902			return loaded;
903		},
904		/**
905		 * Create a new instance of a single class using the supplied name and arguments.
906		 * @instance
907		 * @param {string} name - The name of the class to create.
908		 * @param {*} arg1 - The first argument to supply to the new instance.
909		 * @param {*} [argN...] - Any number of additional arguments to supply to the new instance.
910		 * @returns {FooTable.Class}
911		 * @this FooTable.ClassFactory
912		 */
913		make: function(name, arg1, argN){
914			var self = this, args = Array.prototype.slice.call(arguments), reg;
915			name = args.shift();
916			reg = self.registered[name];
917			if (F.is.fn(reg.klass)){
918				return self._make(reg.klass, args);
919			}
920			return null;
921		},
922		/**
923		 * This in effect lets us use the "apply" method on a function using the "new" keyword.
924		 * @instance
925		 * @private
926		 * @param {function} klass
927		 * @param args
928		 * @returns {FooTable.Class}
929		 * @this FooTable.ClassFactory
930		 */
931		_make: function(klass, args){
932			function Class() {
933				return klass.apply(this, args);
934			}
935			Class.prototype = klass.prototype;
936			return new Class();
937		}
938	});
939
940})(FooTable);
941(function($, F){
942
943	/**
944	 * Converts the supplied cssText string into JSON object.
945	 * @param {string} cssText - The cssText to convert to a JSON object.
946	 * @returns {object}
947	 */
948	F.css2json = function(cssText){
949		if (F.is.emptyString(cssText)) return {};
950		var json = {}, props = cssText.split(';'), pair, key, value;
951		for (var i = 0, i_len = props.length; i < i_len; i++){
952			if (F.is.emptyString(props[i])) continue;
953			pair = props[i].split(':');
954			if (F.is.emptyString(pair[0]) || F.is.emptyString(pair[1])) continue;
955			key = F.str.toCamelCase($.trim(pair[0]));
956			value = $.trim(pair[1]);
957			json[key] = value;
958		}
959		return json;
960	};
961
962	/**
963	 * Attempts to retrieve a function pointer using the given name.
964	 * @param {string} functionName - The name of the function to fetch a pointer to.
965	 * @returns {(function|object|null)}
966	 */
967	F.getFnPointer = function(functionName){
968		if (F.is.emptyString(functionName)) return null;
969		var pointer = window,
970			parts = functionName.split('.');
971		F.arr.each(parts, function(part){
972			if (pointer[part]) pointer = pointer[part];
973		});
974		return F.is.fn(pointer) ? pointer : null;
975	};
976
977	/**
978	 * Checks the value for function properties such as the {@link FooTable.Column#formatter} option which could also be specified using just the name
979	 * and attempts to return the correct function pointer or null if none was found matching the value.
980	 * @param {FooTable.Class} self - The class to use as the 'this' keyword within the context of the function.
981	 * @param {(function|string)} value - The actual function or the name of the function for the property.
982	 * @param {function} [def] - A default function to return if none is found.
983	 * @returns {(function|null)}
984	 */
985	F.checkFnValue = function(self, value, def){
986		def = F.is.fn(def) ? def : null;
987		function wrap(t, fn, d){
988			if (!F.is.fn(fn)) return d;
989			return function(){
990				return fn.apply(t, arguments);
991			};
992		}
993		return F.is.fn(value) ? wrap(self, value, def) : (F.is.type(value, 'string') ? wrap(self, F.getFnPointer(value), def) : def);
994	};
995
996})(jQuery, FooTable);
997(function($, F){
998
999	F.Cell = F.Class.extend(/** @lends FooTable.Cell */{
1000		/**
1001		 * The cell class containing all the properties for cells.
1002		 * @constructs
1003		 * @extends FooTable.Class
1004		 * @param {FooTable.Table} table -  The root {@link FooTable.Table} this cell belongs to.
1005		 * @param {FooTable.Row} row - The parent {@link FooTable.Row} this cell belongs to.
1006		 * @param {FooTable.Column} column - The {@link FooTable.Column} this cell falls under.
1007		 * @param {(*|HTMLElement|jQuery)} valueOrElement - Either the value or the element for the cell.
1008		 * @returns {FooTable.Cell}
1009		 * @this FooTable.Cell
1010		 */
1011		construct: function (table, row, column, valueOrElement) {
1012			/**
1013			 * The root {@link FooTable.Table} for the cell.
1014			 * @instance
1015			 * @readonly
1016			 * @type {FooTable.Table}
1017			 */
1018			this.ft = table;
1019			/**
1020			 * The parent {@link FooTable.Row} for the cell.
1021			 * @instance
1022			 * @readonly
1023			 * @type {FooTable.Row}
1024			 */
1025			this.row = row;
1026			/**
1027			 * The {@link FooTable.Column} this cell falls under.
1028			 * @instance
1029			 * @readonly
1030			 * @type {FooTable.Column}
1031			 */
1032			this.column = column;
1033			this.created = false;
1034			this.define(valueOrElement);
1035		},
1036		/**
1037		 * This is supplied either the value or the cell element/jQuery object if it exists.
1038		 * If supplied the element we need set the $el property and parse the value from it.
1039		 * @instance
1040		 * @protected
1041		 * @param {(*|jQuery)} valueOrElement - The value or element to define the cell.
1042		 * @this FooTable.Cell
1043		 */
1044		define: function(valueOrElement){
1045			/**
1046			 * The jQuery table cell object this instance wraps.
1047			 * @instance
1048			 * @type {jQuery}
1049			 */
1050			this.$el = F.is.element(valueOrElement) || F.is.jq(valueOrElement) ? $(valueOrElement) : null;
1051			/**
1052			 * The jQuery row object that represents this cell in the details table.
1053			 * @type {jQuery}
1054			 */
1055			this.$detail = null;
1056
1057			var hasOptions = F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options) && F.is.defined(valueOrElement.value);
1058
1059			/**
1060			 * The value of the cell.
1061			 * @instance
1062			 * @type {*}
1063			 */
1064			this.value = this.column.parser.call(this.column, F.is.jq(this.$el) ? this.$el : (hasOptions ? valueOrElement.value : valueOrElement), this.ft.o);
1065
1066			/**
1067			 * Contains any options for the cell. These are the options supplied through the plugin constructor as part of the row object itself.
1068			 * @type {object}
1069			 */
1070			this.o = $.extend(true, {
1071				classes: null,
1072				style: null
1073			}, hasOptions ? valueOrElement.options : {});
1074			/**
1075			 * An array of CSS classes for the cell.
1076			 * @instance
1077			 * @protected
1078			 * @type {Array.<string>}
1079			 */
1080			this.classes = F.is.jq(this.$el) && this.$el.attr('class') ? this.$el.attr('class').match(/\S+/g) : (F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []));
1081			/**
1082			 * The inline styles for the cell.
1083			 * @instance
1084			 * @protected
1085			 * @type {object}
1086			 */
1087			this.style = F.is.jq(this.$el) && this.$el.attr('style') ? F.css2json(this.$el.attr('style')) : (F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {}));
1088		},
1089		/**
1090		 * After the cell has been defined this ensures that the $el and #detail properties are jQuery objects by either creating or updating them.
1091		 * @instance
1092		 * @protected
1093		 * @this FooTable.Cell
1094		 */
1095		$create: function(){
1096			if (this.created) return;
1097			(this.$el = F.is.jq(this.$el) ? this.$el : $('<td/>'))
1098				.data('value', this.value)
1099				.contents().detach().end()
1100				.append(this.format(this.value));
1101
1102			this._setClasses(this.$el);
1103			this._setStyle(this.$el);
1104
1105			this.$detail = $('<tr/>').addClass(this.row.classes.join(' '))
1106				.data('__FooTableCell__', this)
1107				.append($('<th/>'))
1108				.append($('<td/>'));
1109
1110			this.created = true;
1111		},
1112		/**
1113		 * Collapses this cell and displays it in the details row.
1114		 * @instance
1115		 * @protected
1116		 */
1117		collapse: function(){
1118			if (!this.created) return;
1119			this.$detail.children('th').html(this.column.title);
1120			this.$el.clone()
1121				.attr('id', this.$el.attr('id') ? this.$el.attr('id') + '-detail' : undefined)
1122				.css('display', 'table-cell')
1123				.html('')
1124				.append(this.$el.contents().detach())
1125				.replaceAll(this.$detail.children('td').first());
1126
1127			if (!F.is.jq(this.$detail.parent()))
1128				this.$detail.appendTo(this.row.$details.find('.footable-details > tbody'));
1129		},
1130		/**
1131		 * Restores this cell from a detail row back into the normal row.
1132		 * @instance
1133		 * @protected
1134		 */
1135		restore: function(){
1136			if (!this.created) return;
1137			if (F.is.jq(this.$detail.parent())){
1138				var $cell = this.$detail.children('td').first();
1139				this.$el
1140					.attr('class', $cell.attr('class'))
1141					.attr('style', $cell.attr('style'))
1142					.css('display', (this.column.hidden || !this.column.visible) ? 'none' : 'table-cell')
1143					.append($cell.contents().detach());
1144			}
1145			this.$detail.detach();
1146		},
1147		/**
1148		 * Helper method to call this cell's column parser function supplying the required parameters.
1149		 * @instance
1150		 * @protected
1151		 * @returns {*}
1152		 * @see FooTable.Column#parser
1153		 * @this FooTable.Cell
1154		 */
1155		parse: function(){
1156			return this.column.parser.call(this.column, this.$el, this.ft.o);
1157		},
1158		/**
1159		 * Helper method to call this cell's column formatter function using the supplied value and any additional required parameters.
1160		 * @instance
1161		 * @protected
1162		 * @param {*} value - The value to format.
1163		 * @returns {(string|HTMLElement|jQuery)}
1164		 * @see FooTable.Column#formatter
1165		 * @this FooTable.Cell
1166		 */
1167		format: function(value){
1168			return this.column.formatter.call(this.column, value, this.ft.o, this.row.value);
1169		},
1170		/**
1171		 * Allows easy access to getting or setting the cell's value. If the value is set all associated properties are also updated along with the actual element.
1172		 * Using this method also allows us to supply an object containing options and the value for the cell.
1173		 * @instance
1174		 * @param {*} [value] - The value to set for the cell. If not supplied the current value of the cell is returned.
1175		 * @param {boolean} [redraw=true] - Whether or not to redraw the row once the value has been set.
1176		 * @param {boolean} [redrawSelf=true] - Whether or not to redraw the cell itself once the value has been set, if `false` this will override the supplied `redraw` value and prevent the row from redrawing as well.
1177		 * @returns {(*|undefined)}
1178		 * @this FooTable.Cell
1179		 */
1180		val: function(value, redraw, redrawSelf){
1181			if (F.is.undef(value)){
1182				// get
1183				return this.value;
1184			}
1185			// set
1186			var self = this, hasOptions = F.is.hash(value) && F.is.hash(value.options) && F.is.defined(value.value);
1187			this.o = $.extend(true, {
1188				classes: self.classes,
1189				style: self.style
1190			}, hasOptions ? value.options : {});
1191
1192			this.value = hasOptions ? value.value : value;
1193			this.classes = F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []);
1194			this.style = F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {});
1195
1196			redrawSelf = F.is.boolean(redrawSelf) ? redrawSelf : true;
1197			if (this.created && redrawSelf){
1198				this.$el.data('value', this.value).empty();
1199
1200				var $detail = this.$detail.children('td').first().empty(),
1201					$target = F.is.jq(this.$detail.parent()) ? $detail : this.$el;
1202
1203				$target.append(this.format(this.value));
1204
1205				this._setClasses($target);
1206				this._setStyle($target);
1207
1208				if (F.is.boolean(redraw) ? redraw : true) this.row.draw();
1209			}
1210		},
1211		_setClasses: function($el){
1212			var hasColClasses = !F.is.emptyArray(this.column.classes),
1213				hasClasses = !F.is.emptyArray(this.classes),
1214				classes = null;
1215			$el.removeAttr('class');
1216			if (!hasColClasses && !hasClasses) return;
1217			if (hasColClasses && hasClasses){
1218				classes = this.classes.concat(this.column.classes).join(' ');
1219			} else if (hasColClasses) {
1220				classes = this.column.classes.join(' ');
1221			} else if (hasClasses){
1222				classes = this.classes.join(' ');
1223			}
1224			if (!F.is.emptyString(classes)){
1225				$el.addClass(classes);
1226			}
1227		},
1228		_setStyle: function($el){
1229			var hasColStyle = !F.is.emptyObject(this.column.style),
1230				hasStyle = !F.is.emptyObject(this.style),
1231				style = null;
1232			$el.removeAttr('style');
1233			if (!hasColStyle && !hasStyle) return;
1234			if (hasColStyle && hasStyle){
1235				style = $.extend({}, this.column.style, this.style);
1236			} else if (hasColStyle) {
1237				style = this.column.style;
1238			} else if (hasStyle){
1239				style = this.style;
1240			}
1241			if (F.is.hash(style)){
1242				$el.css(style);
1243			}
1244		}
1245	});
1246
1247})(jQuery, FooTable);
1248(function($, F){
1249
1250	F.Column = F.Class.extend(/** @lends FooTable.Column */{
1251		/**
1252		 * The column class containing all the properties for columns. All members marked as "readonly" should not be used when defining {@link FooTable.Defaults#columns}.
1253		 * @constructs
1254		 * @extends FooTable.Class
1255		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this component belongs to.
1256		 * @param {object} definition - An object containing all the properties to set for the column.
1257		 * @param {string} [type] - The type of column, "text" by default.
1258		 * @returns {FooTable.Column}
1259		 * @this FooTable.Column
1260		 */
1261		construct: function(instance, definition, type){
1262			/**
1263			 * The root {@link FooTable.Table} for the column.
1264			 * @instance
1265			 * @readonly
1266			 * @type {FooTable.Table}
1267			 */
1268			this.ft = instance;
1269			/**
1270			 * The type of data displayed by the column.
1271			 * @instance
1272			 * @readonly
1273			 * @type {string}
1274			 */
1275			this.type = F.is.emptyString(type) ? 'text' : type;
1276			/**
1277			 * Whether or not the column was parsed from a standard table row containing data instead of from an actual header row.
1278			 * @instance
1279			 * @readonly
1280			 * @type {boolean}
1281			 */
1282			this.virtual = F.is.boolean(definition.virtual) ? definition.virtual : false;
1283			/**
1284			 * The jQuery cell object for the column header.
1285			 * @instance
1286			 * @readonly
1287			 * @type {jQuery}
1288			 */
1289			this.$el = F.is.jq(definition.$el) ? definition.$el : null;
1290			/**
1291			 * The index of the column in the table. This is set by the plugin during initialization.
1292			 * @instance
1293			 * @readonly
1294			 * @type {number}
1295			 * @default -1
1296			 */
1297			this.index = F.is.number(definition.index) ? definition.index : -1;
1298			/**
1299			 * Whether or not this in an internal only column.
1300			 * @instance
1301			 * @readonly
1302			 * @type {boolean}
1303			 * @description Internal columns or there cells will not be returned when calling methods such as `FooTable.Row#val`.
1304			 */
1305			this.internal = false;
1306			this.define(definition);
1307			this.$create();
1308		},
1309		/**
1310		 * This is supplied the column definition in the form of a simple object created by merging options supplied via the plugin constructor with those parsed from the DOM.
1311		 * @instance
1312		 * @protected
1313		 * @param {object} definition - The object containing the column definition.
1314		 * @this FooTable.Column
1315		 */
1316		define: function(definition){
1317			/**
1318			 * Whether or not this column is hidden from view and appears in the details row.
1319			 * @type {boolean}
1320			 * @default false
1321			 */
1322			this.hidden = F.is.boolean(definition.hidden) ? definition.hidden : false;
1323			/**
1324			 * Whether or not this column is completely hidden from view and will not appear in the details row.
1325			 * @type {boolean}
1326			 * @default true
1327			 */
1328			this.visible = F.is.boolean(definition.visible) ? definition.visible : true;
1329
1330			/**
1331			 * The name of the column. This name must correspond to the property name of the JSON row data.
1332			 * @type {string}
1333			 * @default null
1334			 */
1335			this.name = F.is.string(definition.name) ? definition.name : null;
1336			if (this.name == null) this.name = 'col'+(definition.index+1);
1337			/**
1338			 * The title to display in the column header, this can be HTML.
1339			 * @type {string}
1340			 * @default null
1341			 */
1342			this.title = F.is.string(definition.title) ? definition.title : null;
1343			if (!this.virtual && this.title == null && F.is.jq(this.$el)) this.title = this.$el.html();
1344			if (this.title == null) this.title = 'Column '+(definition.index+1);
1345			/**
1346			 * The styles to apply to all cells in this column.
1347			 * @type {object}
1348			 */
1349			this.style = F.is.hash(definition.style) ? definition.style : (F.is.string(definition.style) ? F.css2json(definition.style) : {});
1350			/**
1351			 * The classes to apply to all cells in this column.
1352			 * @type {Array.<string>}
1353			 */
1354			this.classes = F.is.array(definition.classes) ? definition.classes : (F.is.string(definition.classes) ? definition.classes.match(/\S+/g) : []);
1355
1356			// override any default functions ensuring when they are executed "this" within the context of the function points to the instance of this object.
1357			this.parser = F.checkFnValue(this, definition.parser, this.parser);
1358			this.formatter = F.checkFnValue(this, definition.formatter, this.formatter);
1359		},
1360		/**
1361		 * After the column has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value.
1362		 * @instance
1363		 * @protected
1364		 * @this FooTable.Column
1365		 */
1366		$create: function(){
1367			(this.$el = !this.virtual && F.is.jq(this.$el) ? this.$el : $('<th/>')).html(this.title).addClass(this.classes.join(' ')).css(this.style);
1368		},
1369		/**
1370		 * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.Column#format} function
1371		 * to generate the cell contents.
1372		 * @instance
1373		 * @protected
1374		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
1375		 * @returns {string}
1376		 * @this FooTable.Column
1377		 */
1378		parser: function(valueOrElement){
1379			if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value
1380				var data = $(valueOrElement).data('value');
1381				return F.is.defined(data) ? data : $(valueOrElement).html();
1382			}
1383			if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; // use the native toString of the value
1384			return null; // otherwise we have no value so return null
1385		},
1386		/**
1387		 * This is supplied the value retrieved from the {@link FooTable.Column#parse} function and must return a string, HTMLElement or jQuery object.
1388		 * The return value from this function is what is displayed in the cell in the table.
1389		 * @instance
1390		 * @protected
1391		 * @param {string} value - The value to format.
1392		 * @param {object} options - The current plugin options.
1393		 * @param {object} rowData - An object containing the current row data.
1394		 * @returns {(string|HTMLElement|jQuery)}
1395		 * @this FooTable.Column
1396		 */
1397		formatter: function(value, options, rowData){
1398			return value == null ? '' : value;
1399		},
1400		/**
1401		 * Creates a cell for this column from the supplied {@link FooTable.Row} object. This allows different column types to return different types of cells.
1402		 * @instance
1403		 * @protected
1404		 * @param {FooTable.Row} row - The row to create the cell from.
1405		 * @returns {FooTable.Cell}
1406		 * @this FooTable.Column
1407		 */
1408		createCell: function(row){
1409			var element = F.is.jq(row.$el) ? row.$el.children('td,th').get(this.index) : null,
1410				data = F.is.hash(row.value) ? row.value[this.name] : null;
1411			return new F.Cell(this.ft, row, this, element || data);
1412		}
1413	});
1414
1415	F.columns = new F.ClassFactory();
1416
1417	F.columns.register('text', F.Column);
1418
1419})(jQuery, FooTable);
1420(function ($, F) {
1421
1422	F.Component = F.Class.extend(/** @lends FooTable.Component */{
1423		/**
1424		 * The base class for all FooTable components.
1425		 * @constructs
1426		 * @extends FooTable.Class
1427		 * @param {FooTable.Table} instance - The parent {@link FooTable.Table} object for the component.
1428		 * @param {boolean} enabled - Whether or not the component is enabled.
1429		 * @throws {TypeError} The instance parameter must be an instance of {@link FooTable.Table}.
1430		 * @returns {FooTable.Component}
1431		 */
1432		construct: function (instance, enabled) {
1433			if (!(instance instanceof F.Table))
1434				throw new TypeError('The instance parameter must be an instance of FooTable.Table.');
1435
1436			/**
1437			 * The parent {@link FooTable.Table} for the component.
1438			 * @type {FooTable.Table}
1439			 */
1440			this.ft = instance;
1441			/**
1442			 * Whether or not this component is enabled. Disabled components only have there preinit method called allowing for this value to be overridden.
1443			 * @type {boolean}
1444			 */
1445			this.enabled = F.is.boolean(enabled) ? enabled : false;
1446		},
1447		/**
1448		 * The preinit method is called during the parent {@link FooTable.Table} constructor call.
1449		 * @param {object} data - The jQuery.data() object of the root table.
1450		 * @instance
1451		 * @protected
1452		 * @function
1453		 */
1454		preinit: function(data){},
1455		/**
1456		 * The init method is called during the parent {@link FooTable.Table} constructor call.
1457		 * @instance
1458		 * @protected
1459		 * @function
1460		 */
1461		init: function(){},
1462		/**
1463		 * This method is called from the {@link FooTable.Table#destroy} method.
1464		 * @instance
1465		 * @protected
1466		 * @function
1467		 */
1468		destroy: function(){},
1469		/**
1470		 * This method is called from the {@link FooTable.Table#draw} method.
1471		 * @instance
1472		 * @protected
1473		 * @function
1474		 */
1475		predraw: function(){},
1476		/**
1477		 * This method is called from the {@link FooTable.Table#draw} method.
1478		 * @instance
1479		 * @protected
1480		 * @function
1481		 */
1482		draw: function(){},
1483		/**
1484		 * This method is called from the {@link FooTable.Table#draw} method.
1485		 * @instance
1486		 * @protected
1487		 * @function
1488		 */
1489		postdraw: function(){}
1490	});
1491
1492	F.components = new F.ClassFactory();
1493
1494})(jQuery, FooTable);
1495(function ($, F) {
1496	/**
1497	 * Contains all the available options for the FooTable plugin.
1498	 * @name FooTable.Defaults
1499	 * @function
1500	 * @constructor
1501	 * @returns {FooTable.Defaults}
1502	 */
1503	F.Defaults = function () {
1504		/**
1505		 * Whether or not events raised using the {@link FooTable.Table#raise} method are propagated up the DOM. By default this is set to false and all events bubble up the DOM as per usual
1506		 * however the reason for this option is if we have nested tables. If false the parent table would receive all the events raised by it's children and any handlers bound to both the
1507		 * parent and child would be triggered which is not the desired behavior.
1508		 * @type {boolean}
1509		 * @default false
1510		 */
1511		this.stopPropagation = false;
1512		/**
1513		 * An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s).
1514		 * @type {object.<string, function>}
1515		 * @default NULL
1516		 * @example <caption>
1516This example shows how to pass an object containing the events and handlers.</caption>
1517		 * "on": {
1518		 * 	"click": function(e){
1519		 * 		// bind a custom click event to do something whenever the table is clicked
1520		 * 	},
1521		 * 	"init.ft.table": function(e, ft){
1522		 * 		// bind to the FooTable initialize event to do something
1523		 * 	}
1524		 * }
1525		 */
1526		this.on = null;
1527	};
1528
1529	/**
1530	 * Contains all the default options for the plugin.
1531	 * @type {FooTable.Defaults}
1532	 */
1533	F.defaults = new F.Defaults();
1534
1535})(jQuery, FooTable);
1536(function($, F){
1537
1538	F.Row = F.Class.extend(/** @lends FooTable.Row */{
1539		/**
1540		 * The row class containing all the properties for a row and its' cells.
1541		 * @constructs
1542		 * @extends FooTable.Class
1543		 * @param {FooTable.Table} table -  The parent {@link FooTable.Table} this component belongs to.
1544		 * @param {Array.<FooTable.Column>} columns - The array of {@link FooTable.Column} for this row.
1545		 * @param {(*|HTMLElement|jQuery)} dataOrElement - Either the data for the row (create) or the element (parse) for the row.
1546		 * @returns {FooTable.Row}
1547		 */
1548		construct: function (table, columns, dataOrElement) {
1549			/**
1550			 * The {@link FooTable.Table} for the row.
1551			 * @type {FooTable.Table}
1552			 */
1553			this.ft = table;
1554			/**
1555			 * The array of {@link FooTable.Column} for this row.
1556			 * @type {Array.<FooTable.Column>}
1557			 */
1558			this.columns = columns;
1559
1560			this.created = false;
1561			this.define(dataOrElement);
1562		},
1563		/**
1564		 * This is supplied either the object containing the values for the row or the row element/jQuery object if it exists.
1565		 * If supplied the element we need to set the $el property and parse the cells from it using the column index.
1566		 * If we have an object we parse the cells from it using the column name.
1567		 * @param {(object|jQuery)} dataOrElement - The row object or element to define the row.
1568		 */
1569		define: function(dataOrElement){
1570			/**
1571			 * The jQuery table row object this instance wraps.
1572			 * @instance
1573			 * @protected
1574			 * @type {jQuery}
1575			 */
1576			this.$el = F.is.element(dataOrElement) || F.is.jq(dataOrElement) ? $(dataOrElement) : null;
1577			/**
1578			 * The jQuery toggle element for the row.
1579			 * @instance
1580			 * @protected
1581			 * @type {jQuery}
1582			 */
1583			this.$toggle = $('<span/>', {'class': 'footable-toggle fooicon fooicon-plus'});
1584
1585			var isObj = F.is.hash(dataOrElement),
1586				hasOptions = isObj && F.is.hash(dataOrElement.options) && F.is.hash(dataOrElement.value);
1587
1588			/**
1589			 * The value of the row.
1590			 * @instance
1591			 * @protected
1592			 * @type {Object}
1593			 */
1594			this.value = isObj ? (hasOptions ? dataOrElement.value : dataOrElement) : null;
1595
1596			/**
1597			 * Contains any options for the row.
1598			 * @type {object}
1599			 */
1600			this.o = $.extend(true, {
1601				expanded: false,
1602				classes: null,
1603				style: null
1604			}, hasOptions ? dataOrElement.options : {});
1605
1606			/**
1607			 * Whether or not this row is expanded and will display it's detail row when there are any hidden columns.
1608			 * @instance
1609			 * @protected
1610			 * @type {boolean}
1611			 */
1612			this.expanded = F.is.jq(this.$el) ? (this.$el.data('expanded') || this.o.expanded) : this.o.expanded;
1613			/**
1614			 * An array of CSS classes for the row.
1615			 * @instance
1616			 * @protected
1617			 * @type {Array.<string>}
1618			 */
1619			this.classes = F.is.jq(this.$el) && this.$el.attr('class') ? this.$el.attr('class').match(/\S+/g) : (F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []));
1620			/**
1621			 * The inline styles for the row.
1622			 * @instance
1623			 * @protected
1624			 * @type {object}
1625			 */
1626			this.style = F.is.jq(this.$el) && this.$el.attr('style') ? F.css2json(this.$el.attr('style')) : (F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {}));
1627
1628			/**
1629			 * The cells array. This is populated before the call to the {@link FooTable.Row#$create} method.
1630			 * @instance
1631			 * @type {Array.<FooTable.Cell>}
1632			 */
1633			this.cells = this.createCells();
1634
1635			// this ensures the value contains the parsed cell values and not the supplied values
1636			var self = this;
1637			self.value = {};
1638			F.arr.each(self.cells, function(cell){
1639				self.value[cell.column.name] = cell.val();
1640			});
1641		},
1642		/**
1643		 * After the row has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value.
1644		 * @instance
1645		 * @protected
1646		 * @this FooTable.Row
1647		 */
1648		$create: function(){
1649			if (this.created) return;
1650			(this.$el = F.is.jq(this.$el) ? this.$el : $('<tr/>'))
1651				.data('__FooTableRow__', this);
1652
1653			this._setClasses(this.$el);
1654			this._setStyle(this.$el);
1655
1656			if (this.ft.rows.toggleColumn == 'last') this.$toggle.addClass('last-column');
1657
1658			this.$details = $('<tr/>', { 'class': 'footable-detail-row' })
1659				.append($('<td/>', { colspan: this.ft.columns.visibleColspan })
1660					.append($('<table/>', { 'class': 'footable-details ' + this.ft.classes.join(' ') })
1661						.append('<tbody/>')));
1662
1663			var self = this;
1664			F.arr.each(self.cells, function(cell){
1665				if (!cell.created) cell.$create();
1666				self.$el.append(cell.$el);
1667			});
1668			self.$el.off('click.ft.row').on('click.ft.row', { self: self }, self._onToggle);
1669			this.created = true;
1670		},
1671		/**
1672		 * This is called during the construct method and uses the current column definitions to create an array of {@link FooTable.Cell} objects for the row.
1673		 * @instance
1674		 * @protected
1675		 * @returns {Array.<FooTable.Cell>}
1676		 * @this FooTable.Row
1677		 */
1678		createCells: function(){
1679			var self = this;
1680			return F.arr.map(self.columns, function(col){
1681				return col.createCell(self);
1682			});
1683		},
1684		/**
1685		 * Allows easy access to getting or setting the row's data. If the data is set all associated properties are also updated along with the actual element.
1686		 * Using this method also allows us to supply an object containing options and the data for the row at the same time.
1687		 * @instance
1688		 * @param {object} [data] - The data to set for the row. If not supplied the current value of the row is returned.
1689		 * @param {boolean} [redraw=true] - Whether or not to redraw the table once the value has been set.
1690		 * @param {boolean} [redrawSelf=true] - Whether or not to redraw the row itself once the value has been set, if `false` this will override the supplied `redraw` value and prevent the table from redrawing as well.
1691		 * @returns {(*|undefined)}
1692		 */
1693		val: function(data, redraw, redrawSelf){
1694			var self = this;
1695			if (!F.is.hash(data)){
1696				// get - check the value property and build it from the cells if required.
1697				if (!F.is.hash(this.value) || F.is.emptyObject(this.value)){
1698					this.value = {};
1699					F.arr.each(this.cells, function(cell){
1700						if (!cell.column.internal){
1701							self.value[cell.column.name] = cell.val();
1702						}
1703					});
1704				}
1705				return this.value;
1706			}
1707			// set
1708			this.collapse(false);
1709			var isObj = F.is.hash(data),
1710				hasOptions = isObj && F.is.hash(data.options) && F.is.hash(data.value);
1711
1712			this.o = $.extend(true, {
1713				expanded: self.expanded,
1714				classes: self.classes,
1715				style: self.style
1716			}, hasOptions ? data.options : {});
1717
1718			this.expanded = this.o.expanded;
1719			this.classes = F.is.array(this.o.classes) ? this.o.classes : (F.is.string(this.o.classes) ? this.o.classes.match(/\S+/g) : []);
1720			this.style = F.is.hash(this.o.style) ? this.o.style : (F.is.string(this.o.style) ? F.css2json(this.o.style) : {});
1721			if (isObj) {
1722				if ( hasOptions ) data = data.value;
1723				if (F.is.hash(this.value)){
1724					for (var prop in data) {
1725						if (!data.hasOwnProperty(prop)) continue;
1726						this.value[prop] = data[prop];
1727					}
1728				} else {
1729					this.value = data;
1730				}
1731			} else {
1732				this.value = null;
1733			}
1734
1735			redrawSelf = F.is.boolean(redrawSelf) ? redrawSelf : true;
1736			F.arr.each(this.cells, function(cell){
1737				if (!cell.column.internal && F.is.defined(self.value[cell.column.name])){
1738					cell.val(self.value[cell.column.name], false, redrawSelf);
1739				}
1740			});
1741
1742			if (this.created && redrawSelf){
1743				this._setClasses(this.$el);
1744				this._setStyle(this.$el);
1745				if (F.is.boolean(redraw) ? redraw : true) this.draw();
1746			}
1747		},
1748		_setClasses: function($el){
1749			var hasClasses = !F.is.emptyArray(this.classes),
1750				classes = null;
1751			$el.removeAttr('class');
1752			if (!hasClasses) return;
1753			else classes = this.classes.join(' ');
1754			if (!F.is.emptyString(classes)){
1755				$el.addClass(classes);
1756			}
1757		},
1758		_setStyle: function($el){
1759			var hasStyle = !F.is.emptyObject(this.style),
1760				style = null;
1761			$el.removeAttr('style');
1762			if (!hasStyle) return;
1763			else style = this.style;
1764			if (F.is.hash(style)){
1765				$el.css(style);
1766			}
1767		},
1768		/**
1769		 * Sets the current row to an expanded state displaying any hidden columns in a detail row just below it.
1770		 * @instance
1771		 * @fires FooTable.Row#"expand.ft.row"
1772		 */
1773		expand: function(){
1774			if (!this.created) return;
1775			var self = this;
1776			/**
1777			 * The expand.ft.row event is raised before the the row is expanded.
1778			 * Calling preventDefault on this event will stop the row being expanded.
1779			 * @event FooTable.Row#"expand.ft.row"
1780			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
1781			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
1782			 * @param {FooTable.Row} row - The row about to be expanded.
1783			 */
1784			self.ft.raise('expand.ft.row',[self]).then(function(){
1785				self.__hidden__ = F.arr.map(self.cells, function(cell){
1786					return cell.column.hidden && cell.column.visible ? cell : null;
1787				});
1788
1789				if (self.__hidden__.length > 0){
1790					self.$details.insertAfter(self.$el)
1791						.children('td').first()
1792						.attr('colspan', self.ft.columns.visibleColspan);
1793
1794					F.arr.each(self.__hidden__, function(cell){
1795						cell.collapse();
1796					});
1797				}
1798				self.$el.attr('data-expanded', true);
1799				self.$toggle.removeClass('fooicon-plus').addClass('fooicon-minus');
1800				self.expanded = true;
1801				self.ft.raise('expanded.ft.row', [self]);
1802			});
1803		},
1804		/**
1805		 * Sets the current row to a collapsed state removing the detail row if it exists.
1806		 * @instance
1807		 * @param {boolean} [setExpanded] - Whether or not to set the {@link FooTable.Row#expanded} property to false.
1808		 * @fires FooTable.Row#"collapse.ft.row"
1809		 */
1810		collapse: function(setExpanded){
1811			if (!this.created) return;
1812			var self = this;
1813			/**
1814			 * The collapse.ft.row event is raised before the the row is collapsed.
1815			 * Calling preventDefault on this event will stop the row being collapsed.
1816			 * @event FooTable.Row#"collapse.ft.row"
1817			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
1818			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
1819			 * @param {FooTable.Row} row - The row about to be expanded.
1820			 */
1821			self.ft.raise('collapse.ft.row',[self]).then(function(){
1822				F.arr.each(self.__hidden__, function(cell){
1823					cell.restore();
1824				});
1825				self.$details.detach();
1826				self.$el.removeAttr('data-expanded');
1827				self.$toggle.removeClass('fooicon-minus').addClass('fooicon-plus');
1828				if (F.is.boolean(setExpanded) ? setExpanded : true) self.expanded = false;
1829				self.ft.raise('collapsed.ft.row', [self]);
1830			});
1831		},
1832		/**
1833		 * Prior to drawing this moves the details contents back to there original cells and detaches the toggle element from the row.
1834		 * @instance
1835		 * @param {boolean} [detach] - Whether or not to detach the row.
1836		 * @this FooTable.Row
1837		 */
1838		predraw: function(detach){
1839			if (this.created){
1840				if (this.expanded){
1841					this.collapse(false);
1842				}
1843				this.$toggle.detach();
1844				detach = F.is.boolean(detach) ? detach : true;
1845				if (detach) this.$el.detach();
1846			}
1847		},
1848		/**
1849		 * Draws the current row and cells.
1850		 * @instance
1851		 * @this FooTable.Row
1852		 */
1853		draw: function($parent){
1854			if (!this.created) this.$create();
1855			if (F.is.jq($parent)) $parent.append(this.$el);
1856			var self = this;
1857			F.arr.each(self.cells, function(cell){
1858				cell.$el.css('display', (cell.column.hidden || !cell.column.visible  ? 'none' : 'table-cell'));
1859				if (self.ft.rows.showToggle && self.ft.columns.hasHidden){
1860					if ((self.ft.rows.toggleColumn == 'first' && cell.column.index == self.ft.columns.firstVisibleIndex)
1861						|| (self.ft.rows.toggleColumn == 'last' && cell.column.index == self.ft.columns.lastVisibleIndex)) {
1862						cell.$el.prepend(self.$toggle);
1863					}
1864				}
1865				cell.$el.add(cell.column.$el).removeClass('footable-first-visible footable-last-visible footable-first-visible-pad');
1866				if (cell.column.index == self.ft.columns.firstVisibleIndex){
1867					if (self.ft.rows.showToggle && self.ft.columns.hasHidden) {
1868						cell.$el.add(cell.column.$el).addClass('footable-first-visible footable-first-visible-pad');
1869					} else {
1870						cell.$el.add(cell.column.$el).addClass('footable-first-visible');
1871					}
1872				}
1873				if (cell.column.index == self.ft.columns.lastVisibleIndex){
1874					cell.$el.add(cell.column.$el).addClass('footable-last-visible');
1875				}
1876			});
1877			if (this.expanded){
1878				this.expand();
1879			}
1880		},
1881		/**
1882		 * Toggles the row between it's expanded and collapsed state if there are hidden columns.
1883		 * @instance
1884		 * @this FooTable.Row
1885		 */
1886		toggle: function(){
1887			if (this.created && this.ft.columns.hasHidden){
1888				if (this.expanded) this.collapse();
1889				else this.expand();
1890			}
1891		},
1892		/**
1893		 * Handles the toggle click event for rows.
1894		 * @instance
1895		 * @param {jQuery.Event} e - The jQuery.Event object for the click event.
1896		 * @private
1897		 * @this jQuery
1898		 */
1899		_onToggle: function (e) {
1900			var self = e.data.self;
1901			// only execute the toggle if the event.target is one of the approved initiators
1902			if ($(e.target).is(self.ft.rows.toggleSelector)){
1903				self.toggle();
1904			}
1905		}
1906	});
1907
1908})(jQuery, FooTable);
1909
1910(function ($, F) {
1911
1912	/**
1913	 * An array of all currently loaded instances of the plugin.
1914	 * @protected
1915	 * @readonly
1916	 * @type {Array.<FooTable.Table>}
1917	 */
1918	F.instances = [];
1919
1920	F.Table = F.Class.extend(/** @lends FooTable.Table */{
1921		/**
1922		 * This class is the core of the plugin and drives the logic of all components.
1923		 * @constructs
1924		 * @this FooTable.Table
1925		 * @extends FooTable.Class
1926		 * @param {(HTMLTableElement|jQuery)} element - The element or jQuery table object to bind the plugin to.
1927		 * @param {object} options - The options to initialize the plugin with.
1928		 * @param {function} [ready] - A callback function to execute once the plugin is initialized.
1929		 * @returns {FooTable.Table}
1930		 */
1931		construct: function (element, options, ready) {
1932			//BEGIN MEMBERS
1933			/**
1934			 * The timeout ID for the resize event.
1935			 * @instance
1936			 * @private
1937			 * @type {?number}
1938			 */
1939			this._resizeTimeout = null;
1940			/**
1941			 * The ID of the FooTable instance.
1942			 * @instance
1943			 * @type {number}
1944			 */
1945			this.id = F.instances.push(this);
1946			/**
1947			 * Whether or not the plugin and all components and add-ons are fully initialized.
1948			 * @instance
1949			 * @type {boolean}
1950			 */
1951			this.initialized = false;
1952			/**
1953			 * The jQuery table object the plugin is bound to.
1954			 * @instance
1955			 * @type {jQuery}
1956			 */
1957			this.$el = (F.is.jq(element) ? element : $(element)).first(); // ensure one table, one instance
1958			/**
1959			 * A loader jQuery instance
1960			 * @instance
1961			 * @type {jQuery}
1962			 */
1963			this.$loader = $('<div/>', { 'class': 'footable-loader' }).append($('<span/>', {'class': 'fooicon fooicon-loader'}));
1964			/**
1965			 * The options for the plugin. This is a merge of user defined options and the default options.
1966			 * @instance
1967			 * @type {object}
1968			 */
1969			this.o = $.extend(true, {}, F.defaults, options);
1970			/**
1971			 * The jQuery data object for the table at initialization.
1972			 * @instance
1973			 * @type {object}
1974			 */
1975			this.data = this.$el.data() || {};
1976			/**
1977			 * An array of all CSS classes on the table that do not start with "footable".
1978			 * @instance
1979			 * @protected
1980			 * @type {Array.<string>}
1981			 */
1982			this.classes = [];
1983			/**
1984			 * All components for this instance of the plugin. These are executed in the order they appear in the array for the initialize phase and in reverse order for the destroy phase of the plugin.
1985			 * @instance
1986			 * @protected
1987			 * @type {object}
1988			 * @prop {Array.<FooTable.Component>} internal - The internal components for the plugin. These are executed either before all other components in the initialize phase or after them in the destroy phase of the plugin.
1989			 * @prop {Array.<FooTable.Component>} core - The core components for the plugin. These are executed either after the internal components in the initialize phase or before them in the destroy phase of the plugin.
1990			 * @prop {Array.<FooTable.Component>} custom - The custom components for the plugin. These are executed either after the core components in the initialize phase or before them in the destroy phase of the plugin.
1991			 */
1992			this.components = F.components.load((F.is.hash(this.data.components) ? this.data.components : this.o.components), this);
1993			/**
1994			 * The breakpoints component for this instance of the plugin.
1995			 * @instance
1996			 * @type {FooTable.Breakpoints}
1997			 */
1998			this.breakpoints = this.use(FooTable.Breakpoints);
1999			/**
2000			 * The columns component for this instance of the plugin.
2001			 * @instance
2002			 * @type {FooTable.Columns}
2003			 */
2004			this.columns = this.use(FooTable.Columns);
2005			/**
2006			 * The rows component for this instance of the plugin.
2007			 * @instance
2008			 * @type {FooTable.Rows}
2009			 */
2010			this.rows = this.use(FooTable.Rows);
2011
2012			//END MEMBERS
2013			this._construct(ready);
2014		},
2015		/**
2016		 * Once all properties are set this performs the actual initialization of the plugin calling the {@link FooTable.Table#_preinit} and
2017		 * {@link FooTable.Table#_init} methods as well as raising the {@link FooTable.Table#"ready.ft.table"} event.
2018		 * @this FooTable.Table
2019		 * @instance
2020		 * @param {function} [ready] - A callback function to execute once the plugin is initialized.
2021		 * @private
2022		 * @returns {jQuery.Promise}
2023		 * @fires FooTable.Table#"ready.ft.table"
2024		 */
2025		_construct: function(ready){
2026			var self = this;
2027			return this._preinit().then(function(){
2028				return self._init().then(function(){
2029					/**
2030					 * The ready.ft.table event is raised after the plugin has been initialized and the table drawn.
2031					 * Calling preventDefault on this event will stop the ready callback being executed.
2032					 * @event FooTable.Table#"ready.ft.table"
2033					 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2034					 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2035					 */
2036					return self.raise('ready.ft.table').then(function(){
2037						if (F.is.fn(ready)) ready.call(self, self);
2038					});
2039				});
2040			}).always(function(arg){
2041				self.$el.show();
2042				if (F.is.error(arg)){
2043					console.error('FooTable: unhandled error thrown during initialization.', arg);
2044				}
2045			});
2046		},
2047		/**
2048		 * The preinit method is called prior to the plugins actual initialization and provides itself and it's components an opportunity to parse any additional option values.
2049		 * @instance
2050		 * @private
2051		 * @returns {jQuery.Promise}
2052		 * @fires FooTable.Table#"preinit.ft.table"
2053		 */
2054		_preinit: function(){
2055			var self = this;
2056			/**
2057			 * The preinit.ft.table event is raised before any components.
2058			 * Calling preventDefault on this event will disable the entire plugin.
2059			 * @event FooTable.Table#"preinit.ft.table"
2060			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2061			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2062			 * @param {object} data - The jQuery data object from the root table element.
2063			 */
2064			return this.raise('preinit.ft.table', [self.data]).then(function(){
2065				var classes = (self.$el.attr('class') || '').match(/\S+/g) || [];
2066
2067				self.o.ajax = F.checkFnValue(self, self.data.ajax, self.o.ajax);
2068				self.o.stopPropagation = F.is.boolean(self.data.stopPropagation)
2069					? self.data.stopPropagation
2070					: self.o.stopPropagation;
2071
2072				for (var i = 0, len = classes.length; i < len; i++){
2073					if (!F.str.startsWith(classes[i], 'footable')) self.classes.push(classes[i]);
2074				}
2075
2076				self.$el.hide().after(self.$loader);
2077				return self.execute(false, false, 'preinit', self.data);
2078			});
2079		},
2080		/**
2081		 * Initializes this instance of the plugin and calls the callback function if one is supplied once complete.
2082		 * @this FooTable.Table
2083		 * @instance
2084		 * @private
2085		 * @return {jQuery.Promise}
2086		 * @fires FooTable.Table#"init.ft.table"
2087		 */
2088		_init: function(){
2089			var self = this;
2090			/**
2091			 * The init.ft.table event is raised before any components are initialized.
2092			 * Calling preventDefault on this event will disable the entire plugin.
2093			 * @event FooTable.Table#"init.ft.table"
2094			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2095			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2096			 */
2097			return self.raise('init.ft.table').then(function(){
2098				var $thead = self.$el.children('thead'),
2099					$tbody = self.$el.children('tbody'),
2100					$tfoot = self.$el.children('tfoot');
2101				self.$el.addClass('footable footable-' + self.id);
2102				if (F.is.hash(self.o.on)) self.$el.on(self.o.on);
2103				if ($tfoot.length == 0) self.$el.append($tfoot = $('<tfoot/>'));
2104				if ($tbody.length == 0) self.$el.append('<tbody/>');
2105				if ($thead.length == 0) self.$el.prepend($thead = $('<thead/>'));
2106				return self.execute(false, true, 'init').then(function(){
2107					self.$el.data('__FooTable__', self);
2108					if ($tfoot.children('tr').length == 0) $tfoot.remove();
2109					if ($thead.children('tr').length == 0) $thead.remove();
2110
2111					/**
2112					 * The postinit.ft.table event is raised after any components are initialized but before the table is
2113					 * drawn for the first time.
2114					 * Calling preventDefault on this event will disable the initial drawing of the table.
2115					 * @event FooTable.Table#"postinit.ft.table"
2116					 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2117					 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2118					 */
2119					return self.raise('postinit.ft.table').then(function(){
2120						return self.draw();
2121					}).always(function(){
2122						$(window).off('resize.ft'+self.id, self._onWindowResize)
2123							.on('resize.ft'+self.id, { self: self }, self._onWindowResize);
2124						self.initialized = true;
2125					});
2126				});
2127			});
2128		},
2129		/**
2130		 * Destroys this plugin removing it from the table.
2131		 * @this FooTable.Table
2132		 * @instance
2133		 * @fires FooTable.Table#"destroy.ft.table"
2134		 */
2135		destroy: function () {
2136			var self = this;
2137			/**
2138			 * The destroy.ft.table event is called before all core components.
2139			 * Calling preventDefault on this event will prevent the entire plugin from being destroyed.
2140			 * @event FooTable.Table#"destroy.ft.table"
2141			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2142			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2143			 */
2144			return self.raise('destroy.ft.table').then(function(){
2145				return self.execute(true, true, 'destroy').then(function () {
2146					self.$el.removeData('__FooTable__').removeClass('footable-' + self.id);
2147					if (F.is.hash(self.o.on)) self.$el.off(self.o.on);
2148					$(window).off('resize.ft'+self.id, self._onWindowResize);
2149					self.initialized = false;
2150					F.instances[self.id] = null;
2151				});
2152			}).fail(function(err){
2153				if (F.is.error(err)){
2154					console.error('FooTable: unhandled error thrown while destroying the plugin.', err);
2155				}
2156			});
2157		},
2158		/**
2159		 * Raises an event on this instance supplying the args array as additional parameters to the handlers.
2160		 * @this FooTable.Table
2161		 * @instance
2162		 * @param {string} eventName - The name of the event to raise, this can include namespaces.
2163		 * @param {Array} [args] - An array containing additional parameters to be passed to any boun
2163d handlers.
2164		 * @returns {jQuery.Event}
2165		 */
2166		raise: function(eventName, args){
2167			var self = this,
2168				debug = F.__debug__ && (F.is.emptyArray(F.__debug_options__.events) || F.arr.any(F.__debug_options__.events, function(name){ return F.str.contains(eventName, name); }));
2169			args = args || [];
2170			args.unshift(this);
2171			return $.Deferred(function(d){
2172				var evt = $.Event(eventName);
2173				if (self.o.stopPropagation == true){
2174					self.$el.one(eventName, function (e) {e.stopPropagation();});
2175				}
2176				if (debug) console.log('FooTable:'+eventName+': ', args);
2177				self.$el.trigger(evt, args);
2178				if (evt.isDefaultPrevented()){
2179					if (debug) console.log('FooTable: default prevented for the "'+eventName+'" event.');
2180					d.reject(evt);
2181				}	else d.resolve(evt);
2182			});
2183		},
2184		/**
2185		 * Attempts to retrieve the instance of the supplied component type for this instance.
2186		 * @this FooTable.Table
2187		 * @instance
2188		 * @param {object} type - The content type to retrieve for this instance.
2189		 * @returns {(*|null)}
2190		 */
2191		use: function(type){
2192			for (var i = 0, len = this.components.length; i < len; i++){
2193				if (this.components[i] instanceof type) return this.components[i];
2194			}
2195			return null;
2196		},
2197		/**
2198		 * Performs the drawing of the table.
2199		 * @this FooTable.Table
2200		 * @instance
2201		 * @protected
2202		 * @returns {jQuery.Promise}
2203		 * @fires FooTable.Table#"predraw.ft.table"
2204		 * @fires FooTable.Table#"draw.ft.table"
2205		 * @fires FooTable.Table#"postdraw.ft.table"
2206		 */
2207		draw: function () {
2208			var self = this;
2209
2210			// Clone the current table and insert it into the original's place
2211			var $elCopy = self.$el.clone().insertBefore(self.$el);
2212
2213			// Detach `self.$el` from the DOM, retaining its event handlers
2214			self.$el.detach();
2215
2216			// when drawing the order that the components are executed is important so chain the methods but use promises to retain async safety.
2217			return self.execute(false, true, 'predraw').then(function(){
2218				/**
2219				 * The predraw.ft.table event is raised after all core components and add-ons have executed there predraw functions but before they execute there draw functions.
2220				 * @event FooTable.Table#"predraw.ft.table"
2221				 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2222				 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2223				 */
2224				return self.raise('predraw.ft.table').then(function(){
2225					return self.execute(false, true, 'draw').then(function(){
2226						/**
2227						 * The draw.ft.table event is raised after all core components and add-ons have executed there draw functions.
2228						 * @event FooTable.Table#"draw.ft.table"
2229						 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2230						 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2231						 */
2232						return self.raise('draw.ft.table').then(function(){
2233							return self.execute(false, true, 'postdraw').then(function(){
2234								/**
2235								 * The postdraw.ft.table event is raised after all core components and add-ons have executed there postdraw functions.
2236								 * @event FooTable.Table#"postdraw.ft.table"
2237								 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2238								 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2239								 */
2240								return self.raise('postdraw.ft.table');
2241							});
2242						});
2243					});
2244				});
2245			}).fail(function(err){
2246				if (F.is.error(err)){
2247					console.error('FooTable: unhandled error thrown during a draw operation.', err);
2248				}
2249			}).always(function(){
2250				// Replace the copy that we added above with the modified `self.$el`
2251				$elCopy.replaceWith(self.$el);
2252				self.$loader.remove();
2253			});
2254		},
2255		/**
2256		 * Executes the specified method with the optional number of parameters on all components and waits for the promise from each to be resolved before executing the next.
2257		 * @this FooTable.Table
2258		 * @instance
2259		 * @protected
2260		 * @param {boolean} reverse - Whether or not to execute the component methods in the reverse order to what they were registered in.
2261		 * @param {boolean} enabled - Whether or not to execute the method on enabled components only.
2262		 * @param {string} methodName - The name of the method to execute.
2263		 * @param {*} [param1] - The first parameter for the method.
2264		 * @param {...*} [paramN] - Any number of additional parameters for the method.
2265		 * @returns {jQuery.Promise}
2266		 */
2267		execute: function(reverse, enabled, methodName, param1, paramN){
2268			var self = this, args = Array.prototype.slice.call(arguments);
2269			reverse = args.shift();
2270			enabled = args.shift();
2271			var components = enabled ? F.arr.get(self.components, function(c){ return c.enabled; }) : self.components.slice(0);
2272			args.unshift(reverse ? components.reverse() : components);
2273			return self._execute.apply(self, args);
2274		},
2275		/**
2276		 * Executes the specified method with the optional number of parameters on all supplied components waiting for the result of each before executing the next.
2277		 * @this FooTable.Table
2278		 * @instance
2279		 * @private
2280		 * @param {Array.<FooTable.Component>} components - The components to call the method on.
2281		 * @param {string} methodName - The name of the method to execute
2282		 * @param {*} [param1] - The first parameter for the method.
2283		 * @param {...*} [paramN] - Any additional parameters for the method.
2284		 * @returns {jQuery.Promise}
2285		 */
2286		_execute: function(components, methodName, param1, paramN){
2287			if (!components || !components.length) return $.when();
2288			var self = this, args = Array.prototype.slice.call(arguments),
2289				component;
2290			components = args.shift();
2291			methodName = args.shift();
2292			component = components.shift();
2293
2294			if (!F.is.fn(component[methodName]))
2295				return self._execute.apply(self, [components, methodName].concat(args));
2296
2297			return $.Deferred(function(d){
2298				try {
2299					var result = component[methodName].apply(component, args);
2300					if (F.is.promise(result)){
2301						return result.then(d.resolve, d.reject);
2302					} else {
2303						d.resolve(result);
2304					}
2305				} catch (err) {
2306					d.reject(err);
2307				}
2308			}).then(function(){
2309				return self._execute.apply(self, [components, methodName].concat(args));
2310			});
2311		},
2312		/**
2313		 * Listens to the window resize event and performs a check to see if the breakpoint has changed.
2314		 * @this window
2315		 * @instance
2316		 * @private
2317		 * @fires FooTable.Table#"resize.ft.table"
2318		 */
2319		_onWindowResize: function (e) {
2320			var self = e.data.self;
2321			if (self._resizeTimeout != null) { clearTimeout(self._resizeTimeout); }
2322			self._resizeTimeout = setTimeout(function () {
2323				self._resizeTimeout = null;
2324				/**
2325				 * The resize event is raised a short time after window resize operations cease.
2326				 * @event FooTable.Table#"resize.ft.table"
2327				 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2328				 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2329				 */
2330				self.raise('resize.ft.table').then(function(){
2331					self.breakpoints.check();
2332				});
2333			}, 300);
2334		}
2335	});
2336
2337})(jQuery, FooTable);
2338(function($, F){
2339
2340	F.ArrayColumn = F.Column.extend(/** @lends FooTable.ArrayColumn */{
2341		/**
2342		 * @summary A column to handle Array values.
2343		 * @constructs
2344		 * @extends FooTable.Column
2345		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this column belongs to.
2346		 * @param {object} definition - An object containing all the properties to set for the column.
2347		 */
2348		construct: function(instance, definition) {
2349			this._super(instance, definition, 'array');
2350		},
2351		/**
2352		 * @summary Parses the supplied value or element to retrieve a column value.
2353		 * @description This is supplied either the cell value or jQuery object to parse. This method will return either the Array containing the values or null.
2354		 * @instance
2355		 * @protected
2356		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
2357		 * @returns {(array|null)}
2358		 */
2359		parser: function(valueOrElement){
2360			if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value
2361				var $el = $(valueOrElement), data = $el.data('value'); // .data() will automatically convert a JSON string to an array
2362				if (F.is.array(data)) return data;
2363				data = $el.html();
2364				try {
2365					data = JSON.parse(data);
2366				} catch(err) {
2367					data = null;
2368				}
2369				return F.is.array(data) ? data : null; // if we have an array return it
2370			}
2371			if (F.is.array(valueOrElement)) return valueOrElement; // if we have an array return it
2372			return null; // otherwise we have no value so return null
2373		},
2374		/**
2375		 * @summary Formats the column value and creates the HTML seen within a cell.
2376		 * @description This is supplied the value retrieved from the {@link FooTable.ArrayColumn#parser} function and must return a string, HTMLElement or jQuery object.
2377		 * The return value from this function is what is displayed in the cell in the table.
2378		 * @instance
2379		 * @protected
2380		 * @param {?Array} value - The value to format.
2381		 * @param {object} options - The current plugin options.
2382		 * @param {object} rowData - An object containing the current row data.
2383		 * @returns {(string|HTMLElement|jQuery)}
2384		 */
2385		formatter: function(value, options, rowData){
2386			return F.is.array(value) ? JSON.stringify(value) : '';
2387		}
2388	});
2389
2390	F.columns.register('array', F.ArrayColumn);
2391
2392})(jQuery, FooTable);
2393(function($, F){
2394
2395	if (F.is.undef(window.moment)){
2396		// The DateColumn requires moment.js to parse and format date values. Goto http://momentjs.com/ to get it.
2397		return;
2398	}
2399
2400	F.DateColumn = F.Column.extend(/** @lends FooTable.DateColumn */{
2401		/**
2402		 * The date column class is used to handle date values. This column is dependent on [moment.js]{@link http://momentjs.com/} to provide date parsing and formatting functionality.
2403		 * @constructs
2404		 * @extends FooTable.Column
2405		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this column belongs to.
2406		 * @param {object}
2406 definition - An object containing all the properties to set for the column.
2407		 * @returns {FooTable.DateColumn}
2408		 */
2409		construct: function(instance, definition){
2410			this._super(instance, definition, 'date');
2411			/**
2412			 * The format string to use when parsing and formatting dates.
2413			 * @instance
2414			 * @type {string}
2415			 */
2416			this.formatString = F.is.string(definition.formatString) ? definition.formatString : 'MM-DD-YYYY';
2417		},
2418		/**
2419		 * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.DateColumn#format} function
2420		 * to generate the cell contents.
2421		 * @instance
2422		 * @protected
2423		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
2424		 * @returns {(moment|null)}
2425		 * @this FooTable.DateColumn
2426		 */
2427		parser: function(valueOrElement){
2428			if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){
2429				var data = $(valueOrElement).data('value');
2430				valueOrElement = F.is.defined(data) ? data : $(valueOrElement).text();
2431				if (F.is.string(valueOrElement)) valueOrElement = isNaN(valueOrElement) ? valueOrElement : +valueOrElement;
2432			}
2433			if (F.is.date(valueOrElement)) return moment(valueOrElement);
2434			if (F.is.object(valueOrElement) && F.is.boolean(valueOrElement._isAMomentObject)) return valueOrElement;
2435			if (F.is.string(valueOrElement)){
2436				// if it looks like a number convert it and do nothing else otherwise create a new moment using the string value and formatString
2437				if (isNaN(valueOrElement)){
2438					return moment(valueOrElement, this.formatString);
2439				} else {
2440					valueOrElement = +valueOrElement;
2441				}
2442			}
2443			if (F.is.number(valueOrElement)){
2444				return moment(valueOrElement);
2445			}
2446			return null;
2447		},
2448		/**
2449		 * This is supplied the value retrieved from the {@link FooTable.DateColumn#parser} function and must return a string, HTMLElement or jQuery object.
2450		 * The return value from this function is what is displayed in the cell in the table.
2451		 * @instance
2452		 * @protected
2453		 * @param {*} value - The value to format.
2454		 * @param {object} options - The current plugin options.
2455		 * @param {object} rowData - An object containing the current row data.
2456		 * @returns {(string|HTMLElement|jQuery)}
2457		 * @this FooTable.DateColumn
2458		 */
2459		formatter: function(value, options, rowData){
2460			return F.is.object(value) && F.is.boolean(value._isAMomentObject) && value.isValid() ? value.format(this.formatString) : '';
2461		},
2462		/**
2463		 * This is supplied either the cell value or jQuery object to parse. A string value must be returned from this method and will be used during filtering operations.
2464		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
2465		 * @returns {string}
2466		 * @this FooTable.DateColumn
2467		 */
2468		filterValue: function(valueOrElement){
2469			// if we have an element or a jQuery object use jQuery to get the value
2470			if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)) valueOrElement = $(valueOrElement).data('filterValue') || $(valueOrElement).text();
2471			// if options are supplied with the value
2472			if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){
2473				if (F.is.string(valueOrElement.options.filterValue)) valueOrElement = valueOrElement.options.filterValue;
2474				if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value;
2475			}
2476			// if the value is a moment object just return the formatted value
2477			if (F.is.object(valueOrElement) && F.is.boolean(valueOrElement._isAMomentObject)) return valueOrElement.format(this.formatString);
2478			// if its a string
2479			if (F.is.string(valueOrElement)){
2480				// if its not a number return it
2481				if (isNaN(valueOrElement)){
2482					return valueOrElement;
2483				} else { // otherwise convert it and carry on
2484					valueOrElement = +valueOrElement;
2485				}
2486			}
2487			// if the value is a number or date convert to a moment object and return the formatted result.
2488			if (F.is.number(valueOrElement) || F.is.date(valueOrElement)){
2489				return moment(valueOrElement).format(this.formatString);
2490			}
2491			// try use the native toString of the value if its not undefined or null
2492			if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+'';
2493			return ''; // otherwise we have no value so return an empty string
2494		}
2495	});
2496
2497	F.columns.register('date', F.DateColumn);
2498
2499})(jQuery, FooTable);
2500
2501(function($, F){
2502
2503	F.HTMLColumn = F.Column.extend(/** @lends FooTable.HTMLColumn */{
2504		/**
2505		 * The HTML column class is used to handle any raw HTML columns.
2506		 * @constructs
2507		 * @extends FooTable.Column
2508		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this column belongs to.
2509		 * @param {object}
2509 definition - An object containing all the properties to set for the column.
2510		 * @returns {FooTable.HTMLColumn}
2511		 */
2512		construct: function(instance, definition){
2513			this._super(instance, definition, 'html');
2514		},
2515		/**
2516		 * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.HTMLColumn#format} function
2517		 * to generate the cell contents.
2518		 * @instance
2519		 * @protected
2520		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
2521		 * @returns {(jQuery|null)}
2522		 * @this FooTable.HTMLColumn
2523		 */
2524		parser: function(valueOrElement){
2525			if (F.is.string(valueOrElement)) valueOrElement = $($.trim(valueOrElement));
2526			if (F.is.element(valueOrElement)) valueOrElement = $(valueOrElement);
2527			if (F.is.jq(valueOrElement)){
2528				var tagName = valueOrElement.prop('tagName').toLowerCase();
2529				if (tagName == 'td' || tagName == 'th'){
2530					var data = valueOrElement.data('value');
2531					return F.is.defined(data) ? data : valueOrElement.contents();
2532				}
2533				return valueOrElement;
2534			}
2535			return null;
2536		}
2537	});
2538
2539	F.columns.register('html', F.HTMLColumn);
2540
2541})(jQuery, FooTable);
2542(function($, F){
2543
2544	F.NumberColumn = F.Column.extend(/** @lends FooTable.NumberColumn */{
2545		/**
2546		 * The number column class is used to handle simple number columns.
2547		 * @constructs
2548		 * @extends FooTable.Column
2549		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this column belongs to.
2550		 * @param {object} definition - An object containing all the properties to set for the column.
2551		 * @returns {FooTable.NumberColumn}
2552		 */
2553		construct: function(instance, definition){
2554			this._super(instance, definition, 'number');
2555			this.decimalSeparator = F.is.string(definition.decimalSeparator) ? definition.decimalSeparator : '.';
2556			this.thousandSeparator = F.is.string(definition.thousandSeparator) ? definition.thousandSeparator : ',';
2557			this.decimalSeparatorRegex = new RegExp(F.str.escapeRegExp(this.decimalSeparator), 'g');
2558			this.thousandSeparatorRegex = new RegExp(F.str.escapeRegExp(this.thousandSeparator), 'g');
2559			this.cleanRegex = new RegExp('[^\-0-9' + F.str.escapeRegExp(this.decimalSeparator) + ']', 'g');
2560		},
2561		/**
2562		 * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and will be provided to the {@link FooTable.Column#formatter} function
2563		 * to generate the cell contents.
2564		 * @instance
2565		 * @protected
2566		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
2567		 * @returns {(number|null)}
2568		 * @this FooTable.NumberColumn
2569		 */
2570		parser: function(valueOrElement){
2571			if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){
2572				var data = $(valueOrElement).data('value');
2573				valueOrElement = F.is.defined(data) ? data : $(valueOrElement).text().replace(this.cleanRegex, '');
2574			}
2575			if (F.is.string(valueOrElement)){
2576				valueOrElement = valueOrElement.replace(this.thousandSeparatorRegex, '').replace(this.decimalSeparatorRegex, '.');
2577				valueOrElement = parseFloat(valueOrElement);
2578			}
2579			if (F.is.number(valueOrElement)) return valueOrElement;
2580			return null;
2581		},
2582		/**
2583		 * This is supplied the value retrieved from the {@link FooTable.NumberColumn#parse} function and must return a string, HTMLElement or jQuery object.
2584		 * The return value from this function is what is displayed in the cell in the table.
2585		 * @instance
2586		 * @protected
2587		 * @param {number} value - The value to format.
2588		 * @param {object} options - The current plugin options.
2589		 * @param {object} rowData - An object containing the current row data.
2590		 * @returns {(string|HTMLElement|jQuery)}
2591		 * @this FooTable.NumberColumn
2592		 */
2593		formatter: function(value, options, rowData){
2594			if (value == null) return '';
2595			var s = (value + '').split('.');
2596			if (s.length == 2 && s[0].length > 3) {
2597				s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, this.thousandSeparator);
2598			}
2599			return s.join(this.decimalSeparator);
2600		}
2601	});
2602
2603	F.columns.register('number', F.NumberColumn);
2604
2605})(jQuery, FooTable);
2606(function($, F){
2607
2608	F.ObjectColumn = F.Column.extend(/** @lends FooTable.ObjectColumn */{
2609		/**
2610		 * @summary A column to handle Object values.
2611		 * @constructs
2612		 * @extends FooTable.Column
2613		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this column belongs to.
2614		 * @param {object}
2614 definition - An object containing all the properties to set for the column.
2615		 */
2616		construct: function(instance, definition) {
2617			this._super(instance, definition, 'object');
2618		},
2619		/**
2620		 * @summary Parses the supplied value or element to retrieve a column value.
2621		 * @description This is supplied either the cell value or jQuery object to parse. This method will return either the Object containing the values or null.
2622		 * @instance
2623		 * @protected
2624		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
2625		 * @returns {(object|null)}
2626		 */
2627		parser: function(valueOrElement){
2628			if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){ // use jQuery to get the value
2629				var $el = $(valueOrElement), data = $el.data('value'); // .data() will automatically convert a JSON string to an object
2630				if (F.is.object(data)) return data;
2631				data = $el.html();
2632				try {
2633					data = JSON.parse(data);
2634				} catch(err) {
2635					data = null;
2636				}
2637				return F.is.object(data) ? data : null; // if we have an object return it
2638			}
2639			if (F.is.object(valueOrElement)) return valueOrElement; // if we have an object return it
2640			return null; // otherwise we have no value so return null
2641		},
2642		/**
2643		 * @summary Formats the column value and creates the HTML seen within a cell.
2644		 * @description This is supplied the value retrieved from the {@link FooTable.ObjectColumn#parser} function and must return a string, HTMLElement or jQuery object.
2645		 * The return value from this function is what is displayed in the cell in the table.
2646		 * @instance
2647		 * @protected
2648		 * @param {*} value - The value to format.
2649		 * @param {object} options - The current plugin options.
2650		 * @param {object} rowData - An object containing the current row data.
2651		 * @returns {(string|HTMLElement|jQuery)}
2652		 */
2653		formatter: function(value, options, rowData){
2654			return F.is.object(value) ? JSON.stringify(value) : '';
2655		}
2656	});
2657
2658	F.columns.register('object', F.ObjectColumn);
2659
2660})(jQuery, FooTable);
2661(function($, F){
2662
2663	F.Breakpoint = F.Class.extend(/** @lends FooTable.Breakpoint */{
2664		/**
2665		 * The breakpoint class containing the name and maximum width for the breakpoint.
2666		 * @constructs
2667		 * @extends FooTable.Class
2668		 * @param {string} name - The name of the breakpoint. Must contain no spaces or special characters.
2669		 * @param {number} width - The width of the breakpoint in pixels.
2670		 * @returns {FooTable.Breakpoint}
2671		 */
2672		construct: function(name, width){
2673			/**
2674			 * The name of the breakpoint.
2675			 * @type {string}
2676			 */
2677			this.name = name;
2678			/**
2679			 * The maximum width of the breakpoint in pixels.
2680			 * @type {number}
2681			 */
2682			this.width = width;
2683		}
2684	});
2685
2686})(jQuery, FooTable);
2687(function($, F){
2688	F.Breakpoints = F.Component.extend(/** @lends FooTable.Breakpoints */{
2689		/**
2690		 * Contains the logic to calculate and apply breakpoints for the plugin.
2691		 * @constructs
2692		 * @extends FooTable.Component
2693		 * @param {FooTable.Table} table -  The parent {@link FooTable.Table} this component belongs to.
2694		 * @returns {FooTable.Breakpoints}
2695		 */
2696		construct: function(table){
2697			// call the base class constructor
2698			this._super(table, true);
2699
2700			/* PROTECTED */
2701			/**
2702			 * This provides a shortcut to the {@link FooTable.Table#options} object.
2703			 * @protected
2704			 * @type {FooTable.Table#options}
2705			 */
2706			this.o = table.o;
2707
2708			/* PUBLIC */
2709			/**
2710			 * The current breakpoint.
2711			 * @type {FooTable.Breakpoint}
2712			 */
2713			this.current = null;
2714			/**
2715			 * An array of {@link FooTable.Breakpoint} objects created from parsing the options.
2716			 * @type {Array.<FooTable.Breakpoint>}
2717			 */
2718			this.array = [];
2719			/**
2720			 * Whether or not breakpoints cascade. When set to true all breakpoints larger than the current will be hidden along with it.
2721			 * @type {boolean}
2722			 */
2723			this.cascade = this.o.cascade;
2724			/**
2725			 * Whether or not to calculate breakpoints on the width of the parent element rather than the viewport.
2726			 * @type {boolean}
2727			 */
2728			this.useParentWidth = this.o.useParentWidth;
2729			/**
2730			 * This value is updated each time the current breakpoint changes and contains a space delimited string of the names of the current breakpoint and all those smaller than it.
2731			 * @type {string}
2732			 */
2733			this.hidden = null;
2734
2735			/* PRIVATE */
2736			/**
2737			 * This value is set once when the {@link FooTable.Breakpoints#array} is generated and contains a space delimited string of all the breakpoint class names.
2738			 * @type {string}
2739			 * @private
2740			 */
2741			this._classNames = '';
2742
2743			// check if a function was supplied to override the default getWidth
2744			this.getWidth = F.checkFnValue(this, this.o.getWidth, this.getWidth);
2745		},
2746
2747		/* PROTECTED */
2748		/**
2749		 * Checks the supplied data and options for the breakpoints component.
2750		 * @instance
2751		 * @protected
2752		 * @param {object} data - The jQuery data object from the parent table.
2753		 * @fires FooTable.Breakpoints#"preinit.ft.breakpoints"
2754		 */
2755		preinit: function(data){
2756			var self = this;
2757			/**
2758			 * The preinit.ft.breakpoints event is raised before any UI is created and provides the tables jQuery data object for additional options parsing.
2759			 * Calling preventDefault on this event will disable the entire plugin.
2760			 * @event FooTable.Breakpoints#"preinit.ft.breakpoints"
2761			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2762			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2763			 * @param {object} data - The jQuery data object of the table raising the event.
2764			 */
2765			return this.ft.raise('preinit.ft.breakpoints', [data]).then(function(){
2766				self.cascade = F.is.boolean(data.cascade) ? data.cascade : self.cascade;
2767				self.o.breakpoints = F.is.hash(data.breakpoints) ? data.breakpoints : self.o.breakpoints;
2768				self.getWidth = F.checkFnValue(self, data.getWidth, self.getWidth);
2769				if (self.o.breakpoints == null) self.o.breakpoints = { "xs": 480, "sm": 768, "md": 992, "lg": 1200 };
2770				// Create a nice friendly array to work with out of the breakpoints object.
2771				for (var name in self.o.breakpoints) {
2772					if (!self.o.breakpoints.hasOwnProperty(name)) continue;
2773					self.array.push(new F.Breakpoint(name, self.o.breakpoints[name]));
2774					self._classNames += 'breakpoint-' + name + ' ';
2775				}
2776				// Sort the breakpoints so the largest is checked first
2777				self.array.sort(function (a, b) {
2778					return b.width - a.width;
2779				});
2780			});
2781		},
2782		/**
2783		 * Initializes the class parsing the options into a sorted array of {@link FooTable.Breakpoint} objects.
2784		 * @instance
2785		 * @protected
2786		 * @fires FooTable.Breakpoints#"init.ft.breakpoints"
2787		 */
2788		init: function(){
2789			var self = this;
2790			/**
2791			 * The init.ft.breakpoints event is raised before any UI is generated.
2792			 * Calling preventDefault on this event will disable the entire plugin.
2793			 * @event FooTable.Breakpoints#"init.ft.breakpoints"
2794			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2795			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2796			 */
2797			return this.ft.raise('init.ft.breakpoints').then(function(){
2798				self.current = self.get();
2799			});
2800		},
2801		/**
2802		 * Whenever the table is drawn this ensures the correct breakpoint class is applied to the table.
2803		 * @instance
2804		 * @protected
2805		 */
2806		draw: function(){
2807			this.ft.$el.removeClass(this._classNames).addClass('breakpoint-' + this.current.name);
2808		},
2809
2810		/* PUBLIC */
2811		/**
2812		 * Calculates the current breakpoint from the {@link FooTable.Breakpoints#array} and sets the {@link FooTable.Breakpoints#current} property.
2813		 * @instance
2814		 * @returns {FooTable.Breakpoint}
2815		 */
2816		calculate: function(){
2817			var self = this, current = null, hidden = [], breakpoint, prev = null, width = self.getWidth();
2818			for (var i = 0, len = self.array.length; i < len; i++) {
2819				breakpoint = self.array[i];
2820				// if the width is smaller than the smallest breakpoint set the smallest as the current.
2821				// if the width is larger than the largest breakpoint set the largest as the current.
2822				// otherwise if the width is somewhere in between check all breakpoints testing if the width
2823				// is greater than the current but smaller than the previous.
2824				if ((!current && i == len -1)
2825					|| (width >= breakpoint.width && (prev instanceof F.Breakpoint ? width < prev.width : true))) {
2826					current = breakpoint;
2827				}
2828				if (!current) hidden.push(breakpoint.name);
2829				prev = breakpoint;
2830			}
2831			hidden.push(current.name);
2832			self.hidden = hidden.join(' ');
2833			return current;
2834		},
2835		/**
2836		 * Supplied a columns breakpoints this returns a boolean value indicating whether or not the column is visible.
2837		 * @param {string} breakpoints - A space separated string of breakpoint names.
2838		 * @returns {boolean}
2839		 */
2840		visible: function(breakpoints){
2841			if (F.is.emptyString(breakpoints)) return true;
2842			if (breakpoints === 'all') return false;
2843			var parts = breakpoints.split(' '), i = 0, len = parts.length;
2844			for (; i < len; i++){
2845				if (this.cascade ? F.str.containsWord(this.hidden, parts[i]) : parts[i] == this.current.name) return false;
2846			}
2847			return true;
2848		},
2849		/**
2850		 * Performs a check between the current breakpoint and the previous breakpoint and performs a redraw if they differ.
2851		 * @instance
2852		 * @fires FooTable.Breakpoints#"before.ft.breakpoints"
2853		 * @fires FooTable.Breakpoints#"after.ft.breakpoints"
2854		 */
2855		check: function(){
2856			var self = this, bp = self.get();
2857			if (!(bp instanceof F.Breakpoint)
2858				|| bp == self.current)
2859				return;
2860
2861			/**
2862			 * The before.ft.breakpoints event is raised if the breakpoint has changed but before the UI is redrawn and is supplied both the current breakpoint
2863			 * and the next "new" one that is about to be applied.
2864			 * Calling preventDefault on this event will prevent the next breakpoint from being applied.
2865			 * @event FooTable.Breakpoints#"before.ft.breakpoints"
2866			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2867			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2868			 * @param {FooTable.Breakpoint} current - The current breakpoint.
2869			 * @param {FooTable.Breakpoint} next - The breakpoint that is about to be applied.
2870			 */
2871			self.ft.raise('before.ft.breakpoints', [self.current, bp]).then(function(){
2872				var previous = self.current;
2873				self.current = bp;
2874				return self.ft.draw().then(function(){
2875					/**
2876					 * The after.ft.breakpoints event is raised after the breakpoint has changed and the UI is redrawn and is supplied both the "new" current breakpoint
2877					 * and the previous one that was replaced.
2878					 * @event FooTable.Breakpoints#"after.ft.breakpoints"
2879					 * @param {jQuery.Event} e - The jQuery.Event object for the event.
2880					 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
2881					 * @param {FooTable.Breakpoint} current - The current breakpoint.
2882					 * @param {FooTable.Breakpoint} previous - The breakpoint that was just replaced.
2883					 */
2884					self.ft.raise('after.ft.breakpoints', [self.current, previous]);
2885				});
2886			});
2887		},
2888		/**
2889		 * Attempts to return a {@link FooTable.Breakpoint} instance when passed a {@link FooTable.Breakpoint},
2890		 * the {@link FooTable.Breakpoint#name} string or if nothing is supplied the current breakpoint.
2891		 * @instance
2892		 * @param {(FooTable.Breakpoint|string|number)} [breakpoint] - The breakpoint to retrieve.
2893		 * @returns {FooTable.Breakpoint}
2894		 */
2895		get: function(breakpoint){
2896			if (F.is.undef(breakpoint)) return this.calculate();
2897			if (breakpoint instanceof F.Breakpoint) return breakpoint;
2898			if (F.is.string(breakpoint)) return F.arr.first(this.array, function (bp) { return bp.name == breakpoint; });
2899			if (F.is.number(breakpoint)) return breakpoint >= 0 && breakpoint < this.array.length ? this.array[breakpoint] : null;
2900			return null;
2901		},
2902		/**
2903		 * Gets the width used to determine breakpoints whether it be from the viewport, parent or a custom function.
2904		 * @instance
2905		 * @returns {number}
2906		 */
2907		getWidth: function(){
2908			if (F.is.fn(this.o.getWidth)) return this.o.getWidth(this.ft);
2909			if (this.useParentWidth == true) return this.getParentWidth();
2910			return this.getViewportWidth();
2911		},
2912		/**
2913		 * Gets the tables direct parents width.
2914		 * @instance
2915		 * @returns {number}
2916		 */
2917		getParentWidth: function(){
2918			return this.ft.$el.parent().width();
2919		},
2920		/**
2921		 * Gets the current viewport width.
2922		 * @instance
2923		 * @returns {number}
2924		 */
2925		getViewportWidth: function(){
2926			return Math.max(document.documentElement.clientWidth, window.innerWidth, 0);
2927		}
2928	});
2929
2930	F.components.register('breakpoints', F.Breakpoints, 1000);
2931
2932})(jQuery, FooTable);
2933(function(F){
2934	/**
2935	 * A space delimited string of breakpoint names that specify when the column will be hidden. You can also specify "all" to make a column permanently display in an expandable detail row.
2936	 * @type {string}
2937	 * @default null
2938	 * @example <caption>The below shows how this value would be set</caption>
2939	 * breakpoints: "md"
2940	 */
2941	F.Column.prototype.breakpoints = null;
2942
2943	F.Column.prototype.__breakpoints_define__ = function(definition){
2944		this.breakpoints = F.is.emptyString(definition.breakpoints) ? null : definition.breakpoints;
2945	};
2946
2947	F.Column.extend('define', function(definition){
2948		this._super(definition);
2949		this.__breakpoints_define__(definition);
2950	});
2951})(FooTable);
2952(function(F){
2953	/**
2954	 * An object containing the breakpoints for the plugin.
2955	 * @type {object.<string, number>}
2956	 * @default { "xs": 480, "sm": 768, "md": 992, "lg": 1200 }
2957	 */
2958	F.Defaults.prototype.breakpoints = null;
2959
2960	/**
2961	 * Whether or not breakpoints cascade. When set to true all breakpoints larger than the current will also be hidden along with it.
2962	 * @type {boolean}
2963	 * @default false
2964	 */
2965	F.Defaults.prototype.cascade = false;
2966
2967	/**
2968	 * Whether or not to calculate breakpoints on the width of the parent element rather than the viewport.
2969	 * @type {boolean}
2970	 * @default false
2971	 */
2972	F.Defaults.prototype.useParentWidth = false;
2973
2974	/**
2975	 * A function used to override the default getWidth function with a custom one.
2976	 * @type {function}
2977	 * @default null
2978	 * @example <caption>The below shows what the default getWidth function would look like.</caption>
2979	 * getWidth: function(instance){
2980	 * 	if (instance.o.useParentWidth == true) return instance.$el.parent().width();
2981	 * 	return instance.breakpoints.getViewportWidth();
2982	 * }
2983	 */
2984	F.Defaults.prototype.getWidth = null;
2985})(FooTable);
2986(function($, F){
2987	F.Columns = F.Component.extend(/** @lends FooTable.Columns */{
2988		/**
2989		 * The columns class contains all the logic for handling columns.
2990		 * @constructs
2991		 * @extends FooTable.Component
2992		 * @param {FooTable.Table} table -  The parent {@link FooTable.Table} this component belongs to.
2993		 * @returns {FooTable.Columns}
2994		 */
2995		construct: function(table){
2996			// call the base class constructor
2997			this._super(table, true);
2998
2999			/* PROTECTED */
3000			/**
3001			 * This provides a shortcut to the {@link FooTable.Table#options} object.
3002			 * @protected
3003			 * @type {FooTable.Table#options}
3004			 */
3005			this.o = table.o;
3006
3007			/* PUBLIC */
3008			/**
3009			 * An array of {@link FooTable.Column} objects created from parsing the options and/or DOM.
3010			 * @type {Array.<FooTable.Column>}
3011			 */
3012			this.array = [];
3013			/**
3014			 * The jQuery header row object.
3015			 * @type {jQuery}
3016			 */
3017			this.$header = null;
3018			/**
3019			 * Whether or not to display the header row.
3020			 * @type {boolean}
3021			 */
3022			this.showHeader = table.o.showHeader;
3023
3024			this._fromHTML = F.is.emptyArray(table.o.columns) && !F.is.promise(table.o.columns);
3025		},
3026
3027		/* PROTECTED */
3028		/**
3029		 * This parses the columns from either the tables rows or the supplied options.
3030		 * @instance
3031		 * @protected
3032		 * @param {object} data - The tables jQuery data object.
3033		 * @returns {jQuery.Promise}
3034		 * @this FooTable.Columns
3035		 */
3036		parse: function(data){
3037			var self = this;
3038			return $.Deferred(function(d){
3039				function merge(cols1, cols2){
3040					var merged = [];
3041					// check if either of the arrays is empty as it can save us having to merge them by index.
3042					if (cols1.length == 0 || cols2.length == 0){
3043						merged = cols1.concat(cols2);
3044					} else {
3045						// at this point we have two arrays of column definitions, we now need to merge them based on there index properties
3046						// first figure out the highest column index provided so we can loop that many times to merge all columns and provide
3047						// defaults where nothing was specified (fill in the gaps in the array as it were).
3048						var highest = 0;
3049						F.arr.each(cols1.concat(cols2), function(c){
3050							if (c.index > highest) highest = c.index;
3051						});
3052						highest++;
3053						for (var i = 0, cols1_c, cols2_c; i < highest; i++){
3054							cols1_c = {};
3055							F.arr.each(cols1, function(c){
3056								if (c.index == i){
3057									cols1_c = c;
3058									return false;
3059								}
3060							});
3061							cols2_c = {};
3062							F.arr.each(cols2, function(c){
3063								if (c.index == i){
3064									cols2_c = c;
3065									return false;
3066								}
3067							});
3068							merged.push($.extend(true, {}, cols1_c, cols2_c));
3069						}
3070					}
3071					return merged;
3072				}
3073
3074				var json = [], html = [];
3075				// get the column options from the content
3076				var $header = self.ft.$el.find('tr.footable-header, thead > tr:last:has([data-breakpoints]), tbody > tr:first:has([data-breakpoints]), thead > tr:last, tbody > tr:first').first(), $cell, cdata;
3077				if ($header.length > 0){
3078					var virtual = $header.parent().is('tbody') && $header.children().length == $header.children('td').length;
3079					if (!virtual) self.$header = $header.addClass('footable-header');
3080					$header.children('td,th').each(function(i, cell){
3081						$cell = $(cell);
3082						cdata = $cell.data();
3083						cdata.index = i;
3084						cdata.$el = $cell;
3085						cdata.virtual = virtual;
3086						html.push(cdata);
3087					});
3088					if (virtual) self.showHeader = false;
3089				}
3090				// get the supplied column options
3091				if (F.is.array(self.o.columns) && !F.is.emptyArray(self.o.columns)){
3092					F.arr.each(self.o.columns, function(c, i){
3093						c.index = i;
3094						json.push(c);
3095					});
3096					self.parseFinalize(d, merge(json, html));
3097				} else if (F.is.promise(self.o.columns)){
3098					self.o.columns.then(function(cols){
3099						F.arr.each(cols, function(c, i){
3100							c.index = i;
3101							json.push(c);
3102						});
3103						self.parseFinalize(d, merge(json, html));
3104					}, function(xhr){
3105						d.reject(Error('Columns ajax request error: ' + xhr.status + ' (' + xhr.statusText + ')'));
3106					});
3107				} else {
3108					self.parseFinalize(d, merge(json, html));
3109				}
3110			});
3111		},
3112		/**
3113		 * Used to finalize the parsing of columns it is supplied the parse deferred object which must be resolved with an array of {@link FooTable.Column} objects
3114		 * or rejected with an error.
3115		 * @instance
3116		 * @protected
3117		 * @param {jQuery.Deferred} deferred - The deferred object used for parsing.
3118		 * @param {Array.<object>} cols - An array of all merged column definitions.
3119		 */
3120		parseFinalize: function(deferred, cols){
3121			// we now have a merged array of all column definitions supplied to the plugin, time to make the objects.
3122			var self = this, columns = [], column;
3123			F.arr.each(cols, function(def){
3124				// if we have a column registered using the definition type then create an instance of that column otherwise just create a default text column.
3125				if (column = F.columns.contains(def.type) ? F.columns.make(def.type, self.ft, def) : new F.Column(self.ft, def))
3126					columns.push(column);
3127			});
3128			if (F.is.emptyArray(columns)){
3129				deferred.reject(Error("No columns supplied."));
3130			} else {
3131				// make sure to sort by the column index as the merge process may have mixed them up
3132				columns.sort(function(a, b){ return a.index - b.index; });
3133				deferred.resolve(columns);
3134			}
3135		},
3136		/**
3137		 * The columns preinit method is used to parse and check the column options supplied from both static content and through the constructor.
3138		 * @instance
3139		 * @protected
3140		 * @param {object} data - The jQuery data object from the root table element.
3141		 * @this FooTable.Columns
3142		 */
3143		preinit: function(data){
3144			var self = this;
3145			/**
3146			 * The preinit.ft.columns event is raised before any UI is created and provides the tables jQuery data object for additional options parsing.
3147			 * Calling preventDefault on this event will disable the entire plugin.
3148			 * @event FooTable.Columns#"preinit.ft.columns"
3149			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3150			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
3151			 * @param {object} data - The jQuery data object of the table raising the event.
3152			 */
3153			return self.ft.raise('preinit.ft.columns', [data]).then(function(){
3154				return self.parse(data).then(function(columns){
3155					self.array = columns;
3156					self.showHeader = F.is.boolean(data.showHeader) ? data.showHeader : self.showHeader;
3157				});
3158			});
3159		},
3160		/**
3161		 * Initializes the columns creating the table header if required.
3162		 * @instance
3163		 * @protected
3164		 * @fires FooTable.Columns#"init.ft.columns"
3165		 * @this FooTable.Columns
3166		 */
3167		init: function(){
3168			var self = this;
3169			/**
3170			 * The init.ft.columns event is raised after the header row is created/parsed for column data.
3171			 * @event FooTable.Columns#"init.ft.columns"
3172			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3173			 * @param {FooTable.Table} instance - The instance of the plugin raising the event.
3174			 * @param {Array.<FooTable.Column>} columns - The array of {@link FooTable.Column} objects parsed from the options and/or DOM.
3175			 */
3176			return this.ft.raise('init.ft.columns', [ self.array ]).then(function(){
3177				self.$create();
3178			});
3179		},
3180		/**
3181		 * Destroys the columns component removing any UI generated from the table.
3182		 * @instance
3183		 * @protected
3184		 * @fires FooTable.Columns#"destroy.ft.columns"
3185		 */
3186		destroy: function(){
3187			/**
3188			 * The destroy.ft.columns event is raised before its UI is removed.
3189			 * Calling preventDefault on this event will prevent the component from being destroyed.
3190			 * @event FooTable.Columns#"destroy.ft.columns"
3191			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3192			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
3193			 */
3194			var self = this;
3195			this.ft.raise('destroy.ft.columns').then(function(){
3196				if (!self._fromHTML) self.$header.remove();
3197			});
3198		},
3199		/**
3200		 * The predraw method called from within the {@link FooTable.Table#draw} method.
3201		 * @instance
3202		 * @protected
3203		 * @this FooTable.Columns
3204		 */
3205		predraw: function(){
3206			var self = this, first = true;
3207			self.visibleColspan = 0;
3208			self.firstVisibleIndex = 0;
3209			self.lastVisibleIndex = 0;
3210			self.hasHidden = false;
3211			F.arr.each(self.array, function(col){
3212				col.hidden = !self.ft.breakpoints.visible(col.breakpoints);
3213				if (!col.hidden && col.visible){
3214					if (first){
3215						self.firstVisibleIndex = col.index;
3216						first = false;
3217					}
3218					self.lastVisibleIndex = col.index;
3219					self.visibleColspan++;
3220				}
3221				if (col.hidden) self.hasHidden = true;
3222			});
3223			self.ft.$el.toggleClass('breakpoint', self.hasHidden);
3224		},
3225		/**
3226		 * Performs the actual drawing of the columns, hiding or displaying them depending on there breakpoints.
3227		 * @instance
3228		 * @protected
3229		 * @this FooTable.Columns
3230		 */
3231		draw: function(){
3232			F.arr.each(this.array, function(col){
3233				col.$el.css('display', (col.hidden || !col.visible  ? 'none' : 'table-cell'));
3234			});
3235			if (!this.showHeader && F.is.jq(this.$header.parent())){
3236				this.$header.detach();
3237			}
3238		},
3239		/**
3240		 * Creates the header row for the table from the parsed column definitions.
3241		 * @instance
3242		 * @protected
3243		 * @this FooTable.Columns
3244		 */
3245		$create: function(){
3246			var self = this;
3247			self.$header = F.is.jq(self.$header) ? self.$header : $('<tr/>', {'class': 'footable-header'});
3248			self.$header.children('th,td').detach();
3249			F.arr.each(self.array, function(col){
3250				self.$header.append(col.$el);
3251			});
3252			if (self.showHeader && !F.is.jq(self.$header.parent())){
3253				self.ft.$el.children('thead').append(self.$header);
3254			}
3255		},
3256		/**
3257		 * Attempts to return a {@link FooTable.Column} instance when passed the {@link FooTable.Column} instance, the {@link FooTable.Column#name} string or the {@link FooTable.Column#index} number.
3258		 * If supplied a function this will return an array by iterating all columns passing the index and column itself to the supplied callback as arguments.
3259		 * Returning true in the callback will include the column in the result.
3260		 * @instance
3261		 * @param {(FooTable.Column|string|number|function)} column - The column to retrieve.
3262		 * @returns {(Array.<FooTable.Column>|FooTable.Column|null)} The column if one is found otherwise it returns NULL.
3263		 * @example <caption>This example shows retrieving a column by name assuming a column called "id" exists. The <code>columns</code> object is an instance of {@link FooTable.Columns}.</caption>
3264		 * var column = columns.get('id');
3265		 * if (column instanceof FooTable.Column){
3266		 * 	// found the "id" column
3267		 * } else {
3268		 * 	// no column with a name of "id" exists
3269		 * }
3270		 * // to get an array of all hidden columns
3271		 * var columns = columns.get(function(col){
3272		 *  return col.hidden;
3273		 * });
3274		 */
3275		get: function(column){
3276			if (column instanceof F.Column) return column;
3277			if (F.is.string(column)) return F.arr.first(this.array, function (col) { return col.name == column; });
3278			if (F.is.number(column)) return F.arr.first(this.array, function (col) { return col.index == column; });
3279			if (F.is.fn(column)) return F.arr.get(this.array, column);
3280			return null;
3281		},
3282		/**
3283		 * Takes an array of column names, index's or actual {@link FooTable.Column} and ensures that an array of only {@link FooTable.Column} is returned.
3284		 * @instance
3285		 * @param {(Array.<string>|Array.<number>|Array.<FooTable.Column>)} columns - The array of column names, index's or {@link FooTable.Column} to check.
3286		 * @returns {Array.<FooTable.Column>}
3287		 */
3288		ensure: function(columns){
3289			var self = this, result = [];
3290			if (!F.is.array(columns)) return result;
3291			F.arr.each(columns, function(name){
3292				result.push(self.get(name));
3293			});
3294			return result;
3295		}
3296	});
3297
3298	F.components.register('columns', F.Columns, 900);
3299
3300})(jQuery, FooTable);
3301(function(F){
3302	/**
3303	 * An array containing the column options or a jQuery promise that resolves returning the columns. The index of the definitions must match the index of each column as it should appear in the table. For more information on the options available see the {@link FooTable.Column} object.
3304	 * @type {(Array.<object>|jQuery.Promise)}
3305	 * @default []
3306	 * @example <caption>The below shows column definitions for a row defined as <code>{ id: Number, name: String, age: Number }</code>. The ID column has a fixed width, the table is initially sorted on the Name column and the Age column will be hidden on phones.</caption>
3307	 * columns: [
3308	 * 	{ name: 'id', title: 'ID', type: 'number' },
3309	 *	{ name: 'name', title: 'Name', sorted: true, direction: 'ASC' }
3310	 *	{ name: 'age', title: 'Age', type: 'number', breakpoints: 'xs' }
3311	 * ]
3312	 */
3313	F.Defaults.prototype.columns = [];
3314
3315	/**
3316	 * Specifies whether or not the column headers should be displayed.
3317	 * @type {boolean}
3318	 * @default true
3319	 */
3320	F.Defaults.prototype.showHeader = true;
3321})(FooTable);
3322(function ($, F) {
3323	F.Rows = F.Component.extend(/** @lends FooTable.Rows */{
3324		/**
3325		 * The rows class contains all the logic for handling rows.
3326		 * @constructs
3327		 * @extends FooTable.Component
3328		 * @param {FooTable.Table} table -  The parent {@link FooTable.Table} this component belongs to.
3329		 * @returns {FooTable.Rows}
3330		 */
3331		construct: function (table) {
3332			// call the base class constructor
3333			this._super(table, true);
3334
3335			/**
3336			 * This provides a shortcut to the {@link FooTable.Table#options} object.
3337			 * @instance
3338			 * @protected
3339			 * @type {FooTable.Table#options}
3340			 */
3341			this.o = table.o;
3342			/**
3343			 * The current working array of {@link FooTable.Row} objects.
3344			 * @instance
3345			 * @protected
3346			 * @type {Array.<FooTable.Row>}
3347			 * @default []
3348			 */
3349			this.array = [];
3350			/**
3351			 * The base array of rows parsed from either the DOM or the constructor options.
3352			 * The {@link FooTable.Rows#current} member is populated with a shallow clone of this array
3353			 * during the predraw operation before any core or custom components are executed.
3354			 * @instance
3355			 * @protected
3356			 * @type {Array.<FooTable.Row>}
3357			 * @default []
3358			 */
3359			this.all = [];
3360			/**
3361			 * Whether or not to display a toggle in each row when it contains hidden columns.
3362			 * @type {boolean}
3363			 * @default true
3364			 */
3365			this.showToggle = table.o.showToggle;
3366			/**
3367			 * The CSS selector used to filter row click events. If the event.target property matches the selector the row will be toggled.
3368			 * @type {string}
3369			 * @default "tr,td,.footable-toggle"
3370			 */
3371			this.toggleSelector = table.o.toggleSelector;
3372			/**
3373			 * Specifies which column the row toggle is appended to. Supports only two values; "first" and "last"
3374			 * @type {string}
3375			 */
3376			this.toggleColumn = table.o.toggleColumn;
3377			/**
3378			 * The text to display when the table has no rows.
3379			 * @type {string}
3380			 */
3381			this.emptyString = table.o.empty;
3382			/**
3383			 * Whether or not the first rows details are expanded by default when displayed on a device that hides any columns.
3384			 * @type {boolean}
3385			 */
3386			this.expandFirst = table.o.expandFirst;
3387			/**
3388			 * Whether or not all row details are expanded by default when displayed on a device that hides any columns.
3389			 * @type {boolean}
3390			 */
3391			this.expandAll = table.o.expandAll;
3392			/**
3393			 * The jQuery object that contains the empty row control.
3394			 * @type {jQuery}
3395			 */
3396			this.$empty = null;
3397			this._fromHTML = F.is.emptyArray(table.o.rows) && !F.is.promise(table.o.rows);
3398		},
3399		/**
3400		 * This parses the rows from either the tables rows or the supplied options.
3401		 * @instance
3402		 * @protected
3403		 * @returns {jQuery.Promise}
3404		 */
3405		parse: function(){
3406			var self = this;
3407			return $.Deferred(function(d){
3408				var $rows = self.ft.$el.children('tbody').children('tr');
3409				if (F.is.array(self.o.rows) && self.o.rows.length > 0){
3410					self.parseFinalize(d, self.o.rows);
3411				} else if (F.is.promise(self.o.rows)){
3412					self.o.rows.then(function(rows){
3413						self.parseFinalize(d, rows);
3414					}, function(xhr){
3415						d.reject(Error('Rows ajax request error: ' + xhr.status + ' (' + xhr.statusText + ')'));
3416					});
3417				} else if (F.is.jq($rows)){
3418					self.parseFinalize(d, $rows);
3419					$rows.detach();
3420				} else {
3421					self.parseFinalize(d, []);
3422				}
3423			});
3424		},
3425		/**
3426		 * Used to finalize the parsing of rows it is supplied the parse deferred object which must be resolved with an array of {@link FooTable.Row} objects
3427		 * or rejected with an error.
3428		 * @instance
3429		 * @protected
3430		 * @param {jQuery.Deferred} deferred - The deferred object used for parsing.
3431		 * @param {(Array.<object>|jQuery)} rows - An array of row values and options or the jQuery object c
3431ontaining all rows.
3432		 */
3433		parseFinalize: function(deferred, rows){
3434			var self = this, result = $.map(rows, function(r){
3435				return new F.Row(self.ft, self.ft.columns.array, r);
3436			});
3437			deferred.resolve(result);
3438		},
3439		/**
3440		 * The columns preinit method is used to parse and check the column options supplied from both static content and through the constructor.
3441		 * @instance
3442		 * @protected
3443		 * @param {object} data - The jQuery data object from the root table element.
3444		 * @fires FooTable.Rows#"preinit.ft.rows"
3445		 */
3446		preinit: function(data){
3447			var self = this;
3448			/**
3449			 * The preinit.ft.rows event is raised before any UI is created and provides the tables jQuery data object for additional options parsing.
3450			 * Calling preventDefault on this event will disable the entire plugin.
3451			 * @event FooTable.Rows#"preinit.ft.rows"
3452			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3453			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
3454			 * @param {object} data - The jQuery data object of the table raising the event.
3455			 */
3456			return self.ft.raise('preinit.ft.rows', [data]).then(function(){
3457				return self.parse().then(function(rows){
3458					self.all = rows;
3459					self.array = self.all.slice(0);
3460					self.showToggle = F.is.boolean(data.showToggle) ? data.showToggle : self.showToggle;
3461					self.toggleSelector = F.is.string(data.toggleSelector) ? data.toggleSelector : self.toggleSelector;
3462					self.toggleColumn = F.is.string(data.toggleColumn) ? data.toggleColumn : self.toggleColumn;
3463					if (self.toggleColumn != "first" && self.toggleColumn != "last") self.toggleColumn = "first";
3464					self.emptyString = F.is.string(data.empty) ? data.empty : self.emptyString;
3465					self.expandFirst = F.is.boolean(data.expandFirst) ? data.expandFirst : self.expandFirst;
3466					self.expandAll = F.is.boolean(data.expandAll) ? data.expandAll : self.expandAll;
3467				});
3468			});
3469		},
3470		/**
3471		 * Initializes the rows class using the supplied table and options.
3472		 * @instance
3473		 * @protected
3474		 * @fires FooTable.Rows#"init.ft.rows"
3475		 */
3476		init: function () {
3477			var self = this;
3478			/**
3479			 * The init.ft.rows event is raised after the the rows are parsed from either the DOM or the options.
3480			 * Calling preventDefault on this event will disable the entire plugin.
3481			 * @event FooTable.Rows#"init.ft.rows"
3482			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3483			 * @param {FooTable.Table} instance - The instance of the plugin raising the event.
3484			 * @param {Array.<FooTable.Row>} rows - The array of {@link FooTable.Row} objects parsed from the DOM or the options.
3485			 */
3486			return self.ft.raise('init.ft.rows', [self.all]).then(function(){
3487				self.$create();
3488			});
3489		},
3490		/**
3491		 * Destroys the rows component removing any UI generated from the table.
3492		 * @instance
3493		 * @protected
3494		 * @fires FooTable.Rows#"destroy.ft.rows"
3495		 */
3496		destroy: function(){
3497			/**
3498			 * The destroy.ft.rows event is raised before its UI is removed.
3499			 * Calling preventDefault on this event will prevent the component from being destroyed.
3500			 * @event FooTable.Rows#"destroy.ft.rows"
3501			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3502			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
3503			 */
3504			var self = this;
3505			this.ft.raise('destroy.ft.rows').then(function(){
3506				F.arr.each(self.array, function(row){
3507					row.predraw(!self._fromHTML);
3508				});
3509				self.all = self.array = [];
3510			});
3511		},
3512		/**
3513		 * Performs the predraw operations that are required including creating the shallow clone of the {@link FooTable.Rows#array} to work with.
3514		 * @instance
3515		 * @protected
3516		 */
3517		predraw: function(){
3518			F.arr.each(this.array, function(row){
3519				row.predraw();
3520			});
3521			this.array = this.all.slice(0);
3522		},
3523		$create: function(){
3524			this.$empty = $('<tr/>', { 'class': 'footable-empty' }).append($('<td/>').text(this.emptyString));
3525		},
3526		/**
3527		 * Performs the actual drawing of the table rows.
3528		 * @instance
3529		 * @protected
3530		 */
3531		draw: function(){
3532			var self = this, $tbody = self.ft.$el.children('tbody'), first = true;
3533			// if we have rows
3534			if (self.array.length > 0){
3535				self.$empty.detach();
3536				// loop through them appending to the tbody and then drawing
3537				F.arr.each(self.array, function(row){
3538					if ((self.expandFirst && first) || self.expandAll){
3539						row.expanded = true;
3540						first = false;
3541					}
3542					row.draw($tbody);
3543				});
3544			} else {
3545				// otherwise display the $empty row
3546				self.$empty.children('td').attr('colspan', self.ft.columns.visibleColspan);
3547				$tbody.append(self.$empty);
3548			}
3549		},
3550		/**
3551		 * Loads a JSON array of row objects into the table
3552		 * @instance
3553		 * @param {Array.<object>} data - An array of row objects to load.
3554		 * @param {boolean} [append=false] - Whether or not to append the new rows to the current rows array or to replace them entirely.
3555		 */
3556		load: function(data, append){
3557			var self = this, rows = $.map(data, function(r){
3558				return new F.Row(self.ft, self.ft.columns.array, r);
3559			});
3560			F.arr.each(this.array, function(row){
3561				row.predraw();
3562			});
3563			this.all = (F.is.boolean(append) ? append : false) ? this.all.concat(rows) : rows;
3564			this.array = this.all.slice(0);
3565			this.ft.draw();
3566		},
3567		/**
3568		 * Expands all visible rows.
3569		 * @instance
3570		 */
3571		expand: function(){
3572			F.arr.each(this.array, function(row){
3573				row.expand();
3574			});
3575		},
3576		/**
3577		 * Collapses all visible rows.
3578		 * @instance
3579		 */
3580		collapse: function(){
3581			F.arr.each(this.array, function(row){
3582				row.collapse();
3583			});
3584		}
3585	});
3586
3587	F.components.register('rows', F.Rows, 800);
3588
3589})(jQuery, FooTable);
3590(function(F){
3591	/**
3592	 * An array of JSON objects containing the row data or a jQuery promise that resolves returning the row data.
3593	 * @type {(Array.<object>|jQuery.Promise)}
3594	 * @default []
3595	 */
3596	F.Defaults.prototype.rows = [];
3597
3598	/**
3599	 * A string to display when there are no rows in the table.
3600	 * @type {string}
3601	 * @default "No results"
3602	 */
3603	F.Defaults.prototype.empty = 'No results';
3604
3605	/**
3606	 * Whether or not the toggle is appended to each row.
3607	 * @type {boolean}
3608	 * @default true
3609	 */
3610	F.Defaults.prototype.showToggle = true;
3611
3612	/**
3613	 * The CSS selector used to filter row click events. If the event.target property matches the selector the row will be toggled.
3614	 * @type {string}
3615	 * @default "tr,td,.footable-toggle"
3616	 */
3617	F.Defaults.prototype.toggleSelector = 'tr,td,.footable-toggle';
3618
3619	/**
3620	 * Specifies which column to display the row toggle in. The only supported values are "first" or "last".
3621	 * @type {string}
3622	 * @default "first"
3623	 */
3624	F.Defaults.prototype.toggleColumn = 'first';
3625
3626	/**
3627	 * Whether or not the first rows details are expanded by default when displayed on a device that hides any columns.
3628	 * @type {boolean}
3629	 */
3630	F.Defaults.prototype.expandFirst = false;
3631
3632	/**
3633	 * Whether or not all row details are expanded by default when displayed on a device that hides any columns.
3634	 * @type {boolean}
3635	 */
3636	F.Defaults.prototype.expandAll = false;
3637})(FooTable);
3638(function(F){
3639	/**
3640	 * Loads a JSON array of row objects into the table
3641	 * @param {Array.<object>} data - An array of row objects to load.
3642	 * @param {boolean} [append=false] - Whether or not to append the new rows to the current rows array or to replace them entirely.
3643	 */
3644	F.Table.prototype.loadRows = function(data, append){
3645		this.rows.load(data, append);
3646	};
3647})(FooTable);
3648(function(F){
3649	F.Filter = F.Class.extend(/** @lends FooTable.Filter */{
3650		/**
3651		 * The filter object contains the query to filter by and the columns to apply it to.
3652		 * @constructs
3653		 * @extends FooTable.Class
3654		 * @param {string} name - The name for the filter.
3655		 * @param {(string|FooTable.Query)} query - The query for the filter.
3656		 * @param {Array.<FooTable.Column>} columns - The columns to apply the query to.
3657		 * @param {string} [space="AND"] - How the query treats space chars.
3658		 * @param {boolean} [connectors=true] - Whether or not to replace phrase connectors (+.-_) with spaces.
3659		 * @param {boolean} [ignoreCase=true] - Whether or not ignore case when matching.
3660		 * @param {boolean} [hidden=true] - Whether or not this is a hidden filter.
3661		 * @returns {FooTable.Filter}
3662		 */
3663		construct: function(name, query, columns, space, connectors, ignoreCase, hidden){
3664			/**
3665			 * The name of the filter.
3666			 * @instance
3667			 * @type {string}
3668			 */
3669			this.name = name;
3670			/**
3671			 * A string specifying how the filter treats space characters. Can be either "OR" or "AND".
3672			 * @instance
3673			 * @type {string}
3674			 */
3675			this.space = F.is.string(space) && (space == 'OR' || space == 'AND') ? space : 'AND';
3676			/**
3677			 * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query.
3678			 * @instance
3679			 * @type {boolean}
3680			 */
3681			this.connectors = F.is.boolean(connectors) ? connectors : true;
3682			/**
3683			 * Whether or not ignore case when matching.
3684			 * @instance
3685			 * @type {boolean}
3686			 */
3687			this.ignoreCase = F.is.boolean(ignoreCase) ? ignoreCase : true;
3688			/**
3689			 * Whether or not this is a hidden filter.
3690			 * @instance
3691			 * @type {boolean}
3692			 */
3693			this.hidden = F.is.boolean(hidden) ? hidden : false;
3694			/**
3695			 * The query for the filter.
3696			 * @instance
3697			 * @type {(string|FooTable.Query)}
3698			 */
3699			this.query = query instanceof F.Query ? query : new F.Query(query, this.space, this.connectors, this.ignoreCase);
3700			/**
3701			 * The columns to apply the query to.
3702			 * @instance
3703			 * @type {Array.<FooTable.Column>}
3704			 */
3705			this.columns = columns;
3706		},
3707		/**
3708		 * Checks if the current filter matches the supplied string.
3709		 * If the current query property is a string it will be auto converted to a {@link FooTable.Query} object to perform the match.
3710		 * @instance
3711		 * @param {string} str - The string to check.
3712		 * @returns {boolean}
3713		 */
3714		match: function(str){
3715			if (!F.is.string(str)) return false;
3716			if (F.is.string(this.query)){
3717				this.query = new F.Query(this.query, this.space, this.connectors, this.ignoreCase);
3718			}
3719			return this.query instanceof F.Query ? this.query.match(str) : false;
3720		},
3721		/**
3722		 * Checks if the current filter matches the supplied {@link FooTable.Row}.
3723		 * @instance
3724		 * @param {FooTable.Row} row - The row to check.
3725		 * @returns {boolean}
3726		 */
3727		matchRow: function(row){
3728			var self = this, text = F.arr.map(row.cells, function(cell){
3729				return F.arr.contains(self.columns, cell.column) ? cell.filterValue : null;
3730			}).join(' ');
3731			return self.match(text);
3732		}
3733	});
3734
3735})(FooTable);
3736(function ($, F) {
3737	F.Filtering = F.Component.extend(/** @lends FooTable.Filtering */{
3738		/**
3739		 * The filtering component adds a search input and column selector dropdown to the table allowing users to filter the using space delimited queries.
3740		 * @constructs
3741		 * @extends FooTable.Component
3742		 * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component.
3743		 * @returns {FooTable.Filtering}
3744		 */
3745		construct: function (table) {
3746			// call the constructor of the base class
3747			this._super(table, table.o.filtering.enabled);
3748
3749			/* PUBLIC */
3750			/**
3751			 * The filters to apply to the current {@link FooTable.Rows#array}.
3752			 * @instance
3753			 * @type {Array.<FooTable.Filter>}
3754			 */
3755			this.filters = table.o.filtering.filters;
3756			/**
3757			 * The delay in milliseconds before the query is auto applied after a change.
3758			 * @instance
3759			 * @type {number}
3760			 */
3761			this.delay = table.o.filtering.delay;
3762			/**
3763			 * The minimum number of characters allowed in the search input before it is auto applied.
3764			 * @instance
3765			 * @type {number}
3766			 */
3767			this.min = table.o.filtering.min;
3768			/**
3769			 * Specifies how whitespace in a filter query is handled.
3770			 * @instance
3771			 * @type {string}
3772			 */
3773			this.space = table.o.filtering.space;
3774			/**
3775			 * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query.
3776			 * @instance
3777			 * @type {boolean}
3778			 */
3779			this.connectors = table.o.filtering.connectors;
3780			/**
3781			 * Whether or not ignore case when matching.
3782			 * @instance
3783			 * @type {boolean}
3784			 */
3785			this.ignoreCase = table.o.filtering.ignoreCase;
3786			/**
3787			 * Whether or not search queries are treated as phrases when matching.
3788			 * @instance
3789			 * @type {boolean}
3790			 */
3791			this.exactMatch = table.o.filtering.exactMatch;
3792			/**
3793			 * The placeholder text to display within the search $input.
3794			 * @instance
3795			 * @type {string}
3796			 */
3797			this.placeholder = table.o.filtering.placeholder;
3798			/**
3799			 * The title to display at the top of the search input column select.
3800			 * @type {string}
3801			 */
3802			this.dropdownTitle = table.o.filtering.dropdownTitle;
3803			/**
3804			 * The position of the $search input within the filtering rows cell.
3805			 * @type {string}
3806			 */
3807			this.position = table.o.filtering.position;
3808			/**
3809			 * Whether or not to focus the search input after the search/clear button is clicked or after auto applying the search input query.
3810			 * @type {boolean}
3811			 */
3812			this.focus = table.o.filtering.focus;
3813			/**
3814			 * A selector specifying where to place the filtering components form, if null the form is displayed within a row in the head of the table.
3815			 * @type {string}
3816			 */
3817			this.container = table.o.filtering.container;
3818			/**
3819			 * The jQuery object of the element containing the entire filtering form.
3820			 * @instance
3821			 * @type {jQuery}
3822			 */
3823			this.$container = null;
3824			/**
3825			 * The jQuery row object that contains all the filtering specific elements.
3826			 * @instance
3827			 * @type {jQuery}
3828			 */
3829			this.$row = null;
3830			/**
3831			 * The jQuery cell object that contains the search input and column selector.
3832			 * @instance
3833			 * @type {jQuery}
3834			 */
3835			this.$cell = null;
3836			/**
3837			 * The jQuery form object of the form that contains the search input and column selector.
3838			 * @instance
3839			 * @type {jQuery}
3840			 */
3841			this.$form = null;
3842			/**
3843			 * The jQuery object of the column selector dropdown.
3844			 * @instance
3845			 * @type {jQuery}
3846			 */
3847			this.$dropdown = null;
3848			/**
3849			 * The jQuery object of the search input.
3850			 * @instance
3851			 * @type {jQuery}
3852			 */
3853			this.$input = null;
3854			/**
3855			 * The jQuery object of the search button.
3856			 * @instance
3857			 * @type {jQuery}
3858			 */
3859			this.$button = null;
3860
3861			/* PRIVATE */
3862			/**
3863			 * The timeout ID for the filter changed event.
3864			 * @instance
3865			 * @private
3866			 * @type {?number}
3867			 */
3868			this._filterTimeout = null;
3869			/**
3870			 * The regular expression used to check for encapsulating quotations.
3871			 * @instance
3872			 * @private
3873			 * @type {RegExp}
3874			 */
3875			this._exactRegExp = /^"(.*?)"$/;
3876		},
3877
3878		/* PROTECTED */
3879		/**
3880		 * Checks the supplied data and options for the filtering component.
3881		 * @instance
3882		 * @protected
3883		 * @param {object} data - The jQuery data object from the parent table.
3884		 * @fires FooTable.Filtering#"preinit.ft.filtering"
3885		 */
3886		preinit: function(data){
3887			var self = this;
3888			/**
3889			 * The preinit.ft.filtering event is raised before the UI is created and provides the tables jQuery data object for additional options parsing.
3890			 * Calling preventDefault on this event will disable the component.
3891			 * @event FooTable.Filtering#"preinit.ft.filtering"
3892			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3893			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
3894			 * @param {object} data - The jQuery data object of the table raising the event.
3895			 */
3896			return self.ft.raise('preinit.ft.filtering').then(function(){
3897				// first check if filtering is enabled via the class being applied
3898				if (self.ft.$el.hasClass('footable-filtering'))
3899					self.enabled = true;
3900				// then check if the data-filtering-enabled attribute has been set
3901				self.enabled = F.is.boolean(data.filtering)
3902					? data.filtering
3903					: self.enabled;
3904
3905				// if filtering is not enabled exit early as we don't need to do anything else
3906				if (!self.enabled) return;
3907
3908				self.space = F.is.string(data.filterSpace)
3909					? data.filterSpace
3910					: self.space;
3911
3912				self.min = F.is.number(data.filterMin)
3913					? data.filterMin
3914					: self.min;
3915
3916				self.connectors = F.is.boolean(data.filterConnectors)
3917					? data.filterConnectors
3918					: self.connectors;
3919
3920				self.ignoreCase = F.is.boolean(data.filterIgnoreCase)
3921					? data.filterIgnoreCase
3922					: self.ignoreCase;
3923
3924				self.exactMatch = F.is.boolean(data.filterExactMatch)
3925					? data.filterExactMatch
3926					: self.exactMatch;
3927
3928				self.focus = F.is.boolean(data.filterFocus)
3929					? data.filterFocus
3930					: self.focus;
3931
3932				self.delay = F.is.number(data.filterDelay)
3933					? data.filterDelay
3934					: self.delay;
3935
3936				self.placeholder = F.is.string(data.filterPlaceholder)
3937					? data.filterPlaceholder
3938					: self.placeholder;
3939
3940				self.dropdownTitle = F.is.string(data.filterDropdownTitle)
3941					? data.filterDropdownTitle
3942					: self.dropdownTitle;
3943
3944				self.container = F.is.string(data.filterContainer)
3945					? data.filterContainer
3946					: self.container;
3947
3948				self.filters = F.is.array(data.filterFilters)
3949					? self.ensure(data.filterFilters)
3950					: self.ensure(self.filters);
3951
3952				if (self.ft.$el.hasClass('footable-filtering-left'))
3953					self.position = 'left';
3954				if (self.ft.$el.hasClass('footable-filtering-center'))
3955					self.position = 'center';
3956				if (self.ft.$el.hasClass('footable-filtering-right'))
3957					self.position = 'right';
3958
3959				self.position = F.is.string(data.filterPosition)
3960					? data.filterPosition
3961					: self.position;
3962			},function(){
3963				self.enabled = false;
3964			});
3965		},
3966		/**
3967		 * Initializes the filtering component for the plugin.
3968		 * @instance
3969		 * @protected
3970		 * @fires FooTable.Filtering#"init.ft.filtering"
3971		 */
3972		init: function () {
3973			var self = this;
3974			/**
3975			 * The init.ft.filtering event is raised before its UI is generated.
3976			 * Calling preventDefault on this event will disable the component.
3977			 * @event FooTable.Filtering#"init.ft.filtering"
3978			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
3979			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
3980			 */
3981			return self.ft.raise('init.ft.filtering').then(function(){
3982				self.$create();
3983			}, function(){
3984				self.enabled = false;
3985			});
3986		},
3987		/**
3988		 * Destroys the filtering component removing any UI from the table.
3989		 * @instance
3990		 * @protected
3991		 * @fires FooTable.Filtering#"destroy.ft.filtering"
3992		 */
3993		destroy: function () {
3994			var self = this;
3995			/**
3996			 * The destroy.ft.filtering event is raised before its UI is removed.
3997			 * Calling preventDefault on this event will prevent the component from being destroyed.
3998			 * @event FooTable.Filtering#"destroy.ft.filtering"
3999			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
4000			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
4001			 */
4002			return self.ft.raise('destroy.ft.filtering').then(function(){
4003				self.ft.$el.removeClass('footable-filtering')
4004					.find('thead > tr.footable-filtering').remove();
4005			});
4006		},
4007		/**
4008		 * Creates the filtering UI from the current options setting the various jQuery properties of this component.
4009		 * @instance
4010		 * @protected
4011		 * @this FooTable.Filtering
4012		 */
4013		$create: function () {
4014			var self = this;
4015			// generate the cell that actually contains all the UI.
4016			var $form_grp = $('<div/>', {'class': 'form-group footable-filtering-search'})
4017					.append($('<label/>', {'class': 'sr-only', text: 'Search'})),
4018				$input_grp = $('<div/>', {'class': 'input-group'}).appendTo($form_grp),
4019				$input_grp_btn = $('<div/>', {'class': 'input-group-btn'}),
4020				$dropdown_toggle = $('<button/>', {type: 'button', 'class': 'btn btn-default dropdown-toggle'})
4021					.on('click', { self: self }, self._onDropdownToggleClicked)
4022					.append($('<span/>', {'class': 'caret'})),
4023				position;
4024
4025			switch (self.position){
4026				case 'left': position = 'footable-filtering-left'; break;
4027				case 'center': position = 'footable-filtering-center'; break;
4028				default: position = 'footable-filtering-right'; break;
4029			}
4030			self.ft.$el.addClass('footable-filtering').addClass(position);
4031
4032			self.$container = self.container === null ? $() : $(self.container).first();
4033			if (!self.$container.length){
4034				// add it to a row and then populate it with the search input and column selector dropdown.
4035				self.$row = $('<tr/>', {'class': 'footable-filtering'}).prependTo(self.ft.$el.children('thead'));
4036				self.$cell = $('<th/>').attr('colspan', self.ft.columns.visibleColspan).appendTo(self.$row);
4037				self.$container = self.$cell;
4038			} else {
4039				self.$container.addClass('footable-filtering-external').addClass(position);
4040			}
4041			self.$form = $('<form/>', {'class': 'form-inline'}).append($form_grp).appendTo(self.$container);
4042
4043			self.$input = $('<input/>', {type: 'text', 'class': 'form-control', placeholder: self.placeholder});
4044
4045			self.$button = $('<button/>', {type: 'button', 'class': 'btn btn-primary'})
4046				.on('click', { self: self }, self._onSearchButtonClicked)
4047				.append($('<span/>', {'class': 'fooicon fooicon-search'}));
4048
4049			self.$dropdown = $('<ul/>', {'class': 'dropdown-menu dropdown-menu-right'});
4050			if (!F.is.emptyString(self.dropdownTitle)){
4051				self.$dropdown.append($('<li/>', {'class': 'dropdown-header','text': self.dropdownTitle}));
4052			}
4053			self.$dropdown.append(
4054				F.arr.map(self.ft.columns.array, function (col) {
4055					return col.filterable ? $('<li/>').append(
4056						$('<a/>', {'class': 'checkbox'}).append(
4057							$('<label/>', {html: col.title}).prepend(
4058								$('<input/>', {type: 'checkbox', checked: true}).data('__FooTableColumn__', col)
4059							)
4060						)
4061					) : null;
4062				})
4063			);
4064
4065			if (self.delay > 0){
4066				self.$input.on('keypress keyup paste', { self: self }, self._onSearchInputChanged);
4067				self.$dropdown.on('click', 'input[type="checkbox"]', {self: self}, self._onSearchColumnClicked);
4068			}
4069
4070			$input_grp_btn.append(self.$button, $dropdown_toggle, self.$dropdown);
4071			$input_grp.append(self.$input, $input_grp_btn);
4072		},
4073		/**
4074		 * Performs the filtering of rows before they are appended to the page.
4075		 * @instance
4076		 * @protected
4077		 */
4078		predraw: function(){
4079			if (F.is.emptyArray(this.filters))
4080				return;
4081
4082			var self = this;
4083			self.ft.rows.array = $.grep(self.ft.rows.array, function(r){
4084				return r.filtered(self.filters);
4085			});
4086		},
4087		/**
4088		 * As the rows are drawn by the {@link FooTable.Rows#draw}
4088 method this simply updates the colspan for the UI.
4089		 * @instance
4090		 * @protected
4091		 */
4092		draw: function(){
4093			if (F.is.jq(this.$cell)){
4094				this.$cell.attr('colspan', this.ft.columns.visibleColspan);
4095			}
4096			var search = this.find('search');
4097			if (search instanceof F.Filter){
4098				var query = search.query.val();
4099				if (this.exactMatch && this._exactRegExp.test(query)){
4100					query = query.replace(this._exactRegExp, '$1');
4101				}
4102				this.$input.val(query);
4103			} else {
4104				this.$input.val(null);
4105			}
4106			this.setButton(!F.arr.any(this.filters, function(f){ return !f.hidden; }));
4107		},
4108
4109		/* PUBLIC */
4110		/**
4111		 * Adds or updates the filter using the supplied name, query and columns.
4112		 * @instance
4113		 * @param {(string|FooTable.Filter|object)} nameOrFilter - The name for the filter or the actual filter object itself.
4114		 * @param {(string|FooTable.Query)} [query] - The query for the filter. This is only optional when the first parameter is a filter object.
4115		 * @param {(Array.<number>|Array.<string>|Array.<FooTable.Column>)} [columns] - The columns to apply the filter to.
4116		 * 	If not supplied the filter will be applied to all selected columns in the search input dropdown.
4117		 * @param {boolean} [ignoreCase=true] - Whether or not ignore case when matching.
4118		 * @param {boolean} [connectors=true] - Whether or not to replace phrase connectors (+.-_) with spaces.
4119		 * @param {string} [space="AND"] - How the query treats space chars.
4120		 * @param {boolean} [hidden=true] - Whether or not this is a hidden filter.
4121		 */
4122		addFilter: function(nameOrFilter, query, columns, ignoreCase, connectors, space, hidden){
4123			var f = this.createFilter(nameOrFilter, query, columns, ignoreCase, connectors, space, hidden);
4124			if (f instanceof F.Filter){
4125				this.removeFilter(f.name);
4126				this.filters.push(f);
4127			}
4128		},
4129		/**
4130		 * Removes the filter using the supplied name if it exists.
4131		 * @instance
4132		 * @param {string} name - The name of the filter to remove.
4133		 */
4134		removeFilter: function(name){
4135			F.arr.remove(this.filters, function(f){ return f.name == name; });
4136		},
4137		/**
4138		 * Performs the required steps to handle filtering including the raising of the {@link FooTable.Filtering#"before.ft.filtering"} and {@link FooTable.Filtering#"after.ft.filtering"} events.
4139		 * @instance
4140		 * @param {boolean} [focus=false] - Whether or not to set the focus to the input once filtering is complete.
4141		 * @returns {jQuery.Promise}
4142		 * @fires FooTable.Filtering#"before.ft.filtering"
4143		 * @fires FooTable.Filtering#"after.ft.filtering"
4144		 */
4145		filter: function(focus){
4146			var self = this;
4147			self.filters = self.ensure(self.filters);
4148			/**
4149			 * The before.ft.filtering event is raised before a filter is applied and allows listeners to modify the filter or cancel it completely by calling preventDefault on the jQuery.Event object.
4150			 * @event FooTable.Filtering#"before.ft.filtering"
4151			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
4152			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
4153			 * @param {Array.<FooTable.Filter>} filters - The filters that are about to be applied.
4154			 */
4155			return self.ft.raise('before.ft.filtering', [self.filters]).then(function(){
4156				self.filters = self.ensure(self.filters);
4157				if (focus){
4158					var start = self.$input.prop('selectionStart'),
4159						end = self.$input.prop('selectionEnd');
4160				}
4161				return self.ft.draw().then(function(){
4162					if (focus){
4163						self.$input.focus().prop({
4164							selectionStart: start,
4165							selectionEnd: end
4166						});
4167					}
4168					/**
4169					 * The after.ft.filtering event is raised after a filter has been applied.
4170					 * @event FooTable.Filtering#"after.ft.filtering"
4171					 * @param {jQuery.Event} e - The jQuery.Event object for the event.
4172					 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
4173					 * @param {FooTable.Filter} filter - The filters that were applied.
4174					 */
4175					self.ft.raise('after.ft.filtering', [self.filters]);
4176				});
4177			});
4178		},
4179		/**
4180		 * Removes the current search filter.
4181		 * @instance
4182		 * @returns {jQuery.Promise}
4183		 * @fires FooTable.Filtering#"before.ft.filtering"
4184		 * @fires FooTable.Filtering#"after.ft.filtering"
4185		 */
4186		clear: function(){
4187			this.filters = F.arr.get(this.filters, function(f){ return f.hidden; });
4188			return this.filter(this.focus);
4189		},
4190		/**
4191		 * Toggles the button icon between the search and clear icons based on the supplied value.
4192		 * @instance
4193		 * @param {boolean} search - Whether or not to display the search icon.
4194		 */
4195		setButton: function(search){
4196			if (!search){
4197				this.$button.children('.fooicon').removeClass('fooicon-search').addClass('fooicon-remove');
4198			} else {
4199				this.$button.children('.fooicon').removeClass('fooicon-remove').addClass('fooicon-search');
4200			}
4201		},
4202		/**
4203		 * Finds a filter by name.
4204		 * @param {string} name - The name of the filter to find.
4205		 * @returns {(FooTable.Filter|null)}
4206		 */
4207		find: function(name){
4208			return F.arr.first(this.filters, function(f){ return f.name == name; });
4209		},
4210		/**
4211		 * Gets an array of {@link FooTable.Column}
4211 to apply the search filter to. This also doubles as the default columns for filters which do not specify any columns.
4212		 * @instance
4213		 * @returns {Array.<FooTable.Column>}
4214		 */
4215		columns: function(){
4216			if (F.is.jq(this.$dropdown)){
4217				// if we have a dropdown containing the column names get the selected columns from there
4218				return this.$dropdown.find('input:checked').map(function(){
4219					return $(this).data('__FooTableColumn__');
4220				}).get();
4221			} else {
4222				// otherwise find all columns that are set to be filterable.
4223				return this.ft.columns.get(function(c){ return c.filterable; });
4224			}
4225		},
4226		/**
4227		 * Takes an array of plain objects containing the filter values or actual {@link FooTable.Filter} objects and ensures that an array of only {@link FooTable.Filter} is returned.
4228		 * If supplied a plain object that object must contain a name, query and columns properties which are used to create a new {@link FooTable.Filter}.
4229		 * @instance
4230		 * @param {({name: string, query: (string|FooTable.Query), columns: (Array.<string>|Array.<number>|Array.<FooTable.Column>)}|Array.<FooTable.Filter>)} filters - The array of filters to check.
4231		 * @returns {Array.<FooTable.Filter>}
4232		 */
4233		ensure: function(filters){
4234			var self = this, parsed = [], filterable = self.columns();
4235			if (!F.is.emptyArray(filters)){
4236				F.arr.each(filters, function(f){
4237					f = self._ensure(f, filterable);
4238					if (f instanceof F.Filter) parsed.push(f);
4239				});
4240			}
4241			return parsed;
4242		},
4243
4244		/**
4245		 * Creates a new filter using the supplied object or individual parameters to populate it.
4246		 * @instance
4247		 * @param {(string|FooTable.Filter|object)} nameOrObject - The name for the filter or the actual filter object itself.
4248		 * @param {(string|FooTable.Query)} [query] - The query for the filter. This is only optional when the first parameter is a filter object.
4249		 * @param {(Array.<number>|Array.<string>|Array.<FooTable.Column>)} [columns] - The columns to apply the filter to.
4250		 * 	If not supplied the filter will be applied to all selected columns in the search input dropdown.
4251		 * @param {boolean} [ignoreCase=true] - Whether or not ignore case when matching.
4252		 * @param {boolean} [connectors=true] - Whether or not to replace phrase connectors (+.-_) with spaces.
4253		 * @param {string} [space="AND"] - How the query treats space chars.
4254		 * @param {boolean} [hidden=true] - Whether or not this is a hidden filter.
4255		 * @returns {*}
4256		 */
4257		createFilter: function(nameOrObject, query, columns, ignoreCase, connectors, space, hidden){
4258			if (F.is.string(nameOrObject)){
4259				nameOrObject = {name: nameOrObject, query: query, columns: columns, ignoreCase: ignoreCase, connectors: connectors, space: space, hidden: hidden};
4260			}
4261			return this._ensure(nameOrObject, this.columns());
4262		},
4263
4264		/* PRIVATE */
4265		_ensure: function(filter, selectedColumns){
4266			if ((F.is.hash(filter) || filter instanceof F.Filter) && !F.is.emptyString(filter.name) && (!F.is.emptyString(filter.query) || filter.query instanceof F.Query)){
4267				filter.columns = F.is.emptyArray(filter.columns) ? selectedColumns : this.ft.columns.ensure(filter.columns);
4268				filter.ignoreCase = F.is.boolean(filter.ignoreCase) ? filter.ignoreCase : this.ignoreCase;
4269				filter.connectors = F.is.boolean(filter.connectors) ? filter.connectors : this.connectors;
4270				filter.hidden = F.is.boolean(filter.hidden) ? filter.hidden : false;
4271				filter.space = F.is.string(filter.space) && (filter.space === 'AND' || filter.space === 'OR') ? filter.space : this.space;
4272				filter.query = F.is.string(filter.query) ? new F.Query(filter.query, filter.space, filter.connectors, filter.ignoreCase) : filter.query;
4273				return (filter instanceof F.Filter)
4274					? filter
4275					: new F.Filter(filter.name, filter.query, filter.columns, filter.space, filter.connectors, filter.ignoreCase, filter.hidden);
4276			}
4277			return null;
4278		},
4279		/**
4280		 * Handles the change event for the {@link FooTable.Filtering#$input}.
4281		 * @instance
4282		 * @private
4283		 * @param {jQuery.Event} e - The event object for the event.
4284		 */
4285		_onSearchInputChanged: function (e) {
4286			var self = e.data.self;
4287			var alpha = e.type == 'keypress' && !F.is.emptyString(String.fromCharCode(e.charCode)),
4288				ctrl = e.type == 'keyup' && (e.which == 8 || e.which == 46),
4289				paste = e.type == 'paste'; // backspace & delete
4290
4291			// if alphanumeric characters or specific control characters
4292			if(alpha || ctrl || paste) {
4293				if (e.which == 13) e.preventDefault();
4294				if (self._filterTimeout != null) clearTimeout(self._filterTimeout);
4295				self._filterTimeout = setTimeout(function(){
4296					self._filterTimeout = null;
4297					var query = self.$input.val();
4298					if (query.length >= self.min){
4299						if (self.exactMatch && !self._exactRegExp.test(query)){
4300							query = '"' + query + '"';
4301						}
4302						self.addFilter('search', query);
4303						self.filter(self.focus);
4304					} else if (F.is.emptyString(query)){
4305						self.clear();
4306					}
4307				}, self.delay);
4308			}
4309		},
4310		/**
4311		 * Handles the click event for the {@link FooTable.Filtering#$button}.
4312		 * @instance
4313		 * @private
4314		 * @param {jQuery.Event} e - The event object for the event.
4315		 */
4316		_onSearchButtonClicked: function (e) {
4317			e.preventDefault();
4318			var self = e.data.self;
4319			if (self._filterTimeout != null) clearTimeout(self._filterTimeout);
4320			var $icon = self.$button.children('.fooicon');
4321			if ($icon.hasClass('fooicon-remove')) self.clear();
4322			else {
4323				var query = self.$input.val();
4324				if (query.length >= self.min){
4325					if (self.exactMatch && !self._exactRegExp.test(query)){
4326						query = '"' + query + '"';
4327					}
4328					self.addFilter('search', query);
4329					self.filter(self.focus);
4330				}
4331			}
4332		},
4333		/**
4334		 * Handles the click event for the column checkboxes in the {@link FooTable.Filtering#$dropdown}.
4335		 * @instance
4336		 * @private
4337		 * @param {jQuery.Event} e - The event object for the event.
4338		 */
4339		_onSearchColumnClicked: function (e) {
4340			var self = e.data.self;
4341			if (self._filterTimeout != null) clearTimeout(self._filterTimeout);
4342			self._filterTimeout = setTimeout(function(){
4343				self._filterTimeout = null;
4344				var $icon = self.$button.children('.fooicon');
4345				if ($icon.hasClass('fooicon-remove')){
4346					$icon.removeClass('fooicon-remove').addClass('fooicon-search');
4347					self.addFilter('search', self.$input.val());
4348					self.filter();
4349				}
4350			}, self.delay);
4351		},
4352		/**
4353		 * Handles the click event for the {@link FooTable.Filtering#$dropdown} toggle.
4354		 * @instance
4355		 * @private
4356		 * @param {jQuery.Event} e - The event object for the event.
4357		 */
4358		_onDropdownToggleClicked: function (e) {
4359			e.preventDefault();
4360			e.stopPropagation();
4361			var self = e.data.self;
4362			self.$dropdown.parent().toggleClass('open');
4363			if (self.$dropdown.parent().hasClass('open')) $(document).on('click.footable', { self: self }, self._onDocumentClicked);
4364			else $(document).off('click.footable', self._onDocumentClicked);
4365		},
4366		/**
4367		 * Checks all click events when the dropdown is visible and closes the menu if the target is not the dropdown.
4368		 * @instance
4369		 * @private
4370		 * @param {jQuery.Event} e - The event object for the event.
4371		 */
4372		_onDocumentClicked: function(e){
4373			if ($(e.target).closest('.dropdown-menu').length == 0){
4374				e.preventDefault();
4375				var self = e.data.self;
4376				self.$dropdown.parent().removeClass('open');
4377				$(document).off('click.footable', self._onDocumentClicked);
4378			}
4379		}
4380	});
4381
4382	F.components.register('filtering', F.Filtering, 500);
4383
4384})(jQuery, FooTable);
4385
4386(function(F){
4387	F.Query = F.Class.extend(/** @lends FooTable.Query */{
4388		/**
4389		 * The query object is used to parse and test the filtering component's queries
4390		 * @constructs
4391		 * @extends FooTable.Class
4392		 * @param {string} query - The string value of the query.
4393		 * @param {string} [space="AND"] - How the query treats whitespace.
4394		 * @param {boolean} [connectors=true] - Whether or not to replace phrase connectors (+.-_) with spaces.
4395		 * @param {boolean} [ignoreCase=true] - Whether or not ignore case when matching.
4396		 * @returns {FooTable.Query}
4397		 */
4398		construct: function(query, space, connectors, ignoreCase){
4399			/* PRIVATE */
4400			/**
4401			 * Holds the previous value of the query and is used internally in the {@link FooTable.Query#val} method.
4402			 * @type {string}
4403			 * @private
4404			 */
4405			this._original = null;
4406			/**
4407			 * Holds the value for the query. Access to this variable is provided through the {@link FooTable.Query#val} method.
4408			 * @type {string}
4409			 * @private
4410			 */
4411			this._value = null;
4412			/* PUBLIC */
4413			/**
4414			 * A string specifying how the query treats whitespace. Can be either "OR" or "AND".
4415			 * @type {string}
4416			 */
4417			this.space = F.is.string(space) && (space == 'OR' || space == 'AND') ? space : 'AND';
4418			/**
4419			 * Whether or not to replace phrase connectors (+.-_) with spaces before executing the query.
4420			 * @instance
4421			 * @type {boolean}
4422			 */
4423			this.connectors = F.is.boolean(connectors) ? connectors : true;
4424			/**
4425			 * Whether or not ignore case when matching.
4426			 * @instance
4427			 * @type {boolean}
4428			 */
4429			this.ignoreCase = F.is.boolean(ignoreCase) ? ignoreCase : true;
4430			/**
4431			 * The left side of the query if one exists. OR takes precedence over AND.
4432			 * @type {FooTable.Query}
4433			 * @example <caption>
4433The below shows what is meant by the "left" side of a query</caption>
4434			 * query = "Dave AND Mary" - "Dave" is the left side of the query.
4435			 * query = "Dave AND Mary OR John" - "Dave and Mary" is the left side of the query.
4436			 */
4437			this.left = null;
4438			/**
4439			 * The right side of the query if one exists. OR takes precedence over AND.
4440			 * @type {FooTable.Query}
4441			 * @example <caption>The below shows what is meant by the "right" side of a query</caption>
4442			 * query = "Dave AND Mary" - "Mary" is the right side of the query.
4443			 * query = "Dave AND Mary OR John" - "John" is the right side of the query.
4444			 */
4445			this.right = null;
4446			/**
4447			 * The parsed parts of the query. This contains the information used to actually perform a match against a string.
4448			 * @type {Array}
4449			 */
4450			this.parts = [];
4451			/**
4452			 * The type of operand to apply to the results of the individual parts of the query.
4453			 * @type {string}
4454			 */
4455			this.operator = null;
4456			this.val(query);
4457		},
4458		/**
4459		 * Gets or sets the value for the query. During set the value is parsed setting all properties as required.
4460		 * @param {string} [value] - If supplied the value to set for this query.
4461		 * @returns {(string|undefined)}
4462		 */
4463		val: function(value){
4464			// get
4465			if (F.is.emptyString(value)) return this._value;
4466
4467			// set
4468			if (F.is.emptyString(this._original)) this._original = value;
4469			else if (this._original == value) return;
4470
4471			this._value = value;
4472			this._parse();
4473		},
4474		/**
4475		 * Tests the supplied string against the query.
4476		 * @param {string} str - The string to test.
4477		 * @returns {boolean}
4478		 */
4479		match: function(str){
4480			if (F.is.emptyString(this.operator) || this.operator === 'OR')
4481				return this._left(str, false) || this._match(str, false) || this._right(str, false);
4482			if (this.operator === 'AND')
4483				return this._left(str, true) && this._match(str, true) && this._right(str, true);
4484		},
4485		/**
4486		 * Matches this queries parts array against the supplied string.
4487		 * @param {string} str - The string to test.
4488		 * @param {boolean} def - The default value to return based on the operand.
4489		 * @returns {boolean}
4490		 * @private
4491		 */
4492		_match: function(str, def){
4493			var self = this, result = false, empty = F.is.emptyString(str);
4494			if (F.is.emptyArray(self.parts) && self.left instanceof F.Query) return def;
4495			if (F.is.emptyArray(self.parts)) return result;
4496			if (self.space === 'OR'){
4497				// with OR we give the str every part to test and if any match it is a success, we do exit early if a negated match occurs
4498				F.arr.each(self.parts, function(p){
4499					if (p.empty && empty){
4500						result = true;
4501						if (p.negate){
4502							result = false;
4503							return result;
4504						}
4505					} else {
4506						var match = (p.exact ? F.str.containsExact : F.str.contains)(str, p.query, self.ignoreCase);
4507						if (match && !p.negate) result = true;
4508						if (match && p.negate) {
4509							result = false;
4510							return result;
4511						}
4512					}
4513				});
4514			} else {
4515				// otherwise with AND we check until the first failure and then exit
4516				result = true;
4517				F.arr.each(self.parts, function(p){
4518					if (p.empty){
4519						if ((!empty && !p.negate) || (empty && p.negate)) result = false;
4520						return result;
4521					} else {
4522						var match = (p.exact ? F.str.containsExact : F.str.contains)(str, p.query, self.ignoreCase);
4523						if ((!match && !p.negate) || (match && p.negate)) result = false;
4524						return result;
4525					}
4526				});
4527			}
4528			return result;
4529		},
4530		/**
4531		 * Matches the left side of the query if one exists with the supplied string.
4532		 * @param {string} str - The string to test.
4533		 * @param {boolean} def - The default value to return based on the operand.
4534		 * @returns {boolean}
4535		 * @private
4536		 */
4537		_left: function(str, def){
4538			return (this.left instanceof F.Query) ? this.left.match(str) : def;
4539		},
4540		/**
4541		 * Matches the right side of the query if one exists with the supplied string.
4542		 * @param {string} str - The string to test.
4543		 * @param {boolean} def - The default value to return based on the operand.
4544		 * @returns {boolean}
4545		 * @private
4546		 */
4547		_right: function(str, def){
4548			return (this.right instanceof F.Query) ? this.right.match(str) : def;
4549		},
4550		/**
4551		 * Parses the private {@link FooTable.Query#_value} property and populates the object.
4552		 * @private
4553		 */
4554		_parse: function(){
4555			if (F.is.emptyString(this._value)) return;
4556			// OR takes precedence so test for it first
4557			if (/\sOR\s/.test(this._value)){
4558				// we have an OR so split the value on the first occurrence of OR to get the left and right sides of the statement
4559				this.operator = 'OR';
4560				var or = this._value.split(/(?:\sOR\s)(.*)?/);
4561				this.left = new F.Query(or[0], this.space, this.connectors, this.ignoreCase);
4562				this.right = new F.Query(or[1], this.space, this.connectors, this.ignoreCase);
4563			} else if (/\sAND\s/.test(this._value)) {
4564				// there are no more OR's so start with AND
4565				this.operator = 'AND';
4566				var and = this._value.split(/(?:\sAND\s)(.*)?/);
4567				this.left = new F.Query(and[0], this.space, this.connectors, this.ignoreCase);
4568				this.right = new F.Query(and[1], this.space, this.connectors, this.ignoreCase);
4569			} else {
4570				// we have no more statements to parse so set the parts array by parsing each part of the remaining query
4571				var self = this;
4572				this.parts = F.arr.map(this._value.match(/(?:[^\s"]+|"[^"]*")+/g), function(str){
4573					return self._part(str);
4574				});
4575			}
4576		},
4577		/**
4578		 * Parses a single part of a query into an object to use during matching.
4579		 * @param {string} str - The string representation of the part.
4580		 * @returns {{query: string, negate: boolean, phrase: boolean, exact: boolean}}
4581		 * @private
4582		 */
4583		_part: function(str){
4584			var p = {
4585				query: str,
4586				negate: false,
4587				phrase: false,
4588				exact: false,
4589				empty: false
4590			};
4591			// support for NEGATE operand - (minus sign). Remove this first so we can get onto phrase checking
4592			if (F.str.startsWith(p.query, '-')){
4593				p.query = F.str.from(p.query, '-');
4594				p.negate = true;
4595			}
4596			// support for PHRASES (exact matches)
4597			if (/^"(.*?)"$/.test(p.query)){ // if surrounded in quotes strip them and nothing else
4598				p.query = p.query.replace(/^"(.*?)"$/, '$1');
4599				p.phrase = true;
4600				p.exact = true;
4601			} else if (this.connectors && /(?:\w)+?([-_\+\.])(?:\w)+?/.test(p.query)) { // otherwise replace supported phrase connectors (-_+.) with spaces
4602				p.query = p.query.replace(/(?:\w)+?([-_\+\.])(?:\w)+?/g, function(match, p1){
4603					return match.replace(p1, ' ');
4604				});
4605				p.phrase = true;
4606			}
4607			p.empty = p.phrase && F.is.emptyString(p.query);
4608			return p;
4609		}
4610	});
4611
4612})(FooTable);
4613(function(F){
4614
4615	/**
4616	 * The value used by the filtering component during filter operations. Must be a string and can be set using the data-filter-value attribute on the cell itself.
4617	 * If this is not supplied it is set to the result of the toString method called on the value for the cell. Added by the {@link FooTable.Filtering} component.
4618	 * @type {string}
4619	 * @default null
4620	 */
4621	F.Cell.prototype.filterValue = null;
4622
4623	// this is used to define the filtering specific properties on cell creation
4624	F.Cell.prototype.__filtering_define__ = function(valueOrElement){
4625		this.filterValue = this.column.filterValue.call(this.column, valueOrElement);
4626	};
4627
4628	// this is used to update the filterValue property whenever the cell value is changed
4629	F.Cell.prototype.__filtering_val__ = function(value){
4630		if (F.is.defined(value)){
4631			// set only
4632			this.filterValue = this.column.filterValue.call(this.column, value);
4633		}
4634	};
4635
4636	// overrides the public define method and replaces it with our own
4637	F.Cell.extend('define', function(valueOrElement){
4638		this._super(valueOrElement);
4639		this.__filtering_define__(valueOrElement);
4640	});
4641	// overrides the public val method and replaces it with our own
4642	F.Cell.extend('val', function(value, redraw, redrawSelf){
4643		var val = this._super(value, redraw, redrawSelf);
4644		this.__filtering_val__(value);
4645		return val;
4646	});
4647})(FooTable);
4648(function($, F){
4649	/**
4650	 * Whether or not the column can be used during filtering. Added by the {@link FooTable.Filtering} component.
4651	 * @type {boolean}
4652	 * @default true
4653	 */
4654	F.Column.prototype.filterable = true;
4655
4656	/**
4657	 * This is supplied either the cell value or jQuery object to parse. A str
4657ing value must be returned from this method and will be used during filtering operations.
4658	 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
4659	 * @returns {string}
4660	 * @this FooTable.Column
4661	 */
4662	F.Column.prototype.filterValue = function(valueOrElement){
4663		// if we have an element or a jQuery object use jQuery to get the value
4664		if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){
4665			var data = $(valueOrElement).data('filterValue');
4666			return F.is.defined(data) ? ''+data : $(valueOrElement).text();
4667		}
4668		// if options are supplied with the value
4669		if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){
4670			if (F.is.string(valueOrElement.options.filterValue)) return valueOrElement.options.filterValue;
4671			if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value;
4672		}
4673		if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement+''; // use the native toString of the value
4674		return ''; // otherwise we have no value so return an empty string
4675	};
4676
4677	// this is used to define the filtering specific properties on column creation
4678	F.Column.prototype.__filtering_define__ = function(definition){
4679		this.filterable = F.is.boolean(definition.filterable) ? definition.filterable : this.filterable;
4680		this.filterValue = F.checkFnValue(this, definition.filterValue, this.filterValue);
4681	};
4682
4683	// overrides the public define method and replaces it with our own
4684	F.Column.extend('define', function(definition){
4685		this._super(definition); // call the base so we don't have to redefine any previously set properties
4686		this.__filtering_define__(definition); // then call our own
4687	});
4688})(jQuery, FooTable);
4689(function(F){
4690	/**
4691	 * An object containing the filtering options for the plugin. Added by the {@link FooTable.Filtering} component.
4692	 * @type {object}
4693	 * @prop {boolean} enabled=false - Whether or not to allow filtering on the table.
4694	 * @prop {({name: string, query: (string|FooTable.Query), columns: (Array.<string>|Array.<number>|Array.<FooTable.Column>)}|Array.<FooTable.Filter>)} filters - The filters to apply to the current {@link FooTable.Rows#array}.
4695	 * @prop {number} delay=1200 - The delay in milliseconds before the query is auto applied after a change (any value equal to or less than zero will disable this).
4696	 * @prop {number} min=1 - The minimum number of characters allowed in the search input before it is auto applied.
4697	 * @prop {string} space="AND" - Specifies how whitespace in a filter query is handled.
4698	 * @prop {string} placeholder="Search" - The string used as the placeholder for the search input.
4699	 * @prop {string} dropdownTitle=null - The title to display at the top of the search input column select.
4700	 * @prop {string} position="right" - The string used to specify the alignment of the search input.
4701	 * @prop {string} connectors=true - Whether or not to replace phrase connectors (+.-_) with space before executing the query.
4702	 * @prop {boolean} ignoreCase=true - Whether or not ignore case when matching.
4703	 * @prop {boolean} exactMatch=false - Whether or not search queries are treated as phrases when matching.
4704	 * @prop {boolean} focus=true - Whether or not to focus the search input after the search/clear button is clicked or after auto applying the search input query.
4705	 * @prop {string} container=null - A selector specifying where to place the filtering components form, if null the form is displayed within a row in the head of the table.
4706	 */
4707	F.Defaults.prototype.filtering = {
4708		enabled: false,
4709		filters: [],
4710		delay: 1200,
4711		min: 1,
4712		space: 'AND',
4713		placeholder: 'Search',
4714		dropdownTitle: null,
4715		position: 'right',
4716		connectors: true,
4717		ignoreCase: true,
4718		exactMatch: false,
4719		focus: true,
4720		container: null
4721	};
4722})(FooTable);
4723(function(F){
4724	/**
4725	 * Checks if the row is filtered using the supplied filters.
4726	 * @this FooTable.Row
4727	 * @param {Array.<FooTable.Filter>} filters - The filters to apply.
4728	 * @returns {boolean}
4729	 */
4730	F.Row.prototype.filtered = function(filters){
4731		var result = true, self = this;
4732		F.arr.each(filters, function(f){
4733			if ((result = f.matchRow(self)) == false) return false;
4734		});
4735		return result;
4736	};
4737})(FooTable);
4738(function($, F){
4739
4740	F.Sorter = F.Class.extend(/** @lends FooTable.Sorter */{
4741		/**
4742		 * The sorter object contains the column and direction to sort by.
4743		 * @constructs
4744		 * @extends FooTable.Class
4745		 * @param {FooTable.Column} column - The column to sort.
4746		 * @param {string} direction - The direction to sort by.
4747		 * @returns {FooTable.Sorter}
4748		 */
4749		construct: function(column, direction){
4750			/**
4751			 * The column to sort.
4752			 * @type {FooTable.Column}
4753			 */
4754			this.column = column;
4755			/**
4756			 * The direction to sort by.
4757			 * @type {string}
4758			 */
4759			this.direction = direction;
4760		}
4761	});
4762
4763})(jQuery, FooTable);
4764(function ($, F) {
4765	F.Sorting = F.Component.extend(/** @lends FooTable.Sorting */{
4766		/**
4767		 * The sorting component adds a small sort button to specified column headers allowing users to sort those columns in the table.
4768		 * @constructs
4769		 * @extends FooTable.Component
4770		 * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component.
4771		 * @returns {FooTable.Sorting}
4772		 */
4773		construct: function (table) {
4774			// call the constructor of the base class
4775			this._super(table, table.o.sorting.enabled);
4776
4777			/* PROTECTED */
4778			/**
4779			 * This provides a shortcut to the {@link FooTable.Table#options}.[sorting]{@link FooTable.Defaults#sorting} object.
4780			 * @instance
4781			 * @protected
4782			 * @type {object}
4783			 */
4784			this.o = table.o.sorting;
4785			/**
4786			 * The current sorted column.
4787			 * @instance
4788			 * @type {FooTable.Column}
4789			 */
4790			this.column = null;
4791			/**
4792			 * Whether or not to allow sorting to occur, should be set using the {@link FooTable.Sorting#toggleAllowed} method.
4793			 * @instance
4794			 * @type {boolean}
4795			 */
4796			this.allowed = true;
4797			/**
4798			 * The initial sort state of the table, this value is used for determining if the sorting has occurred or to reset the state to default.
4799			 * @instance
4800			 * @type {{isset: boolean, rows: Array.<FooTable.Row>, column: string, direction: ?string}}
4801			 */
4802			this.initial = null;
4803		},
4804
4805		/* PROTECTED */
4806		/**
4807		 * Checks the supplied data and options for the sorting component.
4808		 * @instance
4809		 * @protected
4810		 * @param {object} data - The jQuery data object from the parent table.
4811		 * @fires FooTable.Sorting#"preinit.ft.sorting"
4812		 * @this FooTable.Sorting
4813		 */
4814		preinit: function(data){
4815			var self = this;
4816			/**
4817			 * The preinit.ft.sorting event is raised before the UI is created and provides the tables jQuery data object for additional options parsing.
4818			 * Calling preventDefault on this event will disable the component.
4819			 * @event FooTable.Sorting#"preinit.ft.sorting"
4820			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
4821			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
4822			 * @param {object} data - The jQuery data object of the table raising the event.
4823			 */
4824			this.ft.raise('preinit.ft.sorting', [data]).then(function(){
4825				if (self.ft.$el.hasClass('footable-sorting'))
4826					self.enabled = true;
4827				self.enabled = F.is.boolean(data.sorting)
4828					? data.sorting
4829					: self.enabled;
4830				if (!self.enabled) return;
4831				self.column = F.arr.first(self.ft.columns.array, function(col){ return col.sorted; });
4832			}, function(){
4833				self.enabled = false;
4834			});
4835		},
4836		/**
4837		 * Initializes the sorting component for the plugin using the supplied table and options.
4838		 * @instance
4839		 * @protected
4840		 * @fires FooTable.Sorting#"init.ft.sorting"
4841		 * @this FooTable.Sorting
4842		 */
4843		init: function () {
4844			/**
4845			 * The init.ft.sorting event is raised before its UI is generated.
4846			 * Calling preventDefault on this event will disable the component.
4847			 * @event FooTable.Sorting#"init.ft.sorting"
4848			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
4849			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
4850			 */
4851			var self = this;
4852			this.ft.raise('init.ft.sorting').then(function(){
4853				if (!self.initial){
4854					var isset = !!self.column;
4855					self.initial = {
4856						isset: isset,
4857						// grab a shallow copy of the rows array prior to sorting - allows us to reset without an initial sort
4858						rows: self.ft.rows.all.slice(0),
4859						// if there is a sorted column store its name and direction
4860						column: isset ? self.column.name : null,
4861						direction: isset ? self.column.direction : null
4862					}
4863				}
4864				F.arr.each(self.ft.columns.array, function(col){
4865					if (col.sortable){
4866						col.$el.addClass('footable-sortable').append($('<span/>', {'class': 'fooicon fooicon-sort'}));
4867					}
4868				});
4869				self.ft.$el.on('click.footable', '.footable-sortable', { self: self }, self._onSortClicked);
4870			}, function(){
4871				self.enabled = false;
4872			});
4873		},
4874		/**
4875		 * Destroys the sorting component removing any UI generated from the table.
4876		 * @instance
4877		 * @protected
4878		 * @fires FooTable.Sorting#"destroy.ft.sorting"
4879		 */
4880		destroy: function () {
4881			/**
4882			 * The destroy.ft.sorting event is raised before its UI is removed.
4883			 * Calling preventDefault on this event will prevent the component from being destroyed.
4884			 * @event FooTable.Sorting#"destroy.ft.sorting"
4885			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
4886			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
4887			 */
4888			var self = this;
4889			this.ft.raise('destroy.ft.paging').then(function(){
4890				self.ft.$el.off('click.footable', '.footable-sortable', self._onSortClicked);
4891				self.ft.$el.children('thead').children('tr.footable-header')
4892					.children('.footable-sortable').removeClass('footable-sortable footable-asc footable-desc')
4893					.find('span.fooicon').remove();
4894			});
4895		},
4896		/**
4897		 * Performs the actual sorting against the {@link FooTable.Rows#current} array.
4898		 * @instance
4899		 * @protected
4900		 */
4901		predraw: function () {
4902			if (!this.column) return;
4903			var self = this, col = self.column;
4904			self.ft.rows.array.sort(function (a, b) {
4905				return col.direction == 'DESC'
4906						? col.sorter(b.cells[col.index].sortValue, a.cells[col.index].sortValue)
4907						: col.sorter(a.cells[col.index].sortValue, b.cells[col.index].sortValue);
4908			});
4909		},
4910		/**
4911		 * Updates the sorting UI setting the state of the sort buttons.
4912		 * @instance
4913		 * @protected
4914		 */
4915		draw: function () {
4916			if (!this.column) return;
4917			var self = this,
4918				$sortable = self.ft.$el.find('thead > tr > .footable-sortable'),
4919				$active = self.column.$el;
4920
4921			$sortable.removeClass('footable-asc footable-desc').children('.fooicon').removeClass('fooicon-sort fooicon-sort-asc fooicon-sort-desc');
4922			$sortable.not($active).children('.fooicon').addClass('fooicon-sort');
4923			$active.addClass(self.column.direction == 'DESC' ? 'footable-desc' : 'footable-asc')
4924				.children('.fooicon').addClass(self.column.direction == 'DESC' ? 'fooicon-sort-desc' : 'fooicon-sort-asc');
4925		},
4926
4927		/* PUBLIC */
4928		/**
4929		 * Sets the sorting options and calls the {@link FooTable.Table#draw} method to perform the actual sorting.
4930		 * @instance
4931		 * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by.
4932		 * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC.
4933		 * @returns {jQuery.Promise}
4934		 * @fires FooTable.Sorting#"before.ft.sorting"
4935		 * @fires FooTable.Sorting#"after.ft.sorting"
4936		 */
4937		sort: function(column, direction){
4938			return this._sort(column, direction);
4939		},
4940		/**
4941		 * Toggles whether or not sorting is currently allowed.
4942		 * @param {boolean} [state] - You can optionally specify the state you want it to be, if not supplied the current value is flipped.
4943		 */
4944		toggleAllowed: function(state){
4945			state = F.is.boolean(state) ? state : !this.allowed;
4946			this.allowed = state;
4947			this.ft.$el.toggleClass('footable-sorting-disabled', !this.allowed);
4948		},
4949		/**
4950		 * Checks whether any sorting has occurred for the table.
4951		 * @returns {boolean}
4952		 */
4953		hasChanged: function(){
4954			return !(!this.initial || !this.column ||
4955				(this.column.name === this.initial.column &&
4956					(this.column.direction === this.initial.direction || (this.initial.direction === null && this.column.direction === 'ASC')))
4957			);
4958		},
4959		/**
4960		 * Resets the table sorting to the initial state recorded in the components init method.
4961		 */
4962		reset: function(){
4963			if (!!this.initial){
4964				if (this.initial.isset){
4965					// if the initial value specified a column, sort by it
4966					this.sort(this.initial.column, this.initial.direction);
4967				} else {
4968					// if there was no initial column then we need to reset the rows to there original order
4969					if (!!this.column){
4970						// if there is a currently sorted column remove the asc/desc classes and set it to null.
4971						this.column.$el.removeClass('footable-asc footable-desc');
4972						this.column = null;
4973					}
4974					// replace the current all rows array with the one stored in the initial value
4975					this.ft.rows.all = this.initial.rows;
4976					// force the table to redraw itself using the updated rows array
4977					this.ft.draw();
4978				}
4979			}
4980		},
4981
4982		/* PRIVATE */
4983		/**
4984		 * Performs the required steps to handle sorting including the raising of the {@link FooTable.Sorting#"before.ft.sorting"} and {@link FooTable.Sorting#"after.ft.sorting"} events.
4985		 * @instance
4986		 * @private
4987		 * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by.
4988		 * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC.
4989		 * @returns {jQuery.Promise}
4990		 * @fires FooTable.Sorting#"before.ft.sorting"
4991		 * @fires FooTable.Sorting#"after.ft.sorting"
4992		 */
4993		_sort: function(column, direction){
4994			if (!this.allowed) return $.Deferred().reject('sorting disabled');
4995			var self = this;
4996			var sorter = new F.Sorter(self.ft.columns.get(column), F.Sorting.dir(direction));
4997			/**
4998			 * The before.ft.sorting event is raised before a sort is applied and allows listeners to modify the sorter or cancel it completely by calling preventDefault on the jQuery.Event object.
4999			 * @event FooTable.Sorting#"before.ft.sorting"
5000			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5001			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5002			 * @param {FooTable.Sorter} sorter - The sorter that is about to be applied.
5003			 */
5004			return self.ft.raise('before.ft.sorting', [sorter]).then(function(){
5005				F.arr.each(self.ft.columns.array, function(col){
5006					if (col != self.column) col.direction = null;
5007				});
5008				self.column = self.ft.columns.get(sorter.column);
5009				if (self.column) self.column.direction = F.Sorting.dir(sorter.direction);
5010				return self.ft.draw().then(function(){
5011					/**
5012					 * The after.ft.sorting event is raised after a sorter has been applied.
5013					 * @event FooTable.Sorting#"after.ft.sorting"
5014					 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5015					 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5016					 * @param {FooTable.Sorter} sorter - The sorter that has been applied.
5017					 */
5018					self.ft.raise('after.ft.sorting', [sorter]);
5019				});
5020			});
5021		},
5022		/**
5023		 * Handles the sort button clicked event.
5024		 * @instance
5025		 * @private
5026		 * @param {jQuery.Event} e - The event object for the event.
5027		 */
5028		_onSortClicked: function (e) {
5029			var self = e.data.self, $header = $(this).closest('th,td'),
5030				direction = $header.is('.footable-asc, .footable-desc')
5031					? ($header.hasClass('footable-desc') ? 'ASC' : 'DESC')
5032					: 'ASC';
5033			self._sort($header.index(), direction);
5034		}
5035	});
5036
5037	/**
5038	 * Checks the supplied string is a valid direction and if not returns ASC as default.
5039	 * @static
5040	 * @protected
5041	 * @param {string} str - The string to check.
5042	 */
5043	F.Sorting.dir = function(str){
5044		return F.is.string(str) && (str == 'ASC' || str == 'DESC') ? str : 'ASC';
5045	};
5046
5047	F.components.register('sorting', F.Sorting, 600);
5048
5049})(jQuery, FooTable);
5050(function(F){
5051
5052	/**
5053	 * The value used by the sorting component during sort operations. Can be set using the data-sort-value attribute on the cell itself.
5054	 * If this is not supplied it is set to the result of the toString method called on the value for the cell. Added by the {@link FooTable.Sorting} component.
5055	 * @type {string}
5056	 * @default null
5057	 */
5058	F.Cell.prototype.sortValue = null;
5059
5060	// this is used to define the sorting specific properties on cell creation
5061	F.Cell.prototype.__sorting_define__ = function(valueOrElement){
5062		this.sortValue = this.column.sortValue.call(this.column, valueOrElement);
5063	};
5064
5065	// this is used to update the sortValue property whenever the cell value is changed
5066	F.Cell.prototype.__sorting_val__ = function(value){
5067		if (F.is.defined(value)){
5068			// set only
5069			this.sortValue = this.column.sortValue.call(this.column, value);
5070		}
5071	};
5072
5073	// overrides the public define method and replaces it with our own
5074	F.Cell.extend('define', function(valueOrElement){
5075		this._super(valueOrElement);
5076		this.__sorting_define__(valueOrElement);
5077	});
5078	// overrides the public val method and replaces it with our own
5079	F.Cell.extend('val', function(value, redraw, redrawSelf){
5080		var val = this._super(value, redraw, redrawSelf);
5081		this.__sorting_val__(value);
5082		return val;
5083	});
5084})(FooTable);
5085(function($, F){
5086	/**
5087	 * The direction to sort if the {@link FooTable.Column#sorted} property is set to true. Can be "ASC", "DESC" or NULL. Added by the {@link FooTable.Sorting} component.
5088	 * @type {string}
5089	 * @default null
5090	 */
5091	F.Column.prototype.direction = null;
5092	/**
5093	 * Whether or not the column can be sorted. Added by the {@link FooTable.Sorting} component.
5094	 * @type {boolean}
5095	 * @default true
5096	 */
5097	F.Column.prototype.sortable = true;
5098	/**
5099	 * Whether or not the column is sorted. Added by the {@link FooTable.Sorting} component.
5100	 * @type {boolean}
5101	 * @default false
5102	 */
5103	F.Column.prototype.sorted = false;
5104
5105	/**
5106	 * This is supplied two values from the column for a comparison to be made and the result returned. Added by the {@link FooTable.Sorting} component.
5107	 * @param {*} a - The first value to be compared.
5108	 * @param {*} b - The second value to compare to the first.
5109	 * @returns {number}
5110	 * @example <caption>This example shows using pseudo code what a sort function would look like.</caption>
5111	 * "sorter": function(a, b){
5112	 * 	if (a is less than b by some ordering criterion) {
5113	 * 		return -1;
5114	 * 	}
5115	 * 	if (a is greater than b by the ordering criterion) {
5116	 * 		return 1;
5117	 * 	}
5118	 * 	// a must be equal to b
5119	 * 	return 0;
5120	 * }
5121	 */
5122	F.Column.prototype.sorter = function(a, b){
5123		if (typeof a === 'string') a = a.toLowerCase();
5124		if (typeof b === 'string') b = b.toLowerCase();
5125		if (a === b) return 0;
5126		if (a < b) return -1;
5127		return 1;
5128	};
5129
5130	/**
5131	 * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations.
5132	 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
5133	 * @returns {*}
5134	 * @this FooTable.Column
5135	 */
5136	F.Column.prototype.sortValue = function(valueOrElement){
5137		// if we have an element or a jQuery object use jQuery to get the value
5138		if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){
5139			var data = $(valueOrElement).data('sortValue');
5140			return F.is.defined(data) ? data : this.parser(valueOrElement);
5141		}
5142		// if options are supplied with the value
5143		if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){
5144			if (F.is.string(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue;
5145			if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value;
5146		}
5147		if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement;
5148		return null;
5149	};
5150
5151	// this is used to define the sorting specific properties on column creation
5152	F.Column.prototype.__sorting_define__ = function(definition){
5153		this.sorter = F.checkFnValue(this, definition.sorter, this.sorter);
5154		this.direction = F.is.type(definition.direction, 'string') ? F.Sorting.dir(definition.direction) : null;
5155		this.sortable = F.is.boolean(definition.sortable) ? definition.sortable : true;
5156		this.sorted = F.is.boolean(definition.sorted) ? definition.sorted : false;
5157		this.sortValue = F.checkFnValue(this, definition.sortValue, this.sortValue);
5158	};
5159
5160	// overrides the public define method and replaces it with our own
5161	F.Column.extend('define', function(definition){
5162		this._super(definition);
5163		this.__sorting_define__(definition);
5164	});
5165
5166})(jQuery, FooTable);
5167(function(F){
5168	/**
5169	 * An object containing the sorting options for the plugin. Added by the {@link FooTable.Sorting} component.
5170	 * @type {object}
5171	 * @prop {boolean} enabled=false - Whether or not to allow sorting on the table.
5172	 */
5173	F.Defaults.prototype.sorting = {
5174		enabled: false
5175	};
5176})(FooTable);
5177(function($, F){
5178
5179	F.HTMLColumn.extend('__sorting_define__', function(definition){
5180		this._super(definition);
5181		this.sortUse = F.is.string(definition.sortUse) && $.inArray(definition.sortUse, ['html','text']) !== -1 ? definition.sortUse : 'html';
5182	});
5183
5184	/**
5185	 * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations.
5186	 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
5187	 * @returns {*}
5188	 * @this FooTable.HTMLColumn
5189	 */
5190	F.HTMLColumn.prototype.sortValue = function(valueOrElement){
5191		// if we have an element or a jQuery object use jQuery to get the data value or pass it off to the parser
5192		if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){
5193			var data = $(valueOrElement).data('sortValue');
5194			return F.is.defined(data) ? data : this.parser(valueOrElement);
5195		}
5196		// if options are supplied with the value
5197		if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){
5198			if (F.is.string(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue;
5199			if (F.is.defined(valueOrElement.value)) valueOrElement = valueOrElement.value;
5200		}
5201		if (F.is.defined(valueOrElement) && valueOrElement != null) return valueOrElement;
5202		return null;
5203	};
5204
5205})(jQuery, FooTable);
5206(function($, F){
5207
5208	/**
5209	 * This is supplied either the cell value or jQuery object to parse. A value must be returned from this method and will be used during sorting operations.
5210	 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
5211	 * @returns {*}
5212	 */
5213	F.NumberColumn.prototype.sortValue = function(valueOrElement){
5214		// if we have an element or a jQuery object use jQuery to get the data value or pass it off to the parser
5215		if (F.is.element(valueOrElement) || F.is.jq(valueOrElement)){
5216			var data = $(valueOrElement).data('sortValue');
5217			return F.is.number(data) ? data : this.parser(valueOrElement);
5218		}
5219		// if options are supplied with the value
5220		if (F.is.hash(valueOrElement) && F.is.hash(valueOrElement.options)){
5221			if (F.is.string(valueOrElement.options.sortValue)) return this.parser(valueOrElement);
5222			if (F.is.number(valueOrElement.options.sortValue)) return valueOrElement.options.sortValue;
5223			if (F.is.number(valueOrElement.value)) return valueOrElement.value;
5224		}
5225		if (F.is.string(valueOrElement)) return this.parser(valueOrElement);
5226		if (F.is.number(valueOrElement)) return valueOrElement;
5227		return null;
5228	};
5229
5230})(jQuery, FooTable);
5231(function(F){
5232	/**
5233	 * Sort the table using the specified column and direction. Added by the {@link FooTable.Sorting} component.
5234	 * @instance
5235	 * @param {(string|number|FooTable.Column)} column - The column name, index or the actual {@link FooTable.Column} object to sort by.
5236	 * @param {string} [direction="ASC"] - The direction to sort by, either ASC or DESC.
5237	 * @returns {jQuery.Promise}
5238	 * @fires FooTable.Sorting#"change.ft.sorting"
5239	 * @fires FooTable.Sorting#"changed.ft.sorting"
5240	 * @see FooTable.Sorting#sort
5241	 */
5242	F.Table.prototype.sort = function(column, direction){
5243		return this.use(F.Sorting).sort(column, direction);
5244	};
5245})(FooTable);
5246(function($, F){
5247
5248	F.Pager = F.Class.extend(/** @lends FooTable.Pager */{
5249		/**
5250		 * The pager object contains the page number and direction to page to.
5251		 * @constructs
5252		 * @extends FooTable.Class
5253		 * @param {number} total - The total number of pages available.
5254		 * @param {number} current - The current page number.
5255		 * @param {number} size - The number of rows per page.
5256		 * @param {number} page - The page number to goto.
5257		 * @param {boolean} forward - A boolean indicating the direction of paging, TRUE = forward, FALSE = back.
5258		 * @returns {FooTable.Pager}
5259		 */
5260		construct: function(total, current, size, page, forward){
5261			/**
5262			 * The total number of pages available.
5263			 * @type {number}
5264			 */
5265			this.total = total;
5266			/**
5267			 * The current page number.
5268			 * @type {number}
5269			 */
5270			this.current = current;
5271			/**
5272			 * The number of rows per page.
5273			 * @type {number}
5274			 */
5275			this.size = size;
5276			/**
5277			 * The page number to goto.
5278			 * @type {number}
5279			 */
5280			this.page = page;
5281			/**
5282			 * A boolean indicating the direction of paging, TRUE = forward, FALSE = back.
5283			 * @type {boolean}
5284			 */
5285			this.forward = forward;
5286		}
5287	});
5288
5289})(jQuery, FooTable);
5290(function($, F){
5291	F.Paging = F.Component.extend(/** @lends FooTable.Paging */{
5292		/**
5293		 * The paging component adds a pagination control to the table allowing users to navigate table rows via pages.
5294		 * @constructs
5295		 * @extends FooTable.Component
5296		 * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component.
5297		 * @returns {FooTable.Filtering}
5298		 */
5299		construct: function(table){
5300			// call the base constructor
5301			this._super(table, table.o.paging.enabled);
5302
5303			/* PROTECTED */
5304			/**
5305			 * An object containing the strings used by the paging buttons.
5306			 * @type {{ first: string, prev: string, next: string, last: string }}
5307			 */
5308			this.strings = table.o.paging.strings;
5309
5310			/* PUBLIC */
5311			/**
5312			 * The current page number to display.
5313			 * @instance
5314			 * @type {number}
5315			 */
5316			this.current = table.o.paging.current;
5317			/**
5318			 * The number of rows to display per page.
5319			 * @instance
5320			 * @type {number}
5321			 */
5322			this.size = table.o.paging.size;
5323			/**
5324			 * The maximum number of page links to display at once.
5325			 * @instance
5326			 * @type {number}
5327			 */
5328			this.limit = table.o.paging.limit;
5329			/**
5330			 * The position of the pagination control within the paging rows cell.
5331			 * @instance
5332			 * @type {string}
5333			 */
5334			this.position = table.o.paging.position;
5335			/**
5336			 * The format string used to generate the text displayed under the pagination control.
5337			 * @instance
5338			 * @type {string}
5339			 */
5340			this.countFormat = table.o.paging.countFormat;
5341			/**
5342			 * A selector specifying where to place the paging components UI, if null the UI is displayed within a row in the foot of the table.
5343			 * @instance
5344			 * @type {string}
5345			 */
5346			this.container = table.o.paging.container;
5347				/**
5348			 * The total number of pages.
5349			 * @instance
5350			 * @type {number}
5351			 */
5352			this.total = -1;
5353			/**
5354			 * The number of rows in the {@link FooTable.Rows#array} before paging is applied.
5355			 * @instance
5356			 * @type {number}
5357			 */
5358			this.totalRows = 0;
5359			/**
5360			 * A number indicating the previous page displayed.
5361			 * @instance
5362			 * @type {number}
5363			 */
5364			this.previous = -1;
5365			/**
5366			 * The count string generated using the {@link FooTable.Filtering#countFormat} option. This value is only set after the first call to the {@link FooTable.Filtering#predraw} method.
5367			 * @instance
5368			 * @type {string}
5369			 */
5370			this.formattedCount = null;
5371			/**
5372			 * The jQuery object of the element containing the entire paging UI.
5373			 * @instance
5374			 * @type {jQuery}
5375			 */
5376			this.$container = null;
5377			/**
5378			 * The jQuery object of the element wrapping all the paging UI elements.
5379			 * @instance
5380			 * @type {jQuery}
5381			 */
5382			this.$wrapper = null;
5383			/** +
5384			 * The jQuery row object that contains all the paging specific elements.
5385			 * @instance
5386			 * @type {jQuery}
5387			 */
5388			this.$row = null;
5389			/**
5390			 * The jQuery cell object that contains the pagination control and total count.
5391			 * @instance
5392			 * @type {jQuery}
5393			 */
5394			this.$cell = null;
5395			/**
5396			 * The jQuery object that contains the links for the pagination control.
5397			 * @instance
5398			 * @type {jQuery}
5399			 */
5400			this.$pagination = null;
5401			/**
5402			 * The jQuery object that contains the row count.
5403			 * @instance
5404			 * @type {jQuery}
5405			 */
5406			this.$count = null;
5407			/**
5408			 * Whether or not the pagination row is detached from the table.
5409			 * @instance
5410			 * @type {boolean}
5411			 */
5412			this.detached = true;
5413
5414			/* PRIVATE */
5415			/**
5416			 * Used to hold the number of page links created.
5417			 * @instance
5418			 * @type {number}
5419			 * @private
5420			 */
5421			this._createdLinks = 0;
5422		},
5423
5424		/* PROTECTED */
5425		/**
5426		 * Checks the supplied data and options for the paging component.
5427		 * @instance
5428		 * @protected
5429		 * @param {object} data - The jQuery data object from the parent table.
5430		 * @fires FooTable.Paging#"preinit.ft.paging"
5431		 */
5432		preinit: function(data){
5433			var self = this;
5434			/**
5435			 * The preinit.ft.paging event is raised before the UI is created and provides the tables jQuery data object for additional options parsing.
5436			 * Calling preventDefault on this event will disable the component.
5437			 * @event FooTable.Paging#"preinit.ft.paging"
5438			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5439			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5440			 * @param {object} data - The jQuery data object of the table raising the event.
5441			 */
5442			this.ft.raise('preinit.ft.paging', [data]).then(function(){
5443				if (self.ft.$el.hasClass('footable-paging'))
5444					self.enabled = true;
5445				self.enabled = F.is.boolean(data.paging)
5446					? data.paging
5447					: self.enabled;
5448
5449				if (!self.enabled) return;
5450
5451				self.size = F.is.number(data.pagingSize)
5452					? data.pagingSize
5453					: self.size;
5454
5455				self.current = F.is.number(data.pagingCurrent)
5456					? data.pagingCurrent
5457					: self.current;
5458
5459				self.limit = F.is.number(data.pagingLimit)
5460					? data.pagingLimit
5461					: self.limit;
5462
5463				if (self.ft.$el.hasClass('footable-paging-left'))
5464					self.position = 'left';
5465				if (self.ft.$el.hasClass('footable-paging-center'))
5466					self.position = 'center';
5467				if (self.ft.$el.hasClass('footable-paging-right'))
5468					self.position = 'right';
5469
5470				self.position = F.is.string(data.pagingPosition)
5471					? data.pagingPosition
5472					: self.position;
5473
5474				self.countFormat = F.is.string(data.pagingCountFormat)
5475					? data.pagingCountFormat
5476					: self.countFormat;
5477
5478				self.container = F.is.string(data.pagingContainer)
5479					? data.pagingContainer
5480					: self.container;
5481
5482				self.total = Math.ceil(self.ft.rows.all.length / self.size);
5483			}, function(){
5484				self.enabled = false;
5485			});
5486		},
5487		/**
5488		 * Initializes the paging component for the plugin using the supplied table and options.
5489		 * @instance
5490		 * @protected
5491		 * @fires FooTable.Paging#"init.ft.paging"
5492		 */
5493		init: function(){
5494			/**
5495			 * The init.ft.paging event is raised before its UI is generated.
5496			 * Calling preventDefault on this event will disable the component.
5497			 * @event FooTable.Paging#"init.ft.paging"
5498			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5499			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5500			 */
5501			var self = this;
5502			this.ft.raise('init.ft.paging').then(function(){
5503				self.$create();
5504			}, function(){
5505				self.enabled = false;
5506			});
5507		},
5508		/**
5509		 * Destroys the paging component removing any UI generated from the table.
5510		 * @instance
5511		 * @protected
5512		 * @fires FooTable.Paging#"destroy.ft.paging"
5513		 */
5514		destroy: function () {
5515			/**
5516			 * The destroy.ft.paging event is raised before its UI is removed.
5517			 * Calling preventDefault on this event will prevent the component from being destroyed.
5518			 * @event FooTable.Paging#"destroy.ft.paging"
5519			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5520			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5521			 */
5522			var self = this;
5523			this.ft.raise('destroy.ft.paging').then(function(){
5524				self.ft.$el.removeClass('footable-paging')
5525					.find('tfoot > tr.footable-paging').remove();
5526				self.detached = true;
5527				self._createdLinks = 0;
5528			});
5529		},
5530		/**
5531		 * Performs the actual paging against the {@link FooTable.Rows#current} array removing all rows that are not on the current visible page.
5532		 * @instance
5533		 * @protected
5534		 */
5535		predraw: function(){
5536			this.total = Math.ceil(this.ft.rows.array.length / this.size);
5537			this.current = this.current > this.total ? this.total : (this.current < 1 ? 1 : this.current);
5538			this.totalRows = this.ft.rows.array.length;
5539			if (this.totalRows > this.size){
5540				this.ft.rows.array = this.ft.rows.array.splice((this.current - 1) * this.size, this.size);
5541			}
5542			this.formattedCount = this.format(this.countFormat);
5543		},
5544		/**
5545		 * Updates the paging UI setting the state of the pagination control.
5546		 * @instance
5547		 * @protected
5548		 */
5549		draw: function(){
5550			if (this.total <= 1){
5551				if (!this.detached){
5552					if (this.$row){
5553						this.$row.detach();
5554					} else {
5555						this.$wrapper.detach();
5556					}
5557					this.detached = true;
5558				}
5559			} else {
5560				if (this.detached){
5561					if (this.$row){
5562						var $tfoot = this.ft.$el.children('tfoot');
5563						if ($tfoot.length == 0){
5564							$tfoot = $('<tfoot/>');
5565							this.ft.$el.append($tfoot);
5566						}
5567						this.$row.appendTo($tfoot);
5568					} else {
5569						this.$wrapper.appendTo(this.$container);
5570					}
5571					this.detached = false;
5572				}
5573				if (F.is.jq(this.$cell)){
5574					this.$cell.attr('colspan', this.ft.columns.visibleColspan);
5575				}
5576				this._createLinks();
5577				this._setVisible(this.current, this.current > this.previous);
5578				this._setNavigation(true);
5579				this.$count.text(this.formattedCount);
5580			}
5581		},
5582		/**
5583		 * Creates the paging UI from the current options setting the various jQuery properties of this component.
5584		 * @instance
5585		 * @protected
5586		 */
5587		$create: function(){
5588			this._createdLinks = 0;
5589			var position = 'footable-paging-center';
5590			switch (this.position){
5591				case 'left': position = 'footable-paging-left'; break;
5592				case 'right': position = 'footable-paging-right'; break;
5593			}
5594			this.ft.$el.addClass('footable-paging').addClass(position);
5595
5596			this.$container = this.container === null ? null : $(this.container).first();
5597			if (!F.is.jq(this.$container)){
5598				var $tfoot = this.ft.$el.children('tfoot');
5599				if ($tfoot.length == 0){
5600					$tfoot = $('<tfoot/>');
5601					this.ft.$el.append($tfoot);
5602				}
5603				// add it to a row and then populate it with the search input and column selector dropdown.
5604				this.$row = $('<tr/>', {'class': 'footable-paging'}).prependTo($tfoot);
5605				this.$container = this.$cell = $('<td/>').attr('colspan', this.ft.columns.visibleColspan).appendTo(this.$row);
5606			} else {
5607				this.$container.addClass('footable-paging-external').addClass(position);
5608			}
5609			this.$wrapper = $('<div/>', {'class': 'footable-pagination-wrapper'}).appendTo(this.$container);
5610			this.$pagination = $('<ul/>', { 'class': 'pagination' }).on('click.footable', 'a.footable-page-link', { self: this }, this._onPageClicked);
5611			this.$count = $('<span/>', { 'class': 'label label-default' });
5612			this.$wrapper.append(this.$pagination, $('<div/>', {'class': 'divider'}), this.$count);
5613			this.detached = false;
5614		},
5615
5616		/* PUBLIC */
5617		/**
5618		 * @summary Uses the supplied format string and replaces the placeholder strings with the current values.
5619		 * @description This method is used to generate the short description label for the pagination control. i.e. Showing X of Y records. The placeholders for this string are the following:
5620		 * * {CP} - The current page number.
5621		 * * {TP} - The total number of pages.
5622		 * * {PF} - The first row of the current page.
5623		 * * {PL} - The last row of the current page.
5624		 * * {TR} - The total rows available.
5625		 * These placeholders can be supplied in a string like; "Showing {PF} to {PL} of {TR} rows."
5626		 * @param {string} formatString - The string to be formatted with the paging specific variables.
5627		 * @returns {string}
5628		 */
5629		format: function(formatString){
5630			var firstRow = (this.size * (this.current - 1)) + 1,
5631				lastRow = this.size * this.current;
5632			if (this.ft.rows.array.length == 0){
5633				firstRow = 0;
5634				lastRow = 0;
5635			} else {
5636				lastRow = lastRow > this.totalRows ? this.totalRows : lastRow;
5637			}
5638			return formatString.replace(/\{CP}/g, this.current)
5639				.replace(/\{TP}/g, this.total)
5640				.replace(/\{PF}/g, firstRow)
5641				.replace(/\{PL}/g, lastRow)
5642				.replace(/\{TR}/g, this.totalRows);
5643		},
5644		/**
5645		 * Pages to the first page.
5646		 * @instance
5647		 * @returns {jQuery.Promise}
5648		 * @fires FooTable.Paging#"before.ft.paging"
5649		 * @fires FooTable.Paging#"after.ft.paging"
5650		 */
5651		first: function(){
5652			return this._set(1);
5653		},
5654		/**
5655		 * Pages to the previous page.
5656		 * @instance
5657		 * @returns {jQuery.Promise}
5658		 * @fires FooTable.Paging#"before.ft.paging"
5659		 * @fires FooTable.Paging#"after.ft.paging"
5660		 */
5661		prev: function(){
5662			return this._set(this.current - 1 > 0 ? this.current - 1 : 1);
5663		},
5664		/**
5665		 * Pages to the next page.
5666		 * @instance
5667		 * @returns {jQuery.Promise}
5668		 * @fires FooTable.Paging#"before.ft.paging"
5669		 * @fires FooTable.Paging#"after.ft.paging"
5670		 */
5671		next: function(){
5672			return this._set(this.current + 1 < this.total ? this.current + 1 : this.total);
5673		},
5674		/**
5675		 * Pages to the last page.
5676		 * @instance
5677		 * @returns {jQuery.Promise}
5678		 * @fires FooTable.Paging#"before.ft.paging"
5679		 * @fires FooTable.Paging#"after.ft.paging"
5680		 */
5681		last: function(){
5682			return this._set(this.total);
5683		},
5684		/**
5685		 * Pages to the specified page.
5686		 * @instance
5687		 * @param {number} page - The page number to go to.
5688		 * @returns {jQuery.Promise}
5689		 * @fires FooTable.Paging#"before.ft.paging"
5690		 * @fires FooTable.Paging#"after.ft.paging"
5691		 */
5692		goto: function(page){
5693			return this._set(page > this.total ? this.total : (page < 1 ? 1 : page));
5694		},
5695		/**
5696		 * Shows the previous X number of pages in the pagination control where X is the value set by the {@link FooTable.Defaults#paging} - limit option value.
5697		 * @instance
5698		 */
5699		prevPages: function(){
5700			var page = this.$pagination.children('li.footable-page.visible:first').data('page') - 1;
5701			this._setVisible(page, true);
5702			this._setNavigation(false);
5703		},
5704		/**
5705		 * Shows the next X number of pages in the pagination control where X is the value set by the {@link FooTable.Defaults#paging} - limit option value.
5706		 * @instance
5707		 */
5708		nextPages: function(){
5709			var page = this.$pagination.children('li.footable-page.visible:last').data('page') + 1;
5710			this._setVisible(page, false);
5711			this._setNavigation(false);
5712		},
5713		/**
5714		 * Gets or sets the current page size.
5715		 * @instance
5716		 * @param {(number|string)} [value] - The new page size to use, this value is supplied to `parseInt` so strings can be used. If not supplied or an invalid valid the current page size is returned.
5717		 * @returns {(number|undefined)}
5718		 */
5719		pageSize: function(value){
5720			value = parseInt(value);
5721			if (isNaN(value)){
5722				return this.size;
5723			}
5724			this.size = value;
5725			this.total = Math.ceil(this.ft.rows.all.length / this.size);
5726			if (F.is.jq(this.$wrapper)){
5727				if (this.$container.is("td")){
5728					this.$row.remove();
5729				} else {
5730					this.$wrapper.remove();
5731				}
5732			}
5733			this.$create();
5734			this.ft.draw();
5735		},
5736
5737		/* PRIVATE */
5738		/**
5739		 * Performs the required steps to handle paging including the raising of the {@link FooTable.Paging#"before.ft.paging"} and {@link FooTable.Paging#"after.ft.paging"} events.
5740		 * @instance
5741		 * @private
5742		 * @param {number} page - The page to set.
5743		 * @returns {jQuery.Promise}
5744		 * @fires FooTable.Paging#"before.ft.paging"
5745		 * @fires FooTable.Paging#"after.ft.paging"
5746		 */
5747		_set: function(page){
5748			var self = this,
5749				pager = new F.Pager(self.total, self.current, self.size, page, page > self.current);
5750			/**
5751			 * The before.ft.paging event is raised before a sort is applied and allows listeners to modify the pager or cancel it completely by calling preventDefault on the jQuery.Event object.
5752			 * @event FooTable.Paging#"before.ft.paging"
5753			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5754			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5755			 * @param {FooTable.Pager} pager - The pager that is about to be applied.
5756			 */
5757			return self.ft.raise('before.ft.paging', [pager]).then(function(){
5758				pager.page = pager.page > pager.total ? pager.total	: pager.page;
5759				pager.page = pager.page < 1 ? 1 : pager.page;
5760				if (self.current == page) return $.when();
5761				self.previous = self.current;
5762				self.current = pager.page;
5763				return self.ft.draw().then(function(){
5764					/**
5765					 * The after.ft.paging event is raised after a pager has been applied.
5766					 * @event FooTable.Paging#"after.ft.paging"
5767					 * @param {jQuery.Event} e - The jQuery.Event object for the event.
5768					 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
5769					 * @param {FooTable.Pager} pager - The pager that has been applied.
5770					 */
5771					self.ft.raise('after.ft.paging', [pager]);
5772				});
5773			});
5774		},
5775		/**
5776		 * Creates the pagination links using the current state of the plugin. If the total number of pages is the same as
5777		 * the last time this function was executed it does nothing.
5778		 * @instance
5779		 * @private
5780		 */
5781		_createLinks: function(){
5782			if (this._createdLinks === this.total) return;
5783			var self = this,
5784				multiple = self.total > 1,
5785				link = function(attr, html, klass){
5786					return $('<li/>', {
5787						'class': klass
5788					}).attr('data-page', attr)
5789						.append($('<a/>', {
5790							'class': 'footable-page-link',
5791							href: '#'
5792						}).data('page', attr).html(html));
5793				};
5794			self.$pagination.empty();
5795			if (multiple) {
5796				self.$pagination.append(link('first', self.strings.first, 'footable-page-nav'));
5797				self.$pagination.append(link('prev', self.strings.prev, 'footable-page-nav'));
5798				if (self.limit > 0 && self.limit < self.total){
5799					self.$pagination.append(link('prev-limit', self.strings.prevPages, 'footable-page-nav'));
5800				}
5801			}
5802			for (var i = 0, $li; i < self.total; i++){
5803				$li = link(i + 1, i + 1, 'footable-page');
5804				self.$pagination.append($li);
5805			}
5806			if (multiple){
5807				if (self.limit > 0 && self.limit < self.total){
5808					self.$pagination.append(link('next-limit', self.strings.nextPages, 'footable-page-nav'));
5809				}
5810				self.$pagination.append(link('next', self.strings.next, 'footable-page-nav'));
5811				self.$pagination.append(link('last', self.strings.last, 'footable-page-nav'));
5812			}
5813			self._createdLinks = self.total;
5814		},
5815		/**
5816		 * Sets the state for the navigation links of the pagination control and optionally sets the active class state on the current page link.
5817		 * @instance
5818		 * @private
5819		 * @param {boolean} active - Whether or not to set the active class state on the individual page links.
5820		 */
5821		_setNavigation: function(active){
5822			if (this.current == 1) {
5823				this.$pagination.children('li[data-page="first"],li[data-page="prev"]').addClass('disabled');
5824			} else {
5825				this.$pagination.children('li[data-page="first"],li[data-page="prev"]').removeClass('disabled');
5826			}
5827
5828			if (this.current == this.total) {
5829				this.$pagination.children('li[data-page="next"],li[data-page="last"]').addClass('disabled');
5830			} else {
5831				this.$pagination.children('li[data-page="next"],li[data-page="last"]').removeClass('disabled');
5832			}
5833
5834			if ((this.$pagination.children('li.footable-page.visible:first').data('page') || 1) == 1) {
5835				this.$pagination.children('li[data-page="prev-limit"]').addClass('disabled');
5836			} else {
5837				this.$pagination.children('li[data-page="prev-limit"]').removeClass('disabled');
5838			}
5839
5840			if ((this.$pagination.children('li.footable-page.visible:last').data('page') || this.limit) == this.total) {
5841				this.$pagination.children('li[data-page="next-limit"]').addClass('disabled');
5842			} else {
5843				this.$pagination.children('li[data-page="next-limit"]').removeClass('disabled');
5844			}
5845
5846			if (this.limit > 0 && this.total < this.limit){
5847				this.$pagination.children('li[data-page="prev-limit"],li[data-page="next-limit"]').css('display', 'none');
5848			} else {
5849				this.$pagination.children('li[data-page="prev-limit"],li[data-page="next-limit"]').css('display', '');
5850			}
5851
5852			if (active){
5853				this.$pagination.children('li.footable-page').removeClass('active').filter('li[data-page="' + this.current + '"]').addClass('active');
5854			}
5855		},
5856		/**
5857		 * Sets the visible page using the supplied parameters.
5858		 * @instance
5859		 * @private
5860		 * @param {number} page - The page to make visible.
5861		 * @param {boolean} right - If set to true the supplied page will be the right most visible pagination link.
5862		 */
5863		_setVisible: function(page, right){
5864			if (this.limit > 0 && this.total > this.limit){
5865				if (!this.$pagination.children('li.footable-page[data-page="'+page+'"]').hasClass('visible')){
5866					var start = 0, end = 0;
5867					if (right == true){
5868						end = page > this.total ? this.total : page;
5869						start = end - this.limit;
5870					} else {
5871						start = page < 1 ? 0 : page - 1;
5872						end = start + this.limit;
5873					}
5874					if (start < 0){
5875						start = 0;
5876						end = this.limit > this.total ? this.total : this.limit;
5877					}
5878					if (end > this.total){
5879						end = this.total;
5880						start = this.total - this.limit < 0 ? 0 : this.total - this.limit;
5881					}
5882					this.$pagination.children('li.footable-page').removeClass('visible').slice(start, end).addClass('visible');
5883				}
5884			} else {
5885				this.$pagination.children('li.footable-page').removeClass('visible').slice(0, this.total).addClass('visible');
5886			}
5887		},
5888		/**
5889		 * Handles the click event for all links in the pagination control.
5890		 * @instance
5891		 * @private
5892		 * @param {jQuery.Event} e - The event object for the event.
5893		 */
5894		_onPageClicked: function(e){
5895			e.preventDefault();
5896			if ($(e.target).closest('li').is('.active,.disabled')) return;
5897
5898			var self = e.data.self, page = $(this).data('page');
5899			switch(page){
5900				case 'first': self.first();
5901					return;
5902				case 'prev': self.prev();
5903					return;
5904				case 'next': self.next();
5905					return;
5906				case 'last': self.last();
5907					return;
5908				case 'prev-limit': self.prevPages();
5909					return;
5910				case 'next-limit': self.nextPages();
5911					return;
5912				default: self._set(page);
5913					return;
5914			}
5915		}
5916	});
5917
5918	F.components.register('paging', F.Paging, 400);
5919
5920})(jQuery, FooTable);
5921(function(F){
5922	/**
5923	 * An object containing the paging options for the plugin. Added by the {@link FooTable.Paging} component.
5924	 * @type {object}
5925	 * @prop {boolean} enabled=false - Whether or not to allow paging on the table.
5926	 * @prop {string} countFormat="{CP} of {TP}" - A string format used to generate the page count text.
5927	 * @prop {number} current=1 - The page number to display.
5928	 * @prop {number} limit=5 - The maximum number of page links to display at once.
5929	 * @prop {string} position="center" - The string used to specify the alignment of the pagination control.
5930	 * @prop {number} size=10 - The number of rows displayed per page.
5931	 * @prop {string} container=null - A selector specifying where to place the paging components UI, if null the UI is displayed within a row in the foot of the table.
5932	 * @prop {object} strings - An object containing the strings used by the paging buttons.
5933	 * @prop {string} strings.first="&laquo;" - The string used for the 'first' button.
5934	 * @prop {string} strings.prev="&lsaquo;" - The string used for the 'previous' button.
5935	 * @prop {string} strings.next="&rsaquo;" - The string used for the 'next' button.
5936	 * @prop {string} strings.last="&raquo;" - The string used for the 'last' button.
5937	 * @prop {string} strings.prevPages="..." - The string used for the 'previous X pages' button.
5938	 * @prop {string} strings.nextPages="..." - The string used for the 'next X pages' button.
5939	 */
5940	F.Defaults.prototype.paging = {
5941		enabled: false,
5942		countFormat: '{CP} of {TP}',
5943		current: 1,
5944		limit: 5,
5945		position: 'center',
5946		size: 10,
5947		container: null,
5948		strings: {
5949			first: '&laquo;',
5950			prev: '&lsaquo;',
5951			next: '&rsaquo;',
5952			last: '&raquo;',
5953			prevPages: '...',
5954			nextPages: '...'
5955		}
5956	};
5957})(FooTable);
5958(function(F){
5959	/**
5960	 * Navigates to the specified page number. Added by the {@link FooTable.Paging} component.
5961	 * @instance
5962	 * @param {number} num - The page number to go to.
5963	 * @returns {jQuery.Promise}
5964	 * @fires FooTable.Paging#paging_changing
5965	 * @fires FooTable.Paging#paging_changed
5966	 * @see FooTable.Paging#goto
5967	 */
5968	F.Table.prototype.gotoPage = function(num){
5969		return this.use(F.Paging).goto(num);
5970	};
5971
5972	/**
5973	 * Navigates to the next page. Added by the {@link FooTable.Paging} component.
5974	 * @instance
5975	 * @returns {jQuery.Promise}
5976	 * @fires FooTable.Paging#paging_changing
5977	 * @fires FooTable.Paging#paging_changed
5978	 * @see FooTable.Paging#next
5979	 */
5980	F.Table.prototype.nextPage = function(){
5981		return this.use(F.Paging).next();
5982	};
5983
5984	/**
5985	 * Navigates to the previous page. Added by the {@link FooTable.Paging} component.
5986	 * @instance
5987	 * @returns {jQuery.Promise}
5988	 * @fires FooTable.Paging#paging_changing
5989	 * @fires FooTable.Paging#paging_changed
5990	 * @see FooTable.Paging#prev
5991	 */
5992	F.Table.prototype.prevPage = function(){
5993		return this.use(F.Paging).prev();
5994	};
5995
5996	/**
5997	 * Navigates to the first page. Added by the {@link FooTable.Paging} component.
5998	 * @instance
5999	 * @returns {jQuery.Promise}
6000	 * @fires FooTable.Paging#paging_changing
6001	 * @fires FooTable.Paging#paging_changed
6002	 * @see FooTable.Paging#first
6003	 */
6004	F.Table.prototype.firstPage = function(){
6005		return this.use(F.Paging).first();
6006	};
6007
6008	/**
6009	 * Navigates to the last page. Added by the {@link FooTable.Paging} component.
6010	 * @instance
6011	 * @returns {jQuery.Promise}
6012	 * @fires FooTable.Paging#paging_changing
6013	 * @fires FooTable.Paging#paging_changed
6014	 * @see FooTable.Paging#last
6015	 */
6016	F.Table.prototype.lastPage = function(){
6017		return this.use(F.Paging).last();
6018	};
6019
6020	/**
6021	 * Shows the next X number of pages in the pagination control where X is the value set by the {@link FooTable.Defaults#paging} - limit.size option value. Added by the {@link FooTable.Paging} component.
6022	 * @instance
6023	 * @see FooTable.Paging#nextPages
6024	 */
6025	F.Table.prototype.nextPages = function(){
6026		return this.use(F.Paging).nextPages();
6027	};
6028
6029	/**
6030	 * Shows the previous X number of pages in the pagination control where X is the value set by the {@link FooTable.Defaults#paging} - limit.size option value. Added by the {@link FooTable.Paging} component.
6031	 * @instance
6032	 * @see FooTable.Paging#prevPages
6033	 */
6034	F.Table.prototype.prevPages = function(){
6035		return this.use(F.Paging).prevPages();
6036	};
6037
6038	/**
6039	 * Gets or sets the current page size
6040	 * @instance
6041	 * @param {number} [value] - The new page size to use.
6042	 * @returns {(number|undefined)}
6043	 * @see FooTable.Paging#pageSize
6044	 */
6045	F.Table.prototype.pageSize = function(value){
6046		return this.use(F.Paging).pageSize(value);
6047	};
6048})(FooTable);
6049(function($, F){
6050
6051	F.Editing = F.Component.extend(/** @lends FooTable.Editing */{
6052		/**
6053		 * The editing component adds a column with edit and delete buttons to each row as well as a single add row button in the footer.
6054		 * @constructs
6055		 * @extends FooTable.Component
6056		 * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component.
6057		 * @returns {FooTable.Editing}
6058		 */
6059		construct: function(table){
6060			// call the base constructor
6061			this._super(table, table.o.editing.enabled);
6062
6063			/**
6064			 * Whether or not to automatically page to a new row when it is added to the table.
6065			 * @type {boolean}
6066			 */
6067			this.pageToNew = table.o.editing.pageToNew;
6068
6069			/**
6070			 * Whether or not the editing column and add row button are always visible.
6071			 * @type {boolean}
6072			 */
6073			this.alwaysShow = table.o.editing.alwaysShow;
6074
6075			/**
6076			 * The options for the editing column. @see {@link FooTable.EditingColumn} for more info.
6077			 * @type {object}
6078			 * @prop {string} classes="footable-editing" - A space separated string of class names to apply to all cells in the column.
6079			 * @prop {string} name="editing" - The name of the column.
6080			 * @prop {string} title="" - The title displayed in the header row of the table for the column.
6081			 * @prop {boolean} filterable=false - Whether or not the column should be filterable when using the filtering component.
6082			 * @prop {boolean} sortable=false - Whether or not the column should be sortable when using the sorting component.
6083			 */
6084			this.column = $.extend(true, {}, table.o.editing.column, {visible: this.alwaysShow});
6085
6086			/**
6087			 * The position of the editing column in the table as well as the alignment of the buttons.
6088			 * @type {string}
6089			 */
6090			this.position = table.o.editing.position;
6091
6092
6093			/**
6094			 * The text that appears in the show button. This can contain HTML.
6095			 * @type {string}
6096			 */
6097			this.showText = table.o.editing.showText;
6098
6099			/**
6100			 * The text that appears in the hide button. This can contain HTML.
6101			 * @type {string}
6102			 */
6103			this.hideText = table.o.editing.hideText;
6104
6105			/**
6106			 * The text that appears in the add button. This can contain HTML.
6107			 * @type {string}
6108			 */
6109			this.addText = table.o.editing.addText;
6110
6111			/**
6112			 * The text that appears in the edit button. This can contain HTML.
6113			 * @type {string}
6114			 */
6115			this.editText = table.o.editing.editText;
6116
6117			/**
6118			 * The text that appears in the delete button. This can contain HTML.
6119			 * @type {string}
6120			 */
6121			this.deleteText = table.o.editing.deleteText;
6122
6123			/**
6124			 * The text that appears in the view button. This can contain HTML.
6125			 * @type {string}
6126			 */
6127			this.viewText = table.o.editing.viewText;
6128
6129			/**
6130			 * Whether or not to show the Add Row button.
6131			 * @type {boolean}
6132			 */
6133			this.allowAdd = table.o.editing.allowAdd;
6134
6135			/**
6136			 * Whether or not to show the Edit Row button.
6137			 * @type {boolean}
6138			 */
6139			this.allowEdit = table.o.editing.allowEdit;
6140
6141			/**
6142			 * Whether or not to show the Delete Row button.
6143			 * @type {boolean}
6144			 */
6145			this.allowDelete = table.o.editing.allowDelete;
6146
6147			/**
6148			 * Whether or not to show the View Row button.
6149			 * @type {boolean}
6150			 */
6151			this.allowView = table.o.editing.allowView;
6152
6153			/**
6154			 * Caches the row button elements to help with performance.
6155			 * @type {(null|jQuery)}
6156			 * @private
6157			 */
6158			this._$buttons = null;
6159
6160			/**
6161			 * This object is used to contain the callbacks for the add, edit and delete row buttons.
6162			 * @type {object}
6163			 * @prop {function} addRow
6164			 * @prop {function} editRow
6165			 * @prop {function} deleteRow
6166			 * @prop {function} viewRow
6167			 */
6168			this.callbacks = {
6169				addRow: F.checkFnValue(this, table.o.editing.addRow),
6170				editRow: F.checkFnValue(this, table.o.editing.editRow),
6171				deleteRow: F.checkFnValue(this, table.o.editing.deleteRow),
6172				viewRow: F.checkFnValue(this, table.o.editing.viewRow)
6173			};
6174		},
6175		/* PROTECTED */
6176		/**
6177		 * Checks the supplied data and options for the editing component.
6178		 * @instance
6179		 * @protected
6180		 * @param {object} data - The jQuery data object from the parent table.
6181		 * @fires FooTable.Editing#"preinit.ft.editing"
6182		 */
6183		preinit: function(data){
6184			var self = this;
6185			/**
6186			 * The preinit.ft.editing event is raised before the UI is created and provides the tables jQuery data object for additional options parsing.
6187			 * Calling preventDefault on this event will disable the component.
6188			 * @event FooTable.Editing#"preinit.ft.editing"
6189			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6190			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6191			 * @param {object} data - The jQuery data object of the table raising the event.
6192			 */
6193			this.ft.raise('preinit.ft.editing', [data]).then(function(){
6194				if (self.ft.$el.hasClass('footable-editing'))
6195					self.enabled = true;
6196
6197				self.enabled = F.is.boolean(data.editing)
6198					? data.editing
6199					: self.enabled;
6200
6201				if (!self.enabled) return;
6202
6203				self.pageToNew = F.is.boolean(data.editingPageToNew) ? data.editingPageToNew : self.pageToNew;
6204
6205				self.alwaysShow = F.is.boolean(data.editingAlwaysShow) ? data.editingAlwaysShow : self.alwaysShow;
6206
6207				self.position = F.is.string(data.editingPosition) ? data.editingPosition : self.position;
6208
6209				self.showText = F.is.string(data.editingShowText) ? data.editingShowText : self.showText;
6210
6211				self.hideText = F.is.string(data.editingHideText) ? data.editingHideText : self.hideText;
6212
6213				self.addText = F.is.string(data.editingAddText) ? data.editingAddText : self.addText;
6214
6215				self.editText = F.is.string(data.editingEditText) ? data.editingEditText : self.editText;
6216
6217				self.deleteText = F.is.string(data.editingDeleteText) ? data.editingDeleteText : self.deleteText;
6218
6219				self.viewText = F.is.string(data.editingViewText) ? data.editingViewText : self.viewText;
6220
6221				self.allowAdd = F.is.boolean(data.editingAllowAdd) ? data.editingAllowAdd : self.allowAdd;
6222
6223				self.allowEdit = F.is.boolean(data.editingAllowEdit) ? data.editingAllowEdit : self.allowEdit;
6224
6225				self.allowDelete = F.is.boolean(data.editingAllowDelete) ? data.editingAllowDelete : self.allowDelete;
6226
6227				self.allowView = F.is.boolean(data.editingAllowView) ? data.editingAllowView : self.allowView;
6228
6229				self.column = new F.EditingColumn(self.ft, self, $.extend(true, {}, self.column, data.editingColumn, {visible: self.alwaysShow}));
6230
6231				if (self.ft.$el.hasClass('footable-editing-left'))
6232					self.position = 'left';
6233
6234				if (self.ft.$el.hasClass('footable-editing-right'))
6235					self.position = 'right';
6236
6237				if (self.position === 'right'){
6238					self.column.index = self.ft.columns.array.length;
6239				} else {
6240					self.column.index = 0;
6241					for (var i = 0, len = self.ft.columns.array.length; i < len; i++){
6242						self.ft.columns.array[i].index += 1;
6243					}
6244				}
6245				self.ft.columns.array.push(self.column);
6246				self.ft.columns.array.sort(function(a, b){ return a.index - b.index; });
6247
6248				self.callbacks.addRow = F.checkFnValue(self, data.editingAddRow, self.callbacks.addRow);
6249				self.callbacks.editRow = F.checkFnValue(self, data.editingEditRow, self.callbacks.editRow);
6250				self.callbacks.deleteRow = F.checkFnValue(self, data.editingDeleteRow, self.callbacks.deleteRow);
6251				self.callbacks.viewRow = F.checkFnValue(self, data.editingViewRow, self.callbacks.viewRow);
6252			}, function(){
6253				self.enabled = false;
6254			});
6255		},
6256		/**
6257		 * Initializes the editing component for the plugin using the supplied table and options.
6258		 * @instance
6259		 * @protected
6260		 * @fires FooTable.Editing#"init.ft.editing"
6261		 */
6262		init: function(){
6263			/**
6264			 * The init.ft.editing event is raised before its UI is generated.
6265			 * Calling preventDefault on this event will disable the component.
6266			 * @event FooTable.Editing#"init.ft.editing"
6267			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6268			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6269			 */
6270			var self = this;
6271			this.ft.raise('init.ft.editing').then(function(){
6272				self.$create();
6273			}, function(){
6274				self.enabled = false;
6275			});
6276		},
6277		/**
6278		 * Destroys the editing component removing any UI generated from the table.
6279		 * @instance
6280		 * @protected
6281		 * @fires FooTable.Editing#"destroy.ft.editing"
6282		 */
6283		destroy: function () {
6284			/**
6285			 * The destroy.ft.editing event is raised before its UI is removed.
6286			 * Calling preventDefault on this event will prevent the component from being destroyed.
6287			 * @event FooTable.Editing#"destroy.ft.editing"
6288			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6289			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6290			 */
6291			var self = this;
6292			this.ft.raise('destroy.ft.editing').then(function(){
6293				self.ft.$el.removeClass('footable-editing footable-editing-always-show footable-editing-no-add footable-editing-no-edit footable-editing-no-delete footable-editing-no-view')
6294					.off('click.ft.editing').find('tfoot > tr.footable-editing').remove();
6295			});
6296		},
6297		/**
6298		 * Creates the editing UI from the current options setting the various jQuery properties of this component.
6299		 * @instance
6300		 * @protected
6301		 */
6302		$create: function(){
6303			var self = this, position = self.position === 'right' ? 'footable-editing-right' : 'footable-editing-left';
6304			self.ft.$el.addClass('footable-editing').addClass(position)
6305				.on('click.ft.editing', '.footable-show', {self: self}, self._onShowClick)
6306				.on('click.ft.editing', '.footable-hide', {self: self}, self._onHideClick)
6307				.on('click.ft.editing', '.footable-edit', {self: self}, self._onEditClick)
6308				.on('click.ft.editing', '.footable-delete', {self: self}, self._onDeleteClick)
6309				.on('click.ft.editing', '.footable-view', {self: self}, self._onViewClick)
6310				.on('click.ft.editing', '.footable-add', {self: self}, self._onAddClick);
6311
6312			self.$cell = $('<td/>').attr('colspan', self.ft.columns.visibleColspan).append(self.$buttonShow());
6313			if (self.allowAdd){
6314				self.$cell.append(self.$buttonAdd());
6315			}
6316			self.$cell.append(self.$buttonHide());
6317
6318			if (self.alwaysShow){
6319				self.ft.$el.addClass('footable-editing-always-show');
6320			}
6321
6322			if (!self.allowAdd) self.ft.$el.addClass('footable-editing-no-add');
6323			if (!self.allowEdit) self.ft.$el.addClass('footable-editing-no-edit');
6324			if (!self.allowDelete) self.ft.$el.addClass('footable-editing-no-delete');
6325			if (!self.allowView) self.ft.$el.addClass('footable-editing-no-view');
6326
6327			var $tfoot = self.ft.$el.children('tfoot');
6328			if ($tfoot.length == 0){
6329				$tfoot = $('<tfoot/>');
6330				self.ft.$el.append($tfoot);
6331			}
6332			self.$row = $('<tr/>', { 'class': 'footable-editing' }).append(self.$cell).appendTo($tfoot);
6333		},
6334		/**
6335		 * Creates the show button for the editing component.
6336		 * @instance
6337		 * @protected
6338		 * @returns {(string|HTMLElement|jQuery)}
6339		 */
6340		$buttonShow: function(){
6341			return '<button type="button" class="btn btn-primary footable-show">' + this.showText + '</button>';
6342		},
6343		/**
6344		 * Creates the hide button for the editing component.
6345		 * @instance
6346		 * @protected
6347		 * @returns {(string|HTMLElement|jQuery)}
6348		 */
6349		$buttonHide: function(){
6350			return '<button type="button" class="btn btn-default footable-hide">' + this.hideText + '</button>';
6351		},
6352		/**
6353		 * Creates the add button for the editing component.
6354		 * @instance
6355		 * @protected
6356		 * @returns {(string|HTMLElement|jQuery)}
6357		 */
6358		$buttonAdd: function(){
6359			return '<button type="button" class="btn btn-primary footable-add">' + this.addText + '</button> ';
6360		},
6361		/**
6362		 * Creates the edit button for the editing component.
6363		 * @instance
6364		 * @protected
6365		 * @returns {(string|HTMLElement|jQuery)}
6366		 */
6367		$buttonEdit: function(){
6368			return '<button type="button" class="btn btn-default footable-edit">' + this.editText + '</button> ';
6369		},
6370		/**
6371		 * Creates the delete button for the editing component.
6372		 * @instance
6373		 * @protected
6374		 * @returns {(string|HTMLElement|jQuery)}
6375		 */
6376		$buttonDelete: function(){
6377			return '<button type="button" class="btn btn-default footable-delete">' + this.deleteText + '</button>';
6378		},
6379		/**
6380		 * Creates the view button for the editing component.
6381		 * @instance
6382		 * @protected
6383		 * @returns {(string|HTMLElement|jQuery)}
6384		 */
6385		$buttonView: function(){
6386			return '<button type="button" class="btn btn-default footable-view">' + this.viewText + '</button> ';
6387		},
6388		/**
6389		 * Creates the button group for the row buttons.
6390		 * @instance
6391		 * @protected
6392		 * @returns {(string|HTMLElement|jQuery)}
6393		 */
6394		$rowButtons: function(){
6395			if (F.is.jq(this._$buttons)) return this._$buttons.clone();
6396			this._$buttons = $('<div class="btn-group btn-group-xs" role="group"></div>');
6397			if (this.allowView) this._$buttons.append(this.$buttonView());
6398			if (this.allowEdit) this._$buttons.append(this.$buttonEdit());
6399			if (this.allowDelete) this._$buttons.append(this.$buttonDelete());
6400			return this._$buttons;
6401		},
6402		/**
6403		 * Performs the drawing of the component.
6404		 */
6405		draw: function(){
6406			this.$cell.attr('colspan', this.ft.columns.visibleColspan);
6407		},
6408		/**
6409		 * Handles the edit button click event.
6410		 * @instance
6411		 * @private
6412		 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6413		 * @fires FooTable.Editing#"edit.ft.editing"
6414		 */
6415		_onEditClick: function(e){
6416			e.preventDefault();
6417			var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__');
6418			if (row instanceof F.Row){
6419				/**
6420				 * The edit.ft.editing event is raised before its callback is executed.
6421				 * Calling preventDefault on this event will prevent the callback from being executed.
6422				 * @event FooTable.Editing#"edit.ft.editing"
6423				 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6424				 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6425				 * @param {FooTable.Row} row - The row to be edited.
6426				 */
6427				self.ft.raise('edit.ft.editing', [row]).then(function(){
6428					self.callbacks.editRow.call(self.ft, row);
6429				});
6430			}
6431		},
6432		/**
6433		 * Handles the delete button click event.
6434		 * @instance
6435		 * @private
6436		 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6437		 * @fires FooTable.Editing#"delete.ft.editing"
6438		 */
6439		_onDeleteClick: function(e){
6440			e.preventDefault();
6441			var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__');
6442			if (row instanceof F.Row){
6443				/**
6444				 * The delete.ft.editing event is raised before its callback is executed.
6445				 * Calling preventDefault on this event will prevent the callback from being executed.
6446				 * @event FooTable.Editing#"delete.ft.editing"
6447				 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6448				 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6449				 * @param {FooTable.Row} row - The row to be deleted.
6450				 */
6451				self.ft.raise('delete.ft.editing', [row]).then(function(){
6452					self.callbacks.deleteRow.call(self.ft, row);
6453				});
6454			}
6455		},
6456		/**
6457		 * Handles the view button click event.
6458		 * @instance
6459		 * @private
6460		 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6461		 * @fires FooTable.Editing#"view.ft.editing"
6462		 */
6463		_onViewClick: function(e){
6464			e.preventDefault();
6465			var self = e.data.self, row = $(this).closest('tr').data('__FooTableRow__');
6466			if (row instanceof F.Row){
6467				/**
6468				 * The view.ft.editing event is raised before its callback is executed.
6469				 * Calling preventDefault on this event will prevent the callback from being executed.
6470				 * @event FooTable.Editing#"view.ft.editing"
6471				 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6472				 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6473				 * @param {FooTable.Row} row - The row to be viewed.
6474				 */
6475				self.ft.raise('view.ft.editing', [row]).then(function(){
6476					self.callbacks.viewRow.call(self.ft, row);
6477				});
6478			}
6479		},
6480		/**
6481		 * Handles the add button click event.
6482		 * @instance
6483		 * @private
6484		 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6485		 * @fires FooTable.Editing#"add.ft.editing"
6486		 */
6487		_onAddClick: function(e){
6488			e.preventDefault();
6489			var self = e.data.self;
6490			/**
6491			 * The add.ft.editing event is raised before its callback is executed.
6492			 * Calling preventDefault on this event will prevent the callback from being executed.
6493			 * @event FooTable.Editing#"add.ft.editing"
6494			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6495			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6496			 */
6497			self.ft.raise('add.ft.editing').then(function(){
6498				self.callbacks.addRow.call(self.ft);
6499			});
6500		},
6501		/**
6502		 * Handles the show button click event.
6503		 * @instance
6504		 * @private
6505		 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6506		 * @fires FooTable.Editing#"show.ft.editing"
6507		 */
6508		_onShowClick: function(e){
6509			e.preventDefault();
6510			var self = e.data.self;
6511			/**
6512			 * The show.ft.editing event is raised before its callback is executed.
6513			 * Calling preventDefault on this event will prevent the callback from being executed.
6514			 * @event FooTable.Editing#"show.ft.editing"
6515			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6516			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6517			 */
6518			self.ft.raise('show.ft.editing').then(function(){
6519				self.ft.$el.addClass('footable-editing-show');
6520				self.column.visible = true;
6521				self.ft.draw();
6522			});
6523		},
6524		/**
6525		 * Handles the hide button click event.
6526		 * @instance
6527		 * @private
6528		 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6529		 * @fires FooTable.Editing#"show.ft.editing"
6530		 */
6531		_onHideClick: function(e){
6532			e.preventDefault();
6533			var self = e.data.self;
6534			/**
6535			 * The hide.ft.editing event is raised before its callback is executed.
6536			 * Calling preventDefault on this event will prevent the callback from being executed.
6537			 * @event FooTable.Editing#"hide.ft.editing"
6538			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6539			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6540			 */
6541			self.ft.raise('hide.ft.editing').then(function(){
6542				self.ft.$el.removeClass('footable-editing-show');
6543				self.column.visible = false;
6544				self.ft.draw();
6545			});
6546		}
6547	});
6548
6549	F.components.register('editing', F.Editing, 850);
6550
6551})(jQuery, FooTable);
6552
6553(function($, F){
6554
6555	F.EditingColumn = F.Column.extend(/** @lends FooTable.EditingColumn */{
6556		/**
6557		 * The Editing column class is used to create the column containing the editing buttons.
6558		 * @constructs
6559		 * @extends FooTable.Column
6560		 * @param {FooTable.Table} instance -  The parent {@link FooTable.Table} this column belongs to.
6561		 * @param {FooTable.Editing} editing - The parent {@link FooTable.Editing} component this column is used with.
6562		 * @param {object} definition - An object containing all the properties to set for the column.
6563		 * @returns {FooTable.EditingColumn}
6564		 */
6565		construct: function(instance, editing, definition){
6566			this._super(instance, definition, 'editing');
6567			this.editing = editing;
6568			this.internal = true;
6569		},
6570		/**
6571		 * After the column has been defined this ensures that the $el property is a jQuery object by either creating or updating the current value.
6572		 * @instance
6573		 * @protected
6574		 * @this FooTable.Column
6575		 */
6576		$create: function(){
6577			(this.$el = !this.virtual && F.is.jq(this.$el) ? this.$el : $('<th/>', {'class': 'footable-editing'})).html(this.title);
6578		},
6579		/**
6580		 * This is supplied either the cell value or jQuery object to parse. Any value can be returned from this method and
6581		 * will be provided to the {@link FooTable.EditingColumn#format} function
6582		 * to generate the cell contents.
6583		 * @instance
6584		 * @protected
6585		 * @param {(*|jQuery)} valueOrElement - The value or jQuery cell object.
6586		 * @returns {(jQuery)}
6587		 */
6588		parser: function(valueOrElement){
6589			if (F.is.string(valueOrElement)) valueOrElement = $($.trim(valueOrElement));
6590			if (F.is.element(valueOrElement)) valueOrElement = $(valueOrElement);
6591			if (F.is.jq(valueOrElement)){
6592				var tagName = valueOrElement.prop('tagName').toLowerCase();
6593				if (tagName == 'td' || tagName == 'th') return valueOrElement.data('value') || valueOrElement.contents();
6594				return valueOrElement;
6595			}
6596			return null;
6597		},
6598		/**
6599		 * Creates a cell to be used in the supplied row for this column.
6600		 * @param {FooTable.Row} row - The row to create the cell for.
6601		 * @returns {FooTable.Cell}
6602		 */
6603		createCell: function(row){
6604			var $buttons = this.editing.$rowButtons(), $cell = $('<td/>').append($buttons);
6605			if (F.is.jq(row.$el)){
6606				if (this.index === 0){
6607					$cell.prependTo(row.$el);
6608				} else {
6609					$cell.insertAfter(row.$el.children().eq(this.index-1));
6610				}
6611			}
6612			return new F.Cell(this.ft, row, this, $cell || $cell.html());
6613		}
6614	});
6615
6616	F.columns.register('editing', F.EditingColumn);
6617
6618})(jQuery, FooTable);
6619(function($, F) {
6620
6621	/**
6622	 * An object containing the editing options for the plugin. Added by the {@link FooTable.Editing} component.
6623	 * @type {object}
6624	 * @prop {boolean} enabled=false - Whether or not to allow editing on the table.
6625	 * @prop {boolean} pageToNew=true - Whether or not to automatically page to a new row when it is added to the table.
6626	 * @prop {string} position="right" - The position of the editing column in the table as well as the alignment of the buttons.
6627	 * @prop {boolean} alwaysShow=false - Whether or not the editing column and add row button are always visible.
6628	 * @prop {function} addRow - The callback function to execute when the add row button is clicked.
6629	 * @prop {function} editRow - The callback function to execute when the edit row button is clicked.
6630	 * @prop {function} deleteRow - The callback function to execute when the delete row button is clicked.
6631	 * @prop {function} viewRow - The callback function to execute when the view row button is clicked.
6632	 * @prop {string} showText - The text that appears in the show button. This can contain HTML.
6633	 * @prop {string} hideText - The text that appears in the hide button. This can contain HTML.
6634	 * @prop {string} addText - The text that appears in the add button. This can contain HTML.
6635	 * @prop {string} editText - The text that appears in the edit button. This can contain HTML.
6636	 * @prop {string} deleteText - The text that appears in the delete button. This can contain HTML.
6637	 * @prop {string} viewText - The text that appears in the view button. This can contain HTML.
6638	 * @prop {boolean} allowAdd - Whether or not to show the Add Row button.
6639	 * @prop {boolean} allowEdit - Whether or not to show the Edit Row button.
6640	 * @prop {boolean} allowDelete - Whether or not to show the Delete Row button.
6641	 * @prop {boolean} allowView - Whether or not to show the View Row button.
6642	 * @prop {object} column - The options for the editing column. @see {@link FooTable.EditingColumn} for more info.
6643	 * @prop {string} column.classes="footable-editing" - A space separated string of class names to apply to all cells in the column.
6644	 * @prop {string} column.name="editing" - The name of the column.
6645	 * @prop {string} column.title="" - The title displayed in the header row of the table for the column.
6646	 * @prop {boolean} column.filterable=false - Whether or not the column should be filterable when using the filtering component.
6647	 * @prop {boolean} column.sortable=false - Whether or not the column should be sortable when using the sorting component.
6648	 */
6649	F.Defaults.prototype.editing = {
6650		enabled: false,
6651		pageToNew: true,
6652		position: 'right',
6653		alwaysShow: false,
6654		addRow: function(){},
6655		editRow: function(row){},
6656		deleteRow: function(row){},
6657		viewRow: function(row){},
6658		showText: '<span class="fooicon fooicon-pencil" aria-hidden="true"></span> Edit rows',
6659		hideText: 'Cancel',
6660		addText: 'New row',
6661		editText: '<span class="fooicon fooicon-pencil" aria-hidden="true"></span>',
6662		deleteText: '<span class="fooicon fooicon-trash" aria-hidden="true"></span>',
6663		viewText: '<span class="fooicon fooicon-stats" aria-hidden="true"></span>',
6664		allowAdd: true,
6665		allowEdit: true,
6666		allowDelete: true,
6667		allowView: false,
6668		column: {
6669			classes: 'footable-editing',
6670			name: 'editing',
6671			title: '',
6672			filterable: false,
6673			sortable: false
6674		}
6675	};
6676
6677})(jQuery, FooTable);
6678
6679(function($, F){
6680
6681	if (F.is.defined(F.Paging)){
6682		/**
6683		 * Holds a shallow clone of the un-paged {@link FooTable.Rows#array} value before paging occurs and superfluous rows are removed. Added by the {@link FooTable.Editing} component.
6684		 * @instance
6685		 * @public
6686		 * @type {Array<FooTable.Row>}
6687		 */
6688		F.Paging.prototype.unpaged = [];
6689
6690		// override the default predraw method with one that sets the unpaged property.
6691		F.Paging.extend('predraw', function(){
6692			this.unpaged = this.ft.rows.array.slice(0); // create a shallow clone for later use
6693			this._super(); // call the original method
6694		});
6695	}
6696
6697})(jQuery, FooTable);
6698(function($, F){
6699
6700	/**
6701	 * Adds the row to the table.
6702	 * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this
6703	 * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method.
6704	 * @returns {jQuery.Deferred}
6705	 */
6706	F.Row.prototype.add = function(redraw){
6707		redraw = F.is.boolean(redraw) ? redraw : true;
6708		var self = this;
6709		return $.Deferred(function(d){
6710			var index = self.ft.rows.all.push(self) - 1;
6711			if (redraw){
6712				return self.ft.draw().then(function(){
6713					d.resolve(index);
6714				});
6715			} else {
6716				d.resolve(index);
6717			}
6718		});
6719	};
6720
6721	/**
6722	 * Removes the row from the table.
6723	 * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this
6724	 * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method.
6725	 * @returns {jQuery.Deferred}
6726	 */
6727	F.Row.prototype.delete = function(redraw){
6728		redraw = F.is.boolean(redraw) ? redraw : true;
6729		var self = this;
6730		return $.Deferred(function(d){
6731			var index = self.ft.rows.all.indexOf(self);
6732			if (F.is.number(index) && index >= 0 && index < self.ft.rows.all.length){
6733				self.ft.rows.all.splice(index, 1);
6734				if (redraw){
6735					return self.ft.draw().then(function(){
6736						d.resolve(self);
6737					});
6738				}
6739			}
6740			d.resolve(self);
6741		});
6742	};
6743
6744	if (F.is.defined(F.Paging)){
6745		// override the default add method with one that supports paging
6746		F.Row.extend('add', function(redraw){
6747			redraw = F.is.boolean(redraw) ? redraw : true;
6748			var self = this,
6749				added = this._super(redraw),
6750				editing = self.ft.use(F.Editing),
6751				paging;
6752			if (editing && editing.pageToNew && (paging = self.ft.use(F.Paging)) && redraw){
6753				return added.then(function(){
6754					var index = paging.unpaged.indexOf(self); // find this row in the unpaged array (this array will be sorted and filtered)
6755					var page = Math.ceil((index + 1) / paging.size); // calculate the page the new row is on
6756					if (paging.current !== page){ // goto the page if we need to
6757						return paging.goto(page);
6758					}
6759				});
6760			}
6761			return added;
6762		});
6763	}
6764
6765	if (F.is.defined(F.Sorting)){
6766		// override the default val method with one that supports sorting and paging
6767		F.Row.extend('val', function(data, redraw){
6768			redraw = F.is.boolean(redraw) ? redraw : true;
6769			var result = this._super(data);
6770			if (!F.is.hash(data)){
6771				return result;
6772			}
6773			var self = this;
6774			if (redraw){
6775				self.ft.draw().then(function(){
6776					var editing = self.ft.use(F.Editing), paging;
6777					if (F.is.defined(F.Paging) && editing && editing.pageToNew && (paging = self.ft.use(F.Paging))){
6778						var index = paging.unpaged.indexOf(self); // find this row in the unpaged array (this array will be sorted and filtered)
6779						var page = Math.ceil((index + 1) / paging.size); // calculate the page the new row is on
6780						if (paging.current !== page){ // goto the page if we need to
6781							return paging.goto(page);
6782						}
6783					}
6784				});
6785			}
6786			return result;
6787		});
6788	}
6789
6790})(jQuery, FooTable);
6791(function(F){
6792
6793	/**
6794	 * Adds a row to the underlying {@link FooTable.Rows#all} array.
6795	 * @param {(object|FooTable.Row)} dataOrRow - A hash containing the row values or an actual {@link FooTable.Row} object.
6796	 * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this
6797	 * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method.
6798	 */
6799	F.Rows.prototype.add = function(dataOrRow, redraw){
6800		var row = dataOrRow;
6801		if (F.is.hash(dataOrRow)){
6802			row = new FooTable.Row(this.ft, this.ft.columns.array, dataOrRow);
6803		}
6804		if (row instanceof FooTable.Row){
6805			row.add(redraw);
6806		}
6807	};
6808
6809	/**
6810	 * Updates a row in the underlying {@link FooTable.Rows#all} array.
6811	 * @param {(number|FooTable.Row)} indexOrRow - The index to update or the actual {@link FooTable.Row} object.
6812	 * @param {object} data - A hash containing the new row values.
6813	 * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this
6814	 * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method.
6815	 */
6816	F.Rows.prototype.update = function(indexOrRow, data, redraw){
6817		var len = this.ft.rows.all.length,
6818			row = indexOrRow;
6819		if (F.is.number(indexOrRow) && indexOrRow >= 0 && indexOrRow < len){
6820			row = this.ft.rows.all[indexOrRow];
6821		}
6822		if (row instanceof FooTable.Row && F.is.hash(data)){
6823			row.val(data, redraw);
6824		}
6825	};
6826
6827	/**
6828	 * Deletes a row from the underlying {@link FooTable.Rows#all} array.
6829	 * @param {(number|FooTable.Row)} indexOrRow - The index to delete or the actual {@link FooTable.Row} object.
6830	 * @param {boolean} [redraw=true] - Whether or not to redraw the table, defaults to true but for bulk operations this
6831	 * can be set to false and then followed by a call to the {@link FooTable.Table#draw} method.
6832	 */
6833	F.Rows.prototype.delete = function(indexOrRow, redraw){
6834		var len = this.ft.rows.all.length,
6835			row = indexOrRow;
6836		if (F.is.number(indexOrRow) && indexOrRow >= 0 && indexOrRow < len){
6837			row = this.ft.rows.all[indexOrRow];
6838		}
6839		if (row instanceof FooTable.Row){
6840			row.delete(redraw);
6841		}
6842	};
6843
6844})(FooTable);
6845
6846(function($, F){
6847
6848	// global int to use if the table has no ID
6849	var _uid = 0,
6850	// a hash value for the current url
6851		_url_hash = (function(str){
6852			var i, l, hval = 0x811c9dc5;
6853			for (i = 0, l = str.length; i < l; i++) {
6854				hval ^= str.charCodeAt(i);
6855				hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
6856			}
6857			return hval >>> 0;
6858		})(location.origin + location.pathname);
6859
6860	F.State = F.Component.extend(/** @lends FooTable.State */{
6861		/**
6862		 * The state component adds the ability for the table to remember its basic state for filtering, paging and sorting.
6863		 * @constructs
6864		 * @extends FooTable.Component
6865		 * @param {FooTable.Table} table - The parent {@link FooTable.Table} object for the component.
6866		 * @returns {FooTable.State}
6867		 */
6868		construct: function(table){
6869			// call the constructor of the base class
6870			this._super(table, table.o.state.enabled);
6871			// Change this value if an update to this component requires any stored data to be reset
6872			this._key = '1';
6873			/**
6874			 * The key to use to store the state for this table.
6875			 * @type {(null|string)}
6876			 */
6877			this.key = this._key + (F.is.string(table.o.state.key) ? table.o.state.key : this._uid());
6878			/**
6879			 * Whether or not to allow the filtering component to store it's state.
6880			 * @type {boolean}
6881			 */
6882			this.filtering = F.is.boolean(table.o.state.filtering) ? table.o.state.filtering : true;
6883			/**
6884			 * Whether or not to allow the paging component to store it's state.
6885			 * @type {boolean}
6886			 */
6887			this.paging = F.is.boolean(table.o.state.paging) ? table.o.state.paging : true;
6888			/**
6889			 * Whether or not to allow the sorting component to store it's state.
6890			 * @type {boolean}
6891			 */
6892			this.sorting = F.is.boolean(table.o.state.sorting) ? table.o.state.sorting : true;
6893		},
6894		/* PROTECTED */
6895		/**
6896		 * Checks the supplied data and options for the state component.
6897		 * @instance
6898		 * @protected
6899		 * @param {object} data - The jQuery data object from the parent table.
6900		 * @fires FooTable.State#"preinit.ft.state"
6901		 * @this FooTable.State
6902		 */
6903		preinit: function(data){
6904			var self = this;
6905			/**
6906			 * The preinit.ft.state event is raised before the UI is created and provides the tables jQuery data object for additional options parsing.
6907			 * Calling preventDefault on this event will disable the component.
6908			 * @event FooTable.State#"preinit.ft.state"
6909			 * @param {jQuery.Event} e - The jQuery.Event object for the event.
6910			 * @param {FooTable.Table} ft - The instance of the plugin raising the event.
6911			 * @param {object} data - The jQuery data object of the table raising the event.
6912			 */
6913			this.ft.raise('preinit.ft.state', [data]).then(function(){
6914
6915				self.enabled = F.is.boolean(data.state)
6916					? data.state
6917					: self.enabled;
6918
6919				if (!self.enabled) return;
6920
6921				self.key = self._key + (F.is.string(data.stateKey) ? data.stateKey : self.key);
6922
6923				self.filtering = F.is.boolean(data.stateFiltering) ? data.stateFiltering : self.filtering;
6924
6925				self.paging = F.is.boolean(data.statePaging) ? data.statePaging : self.paging;
6926
6927				self.sorting = F.is.boolean(data.stateSorting) ? data.stateSorting : self.sorting;
6928
6929			}, function(){
6930				self.enabled = false;
6931			});
6932		},
6933		/**
6934		 * Gets the state value for the specified key for this table.
6935		 * @instance
6936		 * @param {string} key - The key to get the value for.
6937		 * @returns {(*|null)}
6938		 */
6939		get: function(key){
6940			return JSON.parse(localStorage.getItem(this.key + ':' + key));
6941		},
6942		/**
6943		 * Sets the state value for the specified key for this table.
6944		 * @instance
6945		 * @param {string} key - The key to set the value for.
6946		 * @param {*} data - The value to store for the key. This value must be JSON.stringify friendly.
6947		 */
6948		set: function(key, data){
6949			localStorage.setItem(this.key + ':' + key, JSON.stringify(data));
6950		},
6951		/**
6952		 * Clears the state value for the specified key for this table.
6953		 * @instance
6954		 * @param {string} key - The key to clear the value for.
6955		 */
6956		remove: function(key){
6957			localStorage.removeItem(this.key + ':' + key);
6958		},
6959		/**
6960		 * Executes the {@link FooTable.Component#readState} function on all components.
6961		 * @instance
6962		 */
6963		read: function(){
6964			this.ft.execute(false, true, 'readState');
6965		},
6966		/**
6967		 * Executes the {@link FooTable.Component#writeState} function on all components.
6968		 * @instance
6969		 */
6970		write: function(){
6971			this.ft.execute(false, true, 'writeState');
6972		},
6973		/**
6974		 * Executes the {@link FooTable.Component#clearState} function on all components.
6975		 * @instance
6976		 */
6977		clear: function(){
6978			this.ft.execute(false, true, 'clearState');
6979		},
6980		/**
6981		 * Generates a unique identifier for the current {@link FooTable.Table} if one is not supplied through the options.
6982		 * This value is a combination of the url hash and either the element ID or an incremented global int value.
6983		 * @instance
6984		 * @returns {*}
6985		 * @private
6986		 */
6987		_uid: function(){
6988			var id = this.ft.$el.attr('id');
6989			return _url_hash + '_' + (F.is.string(id) ? id : ++_uid);
6990		}
6991	});
6992
6993	F.components.register('state', F.State, 700);
6994
6995})(jQuery, FooTable);
6996(function(F){
6997
6998	/**
6999	 * This method is called from the {@link FooTable.State#read} method and allows a component to retrieve its' stored state.
7000	 * @instance
7001	 * @protected
7002	 * @function
7003	 */
7004	F.Component.prototype.readState = function(){};
7005
7006	/**
7007	 * This method is called from the {@link FooTable.State#write} method and allows a component to write its' current state to the store.
7008	 * @instance
7009	 * @protected
7010	 * @function
7011	 */
7012	F.Component.prototype.writeState = function(){};
7013
7014	/**
7015	 * This method is called from the {@link FooTable.State#clear} method and allows a component to clear any stored state.
7016	 * @instance
7017	 * @protected
7018	 * @function
7019	 */
7020	F.Component.prototype.clearState = function(){};
7021
7022})(FooTable);
7023(function(F){
7024
7025	/**
7026	 * An object containing the state options for the plugin. Added by the {@link FooTable.State} component.
7027	 * @type {object}
7028	 * @prop {boolean} enabled=false - Whether or not to allow state to be stored for the table. This overrides the individual component enable options.
7029	 * @prop {boolean} filtering=true - Whether or not to allow the filtering state to be stored.
7030	 * @prop {boolean} paging=true - Whether or not to allow the filtering state to be stored.
7031	 * @prop {boolean} sorting=true - Whether or not to allow the filtering state to be stored.
7032	 * @prop {string} key=null - The unique key to use to store the table's data.
7033	 */
7034	F.Defaults.prototype.state = {
7035		enabled: false,
7036		filtering: true,
7037		paging: true,
7038		sorting: true,
7039		key: null
7040	};
7041
7042})(FooTable);
7043(function(F){
7044
7045	if (!F.Filtering) return;
7046
7047	/**
7048	 * Allows the filtering component to retrieve its' stored state.
7049	 */
7050	F.Filtering.prototype.readState = function(){
7051		if (this.ft.state.filtering){
7052			var state = this.ft.state.get('filtering');
7053			if (F.is.hash(state) && !F.is.emptyArray(state.filters)){
7054				this.filters = this.ensure(state.filters);
7055			}
7056		}
7057	};
7058
7059	/**
7060	 * Allows the filtering component to write its' current state to the store.
7061	 */
7062	F.Filtering.prototype.writeState = function(){
7063		if (this.ft.state.filtering) {
7064			var filters = F.arr.map(this.filters, function (f) {
7065				return {
7066					name: f.name,
7067					query: f.query instanceof F.Query ? f.query.val() : f.query,
7068					columns: F.arr.map(f.columns, function (c) {
7069						return c.name;
7070					}),
7071					hidden: f.hidden,
7072					space: f.space,
7073					connectors: f.connectors,
7074					ignoreCase: f.ignoreCase
7075				};
7076			});
7077			this.ft.state.set('filtering', {filters: filters});
7078		}
7079	};
7080
7081	/**
7082	 * Allows the filtering component to clear any stored state.
7083	 */
7084	F.Filtering.prototype.clearState = function(){
7085		if (this.ft.state.filtering) {
7086			this.ft.state.remove('filtering');
7087		}
7088	};
7089
7090})(FooTable);
7091(function(F){
7092
7093	if (!F.Paging) return;
7094
7095	/**
7096	 * Allows the paging component to retrieve its' stored state.
7097	 */
7098	F.Paging.prototype.readState = function(){
7099		if (this.ft.state.paging) {
7100			var state = this.ft.state.get('paging');
7101			if (F.is.hash(state)) {
7102				this.current = state.current;
7103				this.size = state.size;
7104			}
7105		}
7106	};
7107
7108	/**
7109	 * Allows the paging component to write its' current state to the store.
7110	 */
7111	F.Paging.prototype.writeState = function(){
7112		if (this.ft.state.paging) {
7113			this.ft.state.set('paging', {
7114				current: this.current,
7115				size: this.size
7116			});
7117		}
7118	};
7119
7120	/**
7121	 * Allows the paging component to clear any stored state.
7122	 */
7123	F.Paging.prototype.clearState = function(){
7124		if (this.ft.state.paging) {
7125			this.ft.state.remove('paging');
7126		}
7127	};
7128
7129})(FooTable);
7130(function(F){
7131
7132	if (!F.Sorting) return;
7133
7134	/**
7135	 * Allows the sorting component to retrieve its' stored state.
7136	 */
7137	F.Sorting.prototype.readState = function(){
7138		if (this.ft.state.sorting) {
7139			var state = this.ft.state.get('sorting');
7140			if (F.is.hash(state)) {
7141				var column = this.ft.columns.get(state.column);
7142				if (column instanceof F.Column) {
7143					this.column = column;
7144					this.column.direction = state.direction;
7145				}
7146			}
7147		}
7148	};
7149
7150	/**
7151	 * Allows the sorting component to write its' current state to the store.
7152	 */
7153	F.Sorting.prototype.writeState = function(){
7154		if (this.ft.state.sorting && this.column instanceof F.Column){
7155			this.ft.state.set('sorting', {
7156				column: this.column.name,
7157				direction: this.column.direction
7158			});
7159		}
7160	};
7161
7162	/**
7163	 * Allows the sorting component to clear any stored state.
7164	 */
7165	F.Sorting.prototype.clearState = function(){
7166		if (this.ft.state.sorting) {
7167			this.ft.state.remove('sorting');
7168		}
7169	};
7170
7171})(FooTable);
7172(function(F){
7173
7174	// hook into the _construct method so we can add the state property to the table.
7175	F.Table.extend('_construct', function(ready){
7176		this.state = this.use(FooTable.State);
7177		return this._super(ready);
7178	});
7179
7180	// hook into the _preinit method so we can trigger a plugin wide read state operation.
7181	F.Table.extend('_preinit', function(){
7182		var self = this;
7183		return self._super().then(function(){
7184			if (self.state.enabled){
7185				self.state.read();
7186			}
7187		});
7188	});
7189
7190	// hook into the draw method so we can trigger a plugin wide write state operation.
7191	F.Table.extend('draw', function(){
7192		var self = this;
7193		return self._super().then(function(){
7194			if (self.state.enabled){
7195				self.state.write();
7196			}
7197		});
7198	});
7199
7200})(FooTable);
7201(function($, F){
7202
7203	F.Export = F.Component.extend(/** @lends FooTable.Export */{
7204		/**
7205		 * @summary This component provides some basic export functionality.
7206		 * @memberof FooTable
7207		 * @constructs Export
7208		 * @param {FooTable.Table} table - The current instance of the plugin.
7209		 */
7210		construct: function(table){
7211			// call the constructor of the base class
7212			this._super(table, true);
7213			/**
7214			 * @summary A snapshot of the working set of rows prior to being trimmed by the paging component.
7215			 * @memberof FooTable.Export#
7216			 * @name snapshot
7217			 * @type {FooTable.Row[]}
7218			 */
7219			this.snapshot = [];
7220		},
7221		/**
7222		 * @summary Hooks into the predraw pipeline after sorting and filtering have taken place but prior to paging.
7223		 * @memberof FooTable.Export#
7224		 * @function predraw
7225		 * @description This method allows us to take a snapshot of the working set of rows before they are trimmed by the paging component and is called by the plugin instance.
7226		 */
7227		predraw: function(){
7228			this.snapshot = this.ft.rows.array.slice(0);
7229		},
7230		/**
7231		 * @summary Return the columns as simple JavaScript objects in an array.
7232		 * @memberof FooTable.Export#
7233		 * @function columns
7234		 * @returns {Object[]}
7235		 */
7236		columns: function(){
7237			var result = [];
7238			F.arr.each(this.ft.columns.array, function(column){
7239				if (!column.internal){
7240					result.push({
7241						type: column.type,
7242						name: column.name,
7243						title: column.title,
7244						visible: column.visible,
7245						hidden: column.hidden,
7246						classes: column.classes,
7247						style: column.style
7248					});
7249				}
7250			});
7251			return result;
7252		},
7253		/**
7254		 * @summary Return the rows as simple JavaScript objects in an array.
7255		 * @memberof FooTable.Export#
7256		 * @function rows
7257		 * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result.
7258		 * @returns {Object[]}
7259		 */
7260		rows: function(filtered){
7261			filtered = F.is.boolean(filtered) ? filtered : false;
7262			var rows = filtered ? this.ft.rows.all : this.snapshot, result = [];
7263			F.arr.each(rows, function(row){
7264				result.push(row.val());
7265			});
7266			return result;
7267		},
7268		/**
7269		 * @summary Return the columns and rows as a properly formatted JSON object.
7270		 * @memberof FooTable.Export#
7271		 * @function json
7272		 * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result.
7273		 * @returns {Object}
7274		 */
7275		json: function(filtered){
7276			return JSON.parse(JSON.stringify({columns: this.columns(),rows: this.rows(filtered)}));
7277		},
7278		/**
7279		 * @summary Return the columns and rows as a properly formatted CSV value.
7280		 * @memberof FooTable.Export#
7281		 * @function csv
7282		 * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result.
7283		 * @returns {string}
7284		 */
7285		csv: function(filtered){
7286			var csv = "", columns = this.columns(), value, escaped;
7287			F.arr.each(columns, function(column, i){
7288				escaped = '"' + column.title.replace(/"/g, '""') + '"';
7289				csv += (i === 0 ? escaped : "," + escaped);
7290			});
7291			csv += "\n";
7292
7293			var rows = filtered ? this.ft.rows.all : this.snapshot;
7294			F.arr.each(rows, function(row){
7295				F.arr.each(row.cells, function(cell, i){
7296					if (!cell.column.internal){
7297						value = cell.column.stringify.call(cell.column, cell.value, cell.ft.o, cell.row.value);
7298						escaped = '"' + value.replace(/"/g, '""') + '"';
7299						csv += (i === 0 ? escaped : "," + escaped);
7300					}
7301				});
7302				csv += "\n";
7303			});
7304			return csv;
7305		}
7306	});
7307
7308	// register the component using a priority of 490 which falls just after filtering (500) and before paging (400).
7309	F.components.register("export", F.Export, 490);
7310
7311})(jQuery, FooTable);
7312(function(F){
7313	// this is used to define the filtering specific properties on column creation
7314	F.Column.prototype.__export_define__ = function(definition){
7315		this.stringify = F.checkFnValue(this, definition.stringify, this.stringify);
7316	};
7317
7318	// overrides the public define method and replaces it with our own
7319	F.Column.extend('define', function(definition){
7320		this._super(definition); // call the base so we don't have to redefine any previously set properties
7321		this.__export_define__(definition); // then call our own
7322	});
7323
7324	/**
7325	 * @summary Return the supplied value as a string.
7326	 * @memberof FooTable.Column#
7327	 * @function stringify
7328	 * @returns {string}
7329	 */
7330	F.Column.prototype.stringify = function(value, options, rowData){
7331		return value + "";
7332	};
7333
7334	if (F.is.defined(F.DateColumn)){
7335		// override the base method for DateColumns
7336		F.DateColumn.prototype.stringify = function(value, options, rowData){
7337			return F.is.object(value) && F.is.boolean(value._isAMomentObject) && value.isValid() ? value.format(this.formatString) : '';
7338		};
7339	}
7340
7341	// override the base method for ObjectColumns
7342	F.ObjectColumn.prototype.stringify = function(value, options, rowData){
7343		return F.is.object(value) ? JSON.stringify(value) : "";
7344	};
7345
7346	// override the base method for ArrayColumns
7347	F.ArrayColumn.prototype.stringify = function(value, options, rowData){
7348		return F.is.array(value) ? JSON.stringify(value) : "";
7349	};
7350
7351})(FooTable);
7352(function(F){
7353	/**
7354	 * @summary Return the columns and rows as a properly formatted JSON object.
7355	 * @memberof FooTable.Table#
7356	 * @function toJSON
7357	 * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result.
7358	 * @returns {Object}
7359	 */
7360	F.Table.prototype.toJSON = function(filtered){
7361		return this.use(F.Export).json(filtered);
7362	};
7363
7364	/**
7365	 * @summary Return the columns and rows as a properly formatted CSV value.
7366	 * @memberof FooTable.Table#
7367	 * @function toCSV
7368	 * @param {boolean} [filtered=false] - Whether or not to exclude filtered rows from the result.
7369	 * @returns {string}
7370	 */
7371	F.Table.prototype.toCSV = function(filtered){
7372		return this.use(F.Export).csv(filtered);
7373	};
7374
7375})(FooTable);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.