r80680 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r80679‎ | r80680 | r80681 >
Date:03:48, 21 January 2011
Author:tparscal
Status:deferred
Tags:
Comment:
Starting point for merging ClickTracking, PrefStats and SimpleSurvey together into a single extension.
Modified paths:
  • /trunk/extensions/ResearchTools (added) (history)
  • /trunk/extensions/ResearchTools/README (added) (history)
  • /trunk/extensions/ResearchTools/ResearchTools.alias.php (added) (history)
  • /trunk/extensions/ResearchTools/ResearchTools.hooks.php (added) (history)
  • /trunk/extensions/ResearchTools/ResearchTools.i18n.php (added) (history)
  • /trunk/extensions/ResearchTools/ResearchTools.php (added) (history)
  • /trunk/extensions/ResearchTools/ResearchToolsPage.php (added) (history)
  • /trunk/extensions/ResearchTools/SpecialResearchTools.php (added) (history)
  • /trunk/extensions/ResearchTools/api (added) (history)
  • /trunk/extensions/ResearchTools/modules (added) (history)
  • /trunk/extensions/ResearchTools/modules/ext.researchTools (added) (history)
  • /trunk/extensions/ResearchTools/modules/ext.researchTools/ext.researchTools.css (added) (history)
  • /trunk/extensions/ResearchTools/modules/ext.researchTools/ext.researchTools.js (added) (history)
  • /trunk/extensions/ResearchTools/modules/ext.researchTools/images (added) (history)
  • /trunk/extensions/ResearchTools/modules/ext.researchTools/images/tab.png (added) (history)
  • /trunk/extensions/ResearchTools/modules/jquery.dataTables.js (added) (history)
  • /trunk/extensions/ResearchTools/pages (added) (history)
  • /trunk/extensions/ResearchTools/pages/ResearchToolsClicksPage.php (added) (history)
  • /trunk/extensions/ResearchTools/pages/ResearchToolsDashboardPage.php (added) (history)
  • /trunk/extensions/ResearchTools/pages/ResearchToolsPrefsPage.php (added) (history)
  • /trunk/extensions/ResearchTools/pages/ResearchToolsSurveysPage.php (added) (history)
  • /trunk/extensions/ResearchTools/sql (added) (history)

Diff [purge]

Index: trunk/extensions/ResearchTools/pages/ResearchToolsPrefsPage.php
@@ -0,0 +1,10 @@
 2+<?php
 3+
 4+class ResearchToolsPrefsPage extends ResearchToolsPage {
 5+
 6+ public function main() {
 7+ ?>
 8+ Hello pref stats!
 9+ <?php
 10+ }
 11+}
Property changes on: trunk/extensions/ResearchTools/pages/ResearchToolsPrefsPage.php
___________________________________________________________________
Added: svn:eol-style
112 + native
Index: trunk/extensions/ResearchTools/pages/ResearchToolsSurveysPage.php
@@ -0,0 +1,10 @@
 2+<?php
 3+
 4+class ResearchToolsSurveysPage extends ResearchToolsPage {
 5+
 6+ public function main() {
 7+ ?>
 8+ Hello surveys!
 9+ <?php
 10+ }
 11+}
Property changes on: trunk/extensions/ResearchTools/pages/ResearchToolsSurveysPage.php
___________________________________________________________________
Added: svn:eol-style
112 + native
Index: trunk/extensions/ResearchTools/pages/ResearchToolsDashboardPage.php
@@ -0,0 +1,10 @@
 2+<?php
 3+
 4+class ResearchToolsDashboardPage extends ResearchToolsPage {
 5+
 6+ public function main() {
 7+ ?>
 8+ Hello dashboard!
 9+ <?php
 10+ }
 11+}
Property changes on: trunk/extensions/ResearchTools/pages/ResearchToolsDashboardPage.php
___________________________________________________________________
Added: svn:eol-style
112 + native
Index: trunk/extensions/ResearchTools/pages/ResearchToolsClicksPage.php
@@ -0,0 +1,10 @@
 2+<?php
 3+
 4+class ResearchToolsClicksPage extends ResearchToolsPage {
 5+
 6+ public function main() {
 7+ ?>
 8+ Hello click tracking!
 9+ <?php
 10+ }
 11+}
Property changes on: trunk/extensions/ResearchTools/pages/ResearchToolsClicksPage.php
___________________________________________________________________
Added: svn:eol-style
112 + native
Index: trunk/extensions/ResearchTools/ResearchToolsPage.php
@@ -0,0 +1,10 @@
 2+<?php
 3+
 4+class ResearchToolsPage {
 5+
 6+ public function main() {
 7+ ?>
 8+ Hello research tools!
 9+ <?php
 10+ }
 11+}
Property changes on: trunk/extensions/ResearchTools/ResearchToolsPage.php
___________________________________________________________________
Added: svn:eol-style
112 + native
Index: trunk/extensions/ResearchTools/ResearchTools.i18n.php
@@ -0,0 +1,21 @@
 2+<?php
 3+/**
 4+ * Internationalisation for ResearchTools extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+/** English
 13+ * @author Trevor Parscal
 14+ */
 15+$messages['en'] = array(
 16+ 'researchtools' => 'Research Tools',
 17+ 'researchtools-desc' => 'Set of tools which are useful for conducting user research studies',
 18+ 'researchtools-page-dashboard' => 'Dashboard',
 19+ 'researchtools-page-surveys' => 'Survey Responses',
 20+ 'researchtools-page-clicks' => 'Click Tracking',
 21+ 'researchtools-page-prefs' => 'Preference Statistics',
 22+);
Property changes on: trunk/extensions/ResearchTools/ResearchTools.i18n.php
___________________________________________________________________
Added: svn:eol-style
123 + native
Index: trunk/extensions/ResearchTools/ResearchTools.php
@@ -0,0 +1,35 @@
 2+<?php
 3+/**
 4+ * ResearchTools extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ *
 9+ * @author Trevor Parscal <trevor@wikimedia.org>
 10+ * @license GPL v2 or later
 11+ * @version 0.1.0
 12+ */
 13+
 14+/* Setup */
 15+
 16+$wgExtensionCredits['other'][] = array(
 17+ 'path' => __FILE__,
 18+ 'name' => 'ResearchTools',
 19+ 'author' => array( 'Trevor Parscal' ),
 20+ 'version' => '0.1.0',
 21+ 'url' => 'http://www.mediawiki.org/wiki/Extension:ResearchTools',
 22+ 'descriptionmsg' => 'researchtools-desc',
 23+);
 24+$dir = dirname( __FILE__ );
 25+$wgExtensionMessagesFiles['ResearchTools'] = "$dir/ResearchTools.i18n.php";
 26+$wgExtensionAliasesFiles['ResearchTools'] = "$dir/ResearchTools.alias.php";
 27+$wgAutoloadClasses['ResearchToolsHooks'] = "$dir/ResearchTools.hooks.php";
 28+$wgAutoloadClasses['SpecialResearchTools'] = "$dir/SpecialResearchTools.php";
 29+$wgAutoloadClasses['ResearchToolsPage'] = "$dir/ResearchToolsPage.php";
 30+$wgAutoloadClasses['ResearchToolsDashboardPage'] = "$dir/pages/ResearchToolsDashboardPage.php";
 31+$wgAutoloadClasses['ResearchToolsSurveysPage'] = "$dir/pages/ResearchToolsSurveysPage.php";
 32+$wgAutoloadClasses['ResearchToolsClicksPage'] = "$dir/pages/ResearchToolsClicksPage.php";
 33+$wgAutoloadClasses['ResearchToolsPrefsPage'] = "$dir/pages/ResearchToolsPrefsPage.php";
 34+$wgSpecialPages['ResearchTools'] = 'SpecialResearchTools';
 35+$wgSpecialPageGroups['ResearchTools'] = 'other';
 36+$wgHooks['ResourceLoaderRegisterModules'][] = 'ResearchToolsHooks::resourceLoaderRegisterModules';
Property changes on: trunk/extensions/ResearchTools/ResearchTools.php
___________________________________________________________________
Added: svn:eol-style
137 + native
Index: trunk/extensions/ResearchTools/SpecialResearchTools.php
@@ -0,0 +1,62 @@
 2+<?php
 3+/**
 4+ * SpecialPage for ResearchTools extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+class SpecialResearchTools extends SpecialPage {
 11+
 12+ protected static $pages = array(
 13+ 'dashboard' => 'ResearchToolsDashboardPage',
 14+ 'surveys' => 'ResearchToolsSurveysPage',
 15+ 'clicks' => 'ResearchToolsClicksPage',
 16+ 'prefs' => 'ResearchToolsPrefsPage',
 17+ );
 18+
 19+ public function __construct() {
 20+ parent::__construct( 'ResearchTools' );
 21+ }
 22+
 23+ public function execute( $par ) {
 24+ global $wgUser, $wgOut, $wgRequest;
 25+
 26+ $wgOut->addModules( 'ext.researchTools' );
 27+
 28+ $this->setHeaders();
 29+
 30+ $current = current( explode( '/', $par ) );
 31+ if ( !isset( self::$pages[$current] ) ) {
 32+ $current = key( self::$pages );
 33+ }
 34+
 35+ ob_start();
 36+
 37+ self::renderNavigation( $current );
 38+
 39+ ?><div class="researchTools-page"><?php
 40+
 41+ $page = new self::$pages[$current];
 42+ $page->main();
 43+
 44+ ?></div><?php
 45+
 46+ $wgOut->addHtml( ob_get_clean() );
 47+ }
 48+
 49+ protected function renderNavigation( $current ) {
 50+ global $wgUser;
 51+
 52+ ?>
 53+ <ul class="researchTools-navigation">
 54+ <?php foreach ( self::$pages as $page => $class ): ?>
 55+ <li class="researchTools-navigation-item <?php echo $page == $current ? 'researchTools-navigation-item-current' : '' ?>">
 56+ <?php echo $wgUser->getSkin()->link( $this->getTitle( $page ), wfMsg( "researchtools-page-$page" ) ) ?>
 57+ </li>
 58+ <?php endforeach; ?>
 59+ </ul>
 60+ <div style="clear:both"></div>
 61+ <?php
 62+ }
 63+}
Property changes on: trunk/extensions/ResearchTools/SpecialResearchTools.php
___________________________________________________________________
Added: svn:eol-style
164 + native
Index: trunk/extensions/ResearchTools/ResearchTools.hooks.php
@@ -0,0 +1,34 @@
 2+<?php
 3+/**
 4+ * Hooks for ResearchTools extension
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+class ResearchToolsHooks {
 11+
 12+ /* Protected Static Members */
 13+
 14+ protected static $modules = array(
 15+ 'ext.researchTools' => array(
 16+ 'scripts' => 'ext.researchTools/ext.researchTools.js',
 17+ 'styles' => 'ext.researchTools/ext.researchTools.css',
 18+ ),
 19+ );
 20+
 21+ /*
 22+ * ResourceLoaderRegisterModules hook
 23+ */
 24+ public static function resourceLoaderRegisterModules( &$resourceLoader ) {
 25+ global $wgExtensionAssetsPath;
 26+ $localpath = dirname( __FILE__ ) . '/modules';
 27+ $remotepath = "$wgExtensionAssetsPath/ResearchTools/modules";
 28+ foreach ( self::$modules as $name => $resources ) {
 29+ $resourceLoader->register(
 30+ $name, new ResourceLoaderFileModule( $resources, $localpath, $remotepath )
 31+ );
 32+ }
 33+ return true;
 34+ }
 35+}
Property changes on: trunk/extensions/ResearchTools/ResearchTools.hooks.php
___________________________________________________________________
Added: svn:eol-style
136 + native
Index: trunk/extensions/ResearchTools/modules/ext.researchTools/ext.researchTools.css
@@ -0,0 +1,50 @@
 2+/*
 3+ * CSS for ResearchTools Extension
 4+ */
 5+
 6+.researchTools-navigation {
 7+ position: absolute;
 8+ top: -4em;
 9+ right: 0;
 10+ display: inline-block;
 11+ list-style: none;
 12+ margin: 0;
 13+ margin-top: 3px;
 14+ padding: 0;
 15+ height: 2.5em;
 16+}
 17+
 18+.researchTools-navigation-item {
 19+ list-style: none;
 20+ margin: 0;
 21+ padding: 0;
 22+ display: inline-block;
 23+}
 24+
 25+.researchTools-navigation-item a {
 26+ display: block;
 27+ padding-left: 1em;
 28+ padding-right: 1em;
 29+ height: 2.5em;
 30+ line-height: 2.5em;
 31+ margin: 1px;
 32+ margin-bottom: 0;
 33+}
 34+
 35+.researchTools-navigation-item-current a {
 36+ background: url(images/tab.png) repeat-x center top;
 37+ margin: 0;
 38+ border: solid 1px #aaaaaa;
 39+ border-bottom: none;
 40+ border-top-right-radius: 0.5em;
 41+ border-top-left-radius: 0.5em;
 42+ color: #333333;
 43+}
 44+
 45+.researchTools-navigation-item-current a:hover {
 46+ text-decoration: none;
 47+}
 48+
 49+.researchTools-page {
 50+ margin-top: 1em;
 51+}
\ No newline at end of file
Property changes on: trunk/extensions/ResearchTools/modules/ext.researchTools/ext.researchTools.css
___________________________________________________________________
Added: svn:mime-type
152 + text/plain
Added: svn:eol-style
253 + native
Index: trunk/extensions/ResearchTools/modules/ext.researchTools/images/tab.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/ResearchTools/modules/ext.researchTools/images/tab.png
___________________________________________________________________
Added: svn:mime-type
354 + application/octet-stream
Index: trunk/extensions/ResearchTools/modules/ext.researchTools/ext.researchTools.js
@@ -0,0 +1,3 @@
 2+/*
 3+ * JavaScript for the ResearchTools Extension
 4+ */
