/* * File: jquery.dataSlides.js * Version: 1.5.2 * CVS: $Id$ * Description: Paginate, search and sort HTML tables * Author: Allan Jardine (www.sprymedia.co.uk) * Created: 28/3/2008 * Modified: $Date$ by $Author$ * Language: Javascript * License: GPL v2 or BSD 3 point style * Project: Mtaala * Contact: allan.jardine@sprymedia.co.uk * * Copyright 2008-2009 Allan Jardine, all rights reserved. * * This source file is free software, under either the GPL v2 license or a * BSD style license, as supplied with this software. * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details pleease refer to: http://www.dataSlides.net */ /* * When considering jsLint, we need to allow eval() as it it is used for reading cookies and * building the dynamic multi-column sort functions. */ /*jslint evil: true, undef: true, browser: true */ /*globals $, jQuery,_fnReadCookie,_fnProcessingDisplay,_fnDraw,_fnSort,_fnReDraw,_fnDetectType,_fnSortingClasses,_fnSettingsFromNode,_fnBuildSearchArray,_fnCalculateEnd,_fnFeatureHtmlProcessing,_fnFeatureHtmlPaginate,_fnFeatureHtmlInfo,_fnFeatureHtmlFilter,_fnFilter,_fnSaveState,_fnFilterColumn,_fnEscapeRegex,_fnFilterComplete,_fnFeatureHtmlLength,_fnGetDataMaster,_fnVisibleToColumnIndex,_fnDrawHead,_fnAddData,_fnGetTrNodes,_fnColumnIndexToVisible,_fnCreateCookie,_fnAddOptionsHtml,_fnMap,_fnClearTable,_fnDataToSearch,_fnReOrderIndex,_fnFilterCustom,_fnVisbleColumns,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnColumnOrdering,fnGetMaxLenString*/ (function($) { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * dataSlides variables * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Variable: dataSlideSettings * Purpose: Store the settings for each dataSlides instance * Scope: jQuery.fn */ $.fn.dataSlideSettings = []; /* * Variable: dataSlideExt * Purpose: Container for customisable parts of dataSlides * Scope: jQuery.fn */ $.fn.dataSlideExt = {}; var _oExt = $.fn.dataSlideExt; /* Short reference for fast internal lookup */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * dataSlides extensible objects * * The _oExt object is used to provide an area where user dfined plugins can be * added to dataSlides. The following properties of the object are used: * oApi - Plug-in API functions * aTypes - Auto-detection of types * oSort - Sorting functions used by dataSlides (based on the type) * oPagination - Pagination functions for different input styles * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Variable: sVersion * Purpose: Version string for plug-ins to check compatibility * Scope: jQuery.fn.dataSlideExt */ _oExt.sVersion = "1.5.2"; /* * Variable: iApiIndex * Purpose: Index for what 'this' index API functions should use * Scope: jQuery.fn.dataSlideExt */ _oExt.iApiIndex = 0; /* * Variable: oApi * Purpose: Container for plugin API functions * Scope: jQuery.fn.dataSlideExt */ _oExt.oApi = { }; /* * Variable: aFiltering * Purpose: Container for plugin filtering functions * Scope: jQuery.fn.dataSlideExt */ _oExt.afnFiltering = [ ]; /* * Variable: aoFeatures * Purpose: Container for plugin function functions * Scope: jQuery.fn.dataSlideExt * Notes: Array of objects with the following parameters: * fnInit: Function for initialisation of Feature. Takes oSettings and returns node * cFeature: Character that will be matched in sDom - case sensitive * sFeature: Feature name - just for completeness :-) */ _oExt.aoFeatures = [ ]; /* * Variable: ofnSearch * Purpose: Container for custom filtering functions * Scope: jQuery.fn.dataSlideExt * Notes: This is an object (the name should match the type) for custom filtering function, * which can be used for live DOM checking or formatted text filtering */ _oExt.ofnSearch = { }; /* * Variable: oStdClasses * Purpose: Storage for the various classes that dataSlides uses * Scope: jQuery.fn.dataSlideExt */ _oExt.oStdClasses = { /* Two buttons buttons */ "sPagePrevEnabled": "paginate_enabled_previous", "sPagePrevDisabled": "paginate_disabled_previous", "sPageNextEnabled": "paginate_enabled_next", "sPageNextDisabled": "paginate_disabled_next", "sPageJUINext": "", "sPageJUIPrev": "", /* Full numbers paging buttons */ "sPageButton": "paginate_button", "sPageButtonActive": "paginate_active", "sPageButtonStaticActive": "paginate_button", "sPageFirst": "first", "sPagePrevious": "previous", "sPageNext": "next", "sPageLast": "last", /* Stripping classes */ "sStripOdd": "odd", "sStripEven": "even", /* Empty row */ "sRowEmpty": "dataSlides_empty", /* Features */ "sWrapper": "dataSlides_wrapper", "sFilter": "dataSlides_filter", "sInfo": "dataSlides_info", "sPaging": "dataSlides_paginate paging_", /* Note that the type is postfixed */ "sLength": "dataSlides_length", "sProcessing": "dataSlides_processing", /* Sorting */ "sSortAsc": "sorting_asc", "sSortDesc": "sorting_desc", "sSortable": "sorting", "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ "sSortJUIAsc": "", "sSortJUIDesc": "", "sSortJUI": "" }; /* * Variable: oJUIClasses * Purpose: Storage for the various classes that dataSlides uses - jQuery UI suitable * Scope: jQuery.fn.dataSlideExt */ _oExt.oJUIClasses = { /* Two buttons buttons */ "sPagePrevEnabled": "fg-button ui-state-default ui-corner-left", "sPagePrevDisabled": "fg-button ui-state-default ui-corner-left ui-state-disabled", "sPageNextEnabled": "fg-button ui-state-default ui-corner-right", "sPageNextDisabled": "fg-button ui-state-default ui-corner-right ui-state-disabled", "sPageJUINext": "ui-icon ui-icon-circle-arrow-e", "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w", /* Full numbers paging buttons */ "sPageButton": "fg-button ui-state-default", "sPageButtonActive": "fg-button ui-state-default ui-state-disabled", "sPageButtonStaticActive": "fg-button ui-state-default ui-state-disabled", "sPageFirst": "first ui-corner-tl ui-corner-bl", "sPagePrevious": "previous", "sPageNext": "next", "sPageLast": "last ui-corner-tr ui-corner-br", /* Stripping classes */ "sStripOdd": "odd", "sStripEven": "even", /* Empty row */ "sRowEmpty": "dataSlides_empty", /* Features */ "sWrapper": "dataSlides_wrapper", "sFilter": "dataSlides_filter", "sInfo": "dataSlides_info", "sPaging": "dataSlides_paginate fg-buttonset fg-buttonset-multi paging_", /* Note that the type is postfixed */ "sLength": "dataSlides_length", "sProcessing": "dataSlides_processing", /* Sorting */ "sSortAsc": "ui-state-default", "sSortDesc": "ui-state-default", "sSortable": "ui-state-default", "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n", "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s", "sSortJUI": "css_right ui-icon ui-icon-triangle-2-n-s" }; /* * Variable: oPagination * Purpose: Container for the various type of pagination that dataSlides supports * Scope: jQuery.fn.dataSlideExt */ _oExt.oPagination = { /* * Variable: two_button * Purpose: Standard two button (forward/back) pagination * Scope: jQuery.fn.dataSlideExt.oPagination */ "two_button": { /* * Function: oPagination.two_button.fnInit * Purpose: Initalise dom elements required for pagination with forward/back buttons only * Returns: - * Inputs: object:oSettings - dataSlides settings object * function:fnCallbackDraw - draw function which must be called on update */ "fnInit": function ( oSettings, fnCallbackDraw ) { var nPaging = oSettings.anFeatures.p; /* Store the next and previous elements in the oSettings object as they can be very * usful for automation - particularly testing */ if ( !oSettings.bJUI ) { oSettings.nPrevious = document.createElement( 'div' ); oSettings.nNext = document.createElement( 'div' ); } else { oSettings.nPrevious = document.createElement( 'a' ); oSettings.nNext = document.createElement( 'a' ); var nNextInner = document.createElement('span'); nNextInner.className = oSettings.oClasses.sPageJUINext; oSettings.nNext.appendChild( nNextInner ); var nPreviousInner = document.createElement('span'); nPreviousInner.className = oSettings.oClasses.sPageJUIPrev; oSettings.nPrevious.appendChild( nPreviousInner ); } if ( oSettings.sTableId !== '' ) { nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); oSettings.nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); oSettings.nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); } oSettings.nPrevious.className = oSettings.oClasses.sPagePrevDisabled; oSettings.nNext.className = oSettings.oClasses.sPageNextDisabled; oSettings.nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious; oSettings.nNext.title = oSettings.oLanguage.oPaginate.sNext; nPaging.appendChild( oSettings.nPrevious ); nPaging.appendChild( oSettings.nNext ); $(nPaging).insertAfter( oSettings.nTable ); $(oSettings.nPrevious).click( function() { oSettings._iDisplayStart -= oSettings._iDisplayLength; /* Correct for underrun */ if ( oSettings._iDisplayStart < 0 ) { oSettings._iDisplayStart = 0; } fnCallbackDraw( oSettings ); } ); $(oSettings.nNext).click( function() { /* Make sure we are not over running the display array */ if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) { oSettings._iDisplayStart += oSettings._iDisplayLength; } fnCallbackDraw( oSettings ); } ); /* Take the brutal approach to cancelling text selection */ $(oSettings.nPrevious).bind( 'selectstart', function () { return false; } ); $(oSettings.nNext).bind( 'selectstart', function () { return false; } ); }, /* * Function: oPagination.two_button.fnUpdate * Purpose: Update the two button pagination at the end of the draw * Returns: - * Inputs: object:oSettings - dataSlides settings object * function:fnCallbackDraw - draw function which must be called on update */ "fnUpdate": function ( oSettings, fnCallbackDraw ) { if ( !oSettings.anFeatures.p ) { return; } oSettings.nPrevious.className = ( oSettings._iDisplayStart === 0 ) ? oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled; oSettings.nNext.className = ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ? oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled; } }, /* * Variable: iFullNumbersShowPages * Purpose: Change the number of pages which can be seen * Scope: jQuery.fn.dataSlideExt.oPagination */ "iFullNumbersShowPages": 5, /* * Variable: full_numbers * Purpose: Full numbers pagination * Scope: jQuery.fn.dataSlideExt.oPagination */ "full_numbers": { /* * Function: oPagination.full_numbers.fnInit * Purpose: Initalise dom elements required for pagination with a list of the pages * Returns: - * Inputs: object:oSettings - dataSlides settings object * function:fnCallbackDraw - draw function which must be called on update */ "fnInit": function ( oSettings, fnCallbackDraw ) { var nPaging = oSettings.anFeatures.p; var nFirst = document.createElement( 'span' ); var nPrevious = document.createElement( 'span' ); var nList = document.createElement( 'span' ); var nNext = document.createElement( 'span' ); var nLast = document.createElement( 'span' ); nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst; nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious; nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext; nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast; var oClasses = oSettings.oClasses; nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst; nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious; nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext; nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast; if ( oSettings.sTableId !== '' ) { nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); nFirst.setAttribute( 'id', oSettings.sTableId+'_first' ); nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); nLast.setAttribute( 'id', oSettings.sTableId+'_last' ); } nPaging.appendChild( nFirst ); nPaging.appendChild( nPrevious ); nPaging.appendChild( nList ); nPaging.appendChild( nNext ); nPaging.appendChild( nLast ); $(nFirst).click( function () { oSettings._iDisplayStart = 0; fnCallbackDraw( oSettings ); } ); $(nPrevious).click( function() { oSettings._iDisplayStart -= oSettings._iDisplayLength; /* Correct for underrun */ if ( oSettings._iDisplayStart < 0 ) { oSettings._iDisplayStart = 0; } fnCallbackDraw( oSettings ); } ); $(nNext).click( function() { /* Make sure we are not over running the display array */ if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) { oSettings._iDisplayStart += oSettings._iDisplayLength; } fnCallbackDraw( oSettings ); } ); $(nLast).click( function() { var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1; oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength; fnCallbackDraw( oSettings ); } ); /* Take the brutal approach to cancelling text selection */ $('span', nPaging).bind( 'mousedown', function () { return false; } ); $('span', nPaging).bind( 'selectstart', function () { return false; } ); oSettings.nPaginateList = nList; }, /* * Function: oPagination.full_numbers.fnUpdate * Purpose: Update the list of page buttons shows * Returns: - * Inputs: object:oSettings - dataSlides settings object * function:fnCallbackDraw - draw function which must be called on update */ "fnUpdate": function ( oSettings, fnCallbackDraw ) { if ( !oSettings.anFeatures.p ) { return; } var iPageCount = jQuery.fn.dataSlideExt.oPagination.iFullNumbersShowPages; var iPageCountHalf = Math.floor(iPageCount / 2); var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength); var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1; var sList = ""; var iStartButton; var iEndButton; var oClasses = oSettings.oClasses; if (iPages < iPageCount) { iStartButton = 1; iEndButton = iPages; } else { if (iCurrentPage <= iPageCountHalf) { iStartButton = 1; iEndButton = iPageCount; } else { if (iCurrentPage >= (iPages - iPageCountHalf)) { iStartButton = iPages - iPageCount + 1; iEndButton = iPages; } else { iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1; iEndButton = iStartButton + iPageCount - 1; } } } for ( var i=iStartButton ; i<=iEndButton ; i++ ) { if ( iCurrentPage != i ) { sList += ''+i+''; } else { sList += ''+i+''; } } oSettings.nPaginateList.innerHTML = sList; /* Take the brutal approach to cancelling text selection */ $('span', oSettings.nPaginateList).bind( 'mousedown', function () { return false; } ); $('span', oSettings.nPaginateList).bind( 'selectstart', function () { return false; } ); $('span', oSettings.nPaginateList).click( function() { var iTarget = (this.innerHTML * 1) - 1; oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength; fnCallbackDraw( oSettings ); return false; } ); /* Update the 'premanent botton's classes */ var nButtons = $('span', oSettings.anFeatures.p); var nStatic = [ nButtons[0], nButtons[1], nButtons[nButtons.length-2], nButtons[nButtons.length-1] ]; $(nStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive ); if ( iCurrentPage == 1 ) { nStatic[0].className += " "+oClasses.sPageButtonStaticActive; nStatic[1].className += " "+oClasses.sPageButtonStaticActive; } else { nStatic[0].className += " "+oClasses.sPageButton; nStatic[1].className += " "+oClasses.sPageButton; } if ( iCurrentPage == iPages ) { nStatic[2].className += " "+oClasses.sPageButtonStaticActive; nStatic[3].className += " "+oClasses.sPageButtonStaticActive; } else { nStatic[2].className += " "+oClasses.sPageButton; nStatic[3].className += " "+oClasses.sPageButton; } } } }; /* * Variable: oSort * Purpose: Wrapper for the sorting functions that can be used in dataSlides * Scope: jQuery.fn.dataSlideExt * Notes: The functions provided in this object are basically standard javascript sort * functions - they expect two inputs which they then compare and then return a priority * result. For each sort method added, two functions need to be defined, an ascending sort and * a descending sort. */ _oExt.oSort = { /* * text sorting */ "string-asc": function ( a, b ) { var x = a.toLowerCase(); var y = b.toLowerCase(); return ((x < y) ? -1 : ((x > y) ? 1 : 0)); }, "string-desc": function ( a, b ) { var x = a.toLowerCase(); var y = b.toLowerCase(); return ((x < y) ? 1 : ((x > y) ? -1 : 0)); }, /* * html sorting (ignore html tags) */ "html-asc": function ( a, b ) { var x = a.replace( /<.*?>/g, "" ).toLowerCase(); var y = b.replace( /<.*?>/g, "" ).toLowerCase(); return ((x < y) ? -1 : ((x > y) ? 1 : 0)); }, "html-desc": function ( a, b ) { var x = a.replace( /<.*?>/g, "" ).toLowerCase(); var y = b.replace( /<.*?>/g, "" ).toLowerCase(); return ((x < y) ? 1 : ((x > y) ? -1 : 0)); }, /* * date sorting */ "date-asc": function ( a, b ) { var x = Date.parse( a ); var y = Date.parse( b ); if ( isNaN( x ) ) { x = Date.parse( "01/01/1970 00:00:00" ); } if ( isNaN( y ) ) { y = Date.parse( "01/01/1970 00:00:00" ); } return x - y; }, "date-desc": function ( a, b ) { var x = Date.parse( a ); var y = Date.parse( b ); if ( isNaN( x ) ) { x = Date.parse( "01/01/1970 00:00:00" ); } if ( isNaN( y ) ) { y = Date.parse( "01/01/1970 00:00:00" ); } return y - x; }, /* * numerical sorting */ "numeric-asc": function ( a, b ) { var x = a == "-" ? 0 : a; var y = b == "-" ? 0 : b; return x - y; }, "numeric-desc": function ( a, b ) { var x = a == "-" ? 0 : a; var y = b == "-" ? 0 : b; return y - x; } }; /* * Variable: aTypes * Purpose: Container for the various type of type detection that dataSlides supports * Scope: jQuery.fn.dataSlideExt * Notes: The functions in this array are expected to parse a string to see if it is a data * type that it recognises. If so then the function should return the name of the type (a * corresponding sort function should be defined!), if the type is not recognised then the * function should return null such that the parser and move on to check the next type. * Note that ordering is important in this array - the functions are processed linearly, * starting at index 0. */ _oExt.aTypes = [ /* * Function: - * Purpose: Check to see if a string is numeric * Returns: string:'numeric' or null * Inputs: string:sText - string to check */ function ( sData ) { /* Snaity check that we are dealing with a string or quick return for a number */ if ( typeof sData == 'number' ) { return 'numeric'; } else if ( typeof sData.charAt != 'function' ) { return null; } var sValidFirstChars = "0123456789-"; var sValidChars = "0123456789."; var Char; var bDecimal = false; /* Check for a valid first char (no period and allow negatives) */ Char = sData.charAt(0); if (sValidFirstChars.indexOf(Char) == -1) { return null; } /* Check all the other characters are valid */ for ( var i=1 ; i