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 |
1 | 12 | + 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 |
1 | 12 | + 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 |
1 | 12 | + 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 |
1 | 12 | + 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 |
1 | 12 | + 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 |
1 | 23 | + 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 |
1 | 37 | + 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 |
1 | 64 | + 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 |
1 | 36 | + 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 |
1 | 52 | + text/plain |
Added: svn:eol-style |
2 | 53 | + 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 |
3 | 54 | + 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 |
1 | 5 | + text/plain |
Added: svn:eol-style |
2 | 6 | + 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('"','"') ); |
| 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 |
1 | 6850 | + text/plain |
Added: svn:eol-style |
2 | 6851 | + 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 |
1 | 18 | + 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 |