\ No newline at end of file
Property changes on: trunk/extensions/ResearchTools/modules/ext.researchTools/ext.researchTools.js
___________________________________________________________________
Added: svn:mime-type
15 + text/plain
Added: svn:eol-style
26 + native
Index: trunk/extensions/ResearchTools/modules/jquery.dataTables.js
@@ -0,0 +1,6848 @@
 2+/*
 3+ * File: jquery.dataTables.js
 4+ * Version: 1.7.5
 5+ * Description: Paginate, search and sort HTML tables
 6+ * Author: Allan Jardine (www.sprymedia.co.uk)
 7+ * Created: 28/3/2008
 8+ * Language: Javascript
 9+ * License: GPL v2 or BSD 3 point style
 10+ * Project: Mtaala
 11+ * Contact: allan.jardine@sprymedia.co.uk
 12+ *
 13+ * Copyright 2008-2010 Allan Jardine, all rights reserved.
 14+ *
 15+ * This source file is free software, under either the GPL v2 license or a
 16+ * BSD style license, as supplied with this software.
 17+ *
 18+ * This source file is distributed in the hope that it will be useful, but
 19+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 20+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
 21+ *
 22+ * For details please refer to: http://www.datatables.net
 23+ */
 24+
 25+/*
 26+ * When considering jsLint, we need to allow eval() as it it is used for reading cookies and
 27+ * building the dynamic multi-column sort functions.
 28+ */
 29+/*jslint evil: true, undef: true, browser: true */
 30+/*globals $, jQuery,_fnExternApiFunc,_fnInitalise,_fnInitComplete,_fnLanguageProcess,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnGatherData,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnArrayCmp,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap*/
 31+
 32+(function($, window, document) {
 33+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 34+ * Section - DataTables variables
 35+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 36+
 37+ /*
 38+ * Variable: dataTableSettings
 39+ * Purpose: Store the settings for each dataTables instance
 40+ * Scope: jQuery.fn
 41+ */
 42+ $.fn.dataTableSettings = [];
 43+ var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */
 44+
 45+ /*
 46+ * Variable: dataTableExt
 47+ * Purpose: Container for customisable parts of DataTables
 48+ * Scope: jQuery.fn
 49+ */
 50+ $.fn.dataTableExt = {};
 51+ var _oExt = $.fn.dataTableExt;
 52+
 53+
 54+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 55+ * Section - DataTables extensible objects
 56+ *
 57+ * The _oExt object is used to provide an area where user dfined plugins can be
 58+ * added to DataTables. The following properties of the object are used:
 59+ * oApi - Plug-in API functions
 60+ * aTypes - Auto-detection of types
 61+ * oSort - Sorting functions used by DataTables (based on the type)
 62+ * oPagination - Pagination functions for different input styles
 63+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 64+
 65+ /*
 66+ * Variable: sVersion
 67+ * Purpose: Version string for plug-ins to check compatibility
 68+ * Scope: jQuery.fn.dataTableExt
 69+ * Notes: Allowed format is a.b.c.d.e where:
 70+ * a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional
 71+ */
 72+ _oExt.sVersion = "1.7.5";
 73+
 74+ /*
 75+ * Variable: sErrMode
 76+ * Purpose: How should DataTables report an error. Can take the value 'alert' or 'throw'
 77+ * Scope: jQuery.fn.dataTableExt
 78+ */
 79+ _oExt.sErrMode = "alert";
 80+
 81+ /*
 82+ * Variable: iApiIndex
 83+ * Purpose: Index for what 'this' index API functions should use
 84+ * Scope: jQuery.fn.dataTableExt
 85+ */
 86+ _oExt.iApiIndex = 0;
 87+
 88+ /*
 89+ * Variable: oApi
 90+ * Purpose: Container for plugin API functions
 91+ * Scope: jQuery.fn.dataTableExt
 92+ */
 93+ _oExt.oApi = { };
 94+
 95+ /*
 96+ * Variable: aFiltering
 97+ * Purpose: Container for plugin filtering functions
 98+ * Scope: jQuery.fn.dataTableExt
 99+ */
 100+ _oExt.afnFiltering = [ ];
 101+
 102+ /*
 103+ * Variable: aoFeatures
 104+ * Purpose: Container for plugin function functions
 105+ * Scope: jQuery.fn.dataTableExt
 106+ * Notes: Array of objects with the following parameters:
 107+ * fnInit: Function for initialisation of Feature. Takes oSettings and returns node
 108+ * cFeature: Character that will be matched in sDom - case sensitive
 109+ * sFeature: Feature name - just for completeness :-)
 110+ */
 111+ _oExt.aoFeatures = [ ];
 112+
 113+ /*
 114+ * Variable: ofnSearch
 115+ * Purpose: Container for custom filtering functions
 116+ * Scope: jQuery.fn.dataTableExt
 117+ * Notes: This is an object (the name should match the type) for custom filtering function,
 118+ * which can be used for live DOM checking or formatted text filtering
 119+ */
 120+ _oExt.ofnSearch = { };
 121+
 122+ /*
 123+ * Variable: afnSortData
 124+ * Purpose: Container for custom sorting data source functions
 125+ * Scope: jQuery.fn.dataTableExt
 126+ * Notes: Array (associative) of functions which is run prior to a column of this
 127+ * 'SortDataType' being sorted upon.
 128+ * Function input parameters:
 129+ * object:oSettings- DataTables settings object
 130+ * int:iColumn - Target column number
 131+ * Return value: Array of data which exactly matched the full data set size for the column to
 132+ * be sorted upon
 133+ */
 134+ _oExt.afnSortData = [ ];
 135+
 136+ /*
 137+ * Variable: oStdClasses
 138+ * Purpose: Storage for the various classes that DataTables uses
 139+ * Scope: jQuery.fn.dataTableExt
 140+ */
 141+ _oExt.oStdClasses = {
 142+ /* Two buttons buttons */
 143+ "sPagePrevEnabled": "paginate_enabled_previous",
 144+ "sPagePrevDisabled": "paginate_disabled_previous",
 145+ "sPageNextEnabled": "paginate_enabled_next",
 146+ "sPageNextDisabled": "paginate_disabled_next",
 147+ "sPageJUINext": "",
 148+ "sPageJUIPrev": "",
 149+
 150+ /* Full numbers paging buttons */
 151+ "sPageButton": "paginate_button",
 152+ "sPageButtonActive": "paginate_active",
 153+ "sPageButtonStaticDisabled": "paginate_button",
 154+ "sPageFirst": "first",
 155+ "sPagePrevious": "previous",
 156+ "sPageNext": "next",
 157+ "sPageLast": "last",
 158+
 159+ /* Stripping classes */
 160+ "sStripOdd": "odd",
 161+ "sStripEven": "even",
 162+
 163+ /* Empty row */
 164+ "sRowEmpty": "dataTables_empty",
 165+
 166+ /* Features */
 167+ "sWrapper": "dataTables_wrapper",
 168+ "sFilter": "dataTables_filter",
 169+ "sInfo": "dataTables_info",
 170+ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
 171+ "sLength": "dataTables_length",
 172+ "sProcessing": "dataTables_processing",
 173+
 174+ /* Sorting */
 175+ "sSortAsc": "sorting_asc",
 176+ "sSortDesc": "sorting_desc",
 177+ "sSortable": "sorting", /* Sortable in both directions */
 178+ "sSortableAsc": "sorting_asc_disabled",
 179+ "sSortableDesc": "sorting_desc_disabled",
 180+ "sSortableNone": "sorting_disabled",
 181+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
 182+ "sSortJUIAsc": "",
 183+ "sSortJUIDesc": "",
 184+ "sSortJUI": "",
 185+ "sSortJUIAscAllowed": "",
 186+ "sSortJUIDescAllowed": "",
 187+ "sSortJUIWrapper": "",
 188+
 189+ /* Scrolling */
 190+ "sScrollWrapper": "dataTables_scroll",
 191+ "sScrollHead": "dataTables_scrollHead",
 192+ "sScrollHeadInner": "dataTables_scrollHeadInner",
 193+ "sScrollBody": "dataTables_scrollBody",
 194+ "sScrollFoot": "dataTables_scrollFoot",
 195+ "sScrollFootInner": "dataTables_scrollFootInner",
 196+
 197+ /* Misc */
 198+ "sFooterTH": ""
 199+ };
 200+
 201+ /*
 202+ * Variable: oJUIClasses
 203+ * Purpose: Storage for the various classes that DataTables uses - jQuery UI suitable
 204+ * Scope: jQuery.fn.dataTableExt
 205+ */
 206+ _oExt.oJUIClasses = {
 207+ /* Two buttons buttons */
 208+ "sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
 209+ "sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
 210+ "sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
 211+ "sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
 212+ "sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
 213+ "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
 214+
 215+ /* Full numbers paging buttons */
 216+ "sPageButton": "fg-button ui-button ui-state-default",
 217+ "sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
 218+ "sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
 219+ "sPageFirst": "first ui-corner-tl ui-corner-bl",
 220+ "sPagePrevious": "previous",
 221+ "sPageNext": "next",
 222+ "sPageLast": "last ui-corner-tr ui-corner-br",
 223+
 224+ /* Stripping classes */
 225+ "sStripOdd": "odd",
 226+ "sStripEven": "even",
 227+
 228+ /* Empty row */
 229+ "sRowEmpty": "dataTables_empty",
 230+
 231+ /* Features */
 232+ "sWrapper": "dataTables_wrapper",
 233+ "sFilter": "dataTables_filter",
 234+ "sInfo": "dataTables_info",
 235+ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
 236+ "ui-buttonset-multi paging_", /* Note that the type is postfixed */
 237+ "sLength": "dataTables_length",
 238+ "sProcessing": "dataTables_processing",
 239+
 240+ /* Sorting */
 241+ "sSortAsc": "ui-state-default",
 242+ "sSortDesc": "ui-state-default",
 243+ "sSortable": "ui-state-default",
 244+ "sSortableAsc": "ui-state-default",
 245+ "sSortableDesc": "ui-state-default",
 246+ "sSortableNone": "ui-state-default",
 247+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
 248+ "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
 249+ "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
 250+ "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
 251+ "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
 252+ "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
 253+ "sSortJUIWrapper": "DataTables_sort_wrapper",
 254+
 255+ /* Scrolling */
 256+ "sScrollWrapper": "dataTables_scroll",
 257+ "sScrollHead": "dataTables_scrollHead ui-state-default",
 258+ "sScrollHeadInner": "dataTables_scrollHeadInner",
 259+ "sScrollBody": "dataTables_scrollBody",
 260+ "sScrollFoot": "dataTables_scrollFoot ui-state-default",
 261+ "sScrollFootInner": "dataTables_scrollFootInner",
 262+
 263+ /* Misc */
 264+ "sFooterTH": "ui-state-default"
 265+ };
 266+
 267+ /*
 268+ * Variable: oPagination
 269+ * Purpose: Container for the various type of pagination that dataTables supports
 270+ * Scope: jQuery.fn.dataTableExt
 271+ */
 272+ _oExt.oPagination = {
 273+ /*
 274+ * Variable: two_button
 275+ * Purpose: Standard two button (forward/back) pagination
 276+ * Scope: jQuery.fn.dataTableExt.oPagination
 277+ */
 278+ "two_button": {
 279+ /*
 280+ * Function: oPagination.two_button.fnInit
 281+ * Purpose: Initalise dom elements required for pagination with forward/back buttons only
 282+ * Returns: -
 283+ * Inputs: object:oSettings - dataTables settings object
 284+ * node:nPaging - the DIV which contains this pagination control
 285+ * function:fnCallbackDraw - draw function which must be called on update
 286+ */
 287+ "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
 288+ {
 289+ var nPrevious, nNext, nPreviousInner, nNextInner;
 290+
 291+ /* Store the next and previous elements in the oSettings object as they can be very
 292+ * usful for automation - particularly testing
 293+ */
 294+ if ( !oSettings.bJUI )
 295+ {
 296+ nPrevious = document.createElement( 'div' );
 297+ nNext = document.createElement( 'div' );
 298+ }
 299+ else
 300+ {
 301+ nPrevious = document.createElement( 'a' );
 302+ nNext = document.createElement( 'a' );
 303+
 304+ nNextInner = document.createElement('span');
 305+ nNextInner.className = oSettings.oClasses.sPageJUINext;
 306+ nNext.appendChild( nNextInner );
 307+
 308+ nPreviousInner = document.createElement('span');
 309+ nPreviousInner.className = oSettings.oClasses.sPageJUIPrev;
 310+ nPrevious.appendChild( nPreviousInner );
 311+ }
 312+
 313+ nPrevious.className = oSettings.oClasses.sPagePrevDisabled;
 314+ nNext.className = oSettings.oClasses.sPageNextDisabled;
 315+
 316+ nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious;
 317+ nNext.title = oSettings.oLanguage.oPaginate.sNext;
 318+
 319+ nPaging.appendChild( nPrevious );
 320+ nPaging.appendChild( nNext );
 321+
 322+ $(nPrevious).click( function() {
 323+ if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
 324+ {
 325+ /* Only draw when the page has actually changed */
 326+ fnCallbackDraw( oSettings );
 327+ }
 328+ } );
 329+
 330+ $(nNext).click( function() {
 331+ if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
 332+ {
 333+ fnCallbackDraw( oSettings );
 334+ }
 335+ } );
 336+
 337+ /* Take the brutal approach to cancelling text selection */
 338+ $(nPrevious).bind( 'selectstart', function () { return false; } );
 339+ $(nNext).bind( 'selectstart', function () { return false; } );
 340+
 341+ /* ID the first elements only */
 342+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
 343+ {
 344+ nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
 345+ nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
 346+ nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
 347+ }
 348+ },
 349+
 350+ /*
 351+ * Function: oPagination.two_button.fnUpdate
 352+ * Purpose: Update the two button pagination at the end of the draw
 353+ * Returns: -
 354+ * Inputs: object:oSettings - dataTables settings object
 355+ * function:fnCallbackDraw - draw function to call on page change
 356+ */
 357+ "fnUpdate": function ( oSettings, fnCallbackDraw )
 358+ {
 359+ if ( !oSettings.aanFeatures.p )
 360+ {
 361+ return;
 362+ }
 363+
 364+ /* Loop over each instance of the pager */
 365+ var an = oSettings.aanFeatures.p;
 366+ for ( var i=0, iLen=an.length ; i<iLen ; i++ )
 367+ {
 368+ if ( an[i].childNodes.length !== 0 )
 369+ {
 370+ an[i].childNodes[0].className =
 371+ ( oSettings._iDisplayStart === 0 ) ?
 372+ oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled;
 373+
 374+ an[i].childNodes[1].className =
 375+ ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ?
 376+ oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled;
 377+ }
 378+ }
 379+ }
 380+ },
 381+
 382+
 383+ /*
 384+ * Variable: iFullNumbersShowPages
 385+ * Purpose: Change the number of pages which can be seen
 386+ * Scope: jQuery.fn.dataTableExt.oPagination
 387+ */
 388+ "iFullNumbersShowPages": 5,
 389+
 390+ /*
 391+ * Variable: full_numbers
 392+ * Purpose: Full numbers pagination
 393+ * Scope: jQuery.fn.dataTableExt.oPagination
 394+ */
 395+ "full_numbers": {
 396+ /*
 397+ * Function: oPagination.full_numbers.fnInit
 398+ * Purpose: Initalise dom elements required for pagination with a list of the pages
 399+ * Returns: -
 400+ * Inputs: object:oSettings - dataTables settings object
 401+ * node:nPaging - the DIV which contains this pagination control
 402+ * function:fnCallbackDraw - draw function which must be called on update
 403+ */
 404+ "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
 405+ {
 406+ var nFirst = document.createElement( 'span' );
 407+ var nPrevious = document.createElement( 'span' );
 408+ var nList = document.createElement( 'span' );
 409+ var nNext = document.createElement( 'span' );
 410+ var nLast = document.createElement( 'span' );
 411+
 412+ nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst;
 413+ nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious;
 414+ nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext;
 415+ nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast;
 416+
 417+ var oClasses = oSettings.oClasses;
 418+ nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst;
 419+ nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious;
 420+ nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext;
 421+ nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast;
 422+
 423+ nPaging.appendChild( nFirst );
 424+ nPaging.appendChild( nPrevious );
 425+ nPaging.appendChild( nList );
 426+ nPaging.appendChild( nNext );
 427+ nPaging.appendChild( nLast );
 428+
 429+ $(nFirst).click( function () {
 430+ if ( oSettings.oApi._fnPageChange( oSettings, "first" ) )
 431+ {
 432+ fnCallbackDraw( oSettings );
 433+ }
 434+ } );
 435+
 436+ $(nPrevious).click( function() {
 437+ if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
 438+ {
 439+ fnCallbackDraw( oSettings );
 440+ }
 441+ } );
 442+
 443+ $(nNext).click( function() {
 444+ if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
 445+ {
 446+ fnCallbackDraw( oSettings );
 447+ }
 448+ } );
 449+
 450+ $(nLast).click( function() {
 451+ if ( oSettings.oApi._fnPageChange( oSettings, "last" ) )
 452+ {
 453+ fnCallbackDraw( oSettings );
 454+ }
 455+ } );
 456+
 457+ /* Take the brutal approach to cancelling text selection */
 458+ $('span', nPaging)
 459+ .bind( 'mousedown', function () { return false; } )
 460+ .bind( 'selectstart', function () { return false; } );
 461+
 462+ /* ID the first elements only */
 463+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
 464+ {
 465+ nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
 466+ nFirst.setAttribute( 'id', oSettings.sTableId+'_first' );
 467+ nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
 468+ nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
 469+ nLast.setAttribute( 'id', oSettings.sTableId+'_last' );
 470+ }
 471+ },
 472+
 473+ /*
 474+ * Function: oPagination.full_numbers.fnUpdate
 475+ * Purpose: Update the list of page buttons shows
 476+ * Returns: -
 477+ * Inputs: object:oSettings - dataTables settings object
 478+ * function:fnCallbackDraw - draw function to call on page change
 479+ */
 480+ "fnUpdate": function ( oSettings, fnCallbackDraw )
 481+ {
 482+ if ( !oSettings.aanFeatures.p )
 483+ {
 484+ return;
 485+ }
 486+
 487+ var iPageCount = _oExt.oPagination.iFullNumbersShowPages;
 488+ var iPageCountHalf = Math.floor(iPageCount / 2);
 489+ var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
 490+ var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
 491+ var sList = "";
 492+ var iStartButton, iEndButton, i, iLen;
 493+ var oClasses = oSettings.oClasses;
 494+
 495+ /* Pages calculation */
 496+ if (iPages < iPageCount)
 497+ {
 498+ iStartButton = 1;
 499+ iEndButton = iPages;
 500+ }
 501+ else
 502+ {
 503+ if (iCurrentPage <= iPageCountHalf)
 504+ {
 505+ iStartButton = 1;
 506+ iEndButton = iPageCount;
 507+ }
 508+ else
 509+ {
 510+ if (iCurrentPage >= (iPages - iPageCountHalf))
 511+ {
 512+ iStartButton = iPages - iPageCount + 1;
 513+ iEndButton = iPages;
 514+ }
 515+ else
 516+ {
 517+ iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
 518+ iEndButton = iStartButton + iPageCount - 1;
 519+ }
 520+ }
 521+ }
 522+
 523+ /* Build the dynamic list */
 524+ for ( i=iStartButton ; i<=iEndButton ; i++ )
 525+ {
 526+ if ( iCurrentPage != i )
 527+ {
 528+ sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>';
 529+ }
 530+ else
 531+ {
 532+ sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>';
 533+ }
 534+ }
 535+
 536+ /* Loop over each instance of the pager */
 537+ var an = oSettings.aanFeatures.p;
 538+ var anButtons, anStatic, nPaginateList;
 539+ var fnClick = function() {
 540+ /* Use the information in the element to jump to the required page */
 541+ var iTarget = (this.innerHTML * 1) - 1;
 542+ oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength;
 543+ fnCallbackDraw( oSettings );
 544+ return false;
 545+ };
 546+ var fnFalse = function () { return false; };
 547+
 548+ for ( i=0, iLen=an.length ; i<iLen ; i++ )
 549+ {
 550+ if ( an[i].childNodes.length === 0 )
 551+ {
 552+ continue;
 553+ }
 554+
 555+ /* Build up the dynamic list forst - html and listeners */
 556+ var qjPaginateList = $('span:eq(2)', an[i]);
 557+ qjPaginateList.html( sList );
 558+ $('span', qjPaginateList).click( fnClick ).bind( 'mousedown', fnFalse )
 559+ .bind( 'selectstart', fnFalse );
 560+
 561+ /* Update the 'premanent botton's classes */
 562+ anButtons = an[i].getElementsByTagName('span');
 563+ anStatic = [
 564+ anButtons[0], anButtons[1],
 565+ anButtons[anButtons.length-2], anButtons[anButtons.length-1]
 566+ ];
 567+ $(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
 568+ if ( iCurrentPage == 1 )
 569+ {
 570+ anStatic[0].className += " "+oClasses.sPageButtonStaticDisabled;
 571+ anStatic[1].className += " "+oClasses.sPageButtonStaticDisabled;
 572+ }
 573+ else
 574+ {
 575+ anStatic[0].className += " "+oClasses.sPageButton;
 576+ anStatic[1].className += " "+oClasses.sPageButton;
 577+ }
 578+
 579+ if ( iPages === 0 || iCurrentPage == iPages || oSettings._iDisplayLength == -1 )
 580+ {
 581+ anStatic[2].className += " "+oClasses.sPageButtonStaticDisabled;
 582+ anStatic[3].className += " "+oClasses.sPageButtonStaticDisabled;
 583+ }
 584+ else
 585+ {
 586+ anStatic[2].className += " "+oClasses.sPageButton;
 587+ anStatic[3].className += " "+oClasses.sPageButton;
 588+ }
 589+ }
 590+ }
 591+ }
 592+ };
 593+
 594+ /*
 595+ * Variable: oSort
 596+ * Purpose: Wrapper for the sorting functions that can be used in DataTables
 597+ * Scope: jQuery.fn.dataTableExt
 598+ * Notes: The functions provided in this object are basically standard javascript sort
 599+ * functions - they expect two inputs which they then compare and then return a priority
 600+ * result. For each sort method added, two functions need to be defined, an ascending sort and
 601+ * a descending sort.
 602+ */
 603+ _oExt.oSort = {
 604+ /*
 605+ * text sorting
 606+ */
 607+ "string-asc": function ( a, b )
 608+ {
 609+ var x = a.toLowerCase();
 610+ var y = b.toLowerCase();
 611+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
 612+ },
 613+
 614+ "string-desc": function ( a, b )
 615+ {
 616+ var x = a.toLowerCase();
 617+ var y = b.toLowerCase();
 618+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
 619+ },
 620+
 621+
 622+ /*
 623+ * html sorting (ignore html tags)
 624+ */
 625+ "html-asc": function ( a, b )
 626+ {
 627+ var x = a.replace( /<.*?>/g, "" ).toLowerCase();
 628+ var y = b.replace( /<.*?>/g, "" ).toLowerCase();
 629+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
 630+ },
 631+
 632+ "html-desc": function ( a, b )
 633+ {
 634+ var x = a.replace( /<.*?>/g, "" ).toLowerCase();
 635+ var y = b.replace( /<.*?>/g, "" ).toLowerCase();
 636+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
 637+ },
 638+
 639+
 640+ /*
 641+ * date sorting
 642+ */
 643+ "date-asc": function ( a, b )
 644+ {
 645+ var x = Date.parse( a );
 646+ var y = Date.parse( b );
 647+
 648+ if ( isNaN(x) || x==="" )
 649+ {
 650+ x = Date.parse( "01/01/1970 00:00:00" );
 651+ }
 652+ if ( isNaN(y) || y==="" )
 653+ {
 654+ y = Date.parse( "01/01/1970 00:00:00" );
 655+ }
 656+
 657+ return x - y;
 658+ },
 659+
 660+ "date-desc": function ( a, b )
 661+ {
 662+ var x = Date.parse( a );
 663+ var y = Date.parse( b );
 664+
 665+ if ( isNaN(x) || x==="" )
 666+ {
 667+ x = Date.parse( "01/01/1970 00:00:00" );
 668+ }
 669+ if ( isNaN(y) || y==="" )
 670+ {
 671+ y = Date.parse( "01/01/1970 00:00:00" );
 672+ }
 673+
 674+ return y - x;
 675+ },
 676+
 677+
 678+ /*
 679+ * numerical sorting
 680+ */
 681+ "numeric-asc": function ( a, b )
 682+ {
 683+ var x = (a=="-" || a==="") ? 0 : a*1;
 684+ var y = (b=="-" || b==="") ? 0 : b*1;
 685+ return x - y;
 686+ },
 687+
 688+ "numeric-desc": function ( a, b )
 689+ {
 690+ var x = (a=="-" || a==="") ? 0 : a*1;
 691+ var y = (b=="-" || b==="") ? 0 : b*1;
 692+ return y - x;
 693+ }
 694+ };
 695+
 696+
 697+ /*
 698+ * Variable: aTypes
 699+ * Purpose: Container for the various type of type detection that dataTables supports
 700+ * Scope: jQuery.fn.dataTableExt
 701+ * Notes: The functions in this array are expected to parse a string to see if it is a data
 702+ * type that it recognises. If so then the function should return the name of the type (a
 703+ * corresponding sort function should be defined!), if the type is not recognised then the
 704+ * function should return null such that the parser and move on to check the next type.
 705+ * Note that ordering is important in this array - the functions are processed linearly,
 706+ * starting at index 0.
 707+ * Note that the input for these functions is always a string! It cannot be any other data
 708+ * type
 709+ */
 710+ _oExt.aTypes = [
 711+ /*
 712+ * Function: -
 713+ * Purpose: Check to see if a string is numeric
 714+ * Returns: string:'numeric' or null
 715+ * Inputs: string:sText - string to check
 716+ */
 717+ function ( sData )
 718+ {
 719+ /* Allow zero length strings as a number */
 720+ if ( sData.length === 0 )
 721+ {
 722+ return 'numeric';
 723+ }
 724+
 725+ var sValidFirstChars = "0123456789-";
 726+ var sValidChars = "0123456789.";
 727+ var Char;
 728+ var bDecimal = false;
 729+
 730+ /* Check for a valid first char (no period and allow negatives) */
 731+ Char = sData.charAt(0);
 732+ if (sValidFirstChars.indexOf(Char) == -1)
 733+ {
 734+ return null;
 735+ }
 736+
 737+ /* Check all the other characters are valid */
 738+ for ( var i=1 ; i<sData.length ; i++ )
 739+ {
 740+ Char = sData.charAt(i);
 741+ if (sValidChars.indexOf(Char) == -1)
 742+ {
 743+ return null;
 744+ }
 745+
 746+ /* Only allowed one decimal place... */
 747+ if ( Char == "." )
 748+ {
 749+ if ( bDecimal )
 750+ {
 751+ return null;
 752+ }
 753+ bDecimal = true;
 754+ }
 755+ }
 756+
 757+ return 'numeric';
 758+ },
 759+
 760+ /*
 761+ * Function: -
 762+ * Purpose: Check to see if a string is actually a formatted date
 763+ * Returns: string:'date' or null
 764+ * Inputs: string:sText - string to check
 765+ */
 766+ function ( sData )
 767+ {
 768+ var iParse = Date.parse(sData);
 769+ if ( (iParse !== null && !isNaN(iParse)) || sData.length === 0 )
 770+ {
 771+ return 'date';
 772+ }
 773+ return null;
 774+ },
 775+
 776+ /*
 777+ * Function: -
 778+ * Purpose: Check to see if a string should be treated as an HTML string
 779+ * Returns: string:'html' or null
 780+ * Inputs: string:sText - string to check
 781+ */
 782+ function ( sData )
 783+ {
 784+ if ( sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
 785+ {
 786+ return 'html';
 787+ }
 788+ return null;
 789+ }
 790+ ];
 791+
 792+ /*
 793+ * Function: fnVersionCheck
 794+ * Purpose: Check a version string against this version of DataTables. Useful for plug-ins
 795+ * Returns: bool:true -this version of DataTables is greater or equal to the required version
 796+ * false -this version of DataTales is not suitable
 797+ * Inputs: string:sVersion - the version to check against. May be in the following formats:
 798+ * "a", "a.b" or "a.b.c"
 799+ * Notes: This function will only check the first three parts of a version string. It is
 800+ * assumed that beta and dev versions will meet the requirements. This might change in future
 801+ */
 802+ _oExt.fnVersionCheck = function( sVersion )
 803+ {
 804+ /* This is cheap, but very effective */
 805+ var fnZPad = function (Zpad, count)
 806+ {
 807+ while(Zpad.length < count) {
 808+ Zpad += '0';
 809+ }
 810+ return Zpad;
 811+ };
 812+ var aThis = _oExt.sVersion.split('.');
 813+ var aThat = sVersion.split('.');
 814+ var sThis = '', sThat = '';
 815+
 816+ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
 817+ {
 818+ sThis += fnZPad( aThis[i], 3 );
 819+ sThat += fnZPad( aThat[i], 3 );
 820+ }
 821+
 822+ return parseInt(sThis, 10) >= parseInt(sThat, 10);
 823+ };
 824+
 825+ /*
 826+ * Variable: _oExternConfig
 827+ * Purpose: Store information for DataTables to access globally about other instances
 828+ * Scope: jQuery.fn.dataTableExt
 829+ */
 830+ _oExt._oExternConfig = {
 831+ /* int:iNextUnique - next unique number for an instance */
 832+ "iNextUnique": 0
 833+ };
 834+
 835+
 836+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 837+ * Section - DataTables prototype
 838+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 839+
 840+ /*
 841+ * Function: dataTable
 842+ * Purpose: DataTables information
 843+ * Returns: -
 844+ * Inputs: object:oInit - initalisation options for the table
 845+ */
 846+ $.fn.dataTable = function( oInit )
 847+ {
 848+ /*
 849+ * Function: classSettings
 850+ * Purpose: Settings container function for all 'class' properties which are required
 851+ * by dataTables
 852+ * Returns: -
 853+ * Inputs: -
 854+ */
 855+ function classSettings ()
 856+ {
 857+ this.fnRecordsTotal = function ()
 858+ {
 859+ if ( this.oFeatures.bServerSide ) {
 860+ return parseInt(this._iRecordsTotal, 10);
 861+ } else {
 862+ return this.aiDisplayMaster.length;
 863+ }
 864+ };
 865+
 866+ this.fnRecordsDisplay = function ()
 867+ {
 868+ if ( this.oFeatures.bServerSide ) {
 869+ return parseInt(this._iRecordsDisplay, 10);
 870+ } else {
 871+ return this.aiDisplay.length;
 872+ }
 873+ };
 874+
 875+ this.fnDisplayEnd = function ()
 876+ {
 877+ if ( this.oFeatures.bServerSide ) {
 878+ if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
 879+ return this._iDisplayStart+this.aiDisplay.length;
 880+ } else {
 881+ return Math.min( this._iDisplayStart+this._iDisplayLength,
 882+ this._iRecordsDisplay );
 883+ }
 884+ } else {
 885+ return this._iDisplayEnd;
 886+ }
 887+ };
 888+
 889+ /*
 890+ * Variable: oInstance
 891+ * Purpose: The DataTables object for this table
 892+ * Scope: jQuery.dataTable.classSettings
 893+ */
 894+ this.oInstance = null;
 895+
 896+ /*
 897+ * Variable: sInstance
 898+ * Purpose: Unique idendifier for each instance of the DataTables object
 899+ * Scope: jQuery.dataTable.classSettings
 900+ */
 901+ this.sInstance = null;
 902+
 903+ /*
 904+ * Variable: oFeatures
 905+ * Purpose: Indicate the enablement of key dataTable features
 906+ * Scope: jQuery.dataTable.classSettings
 907+ */
 908+ this.oFeatures = {
 909+ "bPaginate": true,
 910+ "bLengthChange": true,
 911+ "bFilter": true,
 912+ "bSort": true,
 913+ "bInfo": true,
 914+ "bAutoWidth": true,
 915+ "bProcessing": false,
 916+ "bSortClasses": true,
 917+ "bStateSave": false,
 918+ "bServerSide": false
 919+ };
 920+
 921+ /*
 922+ * Variable: oScroll
 923+ * Purpose: Container for scrolling options
 924+ * Scope: jQuery.dataTable.classSettings
 925+ */
 926+ this.oScroll = {
 927+ "sX": "",
 928+ "sXInner": "",
 929+ "sY": "",
 930+ "bCollapse": false,
 931+ "bInfinite": false,
 932+ "iLoadGap": 100,
 933+ "iBarWidth": 0,
 934+ "bAutoCss": true
 935+ };
 936+
 937+ /*
 938+ * Variable: aanFeatures
 939+ * Purpose: Array referencing the nodes which are used for the features
 940+ * Scope: jQuery.dataTable.classSettings
 941+ * Notes: The parameters of this object match what is allowed by sDom - i.e.
 942+ * 'l' - Length changing
 943+ * 'f' - Filtering input
 944+ * 't' - The table!
 945+ * 'i' - Information
 946+ * 'p' - Pagination
 947+ * 'r' - pRocessing
 948+ */
 949+ this.aanFeatures = [];
 950+
 951+ /*
 952+ * Variable: oLanguage
 953+ * Purpose: Store the language strings used by dataTables
 954+ * Scope: jQuery.dataTable.classSettings
 955+ * Notes: The words in the format _VAR_ are variables which are dynamically replaced
 956+ * by javascript
 957+ */
 958+ this.oLanguage = {
 959+ "sProcessing": "Processing...",
 960+ "sLengthMenu": "Show _MENU_ entries",
 961+ "sZeroRecords": "No matching records found",
 962+ "sEmptyTable": "No data available in table",
 963+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
 964+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
 965+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
 966+ "sInfoPostFix": "",
 967+ "sSearch": "Search:",
 968+ "sUrl": "",
 969+ "oPaginate": {
 970+ "sFirst": "First",
 971+ "sPrevious": "Previous",
 972+ "sNext": "Next",
 973+ "sLast": "Last"
 974+ },
 975+ "fnInfoCallback": null
 976+ };
 977+
 978+ /*
 979+ * Variable: aoData
 980+ * Purpose: Store data information
 981+ * Scope: jQuery.dataTable.classSettings
 982+ * Notes: This is an array of objects with the following parameters:
 983+ * int: _iId - internal id for tracking
 984+ * array: _aData - internal data - used for sorting / filtering etc
 985+ * node: nTr - display node
 986+ * array node: _anHidden - hidden TD nodes
 987+ * string: _sRowStripe
 988+ */
 989+ this.aoData = [];
 990+
 991+ /*
 992+ * Variable: aiDisplay
 993+ * Purpose: Array of indexes which are in the current display (after filtering etc)
 994+ * Scope: jQuery.dataTable.classSettings
 995+ */
 996+ this.aiDisplay = [];
 997+
 998+ /*
 999+ * Variable: aiDisplayMaster
 1000+ * Purpose: Array of indexes for display - no filtering
 1001+ * Scope: jQuery.dataTable.classSettings
 1002+ */
 1003+ this.aiDisplayMaster = [];
 1004+
 1005+ /*
 1006+ * Variable: aoColumns
 1007+ * Purpose: Store information about each column that is in use
 1008+ * Scope: jQuery.dataTable.classSettings
 1009+ */
 1010+ this.aoColumns = [];
 1011+
 1012+ /*
 1013+ * Variable: iNextId
 1014+ * Purpose: Store the next unique id to be used for a new row
 1015+ * Scope: jQuery.dataTable.classSettings
 1016+ */
 1017+ this.iNextId = 0;
 1018+
 1019+ /*
 1020+ * Variable: asDataSearch
 1021+ * Purpose: Search data array for regular expression searching
 1022+ * Scope: jQuery.dataTable.classSettings
 1023+ */
 1024+ this.asDataSearch = [];
 1025+
 1026+ /*
 1027+ * Variable: oPreviousSearch
 1028+ * Purpose: Store the previous search incase we want to force a re-search
 1029+ * or compare the old search to a new one
 1030+ * Scope: jQuery.dataTable.classSettings
 1031+ */
 1032+ this.oPreviousSearch = {
 1033+ "sSearch": "",
 1034+ "bRegex": false,
 1035+ "bSmart": true
 1036+ };
 1037+
 1038+ /*
 1039+ * Variable: aoPreSearchCols
 1040+ * Purpose: Store the previous search for each column
 1041+ * Scope: jQuery.dataTable.classSettings
 1042+ */
 1043+ this.aoPreSearchCols = [];
 1044+
 1045+ /*
 1046+ * Variable: aaSorting
 1047+ * Purpose: Sorting information
 1048+ * Scope: jQuery.dataTable.classSettings
 1049+ * Notes: Index 0 - column number
 1050+ * Index 1 - current sorting direction
 1051+ * Index 2 - index of asSorting for this column
 1052+ */
 1053+ this.aaSorting = [ [0, 'asc', 0] ];
 1054+
 1055+ /*
 1056+ * Variable: aaSortingFixed
 1057+ * Purpose: Sorting information that is always applied
 1058+ * Scope: jQuery.dataTable.classSettings
 1059+ */
 1060+ this.aaSortingFixed = null;
 1061+
 1062+ /*
 1063+ * Variable: asStripClasses
 1064+ * Purpose: Classes to use for the striping of a table
 1065+ * Scope: jQuery.dataTable.classSettings
 1066+ */
 1067+ this.asStripClasses = [];
 1068+
 1069+ /*
 1070+ * Variable: asDestoryStrips
 1071+ * Purpose: If restoring a table - we should restore it's striping classes as well
 1072+ * Scope: jQuery.dataTable.classSettings
 1073+ */
 1074+ this.asDestoryStrips = [];
 1075+
 1076+ /*
 1077+ * Variable: sDestroyWidth
 1078+ * Purpose: If restoring a table - we should restore it's width
 1079+ * Scope: jQuery.dataTable.classSettings
 1080+ */
 1081+ this.sDestroyWidth = 0;
 1082+
 1083+ /*
 1084+ * Variable: fnRowCallback
 1085+ * Purpose: Call this function every time a row is inserted (draw)
 1086+ * Scope: jQuery.dataTable.classSettings
 1087+ */
 1088+ this.fnRowCallback = null;
 1089+
 1090+ /*
 1091+ * Variable: fnHeaderCallback
 1092+ * Purpose: Callback function for the header on each draw
 1093+ * Scope: jQuery.dataTable.classSettings
 1094+ */
 1095+ this.fnHeaderCallback = null;
 1096+
 1097+ /*
 1098+ * Variable: fnFooterCallback
 1099+ * Purpose: Callback function for the footer on each draw
 1100+ * Scope: jQuery.dataTable.classSettings
 1101+ */
 1102+ this.fnFooterCallback = null;
 1103+
 1104+ /*
 1105+ * Variable: aoDrawCallback
 1106+ * Purpose: Array of callback functions for draw callback functions
 1107+ * Scope: jQuery.dataTable.classSettings
 1108+ * Notes: Each array element is an object with the following parameters:
 1109+ * function:fn - function to call
 1110+ * string:sName - name callback (feature). useful for arranging array
 1111+ */
 1112+ this.aoDrawCallback = [];
 1113+
 1114+ /*
 1115+ * Variable: fnInitComplete
 1116+ * Purpose: Callback function for when the table has been initalised
 1117+ * Scope: jQuery.dataTable.classSettings
 1118+ */
 1119+ this.fnInitComplete = null;
 1120+
 1121+ /*
 1122+ * Variable: sTableId
 1123+ * Purpose: Cache the table ID for quick access
 1124+ * Scope: jQuery.dataTable.classSettings
 1125+ */
 1126+ this.sTableId = "";
 1127+
 1128+ /*
 1129+ * Variable: nTable
 1130+ * Purpose: Cache the table node for quick access
 1131+ * Scope: jQuery.dataTable.classSettings
 1132+ */
 1133+ this.nTable = null;
 1134+
 1135+ /*
 1136+ * Variable: nTHead
 1137+ * Purpose: Permanent ref to the thead element
 1138+ * Scope: jQuery.dataTable.classSettings
 1139+ */
 1140+ this.nTHead = null;
 1141+
 1142+ /*
 1143+ * Variable: nTFoot
 1144+ * Purpose: Permanent ref to the tfoot element - if it exists
 1145+ * Scope: jQuery.dataTable.classSettings
 1146+ */
 1147+ this.nTFoot = null;
 1148+
 1149+ /*
 1150+ * Variable: nTBody
 1151+ * Purpose: Permanent ref to the tbody element
 1152+ * Scope: jQuery.dataTable.classSettings
 1153+ */
 1154+ this.nTBody = null;
 1155+
 1156+ /*
 1157+ * Variable: nTableWrapper
 1158+ * Purpose: Cache the wrapper node (contains all DataTables controlled elements)
 1159+ * Scope: jQuery.dataTable.classSettings
 1160+ */
 1161+ this.nTableWrapper = null;
 1162+
 1163+ /*
 1164+ * Variable: bInitialised
 1165+ * Purpose: Indicate if all required information has been read in
 1166+ * Scope: jQuery.dataTable.classSettings
 1167+ */
 1168+ this.bInitialised = false;
 1169+
 1170+ /*
 1171+ * Variable: aoOpenRows
 1172+ * Purpose: Information about open rows
 1173+ * Scope: jQuery.dataTable.classSettings
 1174+ * Notes: Has the parameters 'nTr' and 'nParent'
 1175+ */
 1176+ this.aoOpenRows = [];
 1177+
 1178+ /*
 1179+ * Variable: sDom
 1180+ * Purpose: Dictate the positioning that the created elements will take
 1181+ * Scope: jQuery.dataTable.classSettings
 1182+ * Notes:
 1183+ * The following options are allowed:
 1184+ * 'l' - Length changing
 1185+ * 'f' - Filtering input
 1186+ * 't' - The table!
 1187+ * 'i' - Information
 1188+ * 'p' - Pagination
 1189+ * 'r' - pRocessing
 1190+ * The following constants are allowed:
 1191+ * 'H' - jQueryUI theme "header" classes
 1192+ * 'F' - jQueryUI theme "footer" classes
 1193+ * The following syntax is expected:
 1194+ * '<' and '>' - div elements
 1195+ * '<"class" and '>' - div with a class
 1196+ * Examples:
 1197+ * '<"wrapper"flipt>', '<lf<t>ip>'
 1198+ */
 1199+ this.sDom = 'lfrtip';
 1200+
 1201+ /*
 1202+ * Variable: sPaginationType
 1203+ * Purpose: Note which type of sorting should be used
 1204+ * Scope: jQuery.dataTable.classSettings
 1205+ */
 1206+ this.sPaginationType = "two_button";
 1207+
 1208+ /*
 1209+ * Variable: iCookieDuration
 1210+ * Purpose: The cookie duration (for bStateSave) in seconds - default 2 hours
 1211+ * Scope: jQuery.dataTable.classSettings
 1212+ */
 1213+ this.iCookieDuration = 60 * 60 * 2;
 1214+
 1215+ /*
 1216+ * Variable: sCookiePrefix
 1217+ * Purpose: The cookie name prefix
 1218+ * Scope: jQuery.dataTable.classSettings
 1219+ */
 1220+ this.sCookiePrefix = "SpryMedia_DataTables_";
 1221+
 1222+ /*
 1223+ * Variable: fnCookieCallback
 1224+ * Purpose: Callback function for cookie creation
 1225+ * Scope: jQuery.dataTable.classSettings
 1226+ */
 1227+ this.fnCookieCallback = null;
 1228+
 1229+ /*
 1230+ * Variable: aoStateSave
 1231+ * Purpose: Array of callback functions for state saving
 1232+ * Scope: jQuery.dataTable.classSettings
 1233+ * Notes: Each array element is an object with the following parameters:
 1234+ * function:fn - function to call. Takes two parameters, oSettings and the JSON string to
 1235+ * save that has been thus far created. Returns a JSON string to be inserted into a
 1236+ * json object (i.e. '"param": [ 0, 1, 2]')
 1237+ * string:sName - name of callback
 1238+ */
 1239+ this.aoStateSave = [];
 1240+
 1241+ /*
 1242+ * Variable: aoStateLoad
 1243+ * Purpose: Array of callback functions for state loading
 1244+ * Scope: jQuery.dataTable.classSettings
 1245+ * Notes: Each array element is an object with the following parameters:
 1246+ * function:fn - function to call. Takes two parameters, oSettings and the object stored.
 1247+ * May return false to cancel state loading.
 1248+ * string:sName - name of callback
 1249+ */
 1250+ this.aoStateLoad = [];
 1251+
 1252+ /*
 1253+ * Variable: oLoadedState
 1254+ * Purpose: State that was loaded from the cookie. Useful for back reference
 1255+ * Scope: jQuery.dataTable.classSettings
 1256+ */
 1257+ this.oLoadedState = null;
 1258+
 1259+ /*
 1260+ * Variable: sAjaxSource
 1261+ * Purpose: Source url for AJAX data for the table
 1262+ * Scope: jQuery.dataTable.classSettings
 1263+ */
 1264+ this.sAjaxSource = null;
 1265+
 1266+ /*
 1267+ * Variable: bAjaxDataGet
 1268+ * Purpose: Note if draw should be blocked while getting data
 1269+ * Scope: jQuery.dataTable.classSettings
 1270+ */
 1271+ this.bAjaxDataGet = true;
 1272+
 1273+ /*
 1274+ * Variable: fnServerData
 1275+ * Purpose: Function to get the server-side data - can be overruled by the developer
 1276+ * Scope: jQuery.dataTable.classSettings
 1277+ */
 1278+ this.fnServerData = function ( url, data, callback ) {
 1279+ $.ajax( {
 1280+ "url": url,
 1281+ "data": data,
 1282+ "success": callback,
 1283+ "dataType": "json",
 1284+ "cache": false,
 1285+ "error": function (xhr, error, thrown) {
 1286+ if ( error == "parsererror" ) {
 1287+ alert( "DataTables warning: JSON data from server could not be parsed. "+
 1288+ "This is caused by a JSON formatting error." );
 1289+ }
 1290+ }
 1291+ } );
 1292+ };
 1293+
 1294+ /*
 1295+ * Variable: fnFormatNumber
 1296+ * Purpose: Format numbers for display
 1297+ * Scope: jQuery.dataTable.classSettings
 1298+ */
 1299+ this.fnFormatNumber = function ( iIn )
 1300+ {
 1301+ if ( iIn < 1000 )
 1302+ {
 1303+ /* A small optimisation for what is likely to be the vast majority of use cases */
 1304+ return iIn;
 1305+ }
 1306+ else
 1307+ {
 1308+ var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
 1309+
 1310+ for ( var i=0 ; i<iLen ; i++ )
 1311+ {
 1312+ if ( i%3 === 0 && i !== 0 )
 1313+ {
 1314+ out = ','+out;
 1315+ }
 1316+ out = a[iLen-i-1]+out;
 1317+ }
 1318+ }
 1319+ return out;
 1320+ };
 1321+
 1322+ /*
 1323+ * Variable: aLengthMenu
 1324+ * Purpose: List of options that can be used for the user selectable length menu
 1325+ * Scope: jQuery.dataTable.classSettings
 1326+ * Note: This varaible can take for form of a 1D array, in which case the value and the
 1327+ * displayed value in the menu are the same, or a 2D array in which case the value comes
 1328+ * from the first array, and the displayed value to the end user comes from the second
 1329+ * array. 2D example: [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, 'All' ] ];
 1330+ */
 1331+ this.aLengthMenu = [ 10, 25, 50, 100 ];
 1332+
 1333+ /*
 1334+ * Variable: iDraw
 1335+ * Purpose: Counter for the draws that the table does. Also used as a tracker for
 1336+ * server-side processing
 1337+ * Scope: jQuery.dataTable.classSettings
 1338+ */
 1339+ this.iDraw = 0;
 1340+
 1341+ /*
 1342+ * Variable: bDrawing
 1343+ * Purpose: Indicate if a redraw is being done - useful for Ajax
 1344+ * Scope: jQuery.dataTable.classSettings
 1345+ */
 1346+ this.bDrawing = 0;
 1347+
 1348+ /*
 1349+ * Variable: iDrawError
 1350+ * Purpose: Last draw error
 1351+ * Scope: jQuery.dataTable.classSettings
 1352+ */
 1353+ this.iDrawError = -1;
 1354+
 1355+ /*
 1356+ * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd
 1357+ * Purpose: Display length variables
 1358+ * Scope: jQuery.dataTable.classSettings
 1359+ * Notes: These variable must NOT be used externally to get the data length. Rather, use
 1360+ * the fnRecordsTotal() (etc) functions.
 1361+ */
 1362+ this._iDisplayLength = 10;
 1363+ this._iDisplayStart = 0;
 1364+ this._iDisplayEnd = 10;
 1365+
 1366+ /*
 1367+ * Variable: _iRecordsTotal, _iRecordsDisplay
 1368+ * Purpose: Display length variables used for server side processing
 1369+ * Scope: jQuery.dataTable.classSettings
 1370+ * Notes: These variable must NOT be used externally to get the data length. Rather, use
 1371+ * the fnRecordsTotal() (etc) functions.
 1372+ */
 1373+ this._iRecordsTotal = 0;
 1374+ this._iRecordsDisplay = 0;
 1375+
 1376+ /*
 1377+ * Variable: bJUI
 1378+ * Purpose: Should we add the markup needed for jQuery UI theming?
 1379+ * Scope: jQuery.dataTable.classSettings
 1380+ */
 1381+ this.bJUI = false;
 1382+
 1383+ /*
 1384+ * Variable: bJUI
 1385+ * Purpose: Should we add the markup needed for jQuery UI theming?
 1386+ * Scope: jQuery.dataTable.classSettings
 1387+ */
 1388+ this.oClasses = _oExt.oStdClasses;
 1389+
 1390+ /*
 1391+ * Variable: bFiltered and bSorted
 1392+ * Purpose: Flags to allow callback functions to see what actions have been performed
 1393+ * Scope: jQuery.dataTable.classSettings
 1394+ */
 1395+ this.bFiltered = false;
 1396+ this.bSorted = false;
 1397+
 1398+ /*
 1399+ * Variable: oInit
 1400+ * Purpose: Initialisation object that is used for the table
 1401+ * Scope: jQuery.dataTable.classSettings
 1402+ */
 1403+ this.oInit = null;
 1404+ }
 1405+
 1406+ /*
 1407+ * Variable: oApi
 1408+ * Purpose: Container for publicly exposed 'private' functions
 1409+ * Scope: jQuery.dataTable
 1410+ */
 1411+ this.oApi = {};
 1412+
 1413+
 1414+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 1415+ * Section - API functions
 1416+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 1417+
 1418+ /*
 1419+ * Function: fnDraw
 1420+ * Purpose: Redraw the table
 1421+ * Returns: -
 1422+ * Inputs: bool:bComplete - Refilter and resort (if enabled) the table before the draw.
 1423+ * Optional: default - true
 1424+ */
 1425+ this.fnDraw = function( bComplete )
 1426+ {
 1427+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1428+ if ( typeof bComplete != 'undefined' && bComplete === false )
 1429+ {
 1430+ _fnCalculateEnd( oSettings );
 1431+ _fnDraw( oSettings );
 1432+ }
 1433+ else
 1434+ {
 1435+ _fnReDraw( oSettings );
 1436+ }
 1437+ };
 1438+
 1439+ /*
 1440+ * Function: fnFilter
 1441+ * Purpose: Filter the input based on data
 1442+ * Returns: -
 1443+ * Inputs: string:sInput - string to filter the table on
 1444+ * int:iColumn - optional - column to limit filtering to
 1445+ * bool:bRegex - optional - treat as regular expression or not - default false
 1446+ * bool:bSmart - optional - perform smart filtering or not - default true
 1447+ * bool:bShowGlobal - optional - show the input global filter in it's input box(es)
 1448+ * - default true
 1449+ */
 1450+ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal )
 1451+ {
 1452+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1453+
 1454+ if ( !oSettings.oFeatures.bFilter )
 1455+ {
 1456+ return;
 1457+ }
 1458+
 1459+ if ( typeof bRegex == 'undefined' )
 1460+ {
 1461+ bRegex = false;
 1462+ }
 1463+
 1464+ if ( typeof bSmart == 'undefined' )
 1465+ {
 1466+ bSmart = true;
 1467+ }
 1468+
 1469+ if ( typeof bShowGlobal == 'undefined' )
 1470+ {
 1471+ bShowGlobal = true;
 1472+ }
 1473+
 1474+ if ( typeof iColumn == "undefined" || iColumn === null )
 1475+ {
 1476+ /* Global filter */
 1477+ _fnFilterComplete( oSettings, {
 1478+ "sSearch":sInput,
 1479+ "bRegex": bRegex,
 1480+ "bSmart": bSmart
 1481+ }, 1 );
 1482+
 1483+ if ( bShowGlobal && typeof oSettings.aanFeatures.f != 'undefined' )
 1484+ {
 1485+ var n = oSettings.aanFeatures.f;
 1486+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
 1487+ {
 1488+ $('input', n[i]).val( sInput );
 1489+ }
 1490+ }
 1491+ }
 1492+ else
 1493+ {
 1494+ /* Single column filter */
 1495+ oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput;
 1496+ oSettings.aoPreSearchCols[ iColumn ].bRegex = bRegex;
 1497+ oSettings.aoPreSearchCols[ iColumn ].bSmart = bSmart;
 1498+ _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
 1499+ }
 1500+ };
 1501+
 1502+ /*
 1503+ * Function: fnSettings
 1504+ * Purpose: Get the settings for a particular table for extern. manipulation
 1505+ * Returns: -
 1506+ * Inputs: -
 1507+ */
 1508+ this.fnSettings = function( nNode )
 1509+ {
 1510+ return _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1511+ };
 1512+
 1513+ /*
 1514+ * Function: fnVersionCheck
 1515+ * Notes: The function is the same as the 'static' function provided in the ext variable
 1516+ */
 1517+ this.fnVersionCheck = _oExt.fnVersionCheck;
 1518+
 1519+ /*
 1520+ * Function: fnSort
 1521+ * Purpose: Sort the table by a particular row
 1522+ * Returns: -
 1523+ * Inputs: int:iCol - the data index to sort on. Note that this will
 1524+ * not match the 'display index' if you have hidden data entries
 1525+ */
 1526+ this.fnSort = function( aaSort )
 1527+ {
 1528+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1529+ oSettings.aaSorting = aaSort;
 1530+ _fnSort( oSettings );
 1531+ };
 1532+
 1533+ /*
 1534+ * Function: fnSortListener
 1535+ * Purpose: Attach a sort listener to an element for a given column
 1536+ * Returns: -
 1537+ * Inputs: node:nNode - the element to attach the sort listener to
 1538+ * int:iColumn - the column that a click on this node will sort on
 1539+ * function:fnCallback - callback function when sort is run - optional
 1540+ */
 1541+ this.fnSortListener = function( nNode, iColumn, fnCallback )
 1542+ {
 1543+ _fnSortAttachListener( _fnSettingsFromNode( this[_oExt.iApiIndex] ), nNode, iColumn,
 1544+ fnCallback );
 1545+ };
 1546+
 1547+ /*
 1548+ * Function: fnAddData
 1549+ * Purpose: Add new row(s) into the table
 1550+ * Returns: array int: array of indexes (aoData) which have been added (zero length on error)
 1551+ * Inputs: array:mData - the data to be added. The length must match
 1552+ * the original data from the DOM
 1553+ * or
 1554+ * array array:mData - 2D array of data to be added
 1555+ * bool:bRedraw - redraw the table or not - default true
 1556+ * Notes: Warning - the refilter here will cause the table to redraw
 1557+ * starting at zero
 1558+ * Notes: Thanks to Yekimov Denis for contributing the basis for this function!
 1559+ */
 1560+ this.fnAddData = function( mData, bRedraw )
 1561+ {
 1562+ if ( mData.length === 0 )
 1563+ {
 1564+ return [];
 1565+ }
 1566+
 1567+ var aiReturn = [];
 1568+ var iTest;
 1569+
 1570+ /* Find settings from table node */
 1571+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1572+
 1573+ /* Check if we want to add multiple rows or not */
 1574+ if ( typeof mData[0] == "object" )
 1575+ {
 1576+ for ( var i=0 ; i<mData.length ; i++ )
 1577+ {
 1578+ iTest = _fnAddData( oSettings, mData[i] );
 1579+ if ( iTest == -1 )
 1580+ {
 1581+ return aiReturn;
 1582+ }
 1583+ aiReturn.push( iTest );
 1584+ }
 1585+ }
 1586+ else
 1587+ {
 1588+ iTest = _fnAddData( oSettings, mData );
 1589+ if ( iTest == -1 )
 1590+ {
 1591+ return aiReturn;
 1592+ }
 1593+ aiReturn.push( iTest );
 1594+ }
 1595+
 1596+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 1597+
 1598+ if ( typeof bRedraw == 'undefined' || bRedraw )
 1599+ {
 1600+ _fnReDraw( oSettings );
 1601+ }
 1602+ return aiReturn;
 1603+ };
 1604+
 1605+ /*
 1606+ * Function: fnDeleteRow
 1607+ * Purpose: Remove a row for the table
 1608+ * Returns: array:aReturn - the row that was deleted
 1609+ * Inputs: mixed:mTarget -
 1610+ * int: - index of aoData to be deleted, or
 1611+ * node(TR): - TR element you want to delete
 1612+ * function:fnCallBack - callback function - default null
 1613+ * bool:bRedraw - redraw the table or not - default true
 1614+ */
 1615+ this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
 1616+ {
 1617+ /* Find settings from table node */
 1618+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1619+ var i, iAODataIndex;
 1620+
 1621+ iAODataIndex = (typeof mTarget == 'object') ?
 1622+ _fnNodeToDataIndex(oSettings, mTarget) : mTarget;
 1623+
 1624+ /* Return the data array from this row */
 1625+ var oData = oSettings.aoData.splice( iAODataIndex, 1 );
 1626+
 1627+ /* Remove the target row from the search array */
 1628+ var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
 1629+ oSettings.asDataSearch.splice( iDisplayIndex, 1 );
 1630+
 1631+ /* Delete from the display arrays */
 1632+ _fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
 1633+ _fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
 1634+
 1635+ /* If there is a user callback function - call it */
 1636+ if ( typeof fnCallBack == "function" )
 1637+ {
 1638+ fnCallBack.call( this, oSettings, oData );
 1639+ }
 1640+
 1641+ /* Check for an 'overflow' they case for dislaying the table */
 1642+ if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length )
 1643+ {
 1644+ oSettings._iDisplayStart -= oSettings._iDisplayLength;
 1645+ if ( oSettings._iDisplayStart < 0 )
 1646+ {
 1647+ oSettings._iDisplayStart = 0;
 1648+ }
 1649+ }
 1650+
 1651+ if ( typeof bRedraw == 'undefined' || bRedraw )
 1652+ {
 1653+ _fnCalculateEnd( oSettings );
 1654+ _fnDraw( oSettings );
 1655+ }
 1656+
 1657+ return oData;
 1658+ };
 1659+
 1660+ /*
 1661+ * Function: fnClearTable
 1662+ * Purpose: Quickly and simply clear a table
 1663+ * Returns: -
 1664+ * Inputs: bool:bRedraw - redraw the table or not - default true
 1665+ * Notes: Thanks to Yekimov Denis for contributing the basis for this function!
 1666+ */
 1667+ this.fnClearTable = function( bRedraw )
 1668+ {
 1669+ /* Find settings from table node */
 1670+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1671+ _fnClearTable( oSettings );
 1672+
 1673+ if ( typeof bRedraw == 'undefined' || bRedraw )
 1674+ {
 1675+ _fnDraw( oSettings );
 1676+ }
 1677+ };
 1678+
 1679+ /*
 1680+ * Function: fnOpen
 1681+ * Purpose: Open a display row (append a row after the row in question)
 1682+ * Returns: node:nNewRow - the row opened
 1683+ * Inputs: node:nTr - the table row to 'open'
 1684+ * string:sHtml - the HTML to put into the row
 1685+ * string:sClass - class to give the new TD cell
 1686+ */
 1687+ this.fnOpen = function( nTr, sHtml, sClass )
 1688+ {
 1689+ /* Find settings from table node */
 1690+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1691+
 1692+ /* the old open one if there is one */
 1693+ this.fnClose( nTr );
 1694+
 1695+ var nNewRow = document.createElement("tr");
 1696+ var nNewCell = document.createElement("td");
 1697+ nNewRow.appendChild( nNewCell );
 1698+ nNewCell.className = sClass;
 1699+ nNewCell.colSpan = _fnVisbleColumns( oSettings );
 1700+ nNewCell.innerHTML = sHtml;
 1701+
 1702+ /* If the nTr isn't on the page at the moment - then we don't insert at the moment */
 1703+ var nTrs = $('tr', oSettings.nTBody);
 1704+ if ( $.inArray(nTr, nTrs) != -1 )
 1705+ {
 1706+ $(nNewRow).insertAfter(nTr);
 1707+ }
 1708+
 1709+ oSettings.aoOpenRows.push( {
 1710+ "nTr": nNewRow,
 1711+ "nParent": nTr
 1712+ } );
 1713+
 1714+ return nNewRow;
 1715+ };
 1716+
 1717+ /*
 1718+ * Function: fnClose
 1719+ * Purpose: Close a display row
 1720+ * Returns: int: 0 (success) or 1 (failed)
 1721+ * Inputs: node:nTr - the table row to 'close'
 1722+ */
 1723+ this.fnClose = function( nTr )
 1724+ {
 1725+ /* Find settings from table node */
 1726+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1727+
 1728+ for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
 1729+ {
 1730+ if ( oSettings.aoOpenRows[i].nParent == nTr )
 1731+ {
 1732+ var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
 1733+ if ( nTrParent )
 1734+ {
 1735+ /* Remove it if it is currently on display */
 1736+ nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
 1737+ }
 1738+ oSettings.aoOpenRows.splice( i, 1 );
 1739+ return 0;
 1740+ }
 1741+ }
 1742+ return 1;
 1743+ };
 1744+
 1745+ /*
 1746+ * Function: fnGetData
 1747+ * Purpose: Return an array with the data which is used to make up the table
 1748+ * Returns: array array string: 2d data array ([row][column]) or array string: 1d data array
 1749+ * or
 1750+ * array string (if iRow specified)
 1751+ * Inputs: mixed:mRow - optional - if not present, then the full 2D array for the table
 1752+ * if given then:
 1753+ * int: - return 1D array for aoData entry of this index
 1754+ * node(TR): - return 1D array for this TR element
 1755+ * Inputs: int:iRow - optional - if present then the array returned will be the data for
 1756+ * the row with the index 'iRow'
 1757+ */
 1758+ this.fnGetData = function( mRow )
 1759+ {
 1760+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1761+
 1762+ if ( typeof mRow != 'undefined' )
 1763+ {
 1764+ var iRow = (typeof mRow == 'object') ?
 1765+ _fnNodeToDataIndex(oSettings, mRow) : mRow;
 1766+ return oSettings.aoData[iRow]._aData;
 1767+ }
 1768+ return _fnGetDataMaster( oSettings );
 1769+ };
 1770+
 1771+ /*
 1772+ * Function: fnGetNodes
 1773+ * Purpose: Return an array with the TR nodes used for drawing the table
 1774+ * Returns: array node: TR elements
 1775+ * or
 1776+ * node (if iRow specified)
 1777+ * Inputs: int:iRow - optional - if present then the array returned will be the node for
 1778+ * the row with the index 'iRow'
 1779+ */
 1780+ this.fnGetNodes = function( iRow )
 1781+ {
 1782+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1783+
 1784+ if ( typeof iRow != 'undefined' )
 1785+ {
 1786+ return oSettings.aoData[iRow].nTr;
 1787+ }
 1788+ return _fnGetTrNodes( oSettings );
 1789+ };
 1790+
 1791+ /*
 1792+ * Function: fnGetPosition
 1793+ * Purpose: Get the array indexes of a particular cell from it's DOM element
 1794+ * Returns: int: - row index, or array[ int, int, int ]: - row index, column index (visible)
 1795+ * and column index including hidden columns
 1796+ * Inputs: node:nNode - this can either be a TR or a TD in the table, the return is
 1797+ * dependent on this input
 1798+ */
 1799+ this.fnGetPosition = function( nNode )
 1800+ {
 1801+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1802+ var i;
 1803+
 1804+ if ( nNode.nodeName.toUpperCase() == "TR" )
 1805+ {
 1806+ return _fnNodeToDataIndex(oSettings, nNode);
 1807+ }
 1808+ else if ( nNode.nodeName.toUpperCase() == "TD" )
 1809+ {
 1810+ var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode);
 1811+ var iCorrector = 0;
 1812+ for ( var j=0 ; j<oSettings.aoColumns.length ; j++ )
 1813+ {
 1814+ if ( oSettings.aoColumns[j].bVisible )
 1815+ {
 1816+ if ( oSettings.aoData[iDataIndex].nTr.getElementsByTagName('td')[j-iCorrector] == nNode )
 1817+ {
 1818+ return [ iDataIndex, j-iCorrector, j ];
 1819+ }
 1820+ }
 1821+ else
 1822+ {
 1823+ iCorrector++;
 1824+ }
 1825+ }
 1826+ }
 1827+ return null;
 1828+ };
 1829+
 1830+ /*
 1831+ * Function: fnUpdate
 1832+ * Purpose: Update a table cell or row
 1833+ * Returns: int: 0 okay, 1 error
 1834+ * Inputs: array string 'or' string:mData - data to update the cell/row with
 1835+ * mixed:mRow -
 1836+ * int: - index of aoData to be updated, or
 1837+ * node(TR): - TR element you want to update
 1838+ * int:iColumn - the column to update - optional (not used of mData is 2D)
 1839+ * bool:bRedraw - redraw the table or not - default true
 1840+ * bool:bAction - perform predraw actions or not (you will want this as 'true' if
 1841+ * you have bRedraw as true) - default true
 1842+ */
 1843+ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
 1844+ {
 1845+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1846+ var iVisibleColumn;
 1847+ var sDisplay;
 1848+ var iRow = (typeof mRow == 'object') ?
 1849+ _fnNodeToDataIndex(oSettings, mRow) : mRow;
 1850+
 1851+ if ( typeof mData != 'object' )
 1852+ {
 1853+ sDisplay = mData;
 1854+ oSettings.aoData[iRow]._aData[iColumn] = sDisplay;
 1855+
 1856+ if ( oSettings.aoColumns[iColumn].fnRender !== null )
 1857+ {
 1858+ sDisplay = oSettings.aoColumns[iColumn].fnRender( {
 1859+ "iDataRow": iRow,
 1860+ "iDataColumn": iColumn,
 1861+ "aData": oSettings.aoData[iRow]._aData,
 1862+ "oSettings": oSettings
 1863+ } );
 1864+
 1865+ if ( oSettings.aoColumns[iColumn].bUseRendered )
 1866+ {
 1867+ oSettings.aoData[iRow]._aData[iColumn] = sDisplay;
 1868+ }
 1869+ }
 1870+
 1871+ iVisibleColumn = _fnColumnIndexToVisible( oSettings, iColumn );
 1872+ if ( iVisibleColumn !== null )
 1873+ {
 1874+ oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML =
 1875+ sDisplay;
 1876+ }
 1877+ }
 1878+ else
 1879+ {
 1880+ if ( mData.length != oSettings.aoColumns.length )
 1881+ {
 1882+ _fnLog( oSettings, 0, 'An array passed to fnUpdate must have the same number of '+
 1883+ 'columns as the table in question - in this case '+oSettings.aoColumns.length );
 1884+ return 1;
 1885+ }
 1886+
 1887+ for ( var i=0 ; i<mData.length ; i++ )
 1888+ {
 1889+ sDisplay = mData[i];
 1890+ oSettings.aoData[iRow]._aData[i] = sDisplay;
 1891+
 1892+ if ( oSettings.aoColumns[i].fnRender !== null )
 1893+ {
 1894+ sDisplay = oSettings.aoColumns[i].fnRender( {
 1895+ "iDataRow": iRow,
 1896+ "iDataColumn": i,
 1897+ "aData": oSettings.aoData[iRow]._aData,
 1898+ "oSettings": oSettings
 1899+ } );
 1900+
 1901+ if ( oSettings.aoColumns[i].bUseRendered )
 1902+ {
 1903+ oSettings.aoData[iRow]._aData[i] = sDisplay;
 1904+ }
 1905+ }
 1906+
 1907+ iVisibleColumn = _fnColumnIndexToVisible( oSettings, i );
 1908+ if ( iVisibleColumn !== null )
 1909+ {
 1910+ oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML =
 1911+ sDisplay;
 1912+ }
 1913+ }
 1914+ }
 1915+
 1916+ /* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
 1917+ * will rebuild the search array - however, the redraw might be disabled by the user)
 1918+ */
 1919+ var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
 1920+ oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow( oSettings,
 1921+ oSettings.aoData[iRow]._aData );
 1922+
 1923+ /* Perform pre-draw actions */
 1924+ if ( typeof bAction == 'undefined' || bAction )
 1925+ {
 1926+ _fnAjustColumnSizing( oSettings );
 1927+ }
 1928+
 1929+ /* Redraw the table */
 1930+ if ( typeof bRedraw == 'undefined' || bRedraw )
 1931+ {
 1932+ _fnReDraw( oSettings );
 1933+ }
 1934+ return 0;
 1935+ };
 1936+
 1937+
 1938+ /*
 1939+ * Function: fnShowColoumn
 1940+ * Purpose: Show a particular column
 1941+ * Returns: -
 1942+ * Inputs: int:iCol - the column whose display should be changed
 1943+ * bool:bShow - show (true) or hide (false) the column
 1944+ * bool:bRedraw - redraw the table or not - default true
 1945+ */
 1946+ this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
 1947+ {
 1948+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 1949+ var i, iLen;
 1950+ var iColumns = oSettings.aoColumns.length;
 1951+ var nTd, anTds, nCell, anTrs, jqChildren;
 1952+
 1953+ /* No point in doing anything if we are requesting what is already true */
 1954+ if ( oSettings.aoColumns[iCol].bVisible == bShow )
 1955+ {
 1956+ return;
 1957+ }
 1958+
 1959+ var nTrHead = $('>tr', oSettings.nTHead)[0];
 1960+ var nTrFoot = $('>tr', oSettings.nTFoot)[0];
 1961+ var anTheadTh = [];
 1962+ var anTfootTh = [];
 1963+ for ( i=0 ; i<iColumns ; i++ )
 1964+ {
 1965+ anTheadTh.push( oSettings.aoColumns[i].nTh );
 1966+ anTfootTh.push( oSettings.aoColumns[i].nTf );
 1967+ }
 1968+
 1969+ /* Show the column */
 1970+ if ( bShow )
 1971+ {
 1972+ var iInsert = 0;
 1973+ for ( i=0 ; i<iCol ; i++ )
 1974+ {
 1975+ if ( oSettings.aoColumns[i].bVisible )
 1976+ {
 1977+ iInsert++;
 1978+ }
 1979+ }
 1980+
 1981+ /* Need to decide if we should use appendChild or insertBefore */
 1982+ if ( iInsert >= _fnVisbleColumns( oSettings ) )
 1983+ {
 1984+ nTrHead.appendChild( anTheadTh[iCol] );
 1985+ anTrs = $('>tr', oSettings.nTHead);
 1986+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
 1987+ {
 1988+ anTrs[i].appendChild( oSettings.aoColumns[iCol].anThExtra[i-1] );
 1989+ }
 1990+
 1991+ if ( nTrFoot )
 1992+ {
 1993+ nTrFoot.appendChild( anTfootTh[iCol] );
 1994+ anTrs = $('>tr', oSettings.nTFoot);
 1995+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
 1996+ {
 1997+ anTrs[i].appendChild( oSettings.aoColumns[iCol].anTfExtra[i-1] );
 1998+ }
 1999+ }
 2000+
 2001+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
 2002+ {
 2003+ nTd = oSettings.aoData[i]._anHidden[iCol];
 2004+ oSettings.aoData[i].nTr.appendChild( nTd );
 2005+ }
 2006+ }
 2007+ else
 2008+ {
 2009+ /* Which coloumn should we be inserting before? */
 2010+ var iBefore;
 2011+ for ( i=iCol ; i<iColumns ; i++ )
 2012+ {
 2013+ iBefore = _fnColumnIndexToVisible( oSettings, i );
 2014+ if ( iBefore !== null )
 2015+ {
 2016+ break;
 2017+ }
 2018+ }
 2019+
 2020+ nTrHead.insertBefore( anTheadTh[iCol], nTrHead.getElementsByTagName('th')[iBefore] );
 2021+ anTrs = $('>tr', oSettings.nTHead);
 2022+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
 2023+ {
 2024+ jqChildren = $(anTrs[i]).children();
 2025+ anTrs[i].insertBefore( oSettings.aoColumns[iCol].anThExtra[i-1], jqChildren[iBefore] );
 2026+ }
 2027+
 2028+ if ( nTrFoot )
 2029+ {
 2030+ nTrFoot.insertBefore( anTfootTh[iCol], nTrFoot.getElementsByTagName('th')[iBefore] );
 2031+ anTrs = $('>tr', oSettings.nTFoot);
 2032+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
 2033+ {
 2034+ jqChildren = $(anTrs[i]).children();
 2035+ anTrs[i].insertBefore( oSettings.aoColumns[iCol].anTfExtra[i-1], jqChildren[iBefore] );
 2036+ }
 2037+ }
 2038+
 2039+ anTds = _fnGetTdNodes( oSettings );
 2040+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
 2041+ {
 2042+ nTd = oSettings.aoData[i]._anHidden[iCol];
 2043+ oSettings.aoData[i].nTr.insertBefore( nTd, $('>td:eq('+iBefore+')',
 2044+ oSettings.aoData[i].nTr)[0] );
 2045+ }
 2046+ }
 2047+
 2048+ oSettings.aoColumns[iCol].bVisible = true;
 2049+ }
 2050+ else
 2051+ {
 2052+ /* Remove a column from display */
 2053+ nTrHead.removeChild( anTheadTh[iCol] );
 2054+ for ( i=0, iLen=oSettings.aoColumns[iCol].anThExtra.length ; i<iLen ; i++ )
 2055+ {
 2056+ nCell = oSettings.aoColumns[iCol].anThExtra[i];
 2057+ nCell.parentNode.removeChild( nCell );
 2058+ }
 2059+
 2060+ if ( nTrFoot )
 2061+ {
 2062+ nTrFoot.removeChild( anTfootTh[iCol] );
 2063+ for ( i=0, iLen=oSettings.aoColumns[iCol].anTfExtra.length ; i<iLen ; i++ )
 2064+ {
 2065+ nCell = oSettings.aoColumns[iCol].anTfExtra[i];
 2066+ nCell.parentNode.removeChild( nCell );
 2067+ }
 2068+ }
 2069+
 2070+ anTds = _fnGetTdNodes( oSettings );
 2071+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
 2072+ {
 2073+ nTd = anTds[ ( i*oSettings.aoColumns.length) + (iCol*1) ];
 2074+ oSettings.aoData[i]._anHidden[iCol] = nTd;
 2075+ nTd.parentNode.removeChild( nTd );
 2076+ }
 2077+
 2078+ oSettings.aoColumns[iCol].bVisible = false;
 2079+ }
 2080+
 2081+ /* If there are any 'open' rows, then we need to alter the colspan for this col change */
 2082+ for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
 2083+ {
 2084+ oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
 2085+ }
 2086+
 2087+ /* Do a redraw incase anything depending on the table columns needs it
 2088+ * (built-in: scrolling)
 2089+ */
 2090+ if ( typeof bRedraw == 'undefined' || bRedraw )
 2091+ {
 2092+ _fnAjustColumnSizing( oSettings );
 2093+ _fnDraw( oSettings );
 2094+ }
 2095+
 2096+ _fnSaveState( oSettings );
 2097+ };
 2098+
 2099+ /*
 2100+ * Function: fnPageChange
 2101+ * Purpose: Change the pagination
 2102+ * Returns: -
 2103+ * Inputs: string:sAction - paging action to take: "first", "previous", "next" or "last"
 2104+ * bool:bRedraw - redraw the table or not - optional - default true
 2105+ */
 2106+ this.fnPageChange = function ( sAction, bRedraw )
 2107+ {
 2108+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 2109+ _fnPageChange( oSettings, sAction );
 2110+ _fnCalculateEnd( oSettings );
 2111+
 2112+ if ( typeof bRedraw == 'undefined' || bRedraw )
 2113+ {
 2114+ _fnDraw( oSettings );
 2115+ }
 2116+ };
 2117+
 2118+ /*
 2119+ * Function: fnDestroy
 2120+ * Purpose: Destructor for the DataTable
 2121+ * Returns: -
 2122+ * Inputs: -
 2123+ */
 2124+ this.fnDestroy = function ( )
 2125+ {
 2126+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
 2127+ var nOrig = oSettings.nTableWrapper.parentNode;
 2128+ var nBody = oSettings.nTBody;
 2129+ var i, iLen;
 2130+
 2131+ /* Flag to note that the table is currently being destoryed - no action should be taken */
 2132+ oSettings.bDestroying = true;
 2133+
 2134+ /* Restore hidden columns */
 2135+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 2136+ {
 2137+ if ( oSettings.aoColumns[i].bVisible === false )
 2138+ {
 2139+ this.fnSetColumnVis( i, true );
 2140+ }
 2141+ }
 2142+
 2143+ /* If there is an 'empty' indicator row, remove it */
 2144+ $('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
 2145+
 2146+ /* When scrolling we had to break the table up - restore it */
 2147+ if ( oSettings.nTable != oSettings.nTHead.parentNode )
 2148+ {
 2149+ $('>thead', oSettings.nTable).remove();
 2150+ oSettings.nTable.appendChild( oSettings.nTHead );
 2151+ }
 2152+
 2153+ if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
 2154+ {
 2155+ $('>tfoot', oSettings.nTable).remove();
 2156+ oSettings.nTable.appendChild( oSettings.nTFoot );
 2157+ }
 2158+
 2159+ /* Remove the DataTables generated nodes, events and classes */
 2160+ oSettings.nTable.parentNode.removeChild( oSettings.nTable );
 2161+ $(oSettings.nTableWrapper).remove();
 2162+
 2163+ oSettings.aaSorting = [];
 2164+ oSettings.aaSortingFixed = [];
 2165+ _fnSortingClasses( oSettings );
 2166+
 2167+ $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripClasses.join(' ') );
 2168+
 2169+ if ( !oSettings.bJUI )
 2170+ {
 2171+ $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
 2172+ _oExt.oStdClasses.sSortableAsc,
 2173+ _oExt.oStdClasses.sSortableDesc,
 2174+ _oExt.oStdClasses.sSortableNone ].join(' ')
 2175+ );
 2176+ }
 2177+ else
 2178+ {
 2179+ $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
 2180+ _oExt.oJUIClasses.sSortableAsc,
 2181+ _oExt.oJUIClasses.sSortableDesc,
 2182+ _oExt.oJUIClasses.sSortableNone ].join(' ')
 2183+ );
 2184+ $('th span', oSettings.nTHead).remove();
 2185+ }
 2186+
 2187+ /* Add the TR elements back into the table in their original order */
 2188+ nOrig.appendChild( oSettings.nTable );
 2189+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
 2190+ {
 2191+ nBody.appendChild( oSettings.aoData[i].nTr );
 2192+ }
 2193+
 2194+ /* Restore the width of the original table */
 2195+ oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
 2196+
 2197+ /* If the were originally odd/even type classes - then we add them back here. Note
 2198+ * this is not fool proof (for example if not all rows as odd/even classes - but
 2199+ * it's a good effort without getting carried away
 2200+ */
 2201+ $('>tr:even', nBody).addClass( oSettings.asDestoryStrips[0] );
 2202+ $('>tr:odd', nBody).addClass( oSettings.asDestoryStrips[1] );
 2203+
 2204+ /* Remove the settings object from the settings array */
 2205+ for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
 2206+ {
 2207+ if ( _aoSettings[i] == oSettings )
 2208+ {
 2209+ _aoSettings.splice( i, 1 );
 2210+ }
 2211+ }
 2212+
 2213+ /* End it all */
 2214+ oSettings = null;
 2215+ };
 2216+
 2217+ /*
 2218+ * Function: _fnAjustColumnSizing
 2219+ * Purpose: Update tale sizing based on content. This would most likely be used for scrolling
 2220+ * and will typically need a redraw after it.
 2221+ * Returns: -
 2222+ * Inputs: bool:bRedraw - redraw the table or not, you will typically want to - default true
 2223+ */
 2224+ this.fnAdjustColumnSizing = function ( bRedraw )
 2225+ {
 2226+ var oSettings = _fnSettingsFromNode(this[_oExt.iApiIndex]);
 2227+ _fnAjustColumnSizing( oSettings );
 2228+
 2229+ if ( typeof bRedraw == 'undefined' || bRedraw )
 2230+ {
 2231+ this.fnDraw( false );
 2232+ }
 2233+ else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
 2234+ {
 2235+ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
 2236+ this.oApi._fnScrollDraw(oSettings);
 2237+ }
 2238+ };
 2239+
 2240+ /*
 2241+ * Plugin API functions
 2242+ *
 2243+ * This call will add the functions which are defined in _oExt.oApi to the
 2244+ * DataTables object, providing a rather nice way to allow plug-in API functions. Note that
 2245+ * this is done here, so that API function can actually override the built in API functions if
 2246+ * required for a particular purpose.
 2247+ */
 2248+
 2249+ /*
 2250+ * Function: _fnExternApiFunc
 2251+ * Purpose: Create a wrapper function for exporting an internal func to an external API func
 2252+ * Returns: function: - wrapped function
 2253+ * Inputs: string:sFunc - API function name
 2254+ */
 2255+ function _fnExternApiFunc (sFunc)
 2256+ {
 2257+ return function() {
 2258+ var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat(
 2259+ Array.prototype.slice.call(arguments) );
 2260+ return _oExt.oApi[sFunc].apply( this, aArgs );
 2261+ };
 2262+ }
 2263+
 2264+ for ( var sFunc in _oExt.oApi )
 2265+ {
 2266+ if ( sFunc )
 2267+ {
 2268+ /*
 2269+ * Function: anon
 2270+ * Purpose: Wrap the plug-in API functions in order to provide the settings as 1st arg
 2271+ * and execute in this scope
 2272+ * Returns: -
 2273+ * Inputs: -
 2274+ */
 2275+ this[sFunc] = _fnExternApiFunc(sFunc);
 2276+ }
 2277+ }
 2278+
 2279+
 2280+
 2281+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 2282+ * Section - Local functions
 2283+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 2284+
 2285+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 2286+ * Section - Initalisation
 2287+ */
 2288+
 2289+ /*
 2290+ * Function: _fnInitalise
 2291+ * Purpose: Draw the table for the first time, adding all required features
 2292+ * Returns: -
 2293+ * Inputs: object:oSettings - dataTables settings object
 2294+ */
 2295+ function _fnInitalise ( oSettings )
 2296+ {
 2297+ var i, iLen;
 2298+
 2299+ /* Ensure that the table data is fully initialised */
 2300+ if ( oSettings.bInitialised === false )
 2301+ {
 2302+ setTimeout( function(){ _fnInitalise( oSettings ); }, 200 );
 2303+ return;
 2304+ }
 2305+
 2306+ /* Show the display HTML options */
 2307+ _fnAddOptionsHtml( oSettings );
 2308+
 2309+ /* Draw the headers for the table */
 2310+ _fnDrawHead( oSettings );
 2311+
 2312+ /* Okay to show that something is going on now */
 2313+ _fnProcessingDisplay( oSettings, true );
 2314+
 2315+ /* Calculate sizes for columns */
 2316+ if ( oSettings.oFeatures.bAutoWidth )
 2317+ {
 2318+ _fnCalculateColumnWidths( oSettings );
 2319+ }
 2320+
 2321+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 2322+ {
 2323+ if ( oSettings.aoColumns[i].sWidth !== null )
 2324+ {
 2325+ oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
 2326+ }
 2327+ }
 2328+
 2329+ /* If there is default sorting required - let's do it. The sort function will do the
 2330+ * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
 2331+ * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
 2332+ */
 2333+ if ( oSettings.oFeatures.bSort )
 2334+ {
 2335+ _fnSort( oSettings );
 2336+ }
 2337+ else
 2338+ {
 2339+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 2340+ _fnCalculateEnd( oSettings );
 2341+ _fnDraw( oSettings );
 2342+ }
 2343+
 2344+ /* if there is an ajax source load the data */
 2345+ if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
 2346+ {
 2347+ oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, [], function(json) {
 2348+ /* Got the data - add it to the table */
 2349+ for ( i=0 ; i<json.aaData.length ; i++ )
 2350+ {
 2351+ _fnAddData( oSettings, json.aaData[i] );
 2352+ }
 2353+
 2354+ /* Reset the init display for cookie saving. We've already done a filter, and
 2355+ * therefore cleared it before. So we need to make it appear 'fresh'
 2356+ */
 2357+ oSettings.iInitDisplayStart = oSettings._iDisplayStart;
 2358+
 2359+ if ( oSettings.oFeatures.bSort )
 2360+ {
 2361+ _fnSort( oSettings );
 2362+ }
 2363+ else
 2364+ {
 2365+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 2366+ _fnCalculateEnd( oSettings );
 2367+ _fnDraw( oSettings );
 2368+ }
 2369+
 2370+ _fnProcessingDisplay( oSettings, false );
 2371+ _fnInitComplete( oSettings, json );
 2372+ } );
 2373+ return;
 2374+ }
 2375+
 2376+ /* Server-side processing initialisation complete is done at the end of _fnDraw */
 2377+ if ( !oSettings.oFeatures.bServerSide )
 2378+ {
 2379+ _fnProcessingDisplay( oSettings, false );
 2380+ _fnInitComplete( oSettings );
 2381+ }
 2382+ }
 2383+
 2384+ /*
 2385+ * Function: _fnInitalise
 2386+ * Purpose: Draw the table for the first time, adding all required features
 2387+ * Returns: -
 2388+ * Inputs: object:oSettings - dataTables settings object
 2389+ */
 2390+ function _fnInitComplete ( oSettings, json )
 2391+ {
 2392+ oSettings._bInitComplete = true;
 2393+ if ( typeof oSettings.fnInitComplete == 'function' )
 2394+ {
 2395+ if ( typeof json != 'undefined' )
 2396+ {
 2397+ oSettings.fnInitComplete.call( oSettings.oInstance, oSettings, json );
 2398+ }
 2399+ else
 2400+ {
 2401+ oSettings.fnInitComplete.call( oSettings.oInstance, oSettings );
 2402+ }
 2403+ }
 2404+ }
 2405+
 2406+ /*
 2407+ * Function: _fnLanguageProcess
 2408+ * Purpose: Copy language variables from remote object to a local one
 2409+ * Returns: -
 2410+ * Inputs: object:oSettings - dataTables settings object
 2411+ * object:oLanguage - Language information
 2412+ * bool:bInit - init once complete
 2413+ */
 2414+ function _fnLanguageProcess( oSettings, oLanguage, bInit )
 2415+ {
 2416+ _fnMap( oSettings.oLanguage, oLanguage, 'sProcessing' );
 2417+ _fnMap( oSettings.oLanguage, oLanguage, 'sLengthMenu' );
 2418+ _fnMap( oSettings.oLanguage, oLanguage, 'sEmptyTable' );
 2419+ _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords' );
 2420+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfo' );
 2421+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfoEmpty' );
 2422+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfoFiltered' );
 2423+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfoPostFix' );
 2424+ _fnMap( oSettings.oLanguage, oLanguage, 'sSearch' );
 2425+
 2426+ if ( typeof oLanguage.oPaginate != 'undefined' )
 2427+ {
 2428+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sFirst' );
 2429+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sPrevious' );
 2430+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sNext' );
 2431+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sLast' );
 2432+ }
 2433+
 2434+ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
 2435+ * sZeroRecords - assuming that is given.
 2436+ */
 2437+ if ( typeof oLanguage.sEmptyTable == 'undefined' &&
 2438+ typeof oLanguage.sZeroRecords != 'undefined' )
 2439+ {
 2440+ _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
 2441+ }
 2442+
 2443+ if ( bInit )
 2444+ {
 2445+ _fnInitalise( oSettings );
 2446+ }
 2447+ }
 2448+
 2449+ /*
 2450+ * Function: _fnAddColumn
 2451+ * Purpose: Add a column to the list used for the table with default values
 2452+ * Returns: -
 2453+ * Inputs: object:oSettings - dataTables settings object
 2454+ * node:nTh - the th element for this column
 2455+ */
 2456+ function _fnAddColumn( oSettings, nTh )
 2457+ {
 2458+ oSettings.aoColumns[ oSettings.aoColumns.length++ ] = {
 2459+ "sType": null,
 2460+ "_bAutoType": true,
 2461+ "bVisible": true,
 2462+ "bSearchable": true,
 2463+ "bSortable": true,
 2464+ "asSorting": [ 'asc', 'desc' ],
 2465+ "sSortingClass": oSettings.oClasses.sSortable,
 2466+ "sSortingClassJUI": oSettings.oClasses.sSortJUI,
 2467+ "sTitle": nTh ? nTh.innerHTML : '',
 2468+ "sName": '',
 2469+ "sWidth": null,
 2470+ "sWidthOrig": null,
 2471+ "sClass": null,
 2472+ "fnRender": null,
 2473+ "bUseRendered": true,
 2474+ "iDataSort": oSettings.aoColumns.length-1,
 2475+ "sSortDataType": 'std',
 2476+ "nTh": nTh ? nTh : document.createElement('th'),
 2477+ "nTf": null,
 2478+ "anThExtra": [],
 2479+ "anTfExtra": []
 2480+ };
 2481+
 2482+ var iCol = oSettings.aoColumns.length-1;
 2483+ var oCol = oSettings.aoColumns[ iCol ];
 2484+
 2485+ /* Add a column specific filter */
 2486+ if ( typeof oSettings.aoPreSearchCols[ iCol ] == 'undefined' ||
 2487+ oSettings.aoPreSearchCols[ iCol ] === null )
 2488+ {
 2489+ oSettings.aoPreSearchCols[ iCol ] = {
 2490+ "sSearch": "",
 2491+ "bRegex": false,
 2492+ "bSmart": true
 2493+ };
 2494+ }
 2495+ else
 2496+ {
 2497+ /* Don't require that the user must specify bRegex and / or bSmart */
 2498+ if ( typeof oSettings.aoPreSearchCols[ iCol ].bRegex == 'undefined' )
 2499+ {
 2500+ oSettings.aoPreSearchCols[ iCol ].bRegex = true;
 2501+ }
 2502+
 2503+ if ( typeof oSettings.aoPreSearchCols[ iCol ].bSmart == 'undefined' )
 2504+ {
 2505+ oSettings.aoPreSearchCols[ iCol ].bSmart = true;
 2506+ }
 2507+ }
 2508+
 2509+ /* Use the column options function to initialise classes etc */
 2510+ _fnColumnOptions( oSettings, iCol, null );
 2511+ }
 2512+
 2513+ /*
 2514+ * Function: _fnColumnOptions
 2515+ * Purpose: Apply options for a column
 2516+ * Returns: -
 2517+ * Inputs: object:oSettings - dataTables settings object
 2518+ * int:iCol - column index to consider
 2519+ * object:oOptions - object with sType, bVisible and bSearchable
 2520+ */
 2521+ function _fnColumnOptions( oSettings, iCol, oOptions )
 2522+ {
 2523+ var oCol = oSettings.aoColumns[ iCol ];
 2524+
 2525+ /* User specified column options */
 2526+ if ( typeof oOptions != 'undefined' && oOptions !== null )
 2527+ {
 2528+ if ( typeof oOptions.sType != 'undefined' )
 2529+ {
 2530+ oCol.sType = oOptions.sType;
 2531+ oCol._bAutoType = false;
 2532+ }
 2533+
 2534+ _fnMap( oCol, oOptions, "bVisible" );
 2535+ _fnMap( oCol, oOptions, "bSearchable" );
 2536+ _fnMap( oCol, oOptions, "bSortable" );
 2537+ _fnMap( oCol, oOptions, "sTitle" );
 2538+ _fnMap( oCol, oOptions, "sName" );
 2539+ _fnMap( oCol, oOptions, "sWidth" );
 2540+ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
 2541+ _fnMap( oCol, oOptions, "sClass" );
 2542+ _fnMap( oCol, oOptions, "fnRender" );
 2543+ _fnMap( oCol, oOptions, "bUseRendered" );
 2544+ _fnMap( oCol, oOptions, "iDataSort" );
 2545+ _fnMap( oCol, oOptions, "asSorting" );
 2546+ _fnMap( oCol, oOptions, "sSortDataType" );
 2547+ }
 2548+
 2549+ /* Feature sorting overrides column specific when off */
 2550+ if ( !oSettings.oFeatures.bSort )
 2551+ {
 2552+ oCol.bSortable = false;
 2553+ }
 2554+
 2555+ /* Check that the class assignment is correct for sorting */
 2556+ if ( !oCol.bSortable ||
 2557+ ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
 2558+ {
 2559+ oCol.sSortingClass = oSettings.oClasses.sSortableNone;
 2560+ oCol.sSortingClassJUI = "";
 2561+ }
 2562+ else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
 2563+ {
 2564+ oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
 2565+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
 2566+ }
 2567+ else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
 2568+ {
 2569+ oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
 2570+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
 2571+ }
 2572+ }
 2573+
 2574+ /*
 2575+ * Function: _fnAddData
 2576+ * Purpose: Add a data array to the table, creating DOM node etc
 2577+ * Returns: int: - >=0 if successful (index of new aoData entry), -1 if failed
 2578+ * Inputs: object:oSettings - dataTables settings object
 2579+ * array:aData - data array to be added
 2580+ * Notes: There are two basic methods for DataTables to get data to display - a JS array
 2581+ * (which is dealt with by this function), and the DOM, which has it's own optimised
 2582+ * function (_fnGatherData). Be careful to make the same changes here as there and vice-versa
 2583+ */
 2584+ function _fnAddData ( oSettings, aDataSupplied )
 2585+ {
 2586+ /* Sanity check the length of the new array */
 2587+ if ( aDataSupplied.length != oSettings.aoColumns.length &&
 2588+ oSettings.iDrawError != oSettings.iDraw )
 2589+ {
 2590+ _fnLog( oSettings, 0, "Added data (size "+aDataSupplied.length+") does not match known "+
 2591+ "number of columns ("+oSettings.aoColumns.length+")" );
 2592+ oSettings.iDrawError = oSettings.iDraw;
 2593+ return -1;
 2594+ }
 2595+
 2596+
 2597+ /* Create the object for storing information about this new row */
 2598+ var aData = aDataSupplied.slice();
 2599+ var iThisIndex = oSettings.aoData.length;
 2600+ oSettings.aoData.push( {
 2601+ "nTr": document.createElement('tr'),
 2602+ "_iId": oSettings.iNextId++,
 2603+ "_aData": aData,
 2604+ "_anHidden": [],
 2605+ "_sRowStripe": ''
 2606+ } );
 2607+
 2608+ /* Create the cells */
 2609+ var nTd, sThisType;
 2610+ for ( var i=0 ; i<aData.length ; i++ )
 2611+ {
 2612+ nTd = document.createElement('td');
 2613+
 2614+ /* Allow null data (from a data array) - simply deal with it as a blank string */
 2615+ if ( aData[i] === null )
 2616+ {
 2617+ aData[i] = '';
 2618+ }
 2619+
 2620+ if ( typeof oSettings.aoColumns[i].fnRender == 'function' )
 2621+ {
 2622+ var sRendered = oSettings.aoColumns[i].fnRender( {
 2623+ "iDataRow": iThisIndex,
 2624+ "iDataColumn": i,
 2625+ "aData": aData,
 2626+ "oSettings": oSettings
 2627+ } );
 2628+ nTd.innerHTML = sRendered;
 2629+ if ( oSettings.aoColumns[i].bUseRendered )
 2630+ {
 2631+ /* Use the rendered data for filtering/sorting */
 2632+ oSettings.aoData[iThisIndex]._aData[i] = sRendered;
 2633+ }
 2634+ }
 2635+ else
 2636+ {
 2637+ nTd.innerHTML = aData[i];
 2638+ }
 2639+
 2640+ /* Cast everything as a string - so we can treat everything equally when sorting */
 2641+ if ( typeof aData[i] != 'string' )
 2642+ {
 2643+ aData[i] += "";
 2644+ }
 2645+ aData[i] = $.trim(aData[i]);
 2646+
 2647+ /* Add user defined class */
 2648+ if ( oSettings.aoColumns[i].sClass !== null )
 2649+ {
 2650+ nTd.className = oSettings.aoColumns[i].sClass;
 2651+ }
 2652+
 2653+ /* See if we should auto-detect the column type */
 2654+ if ( oSettings.aoColumns[i]._bAutoType && oSettings.aoColumns[i].sType != 'string' )
 2655+ {
 2656+ /* Attempt to auto detect the type - same as _fnGatherData() */
 2657+ sThisType = _fnDetectType( oSettings.aoData[iThisIndex]._aData[i] );
 2658+ if ( oSettings.aoColumns[i].sType === null )
 2659+ {
 2660+ oSettings.aoColumns[i].sType = sThisType;
 2661+ }
 2662+ else if ( oSettings.aoColumns[i].sType != sThisType )
 2663+ {
 2664+ /* String is always the 'fallback' option */
 2665+ oSettings.aoColumns[i].sType = 'string';
 2666+ }
 2667+ }
 2668+
 2669+ if ( oSettings.aoColumns[i].bVisible )
 2670+ {
 2671+ oSettings.aoData[iThisIndex].nTr.appendChild( nTd );
 2672+ oSettings.aoData[iThisIndex]._anHidden[i] = null;
 2673+ }
 2674+ else
 2675+ {
 2676+ oSettings.aoData[iThisIndex]._anHidden[i] = nTd;
 2677+ }
 2678+ }
 2679+
 2680+ /* Add to the display array */
 2681+ oSettings.aiDisplayMaster.push( iThisIndex );
 2682+ return iThisIndex;
 2683+ }
 2684+
 2685+ /*
 2686+ * Function: _fnGatherData
 2687+ * Purpose: Read in the data from the target table from the DOM
 2688+ * Returns: -
 2689+ * Inputs: object:oSettings - dataTables settings object
 2690+ * Notes: This is a optimised version of _fnAddData (more or less) for reading information
 2691+ * from the DOM. The basic actions must be identical in the two functions.
 2692+ */
 2693+ function _fnGatherData( oSettings )
 2694+ {
 2695+ var iLoop, i, iLen, j, jLen, jInner,
 2696+ nTds, nTrs, nTd, aLocalData, iThisIndex,
 2697+ iRow, iRows, iColumn, iColumns;
 2698+
 2699+ /*
 2700+ * Process by row first
 2701+ * Add the data object for the whole table - storing the tr node. Note - no point in getting
 2702+ * DOM based data if we are going to go and replace it with Ajax source data.
 2703+ */
 2704+ if ( oSettings.sAjaxSource === null )
 2705+ {
 2706+ nTrs = oSettings.nTBody.childNodes;
 2707+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
 2708+ {
 2709+ if ( nTrs[i].nodeName.toUpperCase() == "TR" )
 2710+ {
 2711+ iThisIndex = oSettings.aoData.length;
 2712+ oSettings.aoData.push( {
 2713+ "nTr": nTrs[i],
 2714+ "_iId": oSettings.iNextId++,
 2715+ "_aData": [],
 2716+ "_anHidden": [],
 2717+ "_sRowStripe": ''
 2718+ } );
 2719+
 2720+ oSettings.aiDisplayMaster.push( iThisIndex );
 2721+
 2722+ aLocalData = oSettings.aoData[iThisIndex]._aData;
 2723+ nTds = nTrs[i].childNodes;
 2724+ jInner = 0;
 2725+
 2726+ for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
 2727+ {
 2728+ if ( nTds[j].nodeName.toUpperCase() == "TD" )
 2729+ {
 2730+ aLocalData[jInner] = $.trim(nTds[j].innerHTML);
 2731+ jInner++;
 2732+ }
 2733+ }
 2734+ }
 2735+ }
 2736+ }
 2737+
 2738+ /* Gather in the TD elements of the Table - note that this is basically the same as
 2739+ * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
 2740+ * setup!
 2741+ */
 2742+ nTrs = _fnGetTrNodes( oSettings );
 2743+ nTds = [];
 2744+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
 2745+ {
 2746+ for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
 2747+ {
 2748+ nTd = nTrs[i].childNodes[j];
 2749+ if ( nTd.nodeName.toUpperCase() == "TD" )
 2750+ {
 2751+ nTds.push( nTd );
 2752+ }
 2753+ }
 2754+ }
 2755+
 2756+ /* Sanity check */
 2757+ if ( nTds.length != nTrs.length * oSettings.aoColumns.length )
 2758+ {
 2759+ _fnLog( oSettings, 1, "Unexpected number of TD elements. Expected "+
 2760+ (nTrs.length * oSettings.aoColumns.length)+" and got "+nTds.length+". DataTables does "+
 2761+ "not support rowspan / colspan in the table body, and there must be one cell for each "+
 2762+ "row/column combination." );
 2763+ }
 2764+
 2765+ /* Now process by column */
 2766+ for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
 2767+ {
 2768+ /* Get the title of the column - unless there is a user set one */
 2769+ if ( oSettings.aoColumns[iColumn].sTitle === null )
 2770+ {
 2771+ oSettings.aoColumns[iColumn].sTitle = oSettings.aoColumns[iColumn].nTh.innerHTML;
 2772+ }
 2773+
 2774+ var
 2775+ bAutoType = oSettings.aoColumns[iColumn]._bAutoType,
 2776+ bRender = typeof oSettings.aoColumns[iColumn].fnRender == 'function',
 2777+ bClass = oSettings.aoColumns[iColumn].sClass !== null,
 2778+ bVisible = oSettings.aoColumns[iColumn].bVisible,
 2779+ nCell, sThisType, sRendered;
 2780+
 2781+ /* A single loop to rule them all (and be more efficient) */
 2782+ if ( bAutoType || bRender || bClass || !bVisible )
 2783+ {
 2784+ for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
 2785+ {
 2786+ nCell = nTds[ (iRow*iColumns) + iColumn ];
 2787+
 2788+ /* Type detection */
 2789+ if ( bAutoType )
 2790+ {
 2791+ if ( oSettings.aoColumns[iColumn].sType != 'string' )
 2792+ {
 2793+ sThisType = _fnDetectType( oSettings.aoData[iRow]._aData[iColumn] );
 2794+ if ( oSettings.aoColumns[iColumn].sType === null )
 2795+ {
 2796+ oSettings.aoColumns[iColumn].sType = sThisType;
 2797+ }
 2798+ else if ( oSettings.aoColumns[iColumn].sType != sThisType )
 2799+ {
 2800+ /* String is always the 'fallback' option */
 2801+ oSettings.aoColumns[iColumn].sType = 'string';
 2802+ }
 2803+ }
 2804+ }
 2805+
 2806+ /* Rendering */
 2807+ if ( bRender )
 2808+ {
 2809+ sRendered = oSettings.aoColumns[iColumn].fnRender( {
 2810+ "iDataRow": iRow,
 2811+ "iDataColumn": iColumn,
 2812+ "aData": oSettings.aoData[iRow]._aData,
 2813+ "oSettings": oSettings
 2814+ } );
 2815+ nCell.innerHTML = sRendered;
 2816+ if ( oSettings.aoColumns[iColumn].bUseRendered )
 2817+ {
 2818+ /* Use the rendered data for filtering/sorting */
 2819+ oSettings.aoData[iRow]._aData[iColumn] = sRendered;
 2820+ }
 2821+ }
 2822+
 2823+ /* Classes */
 2824+ if ( bClass )
 2825+ {
 2826+ nCell.className += ' '+oSettings.aoColumns[iColumn].sClass;
 2827+ }
 2828+
 2829+ /* Column visability */
 2830+ if ( !bVisible )
 2831+ {
 2832+ oSettings.aoData[iRow]._anHidden[iColumn] = nCell;
 2833+ nCell.parentNode.removeChild( nCell );
 2834+ }
 2835+ else
 2836+ {
 2837+ oSettings.aoData[iRow]._anHidden[iColumn] = null;
 2838+ }
 2839+ }
 2840+ }
 2841+ }
 2842+ }
 2843+
 2844+
 2845+
 2846+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 2847+ * Section - Drawing functions
 2848+ */
 2849+
 2850+ /*
 2851+ * Function: _fnDrawHead
 2852+ * Purpose: Create the HTML header for the table
 2853+ * Returns: -
 2854+ * Inputs: object:oSettings - dataTables settings object
 2855+ */
 2856+ function _fnDrawHead( oSettings )
 2857+ {
 2858+ var i, nTh, iLen, j, jLen;
 2859+ var anTr = oSettings.nTHead.getElementsByTagName('tr');
 2860+ var iThs = oSettings.nTHead.getElementsByTagName('th').length;
 2861+ var iCorrector = 0;
 2862+ var jqChildren;
 2863+
 2864+ /* If there is a header in place - then use it - otherwise it's going to get nuked... */
 2865+ if ( iThs !== 0 )
 2866+ {
 2867+ /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
 2868+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 2869+ {
 2870+ nTh = oSettings.aoColumns[i].nTh;
 2871+
 2872+ if ( oSettings.aoColumns[i].sClass !== null )
 2873+ {
 2874+ $(nTh).addClass( oSettings.aoColumns[i].sClass );
 2875+ }
 2876+
 2877+ /* Cache and remove (if needed) any extra elements for this column in the header */
 2878+ for ( j=1, jLen=anTr.length ; j<jLen ; j++ )
 2879+ {
 2880+ jqChildren = $(anTr[j]).children();
 2881+ oSettings.aoColumns[i].anThExtra.push( jqChildren[i-iCorrector] );
 2882+ if ( !oSettings.aoColumns[i].bVisible )
 2883+ {
 2884+ anTr[j].removeChild( jqChildren[i-iCorrector] );
 2885+ }
 2886+ }
 2887+
 2888+ if ( oSettings.aoColumns[i].bVisible )
 2889+ {
 2890+ /* Set the title of the column if it is user defined (not what was auto detected) */
 2891+ if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
 2892+ {
 2893+ nTh.innerHTML = oSettings.aoColumns[i].sTitle;
 2894+ }
 2895+ }
 2896+ else
 2897+ {
 2898+ nTh.parentNode.removeChild( nTh );
 2899+ iCorrector++;
 2900+ }
 2901+ }
 2902+ }
 2903+ else
 2904+ {
 2905+ /* We don't have a header in the DOM - so we are going to have to create one */
 2906+ var nTr = document.createElement( "tr" );
 2907+
 2908+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 2909+ {
 2910+ nTh = oSettings.aoColumns[i].nTh;
 2911+ nTh.innerHTML = oSettings.aoColumns[i].sTitle;
 2912+
 2913+ if ( oSettings.aoColumns[i].sClass !== null )
 2914+ {
 2915+ $(nTh).addClass( oSettings.aoColumns[i].sClass );
 2916+ }
 2917+
 2918+ if ( oSettings.aoColumns[i].bVisible )
 2919+ {
 2920+ nTr.appendChild( nTh );
 2921+ }
 2922+ }
 2923+ $(oSettings.nTHead).html( '' )[0].appendChild( nTr );
 2924+ }
 2925+
 2926+ /* Add the extra markup needed by jQuery UI's themes */
 2927+ if ( oSettings.bJUI )
 2928+ {
 2929+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 2930+ {
 2931+ nTh = oSettings.aoColumns[i].nTh;
 2932+
 2933+ var nDiv = document.createElement('div');
 2934+ nDiv.className = oSettings.oClasses.sSortJUIWrapper;
 2935+ $(nTh).contents().appendTo(nDiv);
 2936+
 2937+ nDiv.appendChild( document.createElement('span') );
 2938+ nTh.appendChild( nDiv );
 2939+ }
 2940+ }
 2941+
 2942+ /* Add sort listener */
 2943+ var fnNoSelect = function (e) {
 2944+ this.onselectstart = function() { return false; };
 2945+ return false;
 2946+ };
 2947+
 2948+ if ( oSettings.oFeatures.bSort )
 2949+ {
 2950+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
 2951+ {
 2952+ if ( oSettings.aoColumns[i].bSortable !== false )
 2953+ {
 2954+ _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
 2955+
 2956+ /* Take the brutal approach to cancelling text selection in header */
 2957+ $(oSettings.aoColumns[i].nTh).mousedown( fnNoSelect );
 2958+ }
 2959+ else
 2960+ {
 2961+ $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
 2962+ }
 2963+ }
 2964+ }
 2965+
 2966+ /* Cache the footer elements */
 2967+ if ( oSettings.nTFoot !== null )
 2968+ {
 2969+ iCorrector = 0;
 2970+ anTr = oSettings.nTFoot.getElementsByTagName('tr');
 2971+ var nTfs = anTr[0].getElementsByTagName('th');
 2972+
 2973+ for ( i=0, iLen=nTfs.length ; i<iLen ; i++ )
 2974+ {
 2975+ if ( typeof oSettings.aoColumns[i] != 'undefined' )
 2976+ {
 2977+ oSettings.aoColumns[i].nTf = nTfs[i-iCorrector];
 2978+
 2979+ if ( oSettings.oClasses.sFooterTH !== "" )
 2980+ {
 2981+ oSettings.aoColumns[i].nTf.className += " "+oSettings.oClasses.sFooterTH;
 2982+ }
 2983+
 2984+ /* Deal with any extra elements for this column from the footer */
 2985+ for ( j=1, jLen=anTr.length ; j<jLen ; j++ )
 2986+ {
 2987+ jqChildren = $(anTr[j]).children();
 2988+ oSettings.aoColumns[i].anTfExtra.push( jqChildren[i-iCorrector] );
 2989+ if ( !oSettings.aoColumns[i].bVisible )
 2990+ {
 2991+ anTr[j].removeChild( jqChildren[i-iCorrector] );
 2992+ }
 2993+ }
 2994+
 2995+ if ( !oSettings.aoColumns[i].bVisible )
 2996+ {
 2997+ nTfs[i-iCorrector].parentNode.removeChild( nTfs[i-iCorrector] );
 2998+ iCorrector++;
 2999+ }
 3000+ }
 3001+ }
 3002+ }
 3003+ }
 3004+
 3005+ /*
 3006+ * Function: _fnDraw
 3007+ * Purpose: Insert the required TR nodes into the table for display
 3008+ * Returns: -
 3009+ * Inputs: object:oSettings - dataTables settings object
 3010+ */
 3011+ function _fnDraw( oSettings )
 3012+ {
 3013+ var i, iLen;
 3014+ var anRows = [];
 3015+ var iRowCount = 0;
 3016+ var bRowError = false;
 3017+ var iStrips = oSettings.asStripClasses.length;
 3018+ var iOpenRows = oSettings.aoOpenRows.length;
 3019+
 3020+ oSettings.bDrawing = true;
 3021+
 3022+ /* Check and see if we have an initial draw position from state saving */
 3023+ if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 )
 3024+ {
 3025+ if ( oSettings.oFeatures.bServerSide )
 3026+ {
 3027+ oSettings._iDisplayStart = oSettings.iInitDisplayStart;
 3028+ }
 3029+ else
 3030+ {
 3031+ oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
 3032+ 0 : oSettings.iInitDisplayStart;
 3033+ }
 3034+ oSettings.iInitDisplayStart = -1;
 3035+ _fnCalculateEnd( oSettings );
 3036+ }
 3037+
 3038+ /* If we are dealing with Ajax - do it here */
 3039+ if ( !oSettings.bDestroying && oSettings.oFeatures.bServerSide &&
 3040+ !_fnAjaxUpdate( oSettings ) )
 3041+ {
 3042+ return;
 3043+ }
 3044+ else if ( !oSettings.oFeatures.bServerSide )
 3045+ {
 3046+ oSettings.iDraw++;
 3047+ }
 3048+
 3049+ if ( oSettings.aiDisplay.length !== 0 )
 3050+ {
 3051+ var iStart = oSettings._iDisplayStart;
 3052+ var iEnd = oSettings._iDisplayEnd;
 3053+
 3054+ if ( oSettings.oFeatures.bServerSide )
 3055+ {
 3056+ iStart = 0;
 3057+ iEnd = oSettings.aoData.length;
 3058+ }
 3059+
 3060+ for ( var j=iStart ; j<iEnd ; j++ )
 3061+ {
 3062+ var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
 3063+ var nRow = aoData.nTr;
 3064+
 3065+ /* Remove the old stripping classes and then add the new one */
 3066+ if ( iStrips !== 0 )
 3067+ {
 3068+ var sStrip = oSettings.asStripClasses[ iRowCount % iStrips ];
 3069+ if ( aoData._sRowStripe != sStrip )
 3070+ {
 3071+ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStrip );
 3072+ aoData._sRowStripe = sStrip;
 3073+ }
 3074+ }
 3075+
 3076+ /* Custom row callback function - might want to manipule the row */
 3077+ if ( typeof oSettings.fnRowCallback == "function" )
 3078+ {
 3079+ nRow = oSettings.fnRowCallback.call( oSettings.oInstance, nRow,
 3080+ oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j );
 3081+ if ( !nRow && !bRowError )
 3082+ {
 3083+ _fnLog( oSettings, 0, "A node was not returned by fnRowCallback" );
 3084+ bRowError = true;
 3085+ }
 3086+ }
 3087+
 3088+ anRows.push( nRow );
 3089+ iRowCount++;
 3090+
 3091+ /* If there is an open row - and it is attached to this parent - attach it on redraw */
 3092+ if ( iOpenRows !== 0 )
 3093+ {
 3094+ for ( var k=0 ; k<iOpenRows ; k++ )
 3095+ {
 3096+ if ( nRow == oSettings.aoOpenRows[k].nParent )
 3097+ {
 3098+ anRows.push( oSettings.aoOpenRows[k].nTr );
 3099+ }
 3100+ }
 3101+ }
 3102+ }
 3103+ }
 3104+ else
 3105+ {
 3106+ /* Table is empty - create a row with an empty message in it */
 3107+ anRows[ 0 ] = document.createElement( 'tr' );
 3108+
 3109+ if ( typeof oSettings.asStripClasses[0] != 'undefined' )
 3110+ {
 3111+ anRows[ 0 ].className = oSettings.asStripClasses[0];
 3112+ }
 3113+
 3114+ var nTd = document.createElement( 'td' );
 3115+ nTd.setAttribute( 'valign', "top" );
 3116+ nTd.colSpan = _fnVisbleColumns( oSettings );
 3117+ nTd.className = oSettings.oClasses.sRowEmpty;
 3118+ if ( typeof oSettings.oLanguage.sEmptyTable != 'undefined' &&
 3119+ oSettings.fnRecordsTotal() === 0 )
 3120+ {
 3121+ nTd.innerHTML = oSettings.oLanguage.sEmptyTable;
 3122+ }
 3123+ else
 3124+ {
 3125+ nTd.innerHTML = oSettings.oLanguage.sZeroRecords.replace(
 3126+ '_MAX_', oSettings.fnFormatNumber(oSettings.fnRecordsTotal()) );
 3127+ }
 3128+
 3129+ anRows[ iRowCount ].appendChild( nTd );
 3130+ }
 3131+
 3132+ /* Callback the header and footer custom funcation if there is one */
 3133+ if ( typeof oSettings.fnHeaderCallback == 'function' )
 3134+ {
 3135+ oSettings.fnHeaderCallback.call( oSettings.oInstance, $('>tr', oSettings.nTHead)[0],
 3136+ _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
 3137+ oSettings.aiDisplay );
 3138+ }
 3139+
 3140+ if ( typeof oSettings.fnFooterCallback == 'function' )
 3141+ {
 3142+ oSettings.fnFooterCallback.call( oSettings.oInstance, $('>tr', oSettings.nTFoot)[0],
 3143+ _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
 3144+ oSettings.aiDisplay );
 3145+ }
 3146+
 3147+ /*
 3148+ * Need to remove any old row from the display - note we can't just empty the tbody using
 3149+ * $().html('') since this will unbind the jQuery event handlers (even although the node
 3150+ * still exists!) - equally we can't use innerHTML, since IE throws an exception.
 3151+ */
 3152+ var
 3153+ nAddFrag = document.createDocumentFragment(),
 3154+ nRemoveFrag = document.createDocumentFragment(),
 3155+ nBodyPar, nTrs;
 3156+
 3157+ if ( oSettings.nTBody )
 3158+ {
 3159+ nBodyPar = oSettings.nTBody.parentNode;
 3160+ nRemoveFrag.appendChild( oSettings.nTBody );
 3161+
 3162+ /* When doing infinite scrolling, only remove child rows when sorting, filtering or start
 3163+ * up. When not infinite scroll, always do it.
 3164+ */
 3165+ if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
 3166+ oSettings.bSorted || oSettings.bFiltered )
 3167+ {
 3168+ nTrs = oSettings.nTBody.childNodes;
 3169+ for ( i=nTrs.length-1 ; i>=0 ; i-- )
 3170+ {
 3171+ nTrs[i].parentNode.removeChild( nTrs[i] );
 3172+ }
 3173+ }
 3174+
 3175+ /* Put the draw table into the dom */
 3176+ for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
 3177+ {
 3178+ nAddFrag.appendChild( anRows[i] );
 3179+ }
 3180+
 3181+ oSettings.nTBody.appendChild( nAddFrag );
 3182+ if ( nBodyPar !== null )
 3183+ {
 3184+ nBodyPar.appendChild( oSettings.nTBody );
 3185+ }
 3186+ }
 3187+
 3188+ /* Call all required callback functions for the end of a draw */
 3189+ for ( i=oSettings.aoDrawCallback.length-1 ; i>=0 ; i-- )
 3190+ {
 3191+ oSettings.aoDrawCallback[i].fn.call( oSettings.oInstance, oSettings );
 3192+ }
 3193+
 3194+ /* Draw is complete, sorting and filtering must be as well */
 3195+ oSettings.bSorted = false;
 3196+ oSettings.bFiltered = false;
 3197+ oSettings.bDrawing = false;
 3198+
 3199+ if ( oSettings.oFeatures.bServerSide )
 3200+ {
 3201+ _fnProcessingDisplay( oSettings, false );
 3202+ if ( typeof oSettings._bInitComplete == 'undefined' )
 3203+ {
 3204+ _fnInitComplete( oSettings );
 3205+ }
 3206+ }
 3207+ }
 3208+
 3209+ /*
 3210+ * Function: _fnReDraw
 3211+ * Purpose: Redraw the table - taking account of the various features which are enabled
 3212+ * Returns: -
 3213+ * Inputs: object:oSettings - dataTables settings object
 3214+ */
 3215+ function _fnReDraw( oSettings )
 3216+ {
 3217+ if ( oSettings.oFeatures.bSort )
 3218+ {
 3219+ /* Sorting will refilter and draw for us */
 3220+ _fnSort( oSettings, oSettings.oPreviousSearch );
 3221+ }
 3222+ else if ( oSettings.oFeatures.bFilter )
 3223+ {
 3224+ /* Filtering will redraw for us */
 3225+ _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
 3226+ }
 3227+ else
 3228+ {
 3229+ _fnCalculateEnd( oSettings );
 3230+ _fnDraw( oSettings );
 3231+ }
 3232+ }
 3233+
 3234+ /*
 3235+ * Function: _fnAjaxUpdate
 3236+ * Purpose: Update the table using an Ajax call
 3237+ * Returns: bool: block the table drawing or not
 3238+ * Inputs: object:oSettings - dataTables settings object
 3239+ */
 3240+ function _fnAjaxUpdate( oSettings )
 3241+ {
 3242+ if ( oSettings.bAjaxDataGet )
 3243+ {
 3244+ _fnProcessingDisplay( oSettings, true );
 3245+ var iColumns = oSettings.aoColumns.length;
 3246+ var aoData = [];
 3247+ var i;
 3248+
 3249+ /* Paging and general */
 3250+ oSettings.iDraw++;
 3251+ aoData.push( { "name": "sEcho", "value": oSettings.iDraw } );
 3252+ aoData.push( { "name": "iColumns", "value": iColumns } );
 3253+ aoData.push( { "name": "sColumns", "value": _fnColumnOrdering(oSettings) } );
 3254+ aoData.push( { "name": "iDisplayStart", "value": oSettings._iDisplayStart } );
 3255+ aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
 3256+ oSettings._iDisplayLength : -1 } );
 3257+
 3258+ /* Filtering */
 3259+ if ( oSettings.oFeatures.bFilter !== false )
 3260+ {
 3261+ aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
 3262+ aoData.push( { "name": "bRegex", "value": oSettings.oPreviousSearch.bRegex } );
 3263+ for ( i=0 ; i<iColumns ; i++ )
 3264+ {
 3265+ aoData.push( { "name": "sSearch_"+i, "value": oSettings.aoPreSearchCols[i].sSearch } );
 3266+ aoData.push( { "name": "bRegex_"+i, "value": oSettings.aoPreSearchCols[i].bRegex } );
 3267+ aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
 3268+ }
 3269+ }
 3270+
 3271+ /* Sorting */
 3272+ if ( oSettings.oFeatures.bSort !== false )
 3273+ {
 3274+ var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0;
 3275+ var iUser = oSettings.aaSorting.length;
 3276+ aoData.push( { "name": "iSortingCols", "value": iFixed+iUser } );
 3277+ for ( i=0 ; i<iFixed ; i++ )
 3278+ {
 3279+ aoData.push( { "name": "iSortCol_"+i, "value": oSettings.aaSortingFixed[i][0] } );
 3280+ aoData.push( { "name": "sSortDir_"+i, "value": oSettings.aaSortingFixed[i][1] } );
 3281+ }
 3282+
 3283+ for ( i=0 ; i<iUser ; i++ )
 3284+ {
 3285+ aoData.push( { "name": "iSortCol_"+(i+iFixed), "value": oSettings.aaSorting[i][0] } );
 3286+ aoData.push( { "name": "sSortDir_"+(i+iFixed), "value": oSettings.aaSorting[i][1] } );
 3287+ }
 3288+
 3289+ for ( i=0 ; i<iColumns ; i++ )
 3290+ {
 3291+ aoData.push( { "name": "bSortable_"+i, "value": oSettings.aoColumns[i].bSortable } );
 3292+ }
 3293+ }
 3294+
 3295+ oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
 3296+ function(json) {
 3297+ _fnAjaxUpdateDraw( oSettings, json );
 3298+ } );
 3299+ return false;
 3300+ }
 3301+ else
 3302+ {
 3303+ return true;
 3304+ }
 3305+ }
 3306+
 3307+ /*
 3308+ * Function: _fnAjaxUpdateDraw
 3309+ * Purpose: Data the data from the server (nuking the old) and redraw the table
 3310+ * Returns: -
 3311+ * Inputs: object:oSettings - dataTables settings object
 3312+ * object:json - json data return from the server.
 3313+ * The following must be defined:
 3314+ * iTotalRecords, iTotalDisplayRecords, aaData
 3315+ * The following may be defined:
 3316+ * sColumns
 3317+ */
 3318+ function _fnAjaxUpdateDraw ( oSettings, json )
 3319+ {
 3320+ if ( typeof json.sEcho != 'undefined' )
 3321+ {
 3322+ /* Protect against old returns over-writing a new one. Possible when you get
 3323+ * very fast interaction, and later queires are completed much faster
 3324+ */
 3325+ if ( json.sEcho*1 < oSettings.iDraw )
 3326+ {
 3327+ return;
 3328+ }
 3329+ else
 3330+ {
 3331+ oSettings.iDraw = json.sEcho * 1;
 3332+ }
 3333+ }
 3334+
 3335+ if ( !oSettings.oScroll.bInfinite ||
 3336+ (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
 3337+ {
 3338+ _fnClearTable( oSettings );
 3339+ }
 3340+ oSettings._iRecordsTotal = json.iTotalRecords;
 3341+ oSettings._iRecordsDisplay = json.iTotalDisplayRecords;
 3342+
 3343+ /* Determine if reordering is required */
 3344+ var sOrdering = _fnColumnOrdering(oSettings);
 3345+ var bReOrder = (typeof json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering );
 3346+ if ( bReOrder )
 3347+ {
 3348+ var aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
 3349+ }
 3350+
 3351+ for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ )
 3352+ {
 3353+ if ( bReOrder )
 3354+ {
 3355+ /* If we need to re-order, then create a new array with the correct order and add it */
 3356+ var aData = [];
 3357+ for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
 3358+ {
 3359+ aData.push( json.aaData[i][ aiIndex[j] ] );
 3360+ }
 3361+ _fnAddData( oSettings, aData );
 3362+ }
 3363+ else
 3364+ {
 3365+ /* No re-order required, sever got it "right" - just straight add */
 3366+ _fnAddData( oSettings, json.aaData[i] );
 3367+ }
 3368+ }
 3369+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 3370+
 3371+ oSettings.bAjaxDataGet = false;
 3372+ _fnDraw( oSettings );
 3373+ oSettings.bAjaxDataGet = true;
 3374+ _fnProcessingDisplay( oSettings, false );
 3375+ }
 3376+
 3377+
 3378+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 3379+ * Section - Options (features) HTML
 3380+ */
 3381+
 3382+ /*
 3383+ * Function: _fnAddOptionsHtml
 3384+ * Purpose: Add the options to the page HTML for the table
 3385+ * Returns: -
 3386+ * Inputs: object:oSettings - dataTables settings object
 3387+ */
 3388+ function _fnAddOptionsHtml ( oSettings )
 3389+ {
 3390+ /*
 3391+ * Create a temporary, empty, div which we can later on replace with what we have generated
 3392+ * we do it this way to rendering the 'options' html offline - speed :-)
 3393+ */
 3394+ var nHolding = document.createElement( 'div' );
 3395+ oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
 3396+
 3397+ /*
 3398+ * All DataTables are wrapped in a div - this is not currently optional - backwards
 3399+ * compatability. It can be removed if you don't want it.
 3400+ */
 3401+ oSettings.nTableWrapper = document.createElement( 'div' );
 3402+ oSettings.nTableWrapper.className = oSettings.oClasses.sWrapper;
 3403+ if ( oSettings.sTableId !== '' )
 3404+ {
 3405+ oSettings.nTableWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' );
 3406+ }
 3407+
 3408+ /* Track where we want to insert the option */
 3409+ var nInsertNode = oSettings.nTableWrapper;
 3410+
 3411+ /* Loop over the user set positioning and place the elements as needed */
 3412+ var aDom = oSettings.sDom.split('');
 3413+ var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
 3414+ for ( var i=0 ; i<aDom.length ; i++ )
 3415+ {
 3416+ iPushFeature = 0;
 3417+ cOption = aDom[i];
 3418+
 3419+ if ( cOption == '<' )
 3420+ {
 3421+ /* New container div */
 3422+ nNewNode = document.createElement( 'div' );
 3423+
 3424+ /* Check to see if we should append an id and/or a class name to the container */
 3425+ cNext = aDom[i+1];
 3426+ if ( cNext == "'" || cNext == '"' )
 3427+ {
 3428+ sAttr = "";
 3429+ j = 2;
 3430+ while ( aDom[i+j] != cNext )
 3431+ {
 3432+ sAttr += aDom[i+j];
 3433+ j++;
 3434+ }
 3435+
 3436+ /* Replace jQuery UI constants */
 3437+ if ( sAttr == "H" )
 3438+ {
 3439+ sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";
 3440+ }
 3441+ else if ( sAttr == "F" )
 3442+ {
 3443+ sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";
 3444+ }
 3445+
 3446+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
 3447+ * breaks the string into parts and applies them as needed
 3448+ */
 3449+ if ( sAttr.indexOf('.') != -1 )
 3450+ {
 3451+ var aSplit = sAttr.split('.');
 3452+ nNewNode.setAttribute('id', aSplit[0].substr(1, aSplit[0].length-1) );
 3453+ nNewNode.className = aSplit[1];
 3454+ }
 3455+ else if ( sAttr.charAt(0) == "#" )
 3456+ {
 3457+ nNewNode.setAttribute('id', sAttr.substr(1, sAttr.length-1) );
 3458+ }
 3459+ else
 3460+ {
 3461+ nNewNode.className = sAttr;
 3462+ }
 3463+
 3464+ i += j; /* Move along the position array */
 3465+ }
 3466+
 3467+ nInsertNode.appendChild( nNewNode );
 3468+ nInsertNode = nNewNode;
 3469+ }
 3470+ else if ( cOption == '>' )
 3471+ {
 3472+ /* End container div */
 3473+ nInsertNode = nInsertNode.parentNode;
 3474+ }
 3475+ else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
 3476+ {
 3477+ /* Length */
 3478+ nTmp = _fnFeatureHtmlLength( oSettings );
 3479+ iPushFeature = 1;
 3480+ }
 3481+ else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
 3482+ {
 3483+ /* Filter */
 3484+ nTmp = _fnFeatureHtmlFilter( oSettings );
 3485+ iPushFeature = 1;
 3486+ }
 3487+ else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
 3488+ {
 3489+ /* pRocessing */
 3490+ nTmp = _fnFeatureHtmlProcessing( oSettings );
 3491+ iPushFeature = 1;
 3492+ }
 3493+ else if ( cOption == 't' )
 3494+ {
 3495+ /* Table */
 3496+ nTmp = _fnFeatureHtmlTable( oSettings );
 3497+ iPushFeature = 1;
 3498+ }
 3499+ else if ( cOption == 'i' && oSettings.oFeatures.bInfo )
 3500+ {
 3501+ /* Info */
 3502+ nTmp = _fnFeatureHtmlInfo( oSettings );
 3503+ iPushFeature = 1;
 3504+ }
 3505+ else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
 3506+ {
 3507+ /* Pagination */
 3508+ nTmp = _fnFeatureHtmlPaginate( oSettings );
 3509+ iPushFeature = 1;
 3510+ }
 3511+ else if ( _oExt.aoFeatures.length !== 0 )
 3512+ {
 3513+ /* Plug-in features */
 3514+ var aoFeatures = _oExt.aoFeatures;
 3515+ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
 3516+ {
 3517+ if ( cOption == aoFeatures[k].cFeature )
 3518+ {
 3519+ nTmp = aoFeatures[k].fnInit( oSettings );
 3520+ if ( nTmp )
 3521+ {
 3522+ iPushFeature = 1;
 3523+ }
 3524+ break;
 3525+ }
 3526+ }
 3527+ }
 3528+
 3529+ /* Add to the 2D features array */
 3530+ if ( iPushFeature == 1 && nTmp !== null )
 3531+ {
 3532+ if ( typeof oSettings.aanFeatures[cOption] != 'object' )
 3533+ {
 3534+ oSettings.aanFeatures[cOption] = [];
 3535+ }
 3536+ oSettings.aanFeatures[cOption].push( nTmp );
 3537+ nInsertNode.appendChild( nTmp );
 3538+ }
 3539+ }
 3540+
 3541+ /* Built our DOM structure - replace the holding div with what we want */
 3542+ nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
 3543+ }
 3544+
 3545+
 3546+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 3547+ * Section - Feature: Filtering
 3548+ */
 3549+
 3550+ /*
 3551+ * Function: _fnFeatureHtmlTable
 3552+ * Purpose: Add any control elements for the table - specifically scrolling
 3553+ * Returns: node: - Node to add to the DOM
 3554+ * Inputs: object:oSettings - dataTables settings object
 3555+ */
 3556+ function _fnFeatureHtmlTable ( oSettings )
 3557+ {
 3558+ /* Chack if scrolling is enabled or not - if not then leave the DOM unaltered */
 3559+ if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
 3560+ {
 3561+ return oSettings.nTable;
 3562+ }
 3563+
 3564+ /*
 3565+ * The HTML structure that we want to generate in this function is:
 3566+ * div - nScroller
 3567+ * div - nScrollHead
 3568+ * div - nScrollHeadInner
 3569+ * table - nScrollHeadTable
 3570+ * thead - nThead
 3571+ * div - nScrollBody
 3572+ * table - oSettings.nTable
 3573+ * thead - nTheadSize
 3574+ * tbody - nTbody
 3575+ * div - nScrollFoot
 3576+ * div - nScrollFootInner
 3577+ * table - nScrollFootTable
 3578+ * tfoot - nTfoot
 3579+ */
 3580+ var
 3581+ nScroller = document.createElement('div'),
 3582+ nScrollHead = document.createElement('div'),
 3583+ nScrollHeadInner = document.createElement('div'),
 3584+ nScrollBody = document.createElement('div'),
 3585+ nScrollFoot = document.createElement('div'),
 3586+ nScrollFootInner = document.createElement('div'),
 3587+ nScrollHeadTable = oSettings.nTable.cloneNode(false),
 3588+ nScrollFootTable = oSettings.nTable.cloneNode(false),
 3589+ nThead = oSettings.nTable.getElementsByTagName('thead')[0],
 3590+ nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null :
 3591+ oSettings.nTable.getElementsByTagName('tfoot')[0],
 3592+ oClasses = (typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI) ?
 3593+ _oExt.oJUIClasses : _oExt.oStdClasses;
 3594+
 3595+ nScrollHead.appendChild( nScrollHeadInner );
 3596+ nScrollFoot.appendChild( nScrollFootInner );
 3597+ nScrollBody.appendChild( oSettings.nTable );
 3598+ nScroller.appendChild( nScrollHead );
 3599+ nScroller.appendChild( nScrollBody );
 3600+ nScrollHeadInner.appendChild( nScrollHeadTable );
 3601+ nScrollHeadTable.appendChild( nThead );
 3602+ if ( nTfoot !== null )
 3603+ {
 3604+ nScroller.appendChild( nScrollFoot );
 3605+ nScrollFootInner.appendChild( nScrollFootTable );
 3606+ nScrollFootTable.appendChild( nTfoot );
 3607+ }
 3608+
 3609+ nScroller.className = oClasses.sScrollWrapper;
 3610+ nScrollHead.className = oClasses.sScrollHead;
 3611+ nScrollHeadInner.className = oClasses.sScrollHeadInner;
 3612+ nScrollBody.className = oClasses.sScrollBody;
 3613+ nScrollFoot.className = oClasses.sScrollFoot;
 3614+ nScrollFootInner.className = oClasses.sScrollFootInner;
 3615+
 3616+ if ( oSettings.oScroll.bAutoCss )
 3617+ {
 3618+ nScrollHead.style.overflow = "hidden";
 3619+ nScrollHead.style.position = "relative";
 3620+ nScrollFoot.style.overflow = "hidden";
 3621+ nScrollBody.style.overflow = "auto";
 3622+ }
 3623+
 3624+ nScrollHead.style.border = "0";
 3625+ nScrollFoot.style.border = "0";
 3626+ nScrollHeadInner.style.width = "150%"; /* will be overwritten */
 3627+
 3628+ /* Modify attributes to respect the clones */
 3629+ nScrollHeadTable.removeAttribute('id');
 3630+ nScrollHeadTable.style.marginLeft = "0";
 3631+ oSettings.nTable.style.marginLeft = "0";
 3632+ if ( nTfoot !== null )
 3633+ {
 3634+ nScrollFootTable.removeAttribute('id');
 3635+ nScrollFootTable.style.marginLeft = "0";
 3636+ }
 3637+
 3638+ /* Move any caption elements from the body to the header */
 3639+ var nCaptions = $('>caption', oSettings.nTable);
 3640+ for ( var i=0, iLen=nCaptions.length ; i<iLen ; i++ )
 3641+ {
 3642+ nScrollHeadTable.appendChild( nCaptions[i] );
 3643+ }
 3644+
 3645+ /*
 3646+ * Sizing
 3647+ */
 3648+ /* When xscrolling add the width and a scroller to move the header with the body */
 3649+ if ( oSettings.oScroll.sX !== "" )
 3650+ {
 3651+ nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
 3652+ nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
 3653+
 3654+ if ( nTfoot !== null )
 3655+ {
 3656+ nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );
 3657+ }
 3658+
 3659+ /* When the body is scrolled, then we also want to scroll the headers */
 3660+ $(nScrollBody).scroll( function (e) {
 3661+ nScrollHead.scrollLeft = this.scrollLeft;
 3662+
 3663+ if ( nTfoot !== null )
 3664+ {
 3665+ nScrollFoot.scrollLeft = this.scrollLeft;
 3666+ }
 3667+ } );
 3668+ }
 3669+
 3670+ /* When yscrolling, add the height */
 3671+ if ( oSettings.oScroll.sY !== "" )
 3672+ {
 3673+ nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
 3674+ }
 3675+
 3676+ /* Redraw - align columns across the tables */
 3677+ oSettings.aoDrawCallback.push( {
 3678+ "fn": _fnScrollDraw,
 3679+ "sName": "scrolling"
 3680+ } );
 3681+
 3682+ /* Infinite scrolling event handlers */
 3683+ if ( oSettings.oScroll.bInfinite )
 3684+ {
 3685+ $(nScrollBody).scroll( function() {
 3686+ /* Use a blocker to stop scrolling from loading more data while other data is still loading */
 3687+ if ( !oSettings.bDrawing )
 3688+ {
 3689+ /* Check if we should load the next data set */
 3690+ if ( $(this).scrollTop() + $(this).height() >
 3691+ $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
 3692+ {
 3693+ /* Only do the redraw if we have to - we might be at the end of the data */
 3694+ if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
 3695+ {
 3696+ _fnPageChange( oSettings, 'next' );
 3697+ _fnCalculateEnd( oSettings );
 3698+ _fnDraw( oSettings );
 3699+ }
 3700+ }
 3701+ }
 3702+ } );
 3703+ }
 3704+
 3705+ oSettings.nScrollHead = nScrollHead;
 3706+ oSettings.nScrollFoot = nScrollFoot;
 3707+
 3708+ return nScroller;
 3709+ }
 3710+
 3711+ /*
 3712+ * Function: _fnScrollDraw
 3713+ * Purpose: Update the various tables for resizing
 3714+ * Returns: node: - Node to add to the DOM
 3715+ * Inputs: object:o - dataTables settings object
 3716+ * Notes: It's a bit of a pig this function, but basically the idea to:
 3717+ * 1. Re-create the table inside the scrolling div
 3718+ * 2. Take live measurements from the DOM
 3719+ * 3. Apply the measurements
 3720+ * 4. Clean up
 3721+ */
 3722+ function _fnScrollDraw ( o )
 3723+ {
 3724+ var
 3725+ nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
 3726+ nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
 3727+ nScrollBody = o.nTable.parentNode,
 3728+ i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
 3729+ iWidth, aApplied=[], iSanityWidth;
 3730+
 3731+ /*
 3732+ * 1. Re-create the table inside the scrolling div
 3733+ */
 3734+
 3735+ /* Remove the old minimised thead and tfoot elements in the inner table */
 3736+ var nTheadSize = o.nTable.getElementsByTagName('thead');
 3737+ if ( nTheadSize.length > 0 )
 3738+ {
 3739+ o.nTable.removeChild( nTheadSize[0] );
 3740+ }
 3741+
 3742+ if ( o.nTFoot !== null )
 3743+ {
 3744+ /* Remove the old minimised footer element in the cloned header */
 3745+ var nTfootSize = o.nTable.getElementsByTagName('tfoot');
 3746+ if ( nTfootSize.length > 0 )
 3747+ {
 3748+ o.nTable.removeChild( nTfootSize[0] );
 3749+ }
 3750+ }
 3751+
 3752+ /* Clone the current header and footer elements and then place it into the inner table */
 3753+ nTheadSize = o.nTHead.cloneNode(true);
 3754+ o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
 3755+
 3756+ if ( o.nTFoot !== null )
 3757+ {
 3758+ nTfootSize = o.nTFoot.cloneNode(true);
 3759+ o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
 3760+ }
 3761+
 3762+ /*
 3763+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
 3764+ */
 3765+
 3766+ /* Remove old sizing and apply the calculated column widths
 3767+ * Get the unique column headers in the newly created (cloned) header. We want to apply the
 3768+ * calclated sizes to this header
 3769+ */
 3770+ var nThs = _fnGetUniqueThs( nTheadSize );
 3771+ for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
 3772+ {
 3773+ iVis = _fnVisibleToColumnIndex( o, i );
 3774+ nThs[i].style.width = o.aoColumns[iVis].sWidth;
 3775+ }
 3776+
 3777+ if ( o.nTFoot !== null )
 3778+ {
 3779+ _fnApplyToChildren( function(n) {
 3780+ n.style.width = "";
 3781+ }, nTfootSize.getElementsByTagName('tr') );
 3782+ }
 3783+
 3784+ /* Size the table as a whole */
 3785+ iSanityWidth = $(o.nTable).outerWidth();
 3786+ if ( o.oScroll.sX === "" )
 3787+ {
 3788+ /* No x scrolling */
 3789+ o.nTable.style.width = "100%";
 3790+
 3791+ /* I know this is rubbish - but IE7 will make the width of the table when 100% include
 3792+ * the scrollbar - which is shouldn't. This needs feature detection in future - to do
 3793+ */
 3794+ if ( $.browser.msie && $.browser.version <= 7 )
 3795+ {
 3796+ o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth()-o.oScroll.iBarWidth );
 3797+ }
 3798+ }
 3799+ else
 3800+ {
 3801+ if ( o.oScroll.sXInner !== "" )
 3802+ {
 3803+ /* x scroll inner has been given - use it */
 3804+ o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
 3805+ }
 3806+ else if ( iSanityWidth == $(nScrollBody).width() &&
 3807+ $(nScrollBody).height() < $(o.nTable).height() )
 3808+ {
 3809+ /* There is y-scrolling - try to take account of the y scroll bar */
 3810+ o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
 3811+ if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
 3812+ {
 3813+ /* Not possible to take account of it */
 3814+ o.nTable.style.width = _fnStringToCss( iSanityWidth );
 3815+ }
 3816+ }
 3817+ else
 3818+ {
 3819+ /* All else fails */
 3820+ o.nTable.style.width = _fnStringToCss( iSanityWidth );
 3821+ }
 3822+ }
 3823+
 3824+ /* Recalculate the sanity width - now that we've applied the required width, before it was
 3825+ * a temporary variable. This is required because the column width calculation is done
 3826+ * before this table DOM is created.
 3827+ */
 3828+ iSanityWidth = $(o.nTable).outerWidth();
 3829+
 3830+ /* We want the hidden header to have zero height, so remove padding and borders. Then
 3831+ * set the width based on the real headers
 3832+ */
 3833+ anHeadToSize = o.nTHead.getElementsByTagName('tr');
 3834+ anHeadSizers = nTheadSize.getElementsByTagName('tr');
 3835+
 3836+ _fnApplyToChildren( function(nSizer, nToSize) {
 3837+ oStyle = nSizer.style;
 3838+ oStyle.paddingTop = "0";
 3839+ oStyle.paddingBottom = "0";
 3840+ oStyle.borderTopWidth = "0";
 3841+ oStyle.borderBottomWidth = "0";
 3842+ oStyle.height = 0;
 3843+
 3844+ iWidth = $(nSizer).width();
 3845+ nToSize.style.width = _fnStringToCss( iWidth );
 3846+ aApplied.push( iWidth );
 3847+ }, anHeadSizers, anHeadToSize );
 3848+ $(anHeadSizers).height(0);
 3849+
 3850+ if ( o.nTFoot !== null )
 3851+ {
 3852+ /* Clone the current footer and then place it into the body table as a "hidden header" */
 3853+ anFootSizers = nTfootSize.getElementsByTagName('tr');
 3854+ anFootToSize = o.nTFoot.getElementsByTagName('tr');
 3855+
 3856+ _fnApplyToChildren( function(nSizer, nToSize) {
 3857+ oStyle = nSizer.style;
 3858+ oStyle.paddingTop = "0";
 3859+ oStyle.paddingBottom = "0";
 3860+ oStyle.borderTopWidth = "0";
 3861+ oStyle.borderBottomWidth = "0";
 3862+
 3863+ iWidth = $(nSizer).width();
 3864+ nToSize.style.width = _fnStringToCss( iWidth );
 3865+ aApplied.push( iWidth );
 3866+ }, anFootSizers, anFootToSize );
 3867+ $(anFootSizers).height(0);
 3868+ }
 3869+
 3870+ /*
 3871+ * 3. Apply the measurements
 3872+ */
 3873+
 3874+ /* "Hide" the header and footer that we used for the sizing. We want to also fix their width
 3875+ * to what they currently are
 3876+ */
 3877+ _fnApplyToChildren( function(nSizer) {
 3878+ nSizer.innerHTML = "";
 3879+ nSizer.style.width = _fnStringToCss( aApplied.shift() );
 3880+ }, anHeadSizers );
 3881+
 3882+ if ( o.nTFoot !== null )
 3883+ {
 3884+ _fnApplyToChildren( function(nSizer) {
 3885+ nSizer.innerHTML = "";
 3886+ nSizer.style.width = _fnStringToCss( aApplied.shift() );
 3887+ }, anFootSizers );
 3888+ }
 3889+
 3890+ /* Sanity check that the table is of a sensible width. If not then we are going to get
 3891+ * misalignment
 3892+ */
 3893+ if ( $(o.nTable).outerWidth() < iSanityWidth )
 3894+ {
 3895+ if ( o.oScroll.sX === "" )
 3896+ {
 3897+ _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
 3898+ " misalignment. It is suggested that you enable x-scrolling or increase the width"+
 3899+ " the table has in which to be drawn" );
 3900+ }
 3901+ else if ( o.oScroll.sXInner !== "" )
 3902+ {
 3903+ _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
 3904+ " misalignment. It is suggested that you increase the sScrollXInner property to"+
 3905+ " allow it to draw in a larger area, or simply remove that parameter to allow"+
 3906+ " automatic calculation" );
 3907+ }
 3908+ }
 3909+
 3910+
 3911+ /*
 3912+ * 4. Clean up
 3913+ */
 3914+
 3915+ if ( o.oScroll.sY === "" )
 3916+ {
 3917+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
 3918+ * the scrollbar height from the visible display, rather than adding it on. We need to
 3919+ * set the height in order to sort this. Don't want to do it in any other browsers.
 3920+ */
 3921+ if ( $.browser.msie && $.browser.version <= 7 )
 3922+ {
 3923+ nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
 3924+ }
 3925+ }
 3926+
 3927+ if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
 3928+ {
 3929+ nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
 3930+
 3931+ var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
 3932+ o.oScroll.iBarWidth : 0;
 3933+ if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
 3934+ {
 3935+ nScrollBody.style.height = _fnStringToCss( $(o.nTable).height()+iExtra );
 3936+ }
 3937+ }
 3938+
 3939+ /* Finally set the width's of the header and footer tables */
 3940+ var iOuterWidth = $(o.nTable).outerWidth();
 3941+ nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
 3942+ nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth+o.oScroll.iBarWidth );
 3943+ nScrollHeadInner.parentNode.style.width = _fnStringToCss( $(nScrollBody).width() );
 3944+
 3945+ if ( o.nTFoot !== null )
 3946+ {
 3947+ var
 3948+ nScrollFootInner = o.nScrollFoot.getElementsByTagName('div')[0],
 3949+ nScrollFootTable = nScrollFootInner.getElementsByTagName('table')[0];
 3950+
 3951+ nScrollFootInner.style.width = _fnStringToCss( o.nTable.offsetWidth+o.oScroll.iBarWidth );
 3952+ nScrollFootTable.style.width = _fnStringToCss( o.nTable.offsetWidth );
 3953+ }
 3954+
 3955+ /* If sorting or filtering has occured, jump the scrolling back to the top */
 3956+ if ( o.bSorted || o.bFiltered )
 3957+ {
 3958+ nScrollBody.scrollTop = 0;
 3959+ }
 3960+ }
 3961+
 3962+ /*
 3963+ * Function: _fnAjustColumnSizing
 3964+ * Purpose: Ajust the table column widths for new data
 3965+ * Returns: -
 3966+ * Inputs: object:oSettings - dataTables settings object
 3967+ * Notes: You would probably want to do a redraw after calling this function!
 3968+ */
 3969+ function _fnAjustColumnSizing ( oSettings )
 3970+ {
 3971+ /* Not interested in doing column width calculation if autowidth is disabled */
 3972+ if ( oSettings.oFeatures.bAutoWidth === false )
 3973+ {
 3974+ return false;
 3975+ }
 3976+
 3977+ _fnCalculateColumnWidths( oSettings );
 3978+ for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 3979+ {
 3980+ oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
 3981+ }
 3982+ }
 3983+
 3984+
 3985+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 3986+ * Section - Feature: Filtering
 3987+ */
 3988+
 3989+ /*
 3990+ * Function: _fnFeatureHtmlFilter
 3991+ * Purpose: Generate the node required for filtering text
 3992+ * Returns: node
 3993+ * Inputs: object:oSettings - dataTables settings object
 3994+ */
 3995+ function _fnFeatureHtmlFilter ( oSettings )
 3996+ {
 3997+ var nFilter = document.createElement( 'div' );
 3998+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.f == "undefined" )
 3999+ {
 4000+ nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' );
 4001+ }
 4002+ nFilter.className = oSettings.oClasses.sFilter;
 4003+ var sSpace = oSettings.oLanguage.sSearch==="" ? "" : " ";
 4004+ nFilter.innerHTML = oSettings.oLanguage.sSearch+sSpace+'<input type="text" />';
 4005+
 4006+ var jqFilter = $("input", nFilter);
 4007+ jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','&quot;') );
 4008+ jqFilter.keyup( function(e) {
 4009+ /* Update all other filter input elements for the new display */
 4010+ var n = oSettings.aanFeatures.f;
 4011+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
 4012+ {
 4013+ if ( n[i] != this.parentNode )
 4014+ {
 4015+ $('input', n[i]).val( this.value );
 4016+ }
 4017+ }
 4018+
 4019+ /* Now do the filter */
 4020+ if ( this.value != oSettings.oPreviousSearch.sSearch )
 4021+ {
 4022+ _fnFilterComplete( oSettings, {
 4023+ "sSearch": this.value,
 4024+ "bRegex": oSettings.oPreviousSearch.bRegex,
 4025+ "bSmart": oSettings.oPreviousSearch.bSmart
 4026+ } );
 4027+ }
 4028+ } );
 4029+
 4030+ jqFilter.keypress( function(e) {
 4031+ /* Prevent default */
 4032+ if ( e.keyCode == 13 )
 4033+ {
 4034+ return false;
 4035+ }
 4036+ } );
 4037+
 4038+ return nFilter;
 4039+ }
 4040+
 4041+ /*
 4042+ * Function: _fnFilterComplete
 4043+ * Purpose: Filter the table using both the global filter and column based filtering
 4044+ * Returns: -
 4045+ * Inputs: object:oSettings - dataTables settings object
 4046+ * object:oSearch: search information
 4047+ * int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
 4048+ */
 4049+ function _fnFilterComplete ( oSettings, oInput, iForce )
 4050+ {
 4051+ /* Filter on everything */
 4052+ _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart );
 4053+
 4054+ /* Now do the individual column filter */
 4055+ for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
 4056+ {
 4057+ _fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i,
 4058+ oSettings.aoPreSearchCols[i].bRegex, oSettings.aoPreSearchCols[i].bSmart );
 4059+ }
 4060+
 4061+ /* Custom filtering */
 4062+ if ( _oExt.afnFiltering.length !== 0 )
 4063+ {
 4064+ _fnFilterCustom( oSettings );
 4065+ }
 4066+
 4067+ /* Tell the draw function we have been filtering */
 4068+ oSettings.bFiltered = true;
 4069+
 4070+ /* Redraw the table */
 4071+ oSettings._iDisplayStart = 0;
 4072+ _fnCalculateEnd( oSettings );
 4073+ _fnDraw( oSettings );
 4074+
 4075+ /* Rebuild search array 'offline' */
 4076+ _fnBuildSearchArray( oSettings, 0 );
 4077+ }
 4078+
 4079+ /*
 4080+ * Function: _fnFilterCustom
 4081+ * Purpose: Apply custom filtering functions
 4082+ * Returns: -
 4083+ * Inputs: object:oSettings - dataTables settings object
 4084+ */
 4085+ function _fnFilterCustom( oSettings )
 4086+ {
 4087+ var afnFilters = _oExt.afnFiltering;
 4088+ for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
 4089+ {
 4090+ var iCorrector = 0;
 4091+ for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
 4092+ {
 4093+ var iDisIndex = oSettings.aiDisplay[j-iCorrector];
 4094+
 4095+ /* Check if we should use this row based on the filtering function */
 4096+ if ( !afnFilters[i]( oSettings, oSettings.aoData[iDisIndex]._aData, iDisIndex ) )
 4097+ {
 4098+ oSettings.aiDisplay.splice( j-iCorrector, 1 );
 4099+ iCorrector++;
 4100+ }
 4101+ }
 4102+ }
 4103+ }
 4104+
 4105+ /*
 4106+ * Function: _fnFilterColumn
 4107+ * Purpose: Filter the table on a per-column basis
 4108+ * Returns: -
 4109+ * Inputs: object:oSettings - dataTables settings object
 4110+ * string:sInput - string to filter on
 4111+ * int:iColumn - column to filter
 4112+ * bool:bRegex - treat search string as a regular expression or not
 4113+ * bool:bSmart - use smart filtering or not
 4114+ */
 4115+ function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart )
 4116+ {
 4117+ if ( sInput === "" )
 4118+ {
 4119+ return;
 4120+ }
 4121+
 4122+ var iIndexCorrector = 0;
 4123+ var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
 4124+
 4125+ for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
 4126+ {
 4127+ var sData = _fnDataToSearch( oSettings.aoData[ oSettings.aiDisplay[i] ]._aData[iColumn],
 4128+ oSettings.aoColumns[iColumn].sType );
 4129+ if ( ! rpSearch.test( sData ) )
 4130+ {
 4131+ oSettings.aiDisplay.splice( i, 1 );
 4132+ iIndexCorrector++;
 4133+ }
 4134+ }
 4135+ }
 4136+
 4137+ /*
 4138+ * Function: _fnFilter
 4139+ * Purpose: Filter the data table based on user input and draw the table
 4140+ * Returns: -
 4141+ * Inputs: object:oSettings - dataTables settings object
 4142+ * string:sInput - string to filter on
 4143+ * int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
 4144+ * bool:bRegex - treat as a regular expression or not
 4145+ * bool:bSmart - perform smart filtering or not
 4146+ */
 4147+ function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart )
 4148+ {
 4149+ var i;
 4150+ var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
 4151+
 4152+ /* Check if we are forcing or not - optional parameter */
 4153+ if ( typeof iForce == 'undefined' || iForce === null )
 4154+ {
 4155+ iForce = 0;
 4156+ }
 4157+
 4158+ /* Need to take account of custom filtering functions - always filter */
 4159+ if ( _oExt.afnFiltering.length !== 0 )
 4160+ {
 4161+ iForce = 1;
 4162+ }
 4163+
 4164+ /*
 4165+ * If the input is blank - we want the full data set
 4166+ */
 4167+ if ( sInput.length <= 0 )
 4168+ {
 4169+ oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
 4170+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 4171+ }
 4172+ else
 4173+ {
 4174+ /*
 4175+ * We are starting a new search or the new search string is smaller
 4176+ * then the old one (i.e. delete). Search from the master array
 4177+ */
 4178+ if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
 4179+ oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 ||
 4180+ sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 )
 4181+ {
 4182+ /* Nuke the old display array - we are going to rebuild it */
 4183+ oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
 4184+
 4185+ /* Force a rebuild of the search array */
 4186+ _fnBuildSearchArray( oSettings, 1 );
 4187+
 4188+ /* Search through all records to populate the search array
 4189+ * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1
 4190+ * mapping
 4191+ */
 4192+ for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
 4193+ {
 4194+ if ( rpSearch.test(oSettings.asDataSearch[i]) )
 4195+ {
 4196+ oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
 4197+ }
 4198+ }
 4199+ }
 4200+ else
 4201+ {
 4202+ /* Using old search array - refine it - do it this way for speed
 4203+ * Don't have to search the whole master array again
 4204+ */
 4205+ var iIndexCorrector = 0;
 4206+
 4207+ /* Search the current results */
 4208+ for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
 4209+ {
 4210+ if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
 4211+ {
 4212+ oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
 4213+ iIndexCorrector++;
 4214+ }
 4215+ }
 4216+ }
 4217+ }
 4218+ oSettings.oPreviousSearch.sSearch = sInput;
 4219+ oSettings.oPreviousSearch.bRegex = bRegex;
 4220+ oSettings.oPreviousSearch.bSmart = bSmart;
 4221+ }
 4222+
 4223+ /*
 4224+ * Function: _fnBuildSearchArray
 4225+ * Purpose: Create an array which can be quickly search through
 4226+ * Returns: -
 4227+ * Inputs: object:oSettings - dataTables settings object
 4228+ * int:iMaster - use the master data array - optional
 4229+ */
 4230+ function _fnBuildSearchArray ( oSettings, iMaster )
 4231+ {
 4232+ /* Clear out the old data */
 4233+ oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length );
 4234+
 4235+ var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ?
 4236+ oSettings.aiDisplayMaster : oSettings.aiDisplay;
 4237+
 4238+ for ( var i=0, iLen=aArray.length ; i<iLen ; i++ )
 4239+ {
 4240+ oSettings.asDataSearch[i] = _fnBuildSearchRow( oSettings,
 4241+ oSettings.aoData[ aArray[i] ]._aData );
 4242+ }
 4243+ }
 4244+
 4245+ /*
 4246+ * Function: _fnBuildSearchRow
 4247+ * Purpose: Create a searchable string from a single data row
 4248+ * Returns: -
 4249+ * Inputs: object:oSettings - dataTables settings object
 4250+ * array:aData - aoData[]._aData array to use for the data to search
 4251+ */
 4252+ function _fnBuildSearchRow( oSettings, aData )
 4253+ {
 4254+ var sSearch = '';
 4255+ var nTmp = document.createElement('div');
 4256+
 4257+ for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
 4258+ {
 4259+ if ( oSettings.aoColumns[j].bSearchable )
 4260+ {
 4261+ var sData = aData[j];
 4262+ sSearch += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+' ';
 4263+ }
 4264+ }
 4265+
 4266+ /* If it looks like there is an HTML entity in the string, attempt to decode it */
 4267+ if ( sSearch.indexOf('&') !== -1 )
 4268+ {
 4269+ nTmp.innerHTML = sSearch;
 4270+ sSearch = nTmp.textContent ? nTmp.textContent : nTmp.innerText;
 4271+
 4272+ /* IE and Opera appear to put an newline where there is a <br> tag - remove it */
 4273+ sSearch = sSearch.replace(/\n/g," ").replace(/\r/g,"");
 4274+ }
 4275+
 4276+ return sSearch;
 4277+ }
 4278+
 4279+ /*
 4280+ * Function: _fnFilterCreateSearch
 4281+ * Purpose: Build a regular expression object suitable for searching a table
 4282+ * Returns: RegExp: - constructed object
 4283+ * Inputs: string:sSearch - string to search for
 4284+ * bool:bRegex - treat as a regular expression or not
 4285+ * bool:bSmart - perform smart filtering or not
 4286+ */
 4287+ function _fnFilterCreateSearch( sSearch, bRegex, bSmart )
 4288+ {
 4289+ var asSearch, sRegExpString;
 4290+
 4291+ if ( bSmart )
 4292+ {
 4293+ /* Generate the regular expression to use. Something along the lines of:
 4294+ * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
 4295+ */
 4296+ asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
 4297+ sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
 4298+ return new RegExp( sRegExpString, "i" );
 4299+ }
 4300+ else
 4301+ {
 4302+ sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
 4303+ return new RegExp( sSearch, "i" );
 4304+ }
 4305+ }
 4306+
 4307+ /*
 4308+ * Function: _fnDataToSearch
 4309+ * Purpose: Convert raw data into something that the user can search on
 4310+ * Returns: string: - search string
 4311+ * Inputs: string:sData - data to be modified
 4312+ * string:sType - data type
 4313+ */
 4314+ function _fnDataToSearch ( sData, sType )
 4315+ {
 4316+ if ( typeof _oExt.ofnSearch[sType] == "function" )
 4317+ {
 4318+ return _oExt.ofnSearch[sType]( sData );
 4319+ }
 4320+ else if ( sType == "html" )
 4321+ {
 4322+ return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
 4323+ }
 4324+ else if ( typeof sData == "string" )
 4325+ {
 4326+ return sData.replace(/\n/g," ");
 4327+ }
 4328+ return sData;
 4329+ }
 4330+
 4331+
 4332+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 4333+ * Section - Feature: Sorting
 4334+ */
 4335+
 4336+ /*
 4337+ * Function: _fnSort
 4338+ * Purpose: Change the order of the table
 4339+ * Returns: -
 4340+ * Inputs: object:oSettings - dataTables settings object
 4341+ * bool:bApplyClasses - optional - should we apply classes or not
 4342+ * Notes: We always sort the master array and then apply a filter again
 4343+ * if it is needed. This probably isn't optimal - but atm I can't think
 4344+ * of any other way which is (each has disadvantages). we want to sort aiDisplayMaster -
 4345+ * but according to aoData[]._aData
 4346+ */
 4347+ function _fnSort ( oSettings, bApplyClasses )
 4348+ {
 4349+ var
 4350+ iDataSort, iDataType,
 4351+ i, iLen, j, jLen,
 4352+ aaSort = [],
 4353+ aiOrig = [],
 4354+ oSort = _oExt.oSort,
 4355+ aoData = oSettings.aoData,
 4356+ aoColumns = oSettings.aoColumns;
 4357+
 4358+ /* No sorting required if server-side or no sorting array */
 4359+ if ( !oSettings.oFeatures.bServerSide &&
 4360+ (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
 4361+ {
 4362+ if ( oSettings.aaSortingFixed !== null )
 4363+ {
 4364+ aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
 4365+ }
 4366+ else
 4367+ {
 4368+ aaSort = oSettings.aaSorting.slice();
 4369+ }
 4370+
 4371+ /* If there is a sorting data type, and a fuction belonging to it, then we need to
 4372+ * get the data from the developer's function and apply it for this column
 4373+ */
 4374+ for ( i=0 ; i<aaSort.length ; i++ )
 4375+ {
 4376+ var iColumn = aaSort[i][0];
 4377+ var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
 4378+ var sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
 4379+ if ( typeof _oExt.afnSortData[sDataType] != 'undefined' )
 4380+ {
 4381+ var aData = _oExt.afnSortData[sDataType]( oSettings, iColumn, iVisColumn );
 4382+ for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
 4383+ {
 4384+ aoData[j]._aData[iColumn] = aData[j];
 4385+ }
 4386+ }
 4387+ }
 4388+
 4389+ /* Create a value - key array of the current row positions such that we can use their
 4390+ * current position during the sort, if values match, in order to perform stable sorting
 4391+ */
 4392+ for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
 4393+ {
 4394+ aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
 4395+ }
 4396+
 4397+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
 4398+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
 4399+ * follow on it's own, but this is what we want (example two column sorting):
 4400+ * fnLocalSorting = function(a,b){
 4401+ * var iTest;
 4402+ * iTest = oSort['string-asc']('data11', 'data12');
 4403+ * if (iTest !== 0)
 4404+ * return iTest;
 4405+ * iTest = oSort['numeric-desc']('data21', 'data22');
 4406+ * if (iTest !== 0)
 4407+ * return iTest;
 4408+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
 4409+ * }
 4410+ * Basically we have a test for each sorting column, if the data in that column is equal,
 4411+ * test the next column. If all columns match, then we use a numeric sort on the row
 4412+ * positions in the original data array to provide a stable sort.
 4413+ */
 4414+ var iSortLen = aaSort.length;
 4415+ oSettings.aiDisplayMaster.sort( function ( a, b ) {
 4416+ var iTest;
 4417+ for ( i=0 ; i<iSortLen ; i++ )
 4418+ {
 4419+ iDataSort = aoColumns[ aaSort[i][0] ].iDataSort;
 4420+ iDataType = aoColumns[ iDataSort ].sType;
 4421+ iTest = oSort[ iDataType+"-"+aaSort[i][1] ](
 4422+ aoData[a]._aData[iDataSort],
 4423+ aoData[b]._aData[iDataSort]
 4424+ );
 4425+
 4426+ if ( iTest !== 0 )
 4427+ {
 4428+ return iTest;
 4429+ }
 4430+ }
 4431+
 4432+ return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
 4433+ } );
 4434+ }
 4435+
 4436+ /* Alter the sorting classes to take account of the changes */
 4437+ if ( typeof bApplyClasses == 'undefined' || bApplyClasses )
 4438+ {
 4439+ _fnSortingClasses( oSettings );
 4440+ }
 4441+
 4442+ /* Tell the draw function that we have sorted the data */
 4443+ oSettings.bSorted = true;
 4444+
 4445+ /* Copy the master data into the draw array and re-draw */
 4446+ if ( oSettings.oFeatures.bFilter )
 4447+ {
 4448+ /* _fnFilter() will redraw the table for us */
 4449+ _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
 4450+ }
 4451+ else
 4452+ {
 4453+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 4454+ oSettings._iDisplayStart = 0; /* reset display back to page 0 */
 4455+ _fnCalculateEnd( oSettings );
 4456+ _fnDraw( oSettings );
 4457+ }
 4458+ }
 4459+
 4460+ /*
 4461+ * Function: _fnSortAttachListener
 4462+ * Purpose: Attach a sort handler (click) to a node
 4463+ * Returns: -
 4464+ * Inputs: object:oSettings - dataTables settings object
 4465+ * node:nNode - node to attach the handler to
 4466+ * int:iDataIndex - column sorting index
 4467+ * function:fnCallback - callback function - optional
 4468+ */
 4469+ function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
 4470+ {
 4471+ $(nNode).click( function (e) {
 4472+ /* If the column is not sortable - don't to anything */
 4473+ if ( oSettings.aoColumns[iDataIndex].bSortable === false )
 4474+ {
 4475+ return;
 4476+ }
 4477+
 4478+ /*
 4479+ * This is a little bit odd I admit... I declare a temporary function inside the scope of
 4480+ * _fnDrawHead and the click handler in order that the code presented here can be used
 4481+ * twice - once for when bProcessing is enabled, and another time for when it is
 4482+ * disabled, as we need to perform slightly different actions.
 4483+ * Basically the issue here is that the Javascript engine in modern browsers don't
 4484+ * appear to allow the rendering engine to update the display while it is still excuting
 4485+ * it's thread (well - it does but only after long intervals). This means that the
 4486+ * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
 4487+ * I force an execution break by using setTimeout - but this breaks the expected
 4488+ * thread continuation for the end-developer's point of view (their code would execute
 4489+ * too early), so we on;y do it when we absolutely have to.
 4490+ */
 4491+ var fnInnerSorting = function () {
 4492+ var iColumn, iNextSort;
 4493+
 4494+ /* If the shift key is pressed then we are multipe column sorting */
 4495+ if ( e.shiftKey )
 4496+ {
 4497+ /* Are we already doing some kind of sort on this column? */
 4498+ var bFound = false;
 4499+ for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
 4500+ {
 4501+ if ( oSettings.aaSorting[i][0] == iDataIndex )
 4502+ {
 4503+ bFound = true;
 4504+ iColumn = oSettings.aaSorting[i][0];
 4505+ iNextSort = oSettings.aaSorting[i][2]+1;
 4506+
 4507+ if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
 4508+ {
 4509+ /* Reached the end of the sorting options, remove from multi-col sort */
 4510+ oSettings.aaSorting.splice( i, 1 );
 4511+ }
 4512+ else
 4513+ {
 4514+ /* Move onto next sorting direction */
 4515+ oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
 4516+ oSettings.aaSorting[i][2] = iNextSort;
 4517+ }
 4518+ break;
 4519+ }
 4520+ }
 4521+
 4522+ /* No sort yet - add it in */
 4523+ if ( bFound === false )
 4524+ {
 4525+ oSettings.aaSorting.push( [ iDataIndex,
 4526+ oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
 4527+ }
 4528+ }
 4529+ else
 4530+ {
 4531+ /* If no shift key then single column sort */
 4532+ if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
 4533+ {
 4534+ iColumn = oSettings.aaSorting[0][0];
 4535+ iNextSort = oSettings.aaSorting[0][2]+1;
 4536+ if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
 4537+ {
 4538+ iNextSort = 0;
 4539+ }
 4540+ oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
 4541+ oSettings.aaSorting[0][2] = iNextSort;
 4542+ }
 4543+ else
 4544+ {
 4545+ oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
 4546+ oSettings.aaSorting.push( [ iDataIndex,
 4547+ oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
 4548+ }
 4549+ }
 4550+
 4551+ /* Run the sort */
 4552+ _fnSort( oSettings );
 4553+ }; /* /fnInnerSorting */
 4554+
 4555+ if ( !oSettings.oFeatures.bProcessing )
 4556+ {
 4557+ fnInnerSorting();
 4558+ }
 4559+ else
 4560+ {
 4561+ _fnProcessingDisplay( oSettings, true );
 4562+ setTimeout( function() {
 4563+ fnInnerSorting();
 4564+ if ( !oSettings.oFeatures.bServerSide )
 4565+ {
 4566+ _fnProcessingDisplay( oSettings, false );
 4567+ }
 4568+ }, 0 );
 4569+ }
 4570+
 4571+ /* Call the user specified callback function - used for async user interaction */
 4572+ if ( typeof fnCallback == 'function' )
 4573+ {
 4574+ fnCallback( oSettings );
 4575+ }
 4576+ } );
 4577+ }
 4578+
 4579+ /*
 4580+ * Function: _fnSortingClasses
 4581+ * Purpose: Set the sortting classes on the header
 4582+ * Returns: -
 4583+ * Inputs: object:oSettings - dataTables settings object
 4584+ * Notes: It is safe to call this function when bSort and bSortClasses are false
 4585+ */
 4586+ function _fnSortingClasses( oSettings )
 4587+ {
 4588+ var i, iLen, j, jLen, iFound;
 4589+ var aaSort, sClass;
 4590+ var iColumns = oSettings.aoColumns.length;
 4591+ var oClasses = oSettings.oClasses;
 4592+
 4593+ for ( i=0 ; i<iColumns ; i++ )
 4594+ {
 4595+ if ( oSettings.aoColumns[i].bSortable )
 4596+ {
 4597+ $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
 4598+ " "+ oSettings.aoColumns[i].sSortingClass );
 4599+ }
 4600+ }
 4601+
 4602+ if ( oSettings.aaSortingFixed !== null )
 4603+ {
 4604+ aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
 4605+ }
 4606+ else
 4607+ {
 4608+ aaSort = oSettings.aaSorting.slice();
 4609+ }
 4610+
 4611+ /* Apply the required classes to the header */
 4612+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
 4613+ {
 4614+ if ( oSettings.aoColumns[i].bSortable )
 4615+ {
 4616+ sClass = oSettings.aoColumns[i].sSortingClass;
 4617+ iFound = -1;
 4618+ for ( j=0 ; j<aaSort.length ; j++ )
 4619+ {
 4620+ if ( aaSort[j][0] == i )
 4621+ {
 4622+ sClass = ( aaSort[j][1] == "asc" ) ?
 4623+ oClasses.sSortAsc : oClasses.sSortDesc;
 4624+ iFound = j;
 4625+ break;
 4626+ }
 4627+ }
 4628+ $(oSettings.aoColumns[i].nTh).addClass( sClass );
 4629+
 4630+ if ( oSettings.bJUI )
 4631+ {
 4632+ /* jQuery UI uses extra markup */
 4633+ var jqSpan = $("span", oSettings.aoColumns[i].nTh);
 4634+ jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+
 4635+ oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
 4636+
 4637+ var sSpanClass;
 4638+ if ( iFound == -1 )
 4639+ {
 4640+ sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
 4641+ }
 4642+ else if ( aaSort[iFound][1] == "asc" )
 4643+ {
 4644+ sSpanClass = oClasses.sSortJUIAsc;
 4645+ }
 4646+ else
 4647+ {
 4648+ sSpanClass = oClasses.sSortJUIDesc;
 4649+ }
 4650+
 4651+ jqSpan.addClass( sSpanClass );
 4652+ }
 4653+ }
 4654+ else
 4655+ {
 4656+ /* No sorting on this column, so add the base class. This will have been assigned by
 4657+ * _fnAddColumn
 4658+ */
 4659+ $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
 4660+ }
 4661+ }
 4662+
 4663+ /*
 4664+ * Apply the required classes to the table body
 4665+ * Note that this is given as a feature switch since it can significantly slow down a sort
 4666+ * on large data sets (adding and removing of classes is always slow at the best of times..)
 4667+ * Further to this, note that this code is admitadly fairly ugly. It could be made a lot
 4668+ * simpiler using jQuery selectors and add/removeClass, but that is significantly slower
 4669+ * (on the order of 5 times slower) - hence the direct DOM manipulation here.
 4670+ */
 4671+ sClass = oClasses.sSortColumn;
 4672+
 4673+ if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
 4674+ {
 4675+ var nTds = _fnGetTdNodes( oSettings );
 4676+
 4677+ /* Remove the old classes */
 4678+ if ( nTds.length >= iColumns )
 4679+ {
 4680+ for ( i=0 ; i<iColumns ; i++ )
 4681+ {
 4682+ if ( nTds[i].className.indexOf(sClass+"1") != -1 )
 4683+ {
 4684+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
 4685+ {
 4686+ nTds[(iColumns*j)+i].className =
 4687+ $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"1", "" ) );
 4688+ }
 4689+ }
 4690+ else if ( nTds[i].className.indexOf(sClass+"2") != -1 )
 4691+ {
 4692+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
 4693+ {
 4694+ nTds[(iColumns*j)+i].className =
 4695+ $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"2", "" ) );
 4696+ }
 4697+ }
 4698+ else if ( nTds[i].className.indexOf(sClass+"3") != -1 )
 4699+ {
 4700+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
 4701+ {
 4702+ nTds[(iColumns*j)+i].className =
 4703+ $.trim( nTds[(iColumns*j)+i].className.replace( " "+sClass+"3", "" ) );
 4704+ }
 4705+ }
 4706+ }
 4707+ }
 4708+
 4709+ /* Add the new classes to the table */
 4710+ var iClass = 1, iTargetCol;
 4711+ for ( i=0 ; i<aaSort.length ; i++ )
 4712+ {
 4713+ iTargetCol = parseInt( aaSort[i][0], 10 );
 4714+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
 4715+ {
 4716+ nTds[(iColumns*j)+iTargetCol].className += " "+sClass+iClass;
 4717+ }
 4718+
 4719+ if ( iClass < 3 )
 4720+ {
 4721+ iClass++;
 4722+ }
 4723+ }
 4724+ }
 4725+ }
 4726+
 4727+
 4728+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 4729+ * Section - Feature: Pagination. Note that most of the paging logic is done in
 4730+ * _oExt.oPagination
 4731+ */
 4732+
 4733+ /*
 4734+ * Function: _fnFeatureHtmlPaginate
 4735+ * Purpose: Generate the node required for default pagination
 4736+ * Returns: node
 4737+ * Inputs: object:oSettings - dataTables settings object
 4738+ */
 4739+ function _fnFeatureHtmlPaginate ( oSettings )
 4740+ {
 4741+ if ( oSettings.oScroll.bInfinite )
 4742+ {
 4743+ return null;
 4744+ }
 4745+
 4746+ var nPaginate = document.createElement( 'div' );
 4747+ nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
 4748+
 4749+ _oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate,
 4750+ function( oSettings ) {
 4751+ _fnCalculateEnd( oSettings );
 4752+ _fnDraw( oSettings );
 4753+ }
 4754+ );
 4755+
 4756+ /* Add a draw callback for the pagination on first instance, to update the paging display */
 4757+ if ( typeof oSettings.aanFeatures.p == "undefined" )
 4758+ {
 4759+ oSettings.aoDrawCallback.push( {
 4760+ "fn": function( oSettings ) {
 4761+ _oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
 4762+ _fnCalculateEnd( oSettings );
 4763+ _fnDraw( oSettings );
 4764+ } );
 4765+ },
 4766+ "sName": "pagination"
 4767+ } );
 4768+ }
 4769+ return nPaginate;
 4770+ }
 4771+
 4772+ /*
 4773+ * Function: _fnPageChange
 4774+ * Purpose: Alter the display settings to change the page
 4775+ * Returns: bool:true - page has changed, false - no change (no effect) eg 'first' on page 1
 4776+ * Inputs: object:oSettings - dataTables settings object
 4777+ * string:sAction - paging action to take: "first", "previous", "next" or "last"
 4778+ */
 4779+ function _fnPageChange ( oSettings, sAction )
 4780+ {
 4781+ var iOldStart = oSettings._iDisplayStart;
 4782+
 4783+ if ( sAction == "first" )
 4784+ {
 4785+ oSettings._iDisplayStart = 0;
 4786+ }
 4787+ else if ( sAction == "previous" )
 4788+ {
 4789+ oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
 4790+ oSettings._iDisplayStart - oSettings._iDisplayLength :
 4791+ 0;
 4792+
 4793+ /* Correct for underrun */
 4794+ if ( oSettings._iDisplayStart < 0 )
 4795+ {
 4796+ oSettings._iDisplayStart = 0;
 4797+ }
 4798+ }
 4799+ else if ( sAction == "next" )
 4800+ {
 4801+ if ( oSettings._iDisplayLength >= 0 )
 4802+ {
 4803+ /* Make sure we are not over running the display array */
 4804+ if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
 4805+ {
 4806+ oSettings._iDisplayStart += oSettings._iDisplayLength;
 4807+ }
 4808+ }
 4809+ else
 4810+ {
 4811+ oSettings._iDisplayStart = 0;
 4812+ }
 4813+ }
 4814+ else if ( sAction == "last" )
 4815+ {
 4816+ if ( oSettings._iDisplayLength >= 0 )
 4817+ {
 4818+ var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
 4819+ oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
 4820+ }
 4821+ else
 4822+ {
 4823+ oSettings._iDisplayStart = 0;
 4824+ }
 4825+ }
 4826+ else
 4827+ {
 4828+ _fnLog( oSettings, 0, "Unknown paging action: "+sAction );
 4829+ }
 4830+
 4831+ return iOldStart != oSettings._iDisplayStart;
 4832+ }
 4833+
 4834+
 4835+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 4836+ * Section - Feature: HTML info
 4837+ */
 4838+
 4839+ /*
 4840+ * Function: _fnFeatureHtmlInfo
 4841+ * Purpose: Generate the node required for the info display
 4842+ * Returns: node
 4843+ * Inputs: object:oSettings - dataTables settings object
 4844+ */
 4845+ function _fnFeatureHtmlInfo ( oSettings )
 4846+ {
 4847+ var nInfo = document.createElement( 'div' );
 4848+ nInfo.className = oSettings.oClasses.sInfo;
 4849+
 4850+ /* Actions that are to be taken once only for this feature */
 4851+ if ( typeof oSettings.aanFeatures.i == "undefined" )
 4852+ {
 4853+ /* Add draw callback */
 4854+ oSettings.aoDrawCallback.push( {
 4855+ "fn": _fnUpdateInfo,
 4856+ "sName": "information"
 4857+ } );
 4858+
 4859+ /* Add id */
 4860+ if ( oSettings.sTableId !== '' )
 4861+ {
 4862+ nInfo.setAttribute( 'id', oSettings.sTableId+'_info' );
 4863+ }
 4864+ }
 4865+
 4866+ return nInfo;
 4867+ }
 4868+
 4869+ /*
 4870+ * Function: _fnUpdateInfo
 4871+ * Purpose: Update the information elements in the display
 4872+ * Returns: -
 4873+ * Inputs: object:oSettings - dataTables settings object
 4874+ */
 4875+ function _fnUpdateInfo ( oSettings )
 4876+ {
 4877+ /* Show information about the table */
 4878+ if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
 4879+ {
 4880+ return;
 4881+ }
 4882+
 4883+ var
 4884+ iStart = oSettings._iDisplayStart+1, iEnd = oSettings.fnDisplayEnd(),
 4885+ iMax = oSettings.fnRecordsTotal(), iTotal = oSettings.fnRecordsDisplay(),
 4886+ sStart = oSettings.fnFormatNumber( iStart ), sEnd = oSettings.fnFormatNumber( iEnd ),
 4887+ sMax = oSettings.fnFormatNumber( iMax ), sTotal = oSettings.fnFormatNumber( iTotal ),
 4888+ sOut;
 4889+
 4890+ /* When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
 4891+ * internally
 4892+ */
 4893+ if ( oSettings.oScroll.bInfinite )
 4894+ {
 4895+ sStart = oSettings.fnFormatNumber( 1 );
 4896+ }
 4897+
 4898+ if ( oSettings.fnRecordsDisplay() === 0 &&
 4899+ oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
 4900+ {
 4901+ /* Empty record set */
 4902+ sOut = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix;
 4903+ }
 4904+ else if ( oSettings.fnRecordsDisplay() === 0 )
 4905+ {
 4906+ /* Rmpty record set after filtering */
 4907+ sOut = oSettings.oLanguage.sInfoEmpty +' '+
 4908+ oSettings.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+
 4909+ oSettings.oLanguage.sInfoPostFix;
 4910+ }
 4911+ else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
 4912+ {
 4913+ /* Normal record set */
 4914+ sOut = oSettings.oLanguage.sInfo.
 4915+ replace('_START_', sStart).
 4916+ replace('_END_', sEnd).
 4917+ replace('_TOTAL_', sTotal)+
 4918+ oSettings.oLanguage.sInfoPostFix;
 4919+ }
 4920+ else
 4921+ {
 4922+ /* Record set after filtering */
 4923+ sOut = oSettings.oLanguage.sInfo.
 4924+ replace('_START_', sStart).
 4925+ replace('_END_', sEnd).
 4926+ replace('_TOTAL_', sTotal) +' '+
 4927+ oSettings.oLanguage.sInfoFiltered.replace('_MAX_',
 4928+ oSettings.fnFormatNumber(oSettings.fnRecordsTotal()))+
 4929+ oSettings.oLanguage.sInfoPostFix;
 4930+ }
 4931+
 4932+ if ( oSettings.oLanguage.fnInfoCallback !== null )
 4933+ {
 4934+ sOut = oSettings.oLanguage.fnInfoCallback( oSettings, iStart, iEnd, iMax, iTotal, sOut );
 4935+ }
 4936+
 4937+ var n = oSettings.aanFeatures.i;
 4938+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
 4939+ {
 4940+ $(n[i]).html( sOut );
 4941+ }
 4942+ }
 4943+
 4944+
 4945+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 4946+ * Section - Feature: Length change
 4947+ */
 4948+
 4949+ /*
 4950+ * Function: _fnFeatureHtmlLength
 4951+ * Purpose: Generate the node required for user display length changing
 4952+ * Returns: node
 4953+ * Inputs: object:oSettings - dataTables settings object
 4954+ */
 4955+ function _fnFeatureHtmlLength ( oSettings )
 4956+ {
 4957+ if ( oSettings.oScroll.bInfinite )
 4958+ {
 4959+ return null;
 4960+ }
 4961+
 4962+ /* This can be overruled by not using the _MENU_ var/macro in the language variable */
 4963+ var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"';
 4964+ var sStdMenu = '<select size="1" '+sName+'>';
 4965+ var i, iLen;
 4966+
 4967+ if ( oSettings.aLengthMenu.length == 2 && typeof oSettings.aLengthMenu[0] == 'object' &&
 4968+ typeof oSettings.aLengthMenu[1] == 'object' )
 4969+ {
 4970+ for ( i=0, iLen=oSettings.aLengthMenu[0].length ; i<iLen ; i++ )
 4971+ {
 4972+ sStdMenu += '<option value="'+oSettings.aLengthMenu[0][i]+'">'+
 4973+ oSettings.aLengthMenu[1][i]+'</option>';
 4974+ }
 4975+ }
 4976+ else
 4977+ {
 4978+ for ( i=0, iLen=oSettings.aLengthMenu.length ; i<iLen ; i++ )
 4979+ {
 4980+ sStdMenu += '<option value="'+oSettings.aLengthMenu[i]+'">'+
 4981+ oSettings.aLengthMenu[i]+'</option>';
 4982+ }
 4983+ }
 4984+ sStdMenu += '</select>';
 4985+
 4986+ var nLength = document.createElement( 'div' );
 4987+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.l == "undefined" )
 4988+ {
 4989+ nLength.setAttribute( 'id', oSettings.sTableId+'_length' );
 4990+ }
 4991+ nLength.className = oSettings.oClasses.sLength;
 4992+ nLength.innerHTML = oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu );
 4993+
 4994+ /*
 4995+ * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
 4996+ * and Stefan Skopnik for fixing the fix!
 4997+ */
 4998+ $('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true);
 4999+
 5000+ $('select', nLength).change( function(e) {
 5001+ var iVal = $(this).val();
 5002+
 5003+ /* Update all other length options for the new display */
 5004+ var n = oSettings.aanFeatures.l;
 5005+ for ( i=0, iLen=n.length ; i<iLen ; i++ )
 5006+ {
 5007+ if ( n[i] != this.parentNode )
 5008+ {
 5009+ $('select', n[i]).val( iVal );
 5010+ }
 5011+ }
 5012+
 5013+ /* Redraw the table */
 5014+ oSettings._iDisplayLength = parseInt(iVal, 10);
 5015+ _fnCalculateEnd( oSettings );
 5016+
 5017+ /* If we have space to show extra rows (backing up from the end point - then do so */
 5018+ if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
 5019+ {
 5020+ oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
 5021+ if ( oSettings._iDisplayStart < 0 )
 5022+ {
 5023+ oSettings._iDisplayStart = 0;
 5024+ }
 5025+ }
 5026+
 5027+ if ( oSettings._iDisplayLength == -1 )
 5028+ {
 5029+ oSettings._iDisplayStart = 0;
 5030+ }
 5031+
 5032+ _fnDraw( oSettings );
 5033+ } );
 5034+
 5035+ return nLength;
 5036+ }
 5037+
 5038+
 5039+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 5040+ * Section - Feature: Processing incidator
 5041+ */
 5042+
 5043+ /*
 5044+ * Function: _fnFeatureHtmlProcessing
 5045+ * Purpose: Generate the node required for the processing node
 5046+ * Returns: node
 5047+ * Inputs: object:oSettings - dataTables settings object
 5048+ */
 5049+ function _fnFeatureHtmlProcessing ( oSettings )
 5050+ {
 5051+ var nProcessing = document.createElement( 'div' );
 5052+
 5053+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.r == "undefined" )
 5054+ {
 5055+ nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' );
 5056+ }
 5057+ nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
 5058+ nProcessing.className = oSettings.oClasses.sProcessing;
 5059+ oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
 5060+
 5061+ return nProcessing;
 5062+ }
 5063+
 5064+ /*
 5065+ * Function: _fnProcessingDisplay
 5066+ * Purpose: Display or hide the processing indicator
 5067+ * Returns: -
 5068+ * Inputs: object:oSettings - dataTables settings object
 5069+ * bool:
 5070+ * true - show the processing indicator
 5071+ * false - don't show
 5072+ */
 5073+ function _fnProcessingDisplay ( oSettings, bShow )
 5074+ {
 5075+ if ( oSettings.oFeatures.bProcessing )
 5076+ {
 5077+ var an = oSettings.aanFeatures.r;
 5078+ for ( var i=0, iLen=an.length ; i<iLen ; i++ )
 5079+ {
 5080+ an[i].style.visibility = bShow ? "visible" : "hidden";
 5081+ }
 5082+ }
 5083+ }
 5084+
 5085+
 5086+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 5087+ * Section - Support functions
 5088+ */
 5089+
 5090+ /*
 5091+ * Function: _fnVisibleToColumnIndex
 5092+ * Purpose: Covert the index of a visible column to the index in the data array (take account
 5093+ * of hidden columns)
 5094+ * Returns: int:i - the data index
 5095+ * Inputs: object:oSettings - dataTables settings object
 5096+ */
 5097+ function _fnVisibleToColumnIndex( oSettings, iMatch )
 5098+ {
 5099+ var iColumn = -1;
 5100+
 5101+ for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
 5102+ {
 5103+ if ( oSettings.aoColumns[i].bVisible === true )
 5104+ {
 5105+ iColumn++;
 5106+ }
 5107+
 5108+ if ( iColumn == iMatch )
 5109+ {
 5110+ return i;
 5111+ }
 5112+ }
 5113+
 5114+ return null;
 5115+ }
 5116+
 5117+ /*
 5118+ * Function: _fnColumnIndexToVisible
 5119+ * Purpose: Covert the index of an index in the data array and convert it to the visible
 5120+ * column index (take account of hidden columns)
 5121+ * Returns: int:i - the data index
 5122+ * Inputs: object:oSettings - dataTables settings object
 5123+ */
 5124+ function _fnColumnIndexToVisible( oSettings, iMatch )
 5125+ {
 5126+ var iVisible = -1;
 5127+ for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
 5128+ {
 5129+ if ( oSettings.aoColumns[i].bVisible === true )
 5130+ {
 5131+ iVisible++;
 5132+ }
 5133+
 5134+ if ( i == iMatch )
 5135+ {
 5136+ return oSettings.aoColumns[i].bVisible === true ? iVisible : null;
 5137+ }
 5138+ }
 5139+
 5140+ return null;
 5141+ }
 5142+
 5143+
 5144+ /*
 5145+ * Function: _fnNodeToDataIndex
 5146+ * Purpose: Take a TR element and convert it to an index in aoData
 5147+ * Returns: int:i - index if found, null if not
 5148+ * Inputs: object:s - dataTables settings object
 5149+ * node:n - the TR element to find
 5150+ */
 5151+ function _fnNodeToDataIndex( s, n )
 5152+ {
 5153+ var i, iLen;
 5154+
 5155+ /* Optimisation - see if the nodes which are currently visible match, since that is
 5156+ * the most likely node to be asked for (a selector or event for example)
 5157+ */
 5158+ for ( i=s._iDisplayStart, iLen=s._iDisplayEnd ; i<iLen ; i++ )
 5159+ {
 5160+ if ( s.aoData[ s.aiDisplay[i] ].nTr == n )
 5161+ {
 5162+ return s.aiDisplay[i];
 5163+ }
 5164+ }
 5165+
 5166+ /* Otherwise we are in for a slog through the whole data cache */
 5167+ for ( i=0, iLen=s.aoData.length ; i<iLen ; i++ )
 5168+ {
 5169+ if ( s.aoData[i].nTr == n )
 5170+ {
 5171+ return i;
 5172+ }
 5173+ }
 5174+ return null;
 5175+ }
 5176+
 5177+ /*
 5178+ * Function: _fnVisbleColumns
 5179+ * Purpose: Get the number of visible columns
 5180+ * Returns: int:i - the number of visible columns
 5181+ * Inputs: object:oS - dataTables settings object
 5182+ */
 5183+ function _fnVisbleColumns( oS )
 5184+ {
 5185+ var iVis = 0;
 5186+ for ( var i=0 ; i<oS.aoColumns.length ; i++ )
 5187+ {
 5188+ if ( oS.aoColumns[i].bVisible === true )
 5189+ {
 5190+ iVis++;
 5191+ }
 5192+ }
 5193+ return iVis;
 5194+ }
 5195+
 5196+ /*
 5197+ * Function: _fnCalculateEnd
 5198+ * Purpose: Rcalculate the end point based on the start point
 5199+ * Returns: -
 5200+ * Inputs: object:oSettings - dataTables settings object
 5201+ */
 5202+ function _fnCalculateEnd( oSettings )
 5203+ {
 5204+ if ( oSettings.oFeatures.bPaginate === false )
 5205+ {
 5206+ oSettings._iDisplayEnd = oSettings.aiDisplay.length;
 5207+ }
 5208+ else
 5209+ {
 5210+ /* Set the end point of the display - based on how many elements there are
 5211+ * still to display
 5212+ */
 5213+ if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
 5214+ oSettings._iDisplayLength == -1 )
 5215+ {
 5216+ oSettings._iDisplayEnd = oSettings.aiDisplay.length;
 5217+ }
 5218+ else
 5219+ {
 5220+ oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
 5221+ }
 5222+ }
 5223+ }
 5224+
 5225+ /*
 5226+ * Function: _fnConvertToWidth
 5227+ * Purpose: Convert a CSS unit width to pixels (e.g. 2em)
 5228+ * Returns: int:iWidth - width in pixels
 5229+ * Inputs: string:sWidth - width to be converted
 5230+ * node:nParent - parent to get the with for (required for
 5231+ * relative widths) - optional
 5232+ */
 5233+ function _fnConvertToWidth ( sWidth, nParent )
 5234+ {
 5235+ if ( !sWidth || sWidth === null || sWidth === '' )
 5236+ {
 5237+ return 0;
 5238+ }
 5239+
 5240+ if ( typeof nParent == "undefined" )
 5241+ {
 5242+ nParent = document.getElementsByTagName('body')[0];
 5243+ }
 5244+
 5245+ var iWidth;
 5246+ var nTmp = document.createElement( "div" );
 5247+ nTmp.style.width = sWidth;
 5248+
 5249+ nParent.appendChild( nTmp );
 5250+ iWidth = nTmp.offsetWidth;
 5251+ nParent.removeChild( nTmp );
 5252+
 5253+ return ( iWidth );
 5254+ }
 5255+
 5256+ /*
 5257+ * Function: _fnCalculateColumnWidths
 5258+ * Purpose: Calculate the width of columns for the table
 5259+ * Returns: -
 5260+ * Inputs: object:oSettings - dataTables settings object
 5261+ */
 5262+ function _fnCalculateColumnWidths ( oSettings )
 5263+ {
 5264+ var iTableWidth = oSettings.nTable.offsetWidth;
 5265+ var iUserInputs = 0;
 5266+ var iTmpWidth;
 5267+ var iVisibleColumns = 0;
 5268+ var iColums = oSettings.aoColumns.length;
 5269+ var i;
 5270+ var oHeaders = $('th', oSettings.nTHead);
 5271+
 5272+ /* Convert any user input sizes into pixel sizes */
 5273+ for ( i=0 ; i<iColums ; i++ )
 5274+ {
 5275+ if ( oSettings.aoColumns[i].bVisible )
 5276+ {
 5277+ iVisibleColumns++;
 5278+
 5279+ if ( oSettings.aoColumns[i].sWidth !== null )
 5280+ {
 5281+ iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig,
 5282+ oSettings.nTable.parentNode );
 5283+ if ( iTmpWidth !== null )
 5284+ {
 5285+ oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
 5286+ }
 5287+
 5288+ iUserInputs++;
 5289+ }
 5290+ }
 5291+ }
 5292+
 5293+ /* If the number of columns in the DOM equals the number that we have to process in
 5294+ * DataTables, then we can use the offsets that are created by the web-browser. No custom
 5295+ * sizes can be set in order for this to happen, nor scrolling used
 5296+ */
 5297+ if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
 5298+ oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
 5299+ {
 5300+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
 5301+ {
 5302+ iTmpWidth = $(oHeaders[i]).width();
 5303+ if ( iTmpWidth !== null )
 5304+ {
 5305+ oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
 5306+ }
 5307+ }
 5308+ }
 5309+ else
 5310+ {
 5311+ /* Otherwise we are going to have to do some calculations to get the width of each column.
 5312+ * Construct a 1 row table with the widest node in the data, and any user defined widths,
 5313+ * then insert it into the DOM and allow the browser to do all the hard work of
 5314+ * calculating table widths.
 5315+ */
 5316+ var
 5317+ nCalcTmp = oSettings.nTable.cloneNode( false ),
 5318+ nBody = document.createElement( 'tbody' ),
 5319+ nTr = document.createElement( 'tr' ),
 5320+ nDivSizing;
 5321+
 5322+ nCalcTmp.removeAttribute( "id" );
 5323+ nCalcTmp.appendChild( oSettings.nTHead.cloneNode(true) );
 5324+ if ( oSettings.nTFoot !== null )
 5325+ {
 5326+ nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
 5327+ _fnApplyToChildren( function(n) {
 5328+ n.style.width = "";
 5329+ }, nCalcTmp.getElementsByTagName('tr') );
 5330+ }
 5331+
 5332+ nCalcTmp.appendChild( nBody );
 5333+ nBody.appendChild( nTr );
 5334+
 5335+ /* Remove any sizing that was previously applied by the styles */
 5336+ var jqColSizing = $('thead th', nCalcTmp);
 5337+ if ( jqColSizing.length === 0 )
 5338+ {
 5339+ jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
 5340+ }
 5341+ jqColSizing.each( function (i) {
 5342+ this.style.width = "";
 5343+
 5344+ var iIndex = _fnVisibleToColumnIndex( oSettings, i );
 5345+ if ( iIndex !== null && oSettings.aoColumns[iIndex].sWidthOrig !== "" )
 5346+ {
 5347+ this.style.width = oSettings.aoColumns[iIndex].sWidthOrig;
 5348+ }
 5349+ } );
 5350+
 5351+ /* Find the biggest td for each column and put it into the table */
 5352+ for ( i=0 ; i<iColums ; i++ )
 5353+ {
 5354+ if ( oSettings.aoColumns[i].bVisible )
 5355+ {
 5356+ var nTd = _fnGetWidestNode( oSettings, i );
 5357+ if ( nTd !== null )
 5358+ {
 5359+ nTd = nTd.cloneNode(true);
 5360+ nTr.appendChild( nTd );
 5361+ }
 5362+ }
 5363+ }
 5364+
 5365+ /* Build the table and 'display' it */
 5366+ var nWrapper = oSettings.nTable.parentNode;
 5367+ nWrapper.appendChild( nCalcTmp );
 5368+
 5369+ /* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
 5370+ * when not scrolling leave the table width as it is. This results in slightly different,
 5371+ * but I think correct behaviour
 5372+ */
 5373+ if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
 5374+ {
 5375+ nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
 5376+ }
 5377+ else if ( oSettings.oScroll.sX !== "" )
 5378+ {
 5379+ nCalcTmp.style.width = "";
 5380+ if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
 5381+ {
 5382+ nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
 5383+ }
 5384+ }
 5385+ else if ( oSettings.oScroll.sY !== "" )
 5386+ {
 5387+ nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
 5388+ }
 5389+ nCalcTmp.style.visibility = "hidden";
 5390+
 5391+ /* Scrolling considerations */
 5392+ _fnScrollingWidthAdjust( oSettings, nCalcTmp );
 5393+
 5394+ /* Read the width's calculated by the browser and store them for use by the caller. We
 5395+ * first of all try to use the elements in the body, but it is possible that there are
 5396+ * no elements there, under which circumstances we use the header elements
 5397+ */
 5398+ var oNodes = $("tbody tr:eq(0)>td", nCalcTmp);
 5399+ if ( oNodes.length === 0 )
 5400+ {
 5401+ oNodes = $("thead tr:eq(0)>th", nCalcTmp);
 5402+ }
 5403+
 5404+ var iIndex, iCorrector = 0, iWidth;
 5405+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
 5406+ {
 5407+ if ( oSettings.aoColumns[i].bVisible )
 5408+ {
 5409+ iWidth = $(oNodes[iCorrector]).width();
 5410+ if ( iWidth !== null && iWidth > 0 )
 5411+ {
 5412+ oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
 5413+ }
 5414+ iCorrector++;
 5415+ }
 5416+ }
 5417+
 5418+ oSettings.nTable.style.width = _fnStringToCss( $(nCalcTmp).outerWidth() );
 5419+ nCalcTmp.parentNode.removeChild( nCalcTmp );
 5420+ }
 5421+ }
 5422+
 5423+ /*
 5424+ * Function: _fnScrollingWidthAdjust
 5425+ * Purpose: Adjust a table's width to take account of scrolling
 5426+ * Returns: -
 5427+ * Inputs: object:oSettings - dataTables settings object
 5428+ * node:n - table node
 5429+ */
 5430+ function _fnScrollingWidthAdjust ( oSettings, n )
 5431+ {
 5432+ if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
 5433+ {
 5434+ /* When y-scrolling only, we want to remove the width of the scroll bar so the table
 5435+ * + scroll bar will fit into the area avaialble.
 5436+ */
 5437+ var iOrigWidth = $(n).width();
 5438+ n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
 5439+ }
 5440+ else if ( oSettings.oScroll.sX !== "" )
 5441+ {
 5442+ /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
 5443+ n.style.width = _fnStringToCss( $(n).outerWidth() );
 5444+ }
 5445+ }
 5446+
 5447+ /*
 5448+ * Function: _fnGetWidestNode
 5449+ * Purpose: Get the widest node
 5450+ * Returns: string: - max strlens for each column
 5451+ * Inputs: object:oSettings - dataTables settings object
 5452+ * int:iCol - column of interest
 5453+ * boolean:bFast - Should we use fast (but non-accurate) calculation - optional,
 5454+ * default true
 5455+ * Notes: This operation is _expensive_ (!!!). It requires a lot of DOM interaction, but
 5456+ * this is the only way to reliably get the widest string. For example 'mmm' would be wider
 5457+ * than 'iiii' so we can't just ocunt characters. If this can be optimised it would be good
 5458+ * to do so!
 5459+ */
 5460+ function _fnGetWidestNode( oSettings, iCol, bFast )
 5461+ {
 5462+ /* Use fast not non-accurate calculate based on the strlen */
 5463+ if ( typeof bFast == 'undefined' || bFast )
 5464+ {
 5465+ var iMaxLen = _fnGetMaxLenString( oSettings, iCol );
 5466+ var iFastVis = _fnColumnIndexToVisible( oSettings, iCol);
 5467+ if ( iMaxLen < 0 )
 5468+ {
 5469+ return null;
 5470+ }
 5471+ return oSettings.aoData[iMaxLen].nTr.getElementsByTagName('td')[iFastVis];
 5472+ }
 5473+
 5474+ /* Use the slow approach, but get high quality answers - note that this code is not actually
 5475+ * used by DataTables by default. If you want to use it you can alter the call to
 5476+ * _fnGetWidestNode to pass 'false' as the third argument
 5477+ */
 5478+ var
 5479+ iMax = -1, i, iLen,
 5480+ iMaxIndex = -1,
 5481+ n = document.createElement('div');
 5482+
 5483+ n.style.visibility = "hidden";
 5484+ n.style.position = "absolute";
 5485+ document.body.appendChild( n );
 5486+
 5487+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
 5488+ {
 5489+ n.innerHTML = oSettings.aoData[i]._aData[iCol];
 5490+ if ( n.offsetWidth > iMax )
 5491+ {
 5492+ iMax = n.offsetWidth;
 5493+ iMaxIndex = i;
 5494+ }
 5495+ }
 5496+ document.body.removeChild( n );
 5497+
 5498+ if ( iMaxIndex >= 0 )
 5499+ {
 5500+ var iVis = _fnColumnIndexToVisible( oSettings, iCol);
 5501+ var nRet = oSettings.aoData[iMaxIndex].nTr.getElementsByTagName('td')[iVis];
 5502+ if ( nRet )
 5503+ {
 5504+ return nRet;
 5505+ }
 5506+ }
 5507+ return null;
 5508+ }
 5509+
 5510+ /*
 5511+ * Function: _fnGetMaxLenString
 5512+ * Purpose: Get the maximum strlen for each data column
 5513+ * Returns: string: - max strlens for each column
 5514+ * Inputs: object:oSettings - dataTables settings object
 5515+ * int:iCol - column of interest
 5516+ */
 5517+ function _fnGetMaxLenString( oSettings, iCol )
 5518+ {
 5519+ var iMax = -1;
 5520+ var iMaxIndex = -1;
 5521+
 5522+ for ( var i=0 ; i<oSettings.aoData.length ; i++ )
 5523+ {
 5524+ var s = oSettings.aoData[i]._aData[iCol];
 5525+ if ( s.length > iMax )
 5526+ {
 5527+ iMax = s.length;
 5528+ iMaxIndex = i;
 5529+ }
 5530+ }
 5531+
 5532+ return iMaxIndex;
 5533+ }
 5534+
 5535+ /*
 5536+ * Function: _fnStringToCss
 5537+ * Purpose: Append a CSS unit (only if required) to a string
 5538+ * Returns: 0 if match, 1 if length is different, 2 if no match
 5539+ * Inputs: array:aArray1 - first array
 5540+ * array:aArray2 - second array
 5541+ */
 5542+ function _fnStringToCss( s )
 5543+ {
 5544+ if ( s === null )
 5545+ {
 5546+ return "0px";
 5547+ }
 5548+
 5549+ if ( typeof s == 'number' )
 5550+ {
 5551+ if ( s < 0 )
 5552+ {
 5553+ return "0px";
 5554+ }
 5555+ return s+"px";
 5556+ }
 5557+
 5558+ /* Check if the last character is not 0-9 */
 5559+ var c = s.charCodeAt( s.length-1 );
 5560+ if (c < 0x30 || c > 0x39)
 5561+ {
 5562+ return s;
 5563+ }
 5564+ return s+"px";
 5565+ }
 5566+
 5567+ /*
 5568+ * Function: _fnArrayCmp
 5569+ * Purpose: Compare two arrays
 5570+ * Returns: 0 if match, 1 if length is different, 2 if no match
 5571+ * Inputs: array:aArray1 - first array
 5572+ * array:aArray2 - second array
 5573+ */
 5574+ function _fnArrayCmp( aArray1, aArray2 )
 5575+ {
 5576+ if ( aArray1.length != aArray2.length )
 5577+ {
 5578+ return 1;
 5579+ }
 5580+
 5581+ for ( var i=0 ; i<aArray1.length ; i++ )
 5582+ {
 5583+ if ( aArray1[i] != aArray2[i] )
 5584+ {
 5585+ return 2;
 5586+ }
 5587+ }
 5588+
 5589+ return 0;
 5590+ }
 5591+
 5592+ /*
 5593+ * Function: _fnDetectType
 5594+ * Purpose: Get the sort type based on an input string
 5595+ * Returns: string: - type (defaults to 'string' if no type can be detected)
 5596+ * Inputs: string:sData - data we wish to know the type of
 5597+ * Notes: This function makes use of the DataTables plugin objct _oExt
 5598+ * (.aTypes) such that new types can easily be added.
 5599+ */
 5600+ function _fnDetectType( sData )
 5601+ {
 5602+ var aTypes = _oExt.aTypes;
 5603+ var iLen = aTypes.length;
 5604+
 5605+ for ( var i=0 ; i<iLen ; i++ )
 5606+ {
 5607+ var sType = aTypes[i]( sData );
 5608+ if ( sType !== null )
 5609+ {
 5610+ return sType;
 5611+ }
 5612+ }
 5613+
 5614+ return 'string';
 5615+ }
 5616+
 5617+ /*
 5618+ * Function: _fnSettingsFromNode
 5619+ * Purpose: Return the settings object for a particular table
 5620+ * Returns: object: Settings object - or null if not found
 5621+ * Inputs: node:nTable - table we are using as a dataTable
 5622+ */
 5623+ function _fnSettingsFromNode ( nTable )
 5624+ {
 5625+ for ( var i=0 ; i<_aoSettings.length ; i++ )
 5626+ {
 5627+ if ( _aoSettings[i].nTable == nTable )
 5628+ {
 5629+ return _aoSettings[i];
 5630+ }
 5631+ }
 5632+
 5633+ return null;
 5634+ }
 5635+
 5636+ /*
 5637+ * Function: _fnGetDataMaster
 5638+ * Purpose: Return an array with the full table data
 5639+ * Returns: array array:aData - Master data array
 5640+ * Inputs: object:oSettings - dataTables settings object
 5641+ */
 5642+ function _fnGetDataMaster ( oSettings )
 5643+ {
 5644+ var aData = [];
 5645+ var iLen = oSettings.aoData.length;
 5646+ for ( var i=0 ; i<iLen; i++ )
 5647+ {
 5648+ aData.push( oSettings.aoData[i]._aData );
 5649+ }
 5650+ return aData;
 5651+ }
 5652+
 5653+ /*
 5654+ * Function: _fnGetTrNodes
 5655+ * Purpose: Return an array with the TR nodes for the table
 5656+ * Returns: array: - TR array
 5657+ * Inputs: object:oSettings - dataTables settings object
 5658+ */
 5659+ function _fnGetTrNodes ( oSettings )
 5660+ {
 5661+ var aNodes = [];
 5662+ var iLen = oSettings.aoData.length;
 5663+ for ( var i=0 ; i<iLen ; i++ )
 5664+ {
 5665+ aNodes.push( oSettings.aoData[i].nTr );
 5666+ }
 5667+ return aNodes;
 5668+ }
 5669+
 5670+ /*
 5671+ * Function: _fnGetTdNodes
 5672+ * Purpose: Return an array with the TD nodes for the table
 5673+ * Returns: array: - TD array
 5674+ * Inputs: object:oSettings - dataTables settings object
 5675+ */
 5676+ function _fnGetTdNodes ( oSettings )
 5677+ {
 5678+ var nTrs = _fnGetTrNodes( oSettings );
 5679+ var nTds = [], nTd;
 5680+ var anReturn = [];
 5681+ var iCorrector;
 5682+ var iRow, iRows, iColumn, iColumns;
 5683+
 5684+ for ( iRow=0, iRows=nTrs.length ; iRow<iRows ; iRow++ )
 5685+ {
 5686+ nTds = [];
 5687+ for ( iColumn=0, iColumns=nTrs[iRow].childNodes.length ; iColumn<iColumns ; iColumn++ )
 5688+ {
 5689+ nTd = nTrs[iRow].childNodes[iColumn];
 5690+ if ( nTd.nodeName.toUpperCase() == "TD" )
 5691+ {
 5692+ nTds.push( nTd );
 5693+ }
 5694+ }
 5695+
 5696+ iCorrector = 0;
 5697+ for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
 5698+ {
 5699+ if ( oSettings.aoColumns[iColumn].bVisible )
 5700+ {
 5701+ anReturn.push( nTds[iColumn-iCorrector] );
 5702+ }
 5703+ else
 5704+ {
 5705+ anReturn.push( oSettings.aoData[iRow]._anHidden[iColumn] );
 5706+ iCorrector++;
 5707+ }
 5708+ }
 5709+ }
 5710+ return anReturn;
 5711+ }
 5712+
 5713+ /*
 5714+ * Function: _fnEscapeRegex
 5715+ * Purpose: scape a string stuch that it can be used in a regular expression
 5716+ * Returns: string: - escaped string
 5717+ * Inputs: string:sVal - string to escape
 5718+ */
 5719+ function _fnEscapeRegex ( sVal )
 5720+ {
 5721+ var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ];
 5722+ var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
 5723+ return sVal.replace(reReplace, '\\$1');
 5724+ }
 5725+
 5726+ /*
 5727+ * Function: _fnDeleteIndex
 5728+ * Purpose: Take an array of integers (index array) and remove a target integer (value - not
 5729+ * the key!)
 5730+ * Returns: -
 5731+ * Inputs: a:array int - Index array to target
 5732+ * int:iTarget - value to find
 5733+ */
 5734+ function _fnDeleteIndex( a, iTarget )
 5735+ {
 5736+ var iTargetIndex = -1;
 5737+
 5738+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
 5739+ {
 5740+ if ( a[i] == iTarget )
 5741+ {
 5742+ iTargetIndex = i;
 5743+ }
 5744+ else if ( a[i] > iTarget )
 5745+ {
 5746+ a[i]--;
 5747+ }
 5748+ }
 5749+
 5750+ if ( iTargetIndex != -1 )
 5751+ {
 5752+ a.splice( iTargetIndex, 1 );
 5753+ }
 5754+ }
 5755+
 5756+ /*
 5757+ * Function: _fnReOrderIndex
 5758+ * Purpose: Figure out how to reorder a display list
 5759+ * Returns: array int:aiReturn - index list for reordering
 5760+ * Inputs: object:oSettings - dataTables settings object
 5761+ */
 5762+ function _fnReOrderIndex ( oSettings, sColumns )
 5763+ {
 5764+ var aColumns = sColumns.split(',');
 5765+ var aiReturn = [];
 5766+
 5767+ for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 5768+ {
 5769+ for ( var j=0 ; j<iLen ; j++ )
 5770+ {
 5771+ if ( oSettings.aoColumns[i].sName == aColumns[j] )
 5772+ {
 5773+ aiReturn.push( j );
 5774+ break;
 5775+ }
 5776+ }
 5777+ }
 5778+
 5779+ return aiReturn;
 5780+ }
 5781+
 5782+ /*
 5783+ * Function: _fnColumnOrdering
 5784+ * Purpose: Get the column ordering that DataTables expects
 5785+ * Returns: string: - comma separated list of names
 5786+ * Inputs: object:oSettings - dataTables settings object
 5787+ */
 5788+ function _fnColumnOrdering ( oSettings )
 5789+ {
 5790+ var sNames = '';
 5791+ for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
 5792+ {
 5793+ sNames += oSettings.aoColumns[i].sName+',';
 5794+ }
 5795+ if ( sNames.length == iLen )
 5796+ {
 5797+ return "";
 5798+ }
 5799+ return sNames.slice(0, -1);
 5800+ }
 5801+
 5802+ /*
 5803+ * Function: _fnLog
 5804+ * Purpose: Log an error message
 5805+ * Returns: -
 5806+ * Inputs: int:iLevel - log error messages, or display them to the user
 5807+ * string:sMesg - error message
 5808+ */
 5809+ function _fnLog( oSettings, iLevel, sMesg )
 5810+ {
 5811+ var sAlert = oSettings.sTableId === "" ?
 5812+ "DataTables warning: " +sMesg :
 5813+ "DataTables warning (table id = '"+oSettings.sTableId+"'): " +sMesg;
 5814+
 5815+ if ( iLevel === 0 )
 5816+ {
 5817+ if ( _oExt.sErrMode == 'alert' )
 5818+ {
 5819+ alert( sAlert );
 5820+ }
 5821+ else
 5822+ {
 5823+ throw sAlert;
 5824+ }
 5825+ return;
 5826+ }
 5827+ else if ( typeof console != 'undefined' && typeof console.log != 'undefined' )
 5828+ {
 5829+ console.log( sAlert );
 5830+ }
 5831+ }
 5832+
 5833+ /*
 5834+ * Function: _fnClearTable
 5835+ * Purpose: Nuke the table
 5836+ * Returns: -
 5837+ * Inputs: object:oSettings - dataTables settings object
 5838+ */
 5839+ function _fnClearTable( oSettings )
 5840+ {
 5841+ oSettings.aoData.splice( 0, oSettings.aoData.length );
 5842+ oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
 5843+ oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
 5844+ _fnCalculateEnd( oSettings );
 5845+ }
 5846+
 5847+ /*
 5848+ * Function: _fnSaveState
 5849+ * Purpose: Save the state of a table in a cookie such that the page can be reloaded
 5850+ * Returns: -
 5851+ * Inputs: object:oSettings - dataTables settings object
 5852+ */
 5853+ function _fnSaveState ( oSettings )
 5854+ {
 5855+ if ( !oSettings.oFeatures.bStateSave || typeof oSettings.bDestroying != 'undefined' )
 5856+ {
 5857+ return;
 5858+ }
 5859+
 5860+ /* Store the interesting variables */
 5861+ var i, iLen, sTmp;
 5862+ var sValue = "{";
 5863+ sValue += '"iCreate":'+ new Date().getTime()+',';
 5864+ sValue += '"iStart":'+ oSettings._iDisplayStart+',';
 5865+ sValue += '"iEnd":'+ oSettings._iDisplayEnd+',';
 5866+ sValue += '"iLength":'+ oSettings._iDisplayLength+',';
 5867+ sValue += '"sFilter":"'+ encodeURIComponent(oSettings.oPreviousSearch.sSearch)+'",';
 5868+ sValue += '"sFilterEsc":'+ !oSettings.oPreviousSearch.bRegex+',';
 5869+
 5870+ sValue += '"aaSorting":[ ';
 5871+ for ( i=0 ; i<oSettings.aaSorting.length ; i++ )
 5872+ {
 5873+ sValue += '['+oSettings.aaSorting[i][0]+',"'+oSettings.aaSorting[i][1]+'"],';
 5874+ }
 5875+ sValue = sValue.substring(0, sValue.length-1);
 5876+ sValue += "],";
 5877+
 5878+ sValue += '"aaSearchCols":[ ';
 5879+ for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
 5880+ {
 5881+ sValue += '["'+encodeURIComponent(oSettings.aoPreSearchCols[i].sSearch)+
 5882+ '",'+!oSettings.aoPreSearchCols[i].bRegex+'],';
 5883+ }
 5884+ sValue = sValue.substring(0, sValue.length-1);
 5885+ sValue += "],";
 5886+
 5887+ sValue += '"abVisCols":[ ';
 5888+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
 5889+ {
 5890+ sValue += oSettings.aoColumns[i].bVisible+",";
 5891+ }
 5892+ sValue = sValue.substring(0, sValue.length-1);
 5893+ sValue += "]";
 5894+
 5895+ /* Save state from any plug-ins */
 5896+ for ( i=0, iLen=oSettings.aoStateSave.length ; i<iLen ; i++ )
 5897+ {
 5898+ sTmp = oSettings.aoStateSave[i].fn( oSettings, sValue );
 5899+ if ( sTmp !== "" )
 5900+ {
 5901+ sValue = sTmp;
 5902+ }
 5903+ }
 5904+
 5905+ sValue += "}";
 5906+
 5907+ _fnCreateCookie( oSettings.sCookiePrefix+oSettings.sInstance, sValue,
 5908+ oSettings.iCookieDuration, oSettings.sCookiePrefix, oSettings.fnCookieCallback );
 5909+ }
 5910+
 5911+ /*
 5912+ * Function: _fnLoadState
 5913+ * Purpose: Attempt to load a saved table state from a cookie
 5914+ * Returns: -
 5915+ * Inputs: object:oSettings - dataTables settings object
 5916+ * object:oInit - DataTables init object so we can override settings
 5917+ */
 5918+ function _fnLoadState ( oSettings, oInit )
 5919+ {
 5920+ if ( !oSettings.oFeatures.bStateSave )
 5921+ {
 5922+ return;
 5923+ }
 5924+
 5925+ var oData, i, iLen;
 5926+ var sData = _fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
 5927+ if ( sData !== null && sData !== '' )
 5928+ {
 5929+ /* Try/catch the JSON eval - if it is bad then we ignore it - note that 1.7.0 and before
 5930+ * incorrectly used single quotes for some strings - hence the replace below
 5931+ */
 5932+ try
 5933+ {
 5934+ oData = (typeof $.parseJSON == 'function') ?
 5935+ $.parseJSON( sData.replace(/'/g, '"') ) : eval( '('+sData+')' );
 5936+ }
 5937+ catch( e )
 5938+ {
 5939+ return;
 5940+ }
 5941+
 5942+ /* Allow custom and plug-in manipulation functions to alter the data set which was
 5943+ * saved, and also reject any saved state by returning false
 5944+ */
 5945+ for ( i=0, iLen=oSettings.aoStateLoad.length ; i<iLen ; i++ )
 5946+ {
 5947+ if ( !oSettings.aoStateLoad[i].fn( oSettings, oData ) )
 5948+ {
 5949+ return;
 5950+ }
 5951+ }
 5952+
 5953+ /* Store the saved state so it might be accessed at any time (particualrly a plug-in */
 5954+ oSettings.oLoadedState = $.extend( true, {}, oData );
 5955+
 5956+ /* Restore key features */
 5957+ oSettings._iDisplayStart = oData.iStart;
 5958+ oSettings.iInitDisplayStart = oData.iStart;
 5959+ oSettings._iDisplayEnd = oData.iEnd;
 5960+ oSettings._iDisplayLength = oData.iLength;
 5961+ oSettings.oPreviousSearch.sSearch = decodeURIComponent(oData.sFilter);
 5962+ oSettings.aaSorting = oData.aaSorting.slice();
 5963+ oSettings.saved_aaSorting = oData.aaSorting.slice();
 5964+
 5965+ /*
 5966+ * Search filtering - global reference added in 1.4.1
 5967+ * Note that we use a 'not' for the value of the regular expression indicator to maintain
 5968+ * compatibility with pre 1.7 versions, where this was basically inverted. Added in 1.7.0
 5969+ */
 5970+ if ( typeof oData.sFilterEsc != 'undefined' )
 5971+ {
 5972+ oSettings.oPreviousSearch.bRegex = !oData.sFilterEsc;
 5973+ }
 5974+
 5975+ /* Column filtering - added in 1.5.0 beta 6 */
 5976+ if ( typeof oData.aaSearchCols != 'undefined' )
 5977+ {
 5978+ for ( i=0 ; i<oData.aaSearchCols.length ; i++ )
 5979+ {
 5980+ oSettings.aoPreSearchCols[i] = {
 5981+ "sSearch": decodeURIComponent(oData.aaSearchCols[i][0]),
 5982+ "bRegex": !oData.aaSearchCols[i][1]
 5983+ };
 5984+ }
 5985+ }
 5986+
 5987+ /* Column visibility state - added in 1.5.0 beta 10 */
 5988+ if ( typeof oData.abVisCols != 'undefined' )
 5989+ {
 5990+ /* Pass back visibiliy settings to the init handler, but to do not here override
 5991+ * the init object that the user might have passed in
 5992+ */
 5993+ oInit.saved_aoColumns = [];
 5994+ for ( i=0 ; i<oData.abVisCols.length ; i++ )
 5995+ {
 5996+ oInit.saved_aoColumns[i] = {};
 5997+ oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
 5998+ }
 5999+ }
 6000+ }
 6001+ }
 6002+
 6003+ /*
 6004+ * Function: _fnCreateCookie
 6005+ * Purpose: Create a new cookie with a value to store the state of a table
 6006+ * Returns: -
 6007+ * Inputs: string:sName - name of the cookie to create
 6008+ * string:sValue - the value the cookie should take
 6009+ * int:iSecs - duration of the cookie
 6010+ * string:sBaseName - sName is made up of the base + file name - this is the base
 6011+ * function:fnCallback - User definable function to modify the cookie
 6012+ */
 6013+ function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
 6014+ {
 6015+ var date = new Date();
 6016+ date.setTime( date.getTime()+(iSecs*1000) );
 6017+
 6018+ /*
 6019+ * Shocking but true - it would appear IE has major issues with having the path not having
 6020+ * a trailing slash on it. We need the cookie to be available based on the path, so we
 6021+ * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
 6022+ * patch to use at least some of the path
 6023+ */
 6024+ var aParts = window.location.pathname.split('/');
 6025+ var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
 6026+ var sFullCookie, oData;
 6027+
 6028+ if ( fnCallback !== null )
 6029+ {
 6030+ oData = (typeof $.parseJSON == 'function') ?
 6031+ $.parseJSON( sValue ) : eval( '('+sValue+')' );
 6032+ sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
 6033+ aParts.join('/')+"/" );
 6034+ }
 6035+ else
 6036+ {
 6037+ sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
 6038+ "; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
 6039+ }
 6040+
 6041+ /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
 6042+ * belonging to DataTables. This is FAR from bullet proof
 6043+ */
 6044+ var sOldName="", iOldTime=9999999999999;
 6045+ var iLength = _fnReadCookie( sNameFile )!==null ? document.cookie.length :
 6046+ sFullCookie.length + document.cookie.length;
 6047+
 6048+ if ( iLength+10 > 4096 ) /* Magic 10 for padding */
 6049+ {
 6050+ var aCookies =document.cookie.split(';');
 6051+ for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
 6052+ {
 6053+ if ( aCookies[i].indexOf( sBaseName ) != -1 )
 6054+ {
 6055+ /* It's a DataTables cookie, so eval it and check the time stamp */
 6056+ var aSplitCookie = aCookies[i].split('=');
 6057+ try { oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' ); }
 6058+ catch( e ) { continue; }
 6059+
 6060+ if ( typeof oData.iCreate != 'undefined' && oData.iCreate < iOldTime )
 6061+ {
 6062+ sOldName = aSplitCookie[0];
 6063+ iOldTime = oData.iCreate;
 6064+ }
 6065+ }
 6066+ }
 6067+
 6068+ if ( sOldName !== "" )
 6069+ {
 6070+ document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
 6071+ aParts.join('/') + "/";
 6072+ }
 6073+ }
 6074+
 6075+ document.cookie = sFullCookie;
 6076+ }
 6077+
 6078+ /*
 6079+ * Function: _fnReadCookie
 6080+ * Purpose: Read an old cookie to get a cookie with an old table state
 6081+ * Returns: string: - contents of the cookie - or null if no cookie with that name found
 6082+ * Inputs: string:sName - name of the cookie to read
 6083+ */
 6084+ function _fnReadCookie ( sName )
 6085+ {
 6086+ var
 6087+ aParts = window.location.pathname.split('/'),
 6088+ sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
 6089+ sCookieContents = document.cookie.split(';');
 6090+
 6091+ for( var i=0 ; i<sCookieContents.length ; i++ )
 6092+ {
 6093+ var c = sCookieContents[i];
 6094+
 6095+ while (c.charAt(0)==' ')
 6096+ {
 6097+ c = c.substring(1,c.length);
 6098+ }
 6099+
 6100+ if (c.indexOf(sNameEQ) === 0)
 6101+ {
 6102+ return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
 6103+ }
 6104+ }
 6105+ return null;
 6106+ }
 6107+
 6108+ /*
 6109+ * Function: _fnGetUniqueThs
 6110+ * Purpose: Get an array of unique th elements, one for each column
 6111+ * Returns: array node:aReturn - list of unique ths
 6112+ * Inputs: node:nThead - The thead element for the table
 6113+ */
 6114+ function _fnGetUniqueThs ( nThead )
 6115+ {
 6116+ var nTrs = nThead.getElementsByTagName('tr');
 6117+
 6118+ /* Nice simple case */
 6119+ if ( nTrs.length == 1 )
 6120+ {
 6121+ return nTrs[0].getElementsByTagName('th');
 6122+ }
 6123+
 6124+ /* Otherwise we need to figure out the layout array to get the nodes */
 6125+ var aLayout = [], aReturn = [];
 6126+ var ROWSPAN = 2, COLSPAN = 3, TDELEM = 4;
 6127+ var i, j, k, iLen, jLen, iColumnShifted;
 6128+ var fnShiftCol = function ( a, i, j ) {
 6129+ while ( typeof a[i][j] != 'undefined' ) {
 6130+ j++;
 6131+ }
 6132+ return j;
 6133+ };
 6134+ var fnAddRow = function ( i ) {
 6135+ if ( typeof aLayout[i] == 'undefined' ) {
 6136+ aLayout[i] = [];
 6137+ }
 6138+ };
 6139+
 6140+ /* Calculate a layout array */
 6141+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
 6142+ {
 6143+ fnAddRow( i );
 6144+ var iColumn = 0;
 6145+ var nTds = [];
 6146+
 6147+ for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
 6148+ {
 6149+ if ( nTrs[i].childNodes[j].nodeName.toUpperCase() == "TD" ||
 6150+ nTrs[i].childNodes[j].nodeName.toUpperCase() == "TH" )
 6151+ {
 6152+ nTds.push( nTrs[i].childNodes[j] );
 6153+ }
 6154+ }
 6155+
 6156+ for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
 6157+ {
 6158+ var iColspan = nTds[j].getAttribute('colspan') * 1;
 6159+ var iRowspan = nTds[j].getAttribute('rowspan') * 1;
 6160+
 6161+ if ( !iColspan || iColspan===0 || iColspan===1 )
 6162+ {
 6163+ iColumnShifted = fnShiftCol( aLayout, i, iColumn );
 6164+ aLayout[i][iColumnShifted] = (nTds[j].nodeName.toUpperCase()=="TD") ? TDELEM : nTds[j];
 6165+ if ( iRowspan || iRowspan===0 || iRowspan===1 )
 6166+ {
 6167+ for ( k=1 ; k<iRowspan ; k++ )
 6168+ {
 6169+ fnAddRow( i+k );
 6170+ aLayout[i+k][iColumnShifted] = ROWSPAN;
 6171+ }
 6172+ }
 6173+ iColumn++;
 6174+ }
 6175+ else
 6176+ {
 6177+ iColumnShifted = fnShiftCol( aLayout, i, iColumn );
 6178+ for ( k=0 ; k<iColspan ; k++ )
 6179+ {
 6180+ aLayout[i][iColumnShifted+k] = COLSPAN;
 6181+ }
 6182+ iColumn += iColspan;
 6183+ }
 6184+ }
 6185+ }
 6186+
 6187+ /* Convert the layout array into a node array */
 6188+ for ( i=0, iLen=aLayout.length ; i<iLen ; i++ )
 6189+ {
 6190+ for ( j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
 6191+ {
 6192+ if ( typeof aLayout[i][j] == 'object' && typeof aReturn[j] == 'undefined' )
 6193+ {
 6194+ aReturn[j] = aLayout[i][j];
 6195+ }
 6196+ }
 6197+ }
 6198+
 6199+ return aReturn;
 6200+ }
 6201+
 6202+ /*
 6203+ * Function: _fnScrollBarWidth
 6204+ * Purpose: Get the width of a scroll bar in this browser being used
 6205+ * Returns: int: - width in pixels
 6206+ * Inputs: -
 6207+ * Notes: All credit for this function belongs to Alexandre Gomes. Thanks for sharing!
 6208+ * http://www.alexandre-gomes.com/?p=115
 6209+ */
 6210+ function _fnScrollBarWidth ()
 6211+ {
 6212+ var inner = document.createElement('p');
 6213+ var style = inner.style;
 6214+ style.width = "100%";
 6215+ style.height = "200px";
 6216+
 6217+ var outer = document.createElement('div');
 6218+ style = outer.style;
 6219+ style.position = "absolute";
 6220+ style.top = "0px";
 6221+ style.left = "0px";
 6222+ style.visibility = "hidden";
 6223+ style.width = "200px";
 6224+ style.height = "150px";
 6225+ style.overflow = "hidden";
 6226+ outer.appendChild(inner);
 6227+
 6228+ document.body.appendChild(outer);
 6229+ var w1 = inner.offsetWidth;
 6230+ outer.style.overflow = 'scroll';
 6231+ var w2 = inner.offsetWidth;
 6232+ if ( w1 == w2 )
 6233+ {
 6234+ w2 = outer.clientWidth;
 6235+ }
 6236+
 6237+ document.body.removeChild(outer);
 6238+ return (w1 - w2);
 6239+ }
 6240+
 6241+ /*
 6242+ * Function: _fnApplyToChildren
 6243+ * Purpose: Apply a given function to the display child nodes of an element array (typically
 6244+ * TD children of TR rows
 6245+ * Returns: - (done by reference)
 6246+ * Inputs: function:fn - Method to apply to the objects
 6247+ * array nodes:an1 - List of elements to look through for display children
 6248+ * array nodes:an2 - Another list (identical structure to the first) - optional
 6249+ */
 6250+ function _fnApplyToChildren( fn, an1, an2 )
 6251+ {
 6252+ for ( var i=0, iLen=an1.length ; i<iLen ; i++ )
 6253+ {
 6254+ for ( var j=0, jLen=an1[i].childNodes.length ; j<jLen ; j++ )
 6255+ {
 6256+ if ( an1[i].childNodes[j].nodeType == 1 )
 6257+ {
 6258+ if ( typeof an2 != 'undefined' )
 6259+ {
 6260+ fn( an1[i].childNodes[j], an2[i].childNodes[j] );
 6261+ }
 6262+ else
 6263+ {
 6264+ fn( an1[i].childNodes[j] );
 6265+ }
 6266+ }
 6267+ }
 6268+ }
 6269+ }
 6270+
 6271+ /*
 6272+ * Function: _fnMap
 6273+ * Purpose: See if a property is defined on one object, if so assign it to the other object
 6274+ * Returns: - (done by reference)
 6275+ * Inputs: object:oRet - target object
 6276+ * object:oSrc - source object
 6277+ * string:sName - property
 6278+ * string:sMappedName - name to map too - optional, sName used if not given
 6279+ */
 6280+ function _fnMap( oRet, oSrc, sName, sMappedName )
 6281+ {
 6282+ if ( typeof sMappedName == 'undefined' )
 6283+ {
 6284+ sMappedName = sName;
 6285+ }
 6286+ if ( typeof oSrc[sName] != 'undefined' )
 6287+ {
 6288+ oRet[sMappedName] = oSrc[sName];
 6289+ }
 6290+ }
 6291+
 6292+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 6293+ * Section - API
 6294+ *
 6295+ * I'm not overly happy with this solution - I'd much rather that there was a way of getting
 6296+ * a list of all the private functions and do what we need to dynamically - but that doesn't
 6297+ * appear to be possible. Bonkers. A better solution would be to provide a 'bind' type object
 6298+ * To do - bind type method in DTs 2.x.
 6299+ */
 6300+ this.oApi._fnExternApiFunc = _fnExternApiFunc;
 6301+ this.oApi._fnInitalise = _fnInitalise;
 6302+ this.oApi._fnLanguageProcess = _fnLanguageProcess;
 6303+ this.oApi._fnAddColumn = _fnAddColumn;
 6304+ this.oApi._fnColumnOptions = _fnColumnOptions;
 6305+ this.oApi._fnAddData = _fnAddData;
 6306+ this.oApi._fnGatherData = _fnGatherData;
 6307+ this.oApi._fnDrawHead = _fnDrawHead;
 6308+ this.oApi._fnDraw = _fnDraw;
 6309+ this.oApi._fnReDraw = _fnReDraw;
 6310+ this.oApi._fnAjaxUpdate = _fnAjaxUpdate;
 6311+ this.oApi._fnAjaxUpdateDraw = _fnAjaxUpdateDraw;
 6312+ this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml;
 6313+ this.oApi._fnFeatureHtmlTable = _fnFeatureHtmlTable;
 6314+ this.oApi._fnScrollDraw = _fnScrollDraw;
 6315+ this.oApi._fnAjustColumnSizing = _fnAjustColumnSizing;
 6316+ this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter;
 6317+ this.oApi._fnFilterComplete = _fnFilterComplete;
 6318+ this.oApi._fnFilterCustom = _fnFilterCustom;
 6319+ this.oApi._fnFilterColumn = _fnFilterColumn;
 6320+ this.oApi._fnFilter = _fnFilter;
 6321+ this.oApi._fnBuildSearchArray = _fnBuildSearchArray;
 6322+ this.oApi._fnBuildSearchRow = _fnBuildSearchRow;
 6323+ this.oApi._fnFilterCreateSearch = _fnFilterCreateSearch;
 6324+ this.oApi._fnDataToSearch = _fnDataToSearch;
 6325+ this.oApi._fnSort = _fnSort;
 6326+ this.oApi._fnSortAttachListener = _fnSortAttachListener;
 6327+ this.oApi._fnSortingClasses = _fnSortingClasses;
 6328+ this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate;
 6329+ this.oApi._fnPageChange = _fnPageChange;
 6330+ this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo;
 6331+ this.oApi._fnUpdateInfo = _fnUpdateInfo;
 6332+ this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength;
 6333+ this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing;
 6334+ this.oApi._fnProcessingDisplay = _fnProcessingDisplay;
 6335+ this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex;
 6336+ this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible;
 6337+ this.oApi._fnNodeToDataIndex = _fnNodeToDataIndex;
 6338+ this.oApi._fnVisbleColumns = _fnVisbleColumns;
 6339+ this.oApi._fnCalculateEnd = _fnCalculateEnd;
 6340+ this.oApi._fnConvertToWidth = _fnConvertToWidth;
 6341+ this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths;
 6342+ this.oApi._fnScrollingWidthAdjust = _fnScrollingWidthAdjust;
 6343+ this.oApi._fnGetWidestNode = _fnGetWidestNode;
 6344+ this.oApi._fnGetMaxLenString = _fnGetMaxLenString;
 6345+ this.oApi._fnStringToCss = _fnStringToCss;
 6346+ this.oApi._fnArrayCmp = _fnArrayCmp;
 6347+ this.oApi._fnDetectType = _fnDetectType;
 6348+ this.oApi._fnSettingsFromNode = _fnSettingsFromNode;
 6349+ this.oApi._fnGetDataMaster = _fnGetDataMaster;
 6350+ this.oApi._fnGetTrNodes = _fnGetTrNodes;
 6351+ this.oApi._fnGetTdNodes = _fnGetTdNodes;
 6352+ this.oApi._fnEscapeRegex = _fnEscapeRegex;
 6353+ this.oApi._fnDeleteIndex = _fnDeleteIndex;
 6354+ this.oApi._fnReOrderIndex = _fnReOrderIndex;
 6355+ this.oApi._fnColumnOrdering = _fnColumnOrdering;
 6356+ this.oApi._fnLog = _fnLog;
 6357+ this.oApi._fnClearTable = _fnClearTable;
 6358+ this.oApi._fnSaveState = _fnSaveState;
 6359+ this.oApi._fnLoadState = _fnLoadState;
 6360+ this.oApi._fnCreateCookie = _fnCreateCookie;
 6361+ this.oApi._fnReadCookie = _fnReadCookie;
 6362+ this.oApi._fnGetUniqueThs = _fnGetUniqueThs;
 6363+ this.oApi._fnScrollBarWidth = _fnScrollBarWidth;
 6364+ this.oApi._fnApplyToChildren = _fnApplyToChildren;
 6365+ this.oApi._fnMap = _fnMap;
 6366+
 6367+
 6368+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 6369+ * Section - Constructor
 6370+ */
 6371+
 6372+ /* Want to be able to reference "this" inside the this.each function */
 6373+ var _that = this;
 6374+ return this.each(function()
 6375+ {
 6376+ var i=0, iLen, j, jLen, k, kLen;
 6377+
 6378+ /* Check to see if we are re-initalising a table */
 6379+ for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
 6380+ {
 6381+ /* Base check on table node */
 6382+ if ( _aoSettings[i].nTable == this )
 6383+ {
 6384+ if ( typeof oInit == 'undefined' ||
 6385+ ( typeof oInit.bRetrieve != 'undefined' && oInit.bRetrieve === true ) )
 6386+ {
 6387+ return _aoSettings[i].oInstance;
 6388+ }
 6389+ else if ( typeof oInit.bDestroy != 'undefined' && oInit.bDestroy === true )
 6390+ {
 6391+ _aoSettings[i].oInstance.fnDestroy();
 6392+ break;
 6393+ }
 6394+ else
 6395+ {
 6396+ _fnLog( _aoSettings[i], 0, "Cannot reinitialise DataTable.\n\n"+
 6397+ "To retrieve the DataTables object for this table, please pass either no arguments "+
 6398+ "to the dataTable() function, or set bRetrieve to true. Alternatively, to destory "+
 6399+ "the old table and create a new one, set bDestroy to true (note that a lot of "+
 6400+ "changes to the configuration can be made through the API which is usually much "+
 6401+ "faster)." );
 6402+ return;
 6403+ }
 6404+ }
 6405+
 6406+ /* If the element we are initialising has the same ID as a table which was previously
 6407+ * initialised, but the table nodes don't match (from before) then we destory the old
 6408+ * instance by simply deleting it. This is under the assumption that the table has been
 6409+ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
 6410+ */
 6411+ if ( _aoSettings[i].sTableId !== "" && _aoSettings[i].sTableId == this.getAttribute('id') )
 6412+ {
 6413+ _aoSettings.splice( i, 1 );
 6414+ break;
 6415+ }
 6416+ }
 6417+
 6418+ /* Make a complete and independent copy of the settings object */
 6419+ var oSettings = new classSettings();
 6420+ _aoSettings.push( oSettings );
 6421+
 6422+ var bInitHandedOff = false;
 6423+ var bUsePassedData = false;
 6424+
 6425+ /* Set the id */
 6426+ var sId = this.getAttribute( 'id' );
 6427+ if ( sId !== null )
 6428+ {
 6429+ oSettings.sTableId = sId;
 6430+ oSettings.sInstance = sId;
 6431+ }
 6432+ else
 6433+ {
 6434+ oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++;
 6435+ }
 6436+
 6437+ /* Sanity check */
 6438+ if ( this.nodeName.toLowerCase() != 'table' )
 6439+ {
 6440+ _fnLog( oSettings, 0, "Attempted to initialise DataTables on a node which is not a "+
 6441+ "table: "+this.nodeName );
 6442+ return;
 6443+ }
 6444+
 6445+ /* Store 'this' in the settings object for later retrieval */
 6446+ oSettings.oInstance = _that;
 6447+
 6448+ /* Set the table node */
 6449+ oSettings.nTable = this;
 6450+
 6451+ /* Bind the API functions to the settings, so we can perform actions whenever oSettings is
 6452+ * available
 6453+ */
 6454+ oSettings.oApi = _that.oApi;
 6455+
 6456+ /* State the table's width for if a destroy is called at a later time */
 6457+ oSettings.sDestroyWidth = $(this).width();
 6458+
 6459+ /* Store the features that we have available */
 6460+ if ( typeof oInit != 'undefined' && oInit !== null )
 6461+ {
 6462+ oSettings.oInit = oInit;
 6463+ _fnMap( oSettings.oFeatures, oInit, "bPaginate" );
 6464+ _fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
 6465+ _fnMap( oSettings.oFeatures, oInit, "bFilter" );
 6466+ _fnMap( oSettings.oFeatures, oInit, "bSort" );
 6467+ _fnMap( oSettings.oFeatures, oInit, "bInfo" );
 6468+ _fnMap( oSettings.oFeatures, oInit, "bProcessing" );
 6469+ _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
 6470+ _fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
 6471+ _fnMap( oSettings.oFeatures, oInit, "bServerSide" );
 6472+ _fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
 6473+ _fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
 6474+ _fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
 6475+ _fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
 6476+ _fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
 6477+ _fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
 6478+ _fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
 6479+ _fnMap( oSettings, oInit, "asStripClasses" );
 6480+ _fnMap( oSettings, oInit, "fnRowCallback" );
 6481+ _fnMap( oSettings, oInit, "fnHeaderCallback" );
 6482+ _fnMap( oSettings, oInit, "fnFooterCallback" );
 6483+ _fnMap( oSettings, oInit, "fnCookieCallback" );
 6484+ _fnMap( oSettings, oInit, "fnInitComplete" );
 6485+ _fnMap( oSettings, oInit, "fnServerData" );
 6486+ _fnMap( oSettings, oInit, "fnFormatNumber" );
 6487+ _fnMap( oSettings, oInit, "aaSorting" );
 6488+ _fnMap( oSettings, oInit, "aaSortingFixed" );
 6489+ _fnMap( oSettings, oInit, "aLengthMenu" );
 6490+ _fnMap( oSettings, oInit, "sPaginationType" );
 6491+ _fnMap( oSettings, oInit, "sAjaxSource" );
 6492+ _fnMap( oSettings, oInit, "iCookieDuration" );
 6493+ _fnMap( oSettings, oInit, "sCookiePrefix" );
 6494+ _fnMap( oSettings, oInit, "sDom" );
 6495+ _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
 6496+ _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
 6497+ _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
 6498+ _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
 6499+ _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
 6500+
 6501+ /* Callback functions which are array driven */
 6502+ if ( typeof oInit.fnDrawCallback == 'function' )
 6503+ {
 6504+ oSettings.aoDrawCallback.push( {
 6505+ "fn": oInit.fnDrawCallback,
 6506+ "sName": "user"
 6507+ } );
 6508+ }
 6509+
 6510+ if ( typeof oInit.fnStateSaveCallback == 'function' )
 6511+ {
 6512+ oSettings.aoStateSave.push( {
 6513+ "fn": oInit.fnStateSaveCallback,
 6514+ "sName": "user"
 6515+ } );
 6516+ }
 6517+
 6518+ if ( typeof oInit.fnStateLoadCallback == 'function' )
 6519+ {
 6520+ oSettings.aoStateLoad.push( {
 6521+ "fn": oInit.fnStateLoadCallback,
 6522+ "sName": "user"
 6523+ } );
 6524+ }
 6525+
 6526+ if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
 6527+ oSettings.oFeatures.bSortClasses )
 6528+ {
 6529+ /* Enable sort classes for server-side processing. Safe to do it here, since server-side
 6530+ * processing must be enabled by the developer
 6531+ */
 6532+ oSettings.aoDrawCallback.push( {
 6533+ "fn": _fnSortingClasses,
 6534+ "sName": "server_side_sort_classes"
 6535+ } );
 6536+ }
 6537+
 6538+ if ( typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI )
 6539+ {
 6540+ /* Use the JUI classes object for display. You could clone the oStdClasses object if
 6541+ * you want to have multiple tables with multiple independent classes
 6542+ */
 6543+ oSettings.oClasses = _oExt.oJUIClasses;
 6544+
 6545+ if ( typeof oInit.sDom == 'undefined' )
 6546+ {
 6547+ /* Set the DOM to use a layout suitable for jQuery UI's theming */
 6548+ oSettings.sDom = '<"H"lfr>t<"F"ip>';
 6549+ }
 6550+ }
 6551+
 6552+ /* Calculate the scroll bar width and cache it for use later on */
 6553+ if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
 6554+ {
 6555+ oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
 6556+ }
 6557+
 6558+ if ( typeof oInit.iDisplayStart != 'undefined' &&
 6559+ typeof oSettings.iInitDisplayStart == 'undefined' )
 6560+ {
 6561+ /* Display start point, taking into account the save saving */
 6562+ oSettings.iInitDisplayStart = oInit.iDisplayStart;
 6563+ oSettings._iDisplayStart = oInit.iDisplayStart;
 6564+ }
 6565+
 6566+ /* Must be done after everything which can be overridden by a cookie! */
 6567+ if ( typeof oInit.bStateSave != 'undefined' )
 6568+ {
 6569+ oSettings.oFeatures.bStateSave = oInit.bStateSave;
 6570+ _fnLoadState( oSettings, oInit );
 6571+ oSettings.aoDrawCallback.push( {
 6572+ "fn": _fnSaveState,
 6573+ "sName": "state_save"
 6574+ } );
 6575+ }
 6576+
 6577+ if ( typeof oInit.aaData != 'undefined' )
 6578+ {
 6579+ bUsePassedData = true;
 6580+ }
 6581+
 6582+ /* Backwards compatability */
 6583+ /* aoColumns / aoData - remove at some point... */
 6584+ if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' )
 6585+ {
 6586+ oInit.aoColumns = oInit.aoData;
 6587+ }
 6588+
 6589+ /* Language definitions */
 6590+ if ( typeof oInit.oLanguage != 'undefined' )
 6591+ {
 6592+ if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" )
 6593+ {
 6594+ /* Get the language definitions from a file */
 6595+ oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
 6596+ $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
 6597+ _fnLanguageProcess( oSettings, json, true ); } );
 6598+ bInitHandedOff = true;
 6599+ }
 6600+ else
 6601+ {
 6602+ _fnLanguageProcess( oSettings, oInit.oLanguage, false );
 6603+ }
 6604+ }
 6605+ /* Warning: The _fnLanguageProcess function is async to the remainder of this function due
 6606+ * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing
 6607+ * below is complete. The reason for spliting it like this is optimisation - we can fire
 6608+ * off the XHR (if needed) and then continue processing the data.
 6609+ */
 6610+ }
 6611+ else
 6612+ {
 6613+ /* Create a dummy object for quick manipulation later on. */
 6614+ oInit = {};
 6615+ }
 6616+
 6617+ /*
 6618+ * Stripes
 6619+ * Add the strip classes now that we know which classes to apply - unless overruled
 6620+ */
 6621+ if ( typeof oInit.asStripClasses == 'undefined' )
 6622+ {
 6623+ oSettings.asStripClasses.push( oSettings.oClasses.sStripOdd );
 6624+ oSettings.asStripClasses.push( oSettings.oClasses.sStripEven );
 6625+ }
 6626+
 6627+ /* Remove row stripe classes if they are already on the table row */
 6628+ var bStripeRemove = false;
 6629+ var anRows = $('tbody>tr', this);
 6630+ for ( i=0, iLen=oSettings.asStripClasses.length ; i<iLen ; i++ )
 6631+ {
 6632+ if ( anRows.filter(":lt(2)").hasClass( oSettings.asStripClasses[i]) )
 6633+ {
 6634+ bStripeRemove = true;
 6635+ break;
 6636+ }
 6637+ }
 6638+
 6639+ if ( bStripeRemove )
 6640+ {
 6641+ /* Store the classes which we are about to remove so they can be readded on destory */
 6642+ oSettings.asDestoryStrips = [ '', '' ];
 6643+ if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripOdd) )
 6644+ {
 6645+ oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripOdd+" ";
 6646+ }
 6647+ if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripEven) )
 6648+ {
 6649+ oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripEven;
 6650+ }
 6651+ if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripOdd) )
 6652+ {
 6653+ oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripOdd+" ";
 6654+ }
 6655+ if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripEven) )
 6656+ {
 6657+ oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripEven;
 6658+ }
 6659+
 6660+ anRows.removeClass( oSettings.asStripClasses.join(' ') );
 6661+ }
 6662+
 6663+ /*
 6664+ * Columns
 6665+ * See if we should load columns automatically or use defined ones
 6666+ */
 6667+ var nThead = this.getElementsByTagName('thead');
 6668+ var anThs = nThead.length===0 ? [] : _fnGetUniqueThs( nThead[0] );
 6669+ var aoColumnsInit;
 6670+
 6671+ /* If not given a column array, generate one with nulls */
 6672+ if ( typeof oInit.aoColumns == 'undefined' )
 6673+ {
 6674+ aoColumnsInit = [];
 6675+ for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
 6676+ {
 6677+ aoColumnsInit.push( null );
 6678+ }
 6679+ }
 6680+ else
 6681+ {
 6682+ aoColumnsInit = oInit.aoColumns;
 6683+ }
 6684+
 6685+ /* Add the columns */
 6686+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
 6687+ {
 6688+ /* Check if we have column visibilty state to restore */
 6689+ if ( typeof oInit.saved_aoColumns != 'undefined' && oInit.saved_aoColumns.length == iLen )
 6690+ {
 6691+ if ( aoColumnsInit[i] === null )
 6692+ {
 6693+ aoColumnsInit[i] = {};
 6694+ }
 6695+ aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
 6696+ }
 6697+
 6698+ _fnAddColumn( oSettings, anThs ? anThs[i] : null );
 6699+ }
 6700+
 6701+ /* Add options from column definations */
 6702+ if ( typeof oInit.aoColumnDefs != 'undefined' )
 6703+ {
 6704+ /* Loop over the column defs array - loop in reverse so first instace has priority */
 6705+ for ( i=oInit.aoColumnDefs.length-1 ; i>=0 ; i-- )
 6706+ {
 6707+ /* Each column def can target multiple columns, as it is an array */
 6708+ var aTargets = oInit.aoColumnDefs[i].aTargets;
 6709+ if ( !$.isArray( aTargets ) )
 6710+ {
 6711+ _fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
 6712+ }
 6713+ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
 6714+ {
 6715+ if ( typeof aTargets[j] == 'number' && aTargets[j] >= 0 )
 6716+ {
 6717+ /* 0+ integer, left to right column counting. We add columns which are unknown
 6718+ * automatically. Is this the right behaviour for this? We should at least
 6719+ * log it in future. We cannot do this for the negative or class targets, only here.
 6720+ */
 6721+ while( oSettings.aoColumns.length <= aTargets[j] )
 6722+ {
 6723+ _fnAddColumn( oSettings );
 6724+ }
 6725+ _fnColumnOptions( oSettings, aTargets[j], oInit.aoColumnDefs[i] );
 6726+ }
 6727+ else if ( typeof aTargets[j] == 'number' && aTargets[j] < 0 )
 6728+ {
 6729+ /* Negative integer, right to left column counting */
 6730+ _fnColumnOptions( oSettings, oSettings.aoColumns.length+aTargets[j],
 6731+ oInit.aoColumnDefs[i] );
 6732+ }
 6733+ else if ( typeof aTargets[j] == 'string' )
 6734+ {
 6735+ /* Class name matching on TH element */
 6736+ for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
 6737+ {
 6738+ if ( aTargets[j] == "_all" ||
 6739+ oSettings.aoColumns[k].nTh.className.indexOf( aTargets[j] ) != -1 )
 6740+ {
 6741+ _fnColumnOptions( oSettings, k, oInit.aoColumnDefs[i] );
 6742+ }
 6743+ }
 6744+ }
 6745+ }
 6746+ }
 6747+ }
 6748+
 6749+ /* Add options from column array - after the defs array so this has priority */
 6750+ if ( typeof aoColumnsInit != 'undefined' )
 6751+ {
 6752+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
 6753+ {
 6754+ _fnColumnOptions( oSettings, i, aoColumnsInit[i] );
 6755+ }
 6756+ }
 6757+
 6758+ /*
 6759+ * Sorting
 6760+ * Check the aaSorting array
 6761+ */
 6762+ for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
 6763+ {
 6764+ if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
 6765+ {
 6766+ oSettings.aaSorting[i][0] = 0;
 6767+ }
 6768+ var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
 6769+
 6770+ /* Add a default sorting index */
 6771+ if ( typeof oSettings.aaSorting[i][2] == 'undefined' )
 6772+ {
 6773+ oSettings.aaSorting[i][2] = 0;
 6774+ }
 6775+
 6776+ /* If aaSorting is not defined, then we use the first indicator in asSorting */
 6777+ if ( typeof oInit.aaSorting == "undefined" &&
 6778+ typeof oSettings.saved_aaSorting == "undefined" )
 6779+ {
 6780+ oSettings.aaSorting[i][1] = oColumn.asSorting[0];
 6781+ }
 6782+
 6783+ /* Set the current sorting index based on aoColumns.asSorting */
 6784+ for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
 6785+ {
 6786+ if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
 6787+ {
 6788+ oSettings.aaSorting[i][2] = j;
 6789+ break;
 6790+ }
 6791+ }
 6792+ }
 6793+
 6794+ /* Do a first pass on the sorting classes (allows any size changes to be taken into
 6795+ * account, and also will apply sorting disabled classes if disabled
 6796+ */
 6797+ _fnSortingClasses( oSettings );
 6798+
 6799+ /*
 6800+ * Final init
 6801+ * Sanity check that there is a thead and tbody. If not let's just create them
 6802+ */
 6803+ if ( this.getElementsByTagName('thead').length === 0 )
 6804+ {
 6805+ this.appendChild( document.createElement( 'thead' ) );
 6806+ }
 6807+
 6808+ if ( this.getElementsByTagName('tbody').length === 0 )
 6809+ {
 6810+ this.appendChild( document.createElement( 'tbody' ) );
 6811+ }
 6812+
 6813+ oSettings.nTHead = this.getElementsByTagName('thead')[0];
 6814+ oSettings.nTBody = this.getElementsByTagName('tbody')[0];
 6815+ if ( this.getElementsByTagName('tfoot').length > 0 )
 6816+ {
 6817+ oSettings.nTFoot = this.getElementsByTagName('tfoot')[0];
 6818+ }
 6819+
 6820+ /* Check if there is data passing into the constructor */
 6821+ if ( bUsePassedData )
 6822+ {
 6823+ for ( i=0 ; i<oInit.aaData.length ; i++ )
 6824+ {
 6825+ _fnAddData( oSettings, oInit.aaData[ i ] );
 6826+ }
 6827+ }
 6828+ else
 6829+ {
 6830+ /* Grab the data from the page */
 6831+ _fnGatherData( oSettings );
 6832+ }
 6833+
 6834+ /* Copy the data index array */
 6835+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
 6836+
 6837+ /* Initialisation complete - table can be drawn */
 6838+ oSettings.bInitialised = true;
 6839+
 6840+ /* Check if we need to initialise the table (it might not have been handed off to the
 6841+ * language processor)
 6842+ */
 6843+ if ( bInitHandedOff === false )
 6844+ {
 6845+ _fnInitalise( oSettings );
 6846+ }
 6847+ });
 6848+ };
 6849+})(jQuery, window, document);
