PageSourceSearch

https://podarok.by/local/templates/aspro_max/vendor/js/footable.js?1594669696275478

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