Property changes on: trunk/extensions/ResearchTools/modules/jquery.dataTables.js
___________________________________________________________________
Added: svn:mime-type
16850 + text/plain
Added: svn:eol-style
26851 + native
Index: trunk/extensions/ResearchTools/ResearchTools.alias.php
@@ -0,0 +1,16 @@
 2+<?php
 3+/**
 4+ * Aliases for Special:ResearchTools
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$specialPageAliases = array();
 11+
 12+/** English
 13+ * @author Trevor Parscal
 14+ */
 15+$specialPageAliases['en'] = array(
 16+ 'ResearchTools' => array( 'ResearchTools' ),
 17+);
Property changes on: trunk/extensions/ResearchTools/ResearchTools.alias.php
___________________________________________________________________
Added: svn:eol-style
118 + native
Index: trunk/extensions/ResearchTools/README
@@ -0,0 +1,11 @@
 2+= ResearchTools =
 3+
 4+This extension provides a set of tools which are useful for conducting user research studies.
 5+
 6+== Survey ==
 7+
 8+The survey module provides:
 9+
 10+* Survey form rendering
 11+* Survey collection API
 12+* Survey response browser

Follow-up revisions

RevisionCommit summaryAuthorDate
r80751Add new extension per r80680raymond16:16, 22 January 2011

Status & tagging log