From c181096b517bcc0f3aaa1d1e0faa5139bd0dc498 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 14 Sep 2012 20:24:34 -0700 Subject: [PATCH] Series Editor updated New: Series Editor controls are always visible New: Can shift-click to select series in editor Fixed: Maximum form elements increased --- .../media/css/jquery.dataTables.css | 18 +- NzbDrone.Web/Content/SeriesEditor.css | 60 + .../Controllers/AddSeriesController.cs | 41 +- NzbDrone.Web/NzbDrone.Web.csproj | 4 + .../DataTables-1.9.0/media/js/FixedHeader.js | 937 ++++++++++ .../media/js/FixedHeader.min.js | 40 + .../media/js/jquery.dataTables.js | 1506 +++++++++++------ .../media/js/jquery.dataTables.min.js | 273 +-- NzbDrone.Web/Scripts/NzbDrone/common.js | 16 + NzbDrone.Web/Views/Series/Editor.cshtml | 216 ++- .../Views/Shared/_ReferenceLayout.cshtml | 2 + NzbDrone.Web/Web.config | 1 + 12 files changed, 2334 insertions(+), 780 deletions(-) create mode 100644 NzbDrone.Web/Content/SeriesEditor.css create mode 100644 NzbDrone.Web/Scripts/DataTables-1.9.0/media/js/FixedHeader.js create mode 100644 NzbDrone.Web/Scripts/DataTables-1.9.0/media/js/FixedHeader.min.js create mode 100644 NzbDrone.Web/Scripts/NzbDrone/common.js diff --git a/NzbDrone.Web/Content/DataTables-1.9.0/media/css/jquery.dataTables.css b/NzbDrone.Web/Content/DataTables-1.9.0/media/css/jquery.dataTables.css index d28a261df..ddd272f9d 100644 --- a/NzbDrone.Web/Content/DataTables-1.9.0/media/css/jquery.dataTables.css +++ b/NzbDrone.Web/Content/DataTables-1.9.0/media/css/jquery.dataTables.css @@ -28,11 +28,19 @@ table.dataTable thead th { *cursor: hand; } -table.dataTable tfoot th -{ - padding: 3px 18px 3px 10px; - border-top: 1px solid black; - font-weight: bold; +table.dataTable tfoot th { + font-family: "Segoe UI Light" , "Open Sans" , "Segoe UI" , sans-serif; + border-width: 300; + font-size: 17px; + padding: 2px; + border-style: none; + border-color: #EEEEEE; + padding-left: 7px; + text-align: left; + background-color: white; + font-weight: lighter; + cursor: pointer; + *cursor: hand; } table.dataTable td { diff --git a/NzbDrone.Web/Content/SeriesEditor.css b/NzbDrone.Web/Content/SeriesEditor.css new file mode 100644 index 000000000..be5178250 --- /dev/null +++ b/NzbDrone.Web/Content/SeriesEditor.css @@ -0,0 +1,60 @@ +.masterControls { + margin-top: 10px; + overflow: hidden; +} + +.checkboxColumn { + width: 110px; + text-align: center; +} + +.buttons { + width: 600px; + text-align: center; +} + +table input[type="text"], table select { + margin: 2px 2px; +} + +td .path { + width: 300px; +} + +td .backlogSetting { + width: 100px; +} + +td .quality { + width: 120px; +} + +th .footer-control { + width: 100px; +} + +th .footer-control-quality { + width: 120px; +} + +#stylized, .settingsForm { + overflow: hidden; +} + +#stylized { + float: left; +} + +#changesOverview { + margin-top: 15px; + float: left; + font-size: 20px; +} + +#seriesEditorGrid tfoot { + background-color: lightgrey; +} + +#editToggleMaster { + margin-left: 5px; +} \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/AddSeriesController.cs b/NzbDrone.Web/Controllers/AddSeriesController.cs index 84cf0c434..4b164671c 100644 --- a/NzbDrone.Web/Controllers/AddSeriesController.cs +++ b/NzbDrone.Web/Controllers/AddSeriesController.cs @@ -11,6 +11,7 @@ using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using NzbDrone.Web.Filters; using NzbDrone.Web.Models; +using TvdbLib.Exceptions; namespace NzbDrone.Web.Controllers { @@ -186,18 +187,34 @@ namespace NzbDrone.Web.Controllers [HttpGet] public JsonResult LookupSeries(string term) { - var tvDbResults = _tvDbProvider.SearchSeries(term).Select(r => new TvDbSearchResultModel - { - Id = r.Id, - Title = r.SeriesName, - DisplayedTitle = r.FirstAired.Year > 1900 && !r.SeriesName.EndsWith("(" + r.FirstAired.Year + ")") - ?string.Format("{0} ({1})", r.SeriesName, r.FirstAired.Year) - :r.SeriesName, - Banner = r.Banner.BannerPath, - Url = String.Format("http://www.thetvdb.com/?tab=series&id={0}", r.Id) - }).ToList(); - - return Json(tvDbResults, JsonRequestBehavior.AllowGet); + try + { + var tvDbResults = _tvDbProvider.SearchSeries(term).Select(r => new TvDbSearchResultModel + { + Id = r.Id, + Title = r.SeriesName, + DisplayedTitle = r.FirstAired.Year > 1900 && !r.SeriesName.EndsWith("(" + r.FirstAired.Year + ")") + ? string.Format("{0} ({1})", r.SeriesName, r.FirstAired.Year) + : r.SeriesName, + Banner = r.Banner.BannerPath, + Url = String.Format("http://www.thetvdb.com/?tab=series&id={0}", r.Id) + }).ToList(); + + return Json(tvDbResults, JsonRequestBehavior.AllowGet); + } + + catch(TvdbNotAvailableException ex) + { + logger.WarnException("Unable to lookup series on TheTVDB", ex); + return JsonNotificationResult.Info("Lookup Failed", "TheTVDB is not available at this time."); + } + + catch(Exception ex) + { + logger.WarnException("Unknown Error when looking up series on TheTVDB", ex); + return JsonNotificationResult.Info("Lookup Failed", "Unknown error while connecting to TheTVDB"); + } + } public ActionResult RootList() diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index ffcc32816..1aaeb81f9 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -153,6 +153,7 @@ + @@ -360,6 +361,8 @@ + + @@ -388,6 +391,7 @@ + diff --git a/NzbDrone.Web/Scripts/DataTables-1.9.0/media/js/FixedHeader.js b/NzbDrone.Web/Scripts/DataTables-1.9.0/media/js/FixedHeader.js new file mode 100644 index 000000000..bb20a8ae6 --- /dev/null +++ b/NzbDrone.Web/Scripts/DataTables-1.9.0/media/js/FixedHeader.js @@ -0,0 +1,937 @@ +/* + * File: FixedHeader.js + * Version: 2.0.6 + * Description: "Fix" a header at the top of the table, so it scrolls with the table + * Author: Allan Jardine (www.sprymedia.co.uk) + * Created: Wed 16 Sep 2009 19:46:30 BST + * Language: Javascript + * License: GPL v2 or BSD 3 point style + * Project: Just a little bit of fun - enjoy :-) + * Contact: www.sprymedia.co.uk/contact + * + * Copyright 2009-2012 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, available at: + * http://datatables.net/license_gpl2 + * http://datatables.net/license_bsd + */ + +/* + * Function: FixedHeader + * Purpose: Provide 'fixed' header, footer and columns on an HTML table + * Returns: object:FixedHeader - must be called with 'new' + * Inputs: mixed:mTable - target table + * 1. DataTable object - when using FixedHeader with DataTables, or + * 2. HTML table node - when using FixedHeader without DataTables + * object:oInit - initialisation settings, with the following properties (each optional) + * bool:top - fix the header (default true) + * bool:bottom - fix the footer (default false) + * bool:left - fix the left most column (default false) + * bool:right - fix the right most column (default false) + * int:zTop - fixed header zIndex + * int:zBottom - fixed footer zIndex + * int:zLeft - fixed left zIndex + * int:zRight - fixed right zIndex + */ +var FixedHeader = function ( mTable, oInit ) { + /* Sanity check - you just know it will happen */ + if ( typeof this.fnInit != 'function' ) + { + alert( "FixedHeader warning: FixedHeader must be initialised with the 'new' keyword." ); + return; + } + + var that = this; + var oSettings = { + "aoCache": [], + "oSides": { + "top": true, + "bottom": false, + "left": false, + "right": false + }, + "oZIndexes": { + "top": 104, + "bottom": 103, + "left": 102, + "right": 101 + }, + "oMes": { + "iTableWidth": 0, + "iTableHeight": 0, + "iTableLeft": 0, + "iTableRight": 0, /* note this is left+width, not actually "right" */ + "iTableTop": 0, + "iTableBottom": 0 /* note this is top+height, not actually "bottom" */ + }, + "oOffset": { + "top": 0 + }, + "nTable": null, + "bUseAbsPos": false, + "bFooter": false + }; + + /* + * Function: fnGetSettings + * Purpose: Get the settings for this object + * Returns: object: - settings object + * Inputs: - + */ + this.fnGetSettings = function () { + return oSettings; + }; + + /* + * Function: fnUpdate + * Purpose: Update the positioning and copies of the fixed elements + * Returns: - + * Inputs: - + */ + this.fnUpdate = function () { + this._fnUpdateClones(); + this._fnUpdatePositions(); + }; + + /* + * Function: fnPosition + * Purpose: Update the positioning of the fixed elements + * Returns: - + * Inputs: - + */ + this.fnPosition = function () { + this._fnUpdatePositions(); + }; + + /* Let's do it */ + this.fnInit( mTable, oInit ); + + /* Store the instance on the DataTables object for easy access */ + if ( typeof mTable.fnSettings == 'function' ) + { + mTable._oPluginFixedHeader = this; + } +}; + + +/* + * Variable: FixedHeader + * Purpose: Prototype for FixedHeader + * Scope: global + */ +FixedHeader.prototype = { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Initialisation + */ + + /* + * Function: fnInit + * Purpose: The "constructor" + * Returns: - + * Inputs: {as FixedHeader function} + */ + fnInit: function ( oTable, oInit ) + { + var s = this.fnGetSettings(); + var that = this; + + /* Record the user definable settings */ + this.fnInitSettings( s, oInit ); + + /* DataTables specific stuff */ + if ( typeof oTable.fnSettings == 'function' ) + { + if ( typeof oTable.fnVersionCheck == 'functon' && + oTable.fnVersionCheck( '1.6.0' ) !== true ) + { + alert( "FixedHeader 2 required DataTables 1.6.0 or later. "+ + "Please upgrade your DataTables installation" ); + return; + } + + var oDtSettings = oTable.fnSettings(); + + if ( oDtSettings.oScroll.sX != "" || oDtSettings.oScroll.sY != "" ) + { + alert( "FixedHeader 2 is not supported with DataTables' scrolling mode at this time" ); + return; + } + + s.nTable = oDtSettings.nTable; + oDtSettings.aoDrawCallback.push( { + "fn": function () { + FixedHeader.fnMeasure(); + that._fnUpdateClones.call(that); + that._fnUpdatePositions.call(that); + }, + "sName": "FixedHeader" + } ); + } + else + { + s.nTable = oTable; + } + + s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false; + + /* "Detect" browsers that don't support absolute positioing - or have bugs */ + s.bUseAbsPos = (jQuery.browser.msie && (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0")); + + /* Add the 'sides' that are fixed */ + if ( s.oSides.top ) + { + s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) ); + } + if ( s.oSides.bottom ) + { + s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) ); + } + if ( s.oSides.left ) + { + s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft ) ); + } + if ( s.oSides.right ) + { + s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight ) ); + } + + /* Event listeners for window movement */ + FixedHeader.afnScroll.push( function () { + that._fnUpdatePositions.call(that); + } ); + + jQuery(window).resize( function () { + FixedHeader.fnMeasure(); + that._fnUpdateClones.call(that); + that._fnUpdatePositions.call(that); + } ); + + /* Get things right to start with */ + FixedHeader.fnMeasure(); + that._fnUpdateClones(); + that._fnUpdatePositions(); + }, + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Support functions + */ + + /* + * Function: fnInitSettings + * Purpose: Take the user's settings and copy them to our local store + * Returns: - + * Inputs: object:s - the local settings object + * object:oInit - the user's settings object + */ + fnInitSettings: function ( s, oInit ) + { + if ( typeof oInit != 'undefined' ) + { + if ( typeof oInit.top != 'undefined' ) { + s.oSides.top = oInit.top; + } + if ( typeof oInit.bottom != 'undefined' ) { + s.oSides.bottom = oInit.bottom; + } + if ( typeof oInit.left != 'undefined' ) { + s.oSides.left = oInit.left; + } + if ( typeof oInit.right != 'undefined' ) { + s.oSides.right = oInit.right; + } + + if ( typeof oInit.zTop != 'undefined' ) { + s.oZIndexes.top = oInit.zTop; + } + if ( typeof oInit.zBottom != 'undefined' ) { + s.oZIndexes.bottom = oInit.zBottom; + } + if ( typeof oInit.zLeft != 'undefined' ) { + s.oZIndexes.left = oInit.zLeft; + } + if ( typeof oInit.zRight != 'undefined' ) { + s.oZIndexes.right = oInit.zRight; + } + + if ( typeof oInit.offsetTop != 'undefined' ) { + s.oOffset.top = oInit.offsetTop; + } + } + + /* Detect browsers which have poor position:fixed support so we can use absolute positions. + * This is much slower since the position must be updated for each scroll, but widens + * compatibility + */ + s.bUseAbsPos = (jQuery.browser.msie && + (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0")); + }, + + /* + * Function: _fnCloneTable + * Purpose: Clone the table node and do basic initialisation + * Returns: - + * Inputs: - + */ + _fnCloneTable: function ( sType, sClass, fnClone ) + { + var s = this.fnGetSettings(); + var nCTable; + + /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how + * DataTables works. Therefore, we can set this to be relatively position (if it is not + * alreadu absolute, and use this as the base point for the cloned header + */ + if ( jQuery(s.nTable.parentNode).css('position') != "absolute" ) + { + s.nTable.parentNode.style.position = "relative"; + } + + /* Just a shallow clone will do - we only want the table node */ + nCTable = s.nTable.cloneNode( false ); + nCTable.removeAttribute( 'id' ); + + var nDiv = document.createElement( 'div' ); + nDiv.style.position = "absolute"; + nDiv.style.top = "0px"; + nDiv.style.left = "0px"; + nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass; + + /* Set the zIndexes */ + if ( sType == "fixedHeader" ) + { + nDiv.style.zIndex = s.oZIndexes.top; + } + if ( sType == "fixedFooter" ) + { + nDiv.style.zIndex = s.oZIndexes.bottom; + } + if ( sType == "fixedLeft" ) + { + nDiv.style.zIndex = s.oZIndexes.left; + } + else if ( sType == "fixedRight" ) + { + nDiv.style.zIndex = s.oZIndexes.right; + } + + /* remove margins since we are going to poistion it absolutely */ + nCTable.style.margin = "0"; + + /* Insert the newly cloned table into the DOM, on top of the "real" header */ + nDiv.appendChild( nCTable ); + document.body.appendChild( nDiv ); + + return { + "nNode": nCTable, + "nWrapper": nDiv, + "sType": sType, + "sPosition": "", + "sTop": "", + "sLeft": "", + "fnClone": fnClone + }; + }, + + /* + * Function: _fnUpdatePositions + * Purpose: Get the current positioning of the table in the DOM + * Returns: - + * Inputs: - + */ + _fnMeasure: function () + { + var + s = this.fnGetSettings(), + m = s.oMes, + jqTable = jQuery(s.nTable), + oOffset = jqTable.offset(), + iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ), + iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' ); + + m.iTableWidth = jqTable.outerWidth(); + m.iTableHeight = jqTable.outerHeight(); + m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft; + m.iTableTop = oOffset.top + iParentScrollTop; + m.iTableRight = m.iTableLeft + m.iTableWidth; + m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth; + m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight; + }, + + /* + * Function: _fnSumScroll + * Purpose: Sum node parameters all the way to the top + * Returns: int: sum + * Inputs: node:n - node to consider + * string:side - scrollTop or scrollLeft + */ + _fnSumScroll: function ( n, side ) + { + var i = n[side]; + while ( n = n.parentNode ) + { + if ( n.nodeName == 'HTML' || n.nodeName == 'BODY' ) + { + break; + } + i = n[side]; + } + return i; + }, + + /* + * Function: _fnUpdatePositions + * Purpose: Loop over the fixed elements for this table and update their positions + * Returns: - + * Inputs: - + */ + _fnUpdatePositions: function () + { + var s = this.fnGetSettings(); + this._fnMeasure(); + + for ( var i=0, iLen=s.aoCache.length ; i oWin.iScrollTop + s.oOffset.top ) + { + /* Above the table */ + this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style ); + } + else if ( oWin.iScrollTop + s.oOffset.top > oMes.iTableTop+iTbodyHeight ) + { + /* At the bottom of the table */ + this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style ); + } + else + { + /* In the middle of the table */ + if ( s.bUseAbsPos ) + { + this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', oWin.iScrollTop+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style ); + } + else + { + this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', s.oOffset.top+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style ); + } + } + }, + + /* + * Function: _fnUpdateCache + * Purpose: Check the cache and update cache and value if needed + * Returns: - + * Inputs: object:oCache - local cache object + * string:sCache - cache property + * string:sSet - value to set + * string:sProperty - object property to set + * object:oObj - object to update + */ + _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj ) + { + if ( oCache[sCache] != sSet ) + { + oObj[sProperty] = sSet; + oCache[sCache] = sSet; + } + }, + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Cloning functions + */ + + /* + * Function: _fnCloneThead + * Purpose: Clone the thead element + * Returns: - + * Inputs: object:oCache - the cahced values for this fixed element + */ + _fnCloneThead: function ( oCache ) + { + var s = this.fnGetSettings(); + var nTable = oCache.nNode; + + /* Set the wrapper width to match that of the cloned table */ + oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px"; + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + jQuery('thead th', nTable).unbind( 'click' ); + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Clone the DataTables header */ + var nThead = jQuery('thead', s.nTable).clone(true)[0]; + nTable.appendChild( nThead ); + + /* Copy the widths across - apparently a clone isn't good enough for this */ + jQuery("thead>tr th", s.nTable).each( function (i) { + jQuery("thead>tr th:eq("+i+")", nTable).width( jQuery(this).width() ); + } ); + + jQuery("thead>tr td", s.nTable).each( function (i) { + jQuery("thead>tr td:eq("+i+")", nTable).width( jQuery(this).width() ); + } ); + }, + + /* + * Function: _fnCloneTfoot + * Purpose: Clone the tfoot element + * Returns: - + * Inputs: object:oCache - the cahced values for this fixed element + */ + _fnCloneTfoot: function ( oCache ) + { + var s = this.fnGetSettings(); + var nTable = oCache.nNode; + + /* Set the wrapper width to match that of the cloned table */ + oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px"; + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Clone the DataTables footer */ + var nTfoot = jQuery('tfoot', s.nTable).clone(true)[0]; + nTable.appendChild( nTfoot ); + + /* Copy the widths across - apparently a clone isn't good enough for this */ + jQuery("tfoot:eq(0)>tr th", s.nTable).each( function (i) { + jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( jQuery(this).width() ); + } ); + + jQuery("tfoot:eq(0)>tr td", s.nTable).each( function (i) { + jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable)[0].style.width( jQuery(this).width() ); + } ); + }, + + /* + * Function: _fnCloneTLeft + * Purpose: Clone the left column + * Returns: - + * Inputs: object:oCache - the cached values for this fixed element + */ + _fnCloneTLeft: function ( oCache ) + { + var s = this.fnGetSettings(); + var nTable = oCache.nNode; + var nBody = $('tbody', s.nTable)[0]; + var iCols = $('tbody tr:eq(0) td', s.nTable).length; + var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0")); + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Is this the most efficient way to do this - it looks horrible... */ + nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] ); + nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] ); + if ( s.bFooter ) + { + nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] ); + } + + /* Remove unneeded cells */ + $('thead tr', nTable).each( function (k) { + $('th:gt(0)', this).remove(); + } ); + + $('tfoot tr', nTable).each( function (k) { + $('th:gt(0)', this).remove(); + } ); + + $('tbody tr', nTable).each( function (k) { + $('td:gt(0)', this).remove(); + } ); + + this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable ); + + var iWidth = jQuery('thead tr th:eq(0)', s.nTable).outerWidth(); + nTable.style.width = iWidth+"px"; + oCache.nWrapper.style.width = iWidth+"px"; + }, + + /* + * Function: _fnCloneTRight + * Purpose: Clone the right most colun + * Returns: - + * Inputs: object:oCache - the cahced values for this fixed element + */ + _fnCloneTRight: function ( oCache ) + { + var s = this.fnGetSettings(); + var nBody = $('tbody', s.nTable)[0]; + var nTable = oCache.nNode; + var iCols = jQuery('tbody tr:eq(0) td', s.nTable).length; + var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0")); + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Is this the most efficient way to do this - it looks horrible... */ + nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] ); + nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] ); + if ( s.bFooter ) + { + nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] ); + } + jQuery('thead tr th:not(:nth-child('+iCols+'n))', nTable).remove(); + jQuery('tfoot tr th:not(:nth-child('+iCols+'n))', nTable).remove(); + + /* Remove unneeded cells */ + $('tbody tr', nTable).each( function (k) { + $('td:lt('+(iCols-1)+')', this).remove(); + } ); + + this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable ); + + var iWidth = jQuery('thead tr th:eq('+(iCols-1)+')', s.nTable).outerWidth(); + nTable.style.width = iWidth+"px"; + oCache.nWrapper.style.width = iWidth+"px"; + }, + + + /** + * Equalise the heights of the rows in a given table node in a cross browser way. Note that this + * is more or less lifted as is from FixedColumns + * @method fnEqualiseHeights + * @returns void + * @param {string} parent Node type - thead, tbody or tfoot + * @param {element} original Original node to take the heights from + * @param {element} clone Copy the heights to + * @private + */ + "fnEqualiseHeights": function ( parent, original, clone ) + { + var that = this, + jqBoxHack = $(parent+' tr:eq(0)', original).children(':eq(0)'), + iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(), + bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0")); + + /* Remove cells which are not needed and copy the height from the original table */ + $(parent+' tr', clone).each( function (k) { + /* Can we use some kind of object detection here?! This is very nasty - damn browsers */ + if ( $.browser.mozilla || $.browser.opera ) + { + $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() ); + } + else + { + $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() - iBoxHack ); + } + + if ( !bRubbishOldIE ) + { + $(parent+' tr:eq('+k+')', original).height( $(parent+' tr:eq('+k+')', original).outerHeight() ); + } + } ); + } +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Static properties and methods + * We use these for speed! This information is common to all instances of FixedHeader, so no + * point if having them calculated and stored for each different instance. + */ + +/* + * Variable: oWin + * Purpose: Store information about the window positioning + * Scope: FixedHeader + */ +FixedHeader.oWin = { + "iScrollTop": 0, + "iScrollRight": 0, + "iScrollBottom": 0, + "iScrollLeft": 0, + "iHeight": 0, + "iWidth": 0 +}; + +/* + * Variable: oDoc + * Purpose: Store information about the document size + * Scope: FixedHeader + */ +FixedHeader.oDoc = { + "iHeight": 0, + "iWidth": 0 +}; + +/* + * Variable: afnScroll + * Purpose: Array of functions that are to be used for the scrolling components + * Scope: FixedHeader + */ +FixedHeader.afnScroll = []; + +/* + * Function: fnMeasure + * Purpose: Update the measurements for the window and document + * Returns: - + * Inputs: - + */ +FixedHeader.fnMeasure = function () +{ + var + jqWin = jQuery(window), + jqDoc = jQuery(document), + oWin = FixedHeader.oWin, + oDoc = FixedHeader.oDoc; + + oDoc.iHeight = jqDoc.height(); + oDoc.iWidth = jqDoc.width(); + + oWin.iHeight = jqWin.height(); + oWin.iWidth = jqWin.width(); + oWin.iScrollTop = jqWin.scrollTop(); + oWin.iScrollLeft = jqWin.scrollLeft(); + oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth; + oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight; +}; + + +FixedHeader.VERSION = "2.0.6"; +FixedHeader.prototype.VERSION = FixedHeader.VERSION; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Global processing + */ + +/* + * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is + * done as an optimisation, to reduce calculation and proagation time + */ +jQuery(window).scroll( function () { + FixedHeader.fnMeasure(); + for ( var i=0, iLen=FixedHeader.afnScroll.length ; itfoot",b.nTable).length?!0:!1;b.bUseAbsPos=jQuery.browser.msie&&("6.0"==jQuery.browser.version||"7.0"==jQuery.browser.version);b.oSides.top&&b.aoCache.push(d._fnCloneTable("fixedHeader","FixedHeader_Header",d._fnCloneThead));b.oSides.bottom&&b.aoCache.push(d._fnCloneTable("fixedFooter","FixedHeader_Footer",d._fnCloneTfoot));b.oSides.left&&b.aoCache.push(d._fnCloneTable("fixedLeft","FixedHeader_Left", +d._fnCloneTLeft));b.oSides.right&&b.aoCache.push(d._fnCloneTable("fixedRight","FixedHeader_Right",d._fnCloneTRight));FixedHeader.afnScroll.push(function(){d._fnUpdatePositions.call(d)});jQuery(window).resize(function(){FixedHeader.fnMeasure();d._fnUpdateClones.call(d);d._fnUpdatePositions.call(d)});FixedHeader.fnMeasure();d._fnUpdateClones();d._fnUpdatePositions()},fnInitSettings:function(a,c){if("undefined"!=typeof c&&("undefined"!=typeof c.top&&(a.oSides.top=c.top),"undefined"!=typeof c.bottom&& +(a.oSides.bottom=c.bottom),"undefined"!=typeof c.left&&(a.oSides.left=c.left),"undefined"!=typeof c.right&&(a.oSides.right=c.right),"undefined"!=typeof c.zTop&&(a.oZIndexes.top=c.zTop),"undefined"!=typeof c.zBottom&&(a.oZIndexes.bottom=c.zBottom),"undefined"!=typeof c.zLeft&&(a.oZIndexes.left=c.zLeft),"undefined"!=typeof c.zRight&&(a.oZIndexes.right=c.zRight),"undefined"!=typeof c.offsetTop))a.oOffset.top=c.offsetTop;a.bUseAbsPos=jQuery.browser.msie&&("6.0"==jQuery.browser.version||"7.0"==jQuery.browser.version)}, +_fnCloneTable:function(a,c,b){var d=this.fnGetSettings(),e;"absolute"!=jQuery(d.nTable.parentNode).css("position")&&(d.nTable.parentNode.style.position="relative");e=d.nTable.cloneNode(!1);e.removeAttribute("id");var f=document.createElement("div");f.style.position="absolute";f.style.top="0px";f.style.left="0px";f.className+=" FixedHeader_Cloned "+a+" "+c;"fixedHeader"==a&&(f.style.zIndex=d.oZIndexes.top);"fixedFooter"==a&&(f.style.zIndex=d.oZIndexes.bottom);"fixedLeft"==a?f.style.zIndex=d.oZIndexes.left: +"fixedRight"==a&&(f.style.zIndex=d.oZIndexes.right);e.style.margin="0";f.appendChild(e);document.body.appendChild(f);return{nNode:e,nWrapper:f,sType:a,sPosition:"",sTop:"",sLeft:"",fnClone:b}},_fnMeasure:function(){var a=this.fnGetSettings(),c=a.oMes,b=jQuery(a.nTable),d=b.offset(),e=this._fnSumScroll(a.nTable.parentNode,"scrollTop");this._fnSumScroll(a.nTable.parentNode,"scrollLeft");c.iTableWidth=b.outerWidth();c.iTableHeight=b.outerHeight();c.iTableLeft=d.left+a.nTable.parentNode.scrollLeft;c.iTableTop= +d.top+e;c.iTableRight=c.iTableLeft+c.iTableWidth;c.iTableRight=FixedHeader.oDoc.iWidth-c.iTableLeft-c.iTableWidth;c.iTableBottom=FixedHeader.oDoc.iHeight-c.iTableTop-c.iTableHeight},_fnSumScroll:function(a,c){for(var b=a[c];(a=a.parentNode)&&!("HTML"==a.nodeName||"BODY"==a.nodeName);)b=a[c];return b},_fnUpdatePositions:function(){var a=this.fnGetSettings();this._fnMeasure();for(var c=0,b=a.aoCache.length;cd.iScrollTop+c.oOffset.top?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",e.style)):d.iScrollTop+c.oOffset.top>b.iTableTop+f?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+f+"px","top",e.style),this._fnUpdateCache(a, +"sLeft",b.iTableLeft+"px","left",e.style)):c.bUseAbsPos?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",d.iScrollTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",e.style)):(this._fnUpdateCache(a,"sPosition","fixed","position",e.style),this._fnUpdateCache(a,"sTop",c.oOffset.top+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft-d.iScrollLeft+"px","left",e.style))},_fnUpdateCache:function(a,c,b,d,e){a[c]!=b&&(e[d]= +b,a[c]=b)},_fnCloneThead:function(a){var c=this.fnGetSettings(),b=a.nNode;for(a.nWrapper.style.width=jQuery(c.nTable).outerWidth()+"px";0tr th",c.nTable).each(function(a){jQuery("thead>tr th:eq("+a+")",b).width(jQuery(this).width())});jQuery("thead>tr td",c.nTable).each(function(a){jQuery("thead>tr td:eq("+a+")",b).width(jQuery(this).width())})}, +_fnCloneTfoot:function(a){var c=this.fnGetSettings(),b=a.nNode;for(a.nWrapper.style.width=jQuery(c.nTable).outerWidth()+"px";0tr th",c.nTable).each(function(a){jQuery("tfoot:eq(0)>tr th:eq("+a+")",b).width(jQuery(this).width())});jQuery("tfoot:eq(0)>tr td",c.nTable).each(function(a){jQuery("tfoot:eq(0)>tr th:eq("+a+")",b)[0].style.width(jQuery(this).width())})},_fnCloneTLeft:function(a){var c= +this.fnGetSettings(),b=a.nNode,d=$("tbody",c.nTable)[0];for($("tbody tr:eq(0) td",c.nTable);0 */function($, window, document, undefined) { /** @@ -76,7 +76,7 @@ "nTh": nTh ? nTh : document.createElement('th'), "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], - "mDataProp": oDefaults.mDataProp ? oDefaults.oDefaults : iCol + "mData": oDefaults.mData ? oDefaults.oDefaults : iCol } ); oSettings.aoColumns.push( oCol ); @@ -115,7 +115,7 @@ * Apply options for a column * @param {object} oSettings dataTables settings object * @param {int} iCol column index to consider - * @param {object} oOptions object with sType, bVisible and bSearchable + * @param {object} oOptions object with sType, bVisible and bSearchable etc * @memberof DataTable#oApi */ function _fnColumnOptions( oSettings, iCol, oOptions ) @@ -125,6 +125,12 @@ /* User specified column options */ if ( oOptions !== undefined && oOptions !== null ) { + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + if ( oOptions.sType !== undefined ) { oCol.sType = oOptions.sType; @@ -145,8 +151,19 @@ } /* Cache the data get and set functions for speed */ - oCol.fnGetData = _fnGetObjectDataFn( oCol.mDataProp ); - oCol.fnSetData = _fnSetObjectDataFn( oCol.mDataProp ); + var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; + var mData = _fnGetObjectDataFn( oCol.mData ); + + oCol.fnGetData = function (oData, sSpecific) { + var innerData = mData( oData, sSpecific ); + + if ( oCol.mRender && (sSpecific && sSpecific !== '') ) + { + return mRender( innerData, sSpecific, oData ); + } + return innerData; + }; + oCol.fnSetData = _fnSetObjectDataFn( oCol.mData ); /* Feature sorting overrides column specific when off */ if ( !oSettings.oFeatures.bSort ) @@ -188,7 +205,7 @@ */ function _fnAdjustColumnSizing ( oSettings ) { - /* Not interested in doing column width calculation if autowidth is disabled */ + /* Not interested in doing column width calculation if auto-width is disabled */ if ( oSettings.oFeatures.bAutoWidth === false ) { return false; @@ -212,22 +229,11 @@ */ function _fnVisibleToColumnIndex( oSettings, iMatch ) { - var iColumn = -1; - - for ( var i=0 ; i tag - remove it */ - sSearch = sSearch.replace(/\n/g," ").replace(/\r/g,""); + sSearch = $('
').html(sSearch).text(); } - return sSearch; + // Strip newline characters + return sSearch.replace( /[\n\r]/g, " " ); } /** @@ -2259,7 +2363,7 @@ * @param {string} sSearch string to search for * @param {bool} bRegex treat as a regular expression or not * @param {bool} bSmart perform smart filtering or not - * @param {bool} bCaseInsensitive Do case insenstive matching or not + * @param {bool} bCaseInsensitive Do case insensitive matching or not * @returns {RegExp} constructed object * @memberof DataTable#oApi */ @@ -2297,6 +2401,10 @@ { return DataTable.ext.ofnSearch[sType]( sData ); } + else if ( sData === null ) + { + return ''; + } else if ( sType == "html" ) { return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" ); @@ -2305,23 +2413,19 @@ { return sData.replace(/[\r\n]/g," "); } - else if ( sData === null ) - { - return ''; - } return sData; } /** - * scape a string stuch that it can be used in a regular expression + * scape a string such that it can be used in a regular expression * @param {string} sVal string to escape * @returns {string} escaped string * @memberof DataTable#oApi */ function _fnEscapeRegex ( sVal ) { - var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ]; + var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ]; var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); return sVal.replace(reReplace, '\\$1'); } @@ -2371,57 +2475,41 @@ } var - iStart = oSettings._iDisplayStart+1, iEnd = oSettings.fnDisplayEnd(), - iMax = oSettings.fnRecordsTotal(), iTotal = oSettings.fnRecordsDisplay(), - sStart = oSettings.fnFormatNumber( iStart ), sEnd = oSettings.fnFormatNumber( iEnd ), - sMax = oSettings.fnFormatNumber( iMax ), sTotal = oSettings.fnFormatNumber( iTotal ), + oLang = oSettings.oLanguage, + iStart = oSettings._iDisplayStart+1, + iEnd = oSettings.fnDisplayEnd(), + iMax = oSettings.fnRecordsTotal(), + iTotal = oSettings.fnRecordsDisplay(), sOut; - /* When infinite scrolling, we are always starting at 1. _iDisplayStart is used only - * internally - */ - if ( oSettings.oScroll.bInfinite ) - { - sStart = oSettings.fnFormatNumber( 1 ); - } - - if ( oSettings.fnRecordsDisplay() === 0 && - oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) + if ( iTotal === 0 && iTotal == iMax ) { /* Empty record set */ - sOut = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix; + sOut = oLang.sInfoEmpty; } - else if ( oSettings.fnRecordsDisplay() === 0 ) + else if ( iTotal === 0 ) { - /* Rmpty record set after filtering */ - sOut = oSettings.oLanguage.sInfoEmpty +' '+ - oSettings.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+ - oSettings.oLanguage.sInfoPostFix; + /* Empty record set after filtering */ + sOut = oLang.sInfoEmpty +' '+ oLang.sInfoFiltered; } - else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) + else if ( iTotal == iMax ) { /* Normal record set */ - sOut = oSettings.oLanguage.sInfo. - replace('_START_', sStart). - replace('_END_', sEnd). - replace('_TOTAL_', sTotal)+ - oSettings.oLanguage.sInfoPostFix; + sOut = oLang.sInfo; } else { /* Record set after filtering */ - sOut = oSettings.oLanguage.sInfo. - replace('_START_', sStart). - replace('_END_', sEnd). - replace('_TOTAL_', sTotal) +' '+ - oSettings.oLanguage.sInfoFiltered.replace('_MAX_', - oSettings.fnFormatNumber(oSettings.fnRecordsTotal()))+ - oSettings.oLanguage.sInfoPostFix; + sOut = oLang.sInfo +' '+ oLang.sInfoFiltered; } + + // Convert the macros + sOut += oLang.sInfoPostFix; + sOut = _fnInfoMacros( oSettings, sOut ); - if ( oSettings.oLanguage.fnInfoCallback !== null ) + if ( oLang.fnInfoCallback !== null ) { - sOut = oSettings.oLanguage.fnInfoCallback.call( oSettings.oInstance, + sOut = oLang.fnInfoCallback.call( oSettings.oInstance, oSettings, iStart, iEnd, iMax, iTotal, sOut ); } @@ -2433,6 +2521,33 @@ } + function _fnInfoMacros ( oSettings, str ) + { + var + iStart = oSettings._iDisplayStart+1, + sStart = oSettings.fnFormatNumber( iStart ), + iEnd = oSettings.fnDisplayEnd(), + sEnd = oSettings.fnFormatNumber( iEnd ), + iTotal = oSettings.fnRecordsDisplay(), + sTotal = oSettings.fnFormatNumber( iTotal ), + iMax = oSettings.fnRecordsTotal(), + sMax = oSettings.fnFormatNumber( iMax ); + + // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only + // internally + if ( oSettings.oScroll.bInfinite ) + { + sStart = oSettings.fnFormatNumber( 1 ); + } + + return str. + replace('_START_', sStart). + replace('_END_', sEnd). + replace('_TOTAL_', sTotal). + replace('_MAX_', sMax); + } + + /** * Draw the table for the first time, adding all required features @@ -2566,16 +2681,20 @@ */ function _fnLanguageCompat( oLanguage ) { + var oDefaults = DataTable.defaults.oLanguage; + /* Backwards compatibility - if there is no sEmptyTable given, then use the same as * sZeroRecords - assuming that is given. */ - if ( !oLanguage.sEmptyTable && oLanguage.sZeroRecords ) + if ( !oLanguage.sEmptyTable && oLanguage.sZeroRecords && + oDefaults.sEmptyTable === "No data available in table" ) { _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' ); } /* Likewise with loading records */ - if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords ) + if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords && + oDefaults.sLoadingRecords === "Loading..." ) { _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sLoadingRecords' ); } @@ -2676,7 +2795,7 @@ /** - * Rcalculate the end point based on the start point + * Recalculate the end point based on the start point * @param {object} oSettings dataTables settings object * @memberof DataTable#oApi */ @@ -2780,7 +2899,7 @@ oSettings._iDisplayStart - oSettings._iDisplayLength : 0; - /* Correct for underrun */ + /* Correct for under-run */ if ( oSettings._iDisplayStart < 0 ) { oSettings._iDisplayStart = 0; @@ -2944,7 +3063,8 @@ nScrollHead.style.border = "0"; nScrollHead.style.width = "100%"; nScrollFoot.style.border = "0"; - nScrollHeadInner.style.width = "150%"; /* will be overwritten */ + nScrollHeadInner.style.width = oSettings.oScroll.sXInner !== "" ? + oSettings.oScroll.sXInner : "100%"; /* will be overwritten */ /* Modify attributes to respect the clones */ nScrollHeadTable.removeAttribute('id'); @@ -2956,17 +3076,26 @@ nScrollFootTable.style.marginLeft = "0"; } - /* Move any caption elements from the body to the header */ - var nCaptions = $(oSettings.nTable).children('caption'); - for ( var i=0, iLen=nCaptions.length ; i 0 ) { - nScrollHeadTable.appendChild( nCaptions[i] ); + nCaption = nCaption[0]; + if ( nCaption._captionSide === "top" ) + { + nScrollHeadTable.appendChild( nCaption ); + } + else if ( nCaption._captionSide === "bottom" && nTfoot ) + { + nScrollFootTable.appendChild( nCaption ); + } } /* * Sizing */ - /* When xscrolling add the width and a scroller to move the header with the body */ + /* When x-scrolling add the width and a scroller to move the header with the body */ if ( oSettings.oScroll.sX !== "" ) { nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX ); @@ -3048,40 +3177,26 @@ nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0], nScrollBody = o.nTable.parentNode, i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis, + nTheadSize, nTfootSize, iWidth, aApplied=[], iSanityWidth, nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null, nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null, - ie67 = $.browser.msie && $.browser.version <= 7; + ie67 = o.oBrowser.bScrollOversize; /* * 1. Re-create the table inside the scrolling div */ /* Remove the old minimised thead and tfoot elements in the inner table */ - var nTheadSize = o.nTable.getElementsByTagName('thead'); - if ( nTheadSize.length > 0 ) - { - o.nTable.removeChild( nTheadSize[0] ); - } - - var nTfootSize; - if ( o.nTFoot !== null ) - { - /* Remove the old minimised footer element in the cloned header */ - nTfootSize = o.nTable.getElementsByTagName('tfoot'); - if ( nTfootSize.length > 0 ) - { - o.nTable.removeChild( nTfootSize[0] ); - } - } - + $(o.nTable).children('thead, tfoot').remove(); + /* Clone the current header and footer elements and then place it into the inner table */ - nTheadSize = o.nTHead.cloneNode(true); + nTheadSize = $(o.nTHead).clone()[0]; o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] ); if ( o.nTFoot !== null ) { - nTfootSize = o.nTFoot.cloneNode(true); + nTfootSize = $(o.nTFoot).clone()[0]; o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] ); } @@ -3091,7 +3206,7 @@ /* Remove old sizing and apply the calculated column widths * Get the unique column headers in the newly created (cloned) header. We want to apply the - * calclated sizes to this header + * calculated sizes to this header */ if ( o.oScroll.sX === "" ) { @@ -3112,6 +3227,14 @@ n.style.width = ""; }, nTfootSize.getElementsByTagName('tr') ); } + + // If scroll collapse is enabled, when we put the headers back into the body for sizing, we + // will end up forcing the scrollbar to appear, making our measurements wrong for when we + // then hide it (end of this function), so add the header height to the body scroller. + if ( o.oScroll.bCollapse && o.oScroll.sY !== "" ) + { + nScrollBody.style.height = (nScrollBody.offsetHeight + o.nTHead.offsetHeight)+"px"; + } /* Size the table as a whole */ iSanityWidth = $(o.nTable).outerWidth(); @@ -3127,7 +3250,7 @@ if ( ie67 && ($('tbody', nScrollBody).height() > nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll") ) { - o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth()-o.oScroll.iBarWidth ); + o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth() - o.oScroll.iBarWidth); } } else @@ -3296,7 +3419,7 @@ o.oScroll.iBarWidth : 0; if ( o.nTable.offsetHeight < nScrollBody.offsetHeight ) { - nScrollBody.style.height = _fnStringToCss( $(o.nTable).height()+iExtra ); + nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+iExtra ); } } @@ -3304,12 +3427,21 @@ var iOuterWidth = $(o.nTable).outerWidth(); nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth ); nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth ); + + // Figure out if there are scrollbar present - if so then we need a the header and footer to + // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) + var bScrolling = $(o.nTable).height() > nScrollBody.clientHeight || $(nScrollBody).css('overflow-y') == "scroll"; + nScrollHeadInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px"; if ( o.nTFoot !== null ) { - nScrollFootInner.style.width = _fnStringToCss( o.nTable.offsetWidth ); - nScrollFootTable.style.width = _fnStringToCss( o.nTable.offsetWidth ); + nScrollFootTable.style.width = _fnStringToCss( iOuterWidth ); + nScrollFootInner.style.width = _fnStringToCss( iOuterWidth ); + nScrollFootInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px"; } + + /* Adjust the position of the header in case we loose the y-scrollbar */ + $(nScrollBody).scroll(); /* If sorting or filtering has occurred, jump the scrolling back to the top */ if ( o.bSorted || o.bFiltered ) @@ -3634,7 +3766,7 @@ * Get the widest node * @param {object} oSettings dataTables settings object * @param {int} iCol column of interest - * @returns {string} max strlens for each column + * @returns {string} max string length for each column * @memberof DataTable#oApi */ function _fnGetWidestNode( oSettings, iCol ) @@ -3659,7 +3791,7 @@ * Get the maximum strlen for each data column * @param {object} oSettings dataTables settings object * @param {int} iCol column of interest - * @returns {string} max strlens for each column + * @returns {string} max string length for each column * @memberof DataTable#oApi */ function _fnGetMaxLenString( oSettings, iCol ) @@ -3777,16 +3909,11 @@ if ( !oSettings.oFeatures.bServerSide && (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) ) { - if ( oSettings.aaSortingFixed !== null ) - { - aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); - } - else - { - aaSort = oSettings.aaSorting.slice(); - } + aaSort = ( oSettings.aaSortingFixed !== null ) ? + oSettings.aaSortingFixed.concat( oSettings.aaSorting ) : + oSettings.aaSorting.slice(); - /* If there is a sorting data type, and a fuction belonging to it, then we need to + /* If there is a sorting data type, and a function belonging to it, then we need to * get the data from the developer's function and apply it for this column */ for ( i=0 ; i/g, "" ); nTh = aoColumns[i].nTh; nTh.removeAttribute('aria-sort'); nTh.removeAttribute('aria-label'); @@ -3900,18 +4037,18 @@ var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0]; - nTh.setAttribute('aria-label', aoColumns[i].sTitle+ + nTh.setAttribute('aria-label', sTitle+ (nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) ); } else { - nTh.setAttribute('aria-label', aoColumns[i].sTitle+ + nTh.setAttribute('aria-label', sTitle+ (aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) ); } } else { - nTh.setAttribute('aria-label', aoColumns[i].sTitle); + nTh.setAttribute('aria-label', sTitle); } } @@ -3958,17 +4095,17 @@ * twice - once for when bProcessing is enabled, and another time for when it is * disabled, as we need to perform slightly different actions. * Basically the issue here is that the Javascript engine in modern browsers don't - * appear to allow the rendering engine to update the display while it is still excuting + * appear to allow the rendering engine to update the display while it is still executing * it's thread (well - it does but only after long intervals). This means that the * 'processing' display doesn't appear for a table sort. To break the js thread up a bit * I force an execution break by using setTimeout - but this breaks the expected * thread continuation for the end-developer's point of view (their code would execute - * too early), so we on;y do it when we absolutely have to. + * too early), so we only do it when we absolutely have to. */ var fnInnerSorting = function () { var iColumn, iNextSort; - /* If the shift key is pressed then we are multipe column sorting */ + /* If the shift key is pressed then we are multiple column sorting */ if ( e.shiftKey ) { /* Are we already doing some kind of sort on this column? */ @@ -4141,10 +4278,10 @@ * Apply the required classes to the table body * Note that this is given as a feature switch since it can significantly slow down a sort * on large data sets (adding and removing of classes is always slow at the best of times..) - * Further to this, note that this code is admitadly fairly ugly. It could be made a lot - * simpiler using jQuery selectors and add/removeClass, but that is significantly slower + * Further to this, note that this code is admittedly fairly ugly. It could be made a lot + * simpler using jQuery selectors and add/removeClass, but that is significantly slower * (on the order of 5 times slower) - hence the direct DOM manipulation here. - * Note that for defered drawing we do use jQuery - the reason being that taking the first + * Note that for deferred drawing we do use jQuery - the reason being that taking the first * row found to see if the whole column needs processed can miss classes since the first * column might be new. */ @@ -4290,7 +4427,7 @@ $.extend( true, oSettings.aoPreSearchCols, oData.aoSearchCols ); /* Column visibility state - * Pass back visibiliy settings to the init handler, but to do not here override + * Pass back visibility settings to the init handler, but to do not here override * the init object that the user might have passed in */ oInit.saved_aoColumns = []; @@ -4532,11 +4669,11 @@ } else { - throw sAlert; + throw new Error(sAlert); } return; } - else if ( console !== undefined && console.log ) + else if ( window.console && console.log ) { console.log( sAlert ); } @@ -4577,17 +4714,21 @@ */ function _fnExtend( oOut, oExtender ) { - for ( var prop in oOut ) + var val; + + for ( var prop in oExtender ) { - if ( oOut.hasOwnProperty(prop) && oExtender[prop] !== undefined ) + if ( oExtender.hasOwnProperty(prop) ) { - if ( typeof oInit[prop] === 'object' && $.isArray(oExtender[prop]) === false ) + val = oExtender[prop]; + + if ( typeof oInit[prop] === 'object' && val !== null && $.isArray(val) === false ) { - $.extend( true, oOut[prop], oExtender[prop] ); + $.extend( true, oOut[prop], val ); } else { - oOut[prop] = oExtender[prop]; + oOut[prop] = val; } } } @@ -4598,19 +4739,19 @@ /** * Bind an event handers to allow a click or return key to activate the callback. - * This is good for accessability since a return on the keyboard will have the + * This is good for accessibility since a return on the keyboard will have the * same effect as a click, if the element has focus. * @param {element} n Element to bind the action to * @param {object} oData Data object to pass to the triggered function - * @param {function) fn Callback function for when the event is triggered + * @param {function} fn Callback function for when the event is triggered * @memberof DataTable#oApi */ function _fnBindAction( n, oData, fn ) { $(n) .bind( 'click.DT', oData, function (e) { - fn(e); n.blur(); // Remove focus outline for mouse users + fn(e); } ) .bind( 'keypress.DT', oData, function (e){ if ( e.which === 13 ) { @@ -4627,9 +4768,9 @@ * Register a callback function. Easily allows a callback function to be added to * an array store of callback functions that can then all be called together. * @param {object} oSettings dataTables settings object - * @param {string} sStore Name of the array storeage for the callbacks in oSettings + * @param {string} sStore Name of the array storage for the callbacks in oSettings * @param {function} fn Function to be called back - * @param {string) sName Identifying name for the callback (i.e. a label) + * @param {string} sName Identifying name for the callback (i.e. a label) * @memberof DataTable#oApi */ function _fnCallbackReg( oSettings, sStore, fn, sName ) @@ -4649,10 +4790,10 @@ * array store is done backwards! Further note that you do not want to fire off triggers * in time sensitive applications (for example cell creation) as its slow. * @param {object} oSettings dataTables settings object - * @param {string} sStore Name of the array storeage for the callbacks in oSettings + * @param {string} sStore Name of the array storage for the callbacks in oSettings * @param {string} sTrigger Name of the jQuery custom event to trigger. If null no trigger * is fired - * @param {array) aArgs Array of arguments to pass to the callback function / trigger + * @param {array} aArgs Array of arguments to pass to the callback function / trigger * @memberof DataTable#oApi */ function _fnCallbackFire( oSettings, sStore, sTrigger, aArgs ) @@ -4677,7 +4818,7 @@ /** * JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other * library, then we use that as it is fast, safe and accurate. If the function isn't - * available then we need to built it ourselves - the insperation for this function comes + * available then we need to built it ourselves - the inspiration for this function comes * from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is * not perfect and absolutely should not be used as a replacement to json2.js - but it does * do what we need, without requiring a dependency for DataTables. @@ -4725,6 +4866,31 @@ return (bArr ? "[" : "{") + json + (bArr ? "]" : "}"); }; + + /** + * From some browsers (specifically IE6/7) we need special handling to work around browser + * bugs - this function is used to detect when these workarounds are needed. + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnBrowserDetect( oSettings ) + { + /* IE6/7 will oversize a width 100% element inside a scrolling element, to include the + * width of the scrollbar, while other browsers ensure the inner element is contained + * without forcing scrolling + */ + var n = $( + '
'+ + '
'+ + '
'+ + '
'+ + '
')[0]; + + document.body.appendChild( n ); + oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false; + document.body.removeChild( n ); + } + @@ -4765,8 +4931,11 @@ */ this.$ = function ( sSelector, oOpts ) { - var i, iLen, a = []; + var i, iLen, a = [], tr; var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ); + var aoData = oSettings.aoData; + var aiDisplay = oSettings.aiDisplay; + var aiDisplayMaster = oSettings.aiDisplayMaster; if ( !oOpts ) { @@ -4785,37 +4954,54 @@ { for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i *
  • 1D array of data - add a single row with the data provided
  • *
  • 2D array of arrays - add multiple rows in a single call
  • - *
  • object - data object when using mDataProp
  • - *
  • array of objects - multiple data objects when using mDataProp
  • + *
  • object - data object when using mData
  • + *
  • array of objects - multiple data objects when using mData
  • * * @param {bool} [bRedraw=true] redraw the table or not * @returns {array} An array of integers, representing the list of indexes in @@ -5142,8 +5328,8 @@ fnCallBack.call( this, oSettings, oData ); } - /* Check for an 'overflow' they case for dislaying the table */ - if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length ) + /* Check for an 'overflow' they case for displaying the table */ + if ( oSettings._iDisplayStart >= oSettings.fnRecordsDisplay() ) { oSettings._iDisplayStart -= oSettings._iDisplayLength; if ( oSettings._iDisplayStart < 0 ) @@ -5187,10 +5373,8 @@ /* Flag to note that the table is currently being destroyed - no action should be taken */ oSettings.bDestroying = true; - /* Restore hidden columns */ - for ( i=0, iLen=oSettings.aoDestroyCallback.length ; i
    ")[0];a.nTable.parentNode.insertBefore(b,a.nTable);a.nTableWrapper=i('
    ')[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var c=a.nTableWrapper,d=a.sDom.split(""),h,f,g,e,s,m,o,k=0;k")[0];s=d[k+ +1];if("'"==s||'"'==s){m="";for(o=2;d[k+o]!=s;)m+=d[k+o],o++;"H"==m?m=a.oClasses.sJUIHeader:"F"==m&&(m=a.oClasses.sJUIFooter);-1!=m.indexOf(".")?(s=m.split("."),e.id=s[0].substr(1,s[0].length-1),e.className=s[1]):"#"==m.charAt(0)?e.id=m.substr(1,m.length-1):e.className=m;k+=o}c.appendChild(e);c=e}else if(">"==g)c=c.parentNode;else if("l"==g&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange)h=za(a),f=1;else if("f"==g&&a.oFeatures.bFilter)h=Aa(a),f=1;else if("r"==g&&a.oFeatures.bProcessing)h=Ba(a),f= +1;else if("t"==g)h=Ca(a),f=1;else if("i"==g&&a.oFeatures.bInfo)h=Da(a),f=1;else if("p"==g&&a.oFeatures.bPaginate)h=Ea(a),f=1;else if(0!==j.ext.aoFeatures.length){e=j.ext.aoFeatures;o=0;for(s=e.length;o'):""===c?'':c+' ',d=l.createElement("div");d.className=a.oClasses.sFilter;d.innerHTML="";a.aanFeatures.f||(d.id=a.sTableId+"_filter");c=i('input[type="text"]',d);d._DT_Input=c[0];c.val(b.sSearch.replace('"',"""));c.bind("keyup.DT",function(){for(var c=a.aanFeatures.f,d=this.value===""?"":this.value, +g=0,e=c.length;g=b.length)a.aiDisplay.splice(0,a.aiDisplay.length),a.aiDisplay=a.aiDisplayMaster.slice();else if(a.aiDisplay.length==a.aiDisplayMaster.length||h.sSearch.length>b.length||1==c||0!==b.indexOf(h.sSearch)){a.aiDisplay.splice(0, +a.aiDisplay.length);ka(a,1);for(b=0;b").html(c).text()); +return c.replace(/[\n\r]/g," ")}function la(a,b,c,d){if(c)return a=b?a.split(" "):na(a).split(" "),a="^(?=.*?"+a.join(")(?=.*?")+").*$",RegExp(a,d?"i":"");a=b?a:na(a);return RegExp(a,d?"i":"")}function Ka(a,b){return"function"===typeof j.ext.ofnSearch[b]?j.ext.ofnSearch[b](a):null===a?"":"html"==b?a.replace(/[\r\n]/g," ").replace(/<.*?>/g,""):"string"===typeof a?a.replace(/[\r\n]/g," "):a}function na(a){return a.replace(RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"), +"\\$1")}function Da(a){var b=l.createElement("div");b.className=a.oClasses.sInfo;a.aanFeatures.i||(a.aoDrawCallback.push({fn:La,sName:"information"}),b.id=a.sTableId+"_info");a.nTable.setAttribute("aria-describedby",a.sTableId+"_info");return b}function La(a){if(a.oFeatures.bInfo&&0!==a.aanFeatures.i.length){var b=a.oLanguage,c=a._iDisplayStart+1,d=a.fnDisplayEnd(),h=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),g;g=0===f&&f==h?b.sInfoEmpty:0===f?b.sInfoEmpty+" "+b.sInfoFiltered:f==h?b.sInfo:b.sInfo+ +" "+b.sInfoFiltered;g+=b.sInfoPostFix;g=ia(a,g);null!==b.fnInfoCallback&&(g=b.fnInfoCallback.call(a.oInstance,a,c,d,h,f,g));a=a.aanFeatures.i;b=0;for(c=a.length;b",c,d,h=a.aLengthMenu;if(2==h.length&&"object"===typeof h[0]&&"object"===typeof h[1]){c=0;for(d=h[0].length;c'+h[1][c]+""}else{c=0;for(d=h.length;c'+h[c]+""}b+= +"";h=l.createElement("div");a.aanFeatures.l||(h.id=a.sTableId+"_length");h.className=a.oClasses.sLength;h.innerHTML="";i('select option[value="'+a._iDisplayLength+'"]',h).attr("selected",!0);i("select",h).bind("change.DT",function(){var b=i(this).val(),h=a.aanFeatures.l;c=0;for(d=h.length;ca.aiDisplay.length||-1==a._iDisplayLength?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Ea(a){if(a.oScroll.bInfinite)return null;var b=l.createElement("div");b.className=a.oClasses.sPaging+a.sPaginationType; +j.ext.oPagination[a.sPaginationType].fnInit(a,b,function(a){A(a);z(a)});a.aanFeatures.p||a.aoDrawCallback.push({fn:function(a){j.ext.oPagination[a.sPaginationType].fnUpdate(a,function(a){A(a);z(a)})},sName:"pagination"});return b}function pa(a,b){var c=a._iDisplayStart;if("number"===typeof b)a._iDisplayStart=b*a._iDisplayLength,a._iDisplayStart>a.fnRecordsDisplay()&&(a._iDisplayStart=0);else if("first"==b)a._iDisplayStart=0;else if("previous"==b)a._iDisplayStart=0<=a._iDisplayLength?a._iDisplayStart- +a._iDisplayLength:0,0>a._iDisplayStart&&(a._iDisplayStart=0);else if("next"==b)0<=a._iDisplayLength?a._iDisplayStart+a._iDisplayLengthi(a.nTable).height()-a.oScroll.iLoadGap&&a.fnDisplayEnd()d.offsetHeight||"scroll"==i(d).css("overflow-y")))a.nTable.style.width=q(i(a.nTable).outerWidth()-a.oScroll.iBarWidth)}else""!==a.oScroll.sXInner?a.nTable.style.width=q(a.oScroll.sXInner):h==i(d).width()&&i(d).height()h-a.oScroll.iBarWidth&& +(a.nTable.style.width=q(h))):a.nTable.style.width=q(h);h=i(a.nTable).outerWidth();f=a.nTHead.getElementsByTagName("tr");g=g.getElementsByTagName("tr");N(function(a,b){m=a.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;k=i(a).width();b.style.width=q(k);r.push(k)},g,f);i(g).height(0);null!==a.nTFoot&&(e=j.getElementsByTagName("tr"),j=a.nTFoot.getElementsByTagName("tr"),N(function(a,b){m=a.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth= +"0";m.borderBottomWidth="0";m.height=0;k=i(a).width();b.style.width=q(k);r.push(k)},e,j),i(e).height(0));N(function(a){a.innerHTML="";a.style.width=q(r.shift())},g);null!==a.nTFoot&&N(function(a){a.innerHTML="";a.style.width=q(r.shift())},e);if(i(a.nTable).outerWidth()d.offsetHeight||"scroll"==i(d).css("overflow-y")?h+a.oScroll.iBarWidth:h;if(l&&(d.scrollHeight>d.offsetHeight||"scroll"==i(d).css("overflow-y")))a.nTable.style.width=q(e-a.oScroll.iBarWidth);d.style.width=q(e);b.parentNode.style.width= +q(e);null!==a.nTFoot&&(n.parentNode.style.width=q(e));""===a.oScroll.sX?E(a,1,"The table cannot fit into the current element which will cause column misalignment. The table has been drawn at its minimum possible width."):""!==a.oScroll.sXInner&&E(a,1,"The table cannot fit into the current element which will cause column misalignment. Increase the sScrollXInner value or remove it to allow automatic calculation")}else d.style.width=q("100%"),b.parentNode.style.width=q("100%"),null!==a.nTFoot&&(n.parentNode.style.width= +q("100%"));""===a.oScroll.sY&&l&&(d.style.height=q(a.nTable.offsetHeight+a.oScroll.iBarWidth));""!==a.oScroll.sY&&a.oScroll.bCollapse&&(d.style.height=q(a.oScroll.sY),l=""!==a.oScroll.sX&&a.nTable.offsetWidth>d.offsetWidth?a.oScroll.iBarWidth:0,a.nTable.offsetHeightd.clientHeight||"scroll"==i(d).css("overflow-y");b.style.paddingRight=c?a.oScroll.iBarWidth+ +"px":"0px";null!==a.nTFoot&&(p.style.width=q(l),n.style.width=q(l),n.style.paddingRight=c?a.oScroll.iBarWidth+"px":"0px");i(d).scroll();if(a.bSorted||a.bFiltered)d.scrollTop=0}function N(a,b,c){for(var d=0,h=b.length;dtd",b));g=P(a,f);for(f=d=0;fc)return null;if(null===a.aoData[c].nTr){var d=l.createElement("td");d.innerHTML=x(a,c,b,"");return d}return L(a,c)[b]}function Qa(a,b){for(var c= +-1,d=-1,h=0;h/g,"");f.length>c&&(c=f.length,d=h)}return d}function q(a){if(null===a)return"0px";if("number"==typeof a)return 0>a?"0px":a+"px";var b=a.charCodeAt(a.length-1);return 48>b||57/g,""),h=l[c].nTh,h.removeAttribute("aria-sort"),h.removeAttribute("aria-label"),l[c].bSortable?0=e)for(b=0;bj&&j++}}}function qa(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b,c;b=a.oScroll.bInfinite;var d={iCreate:(new Date).getTime(),iStart:b?0:a._iDisplayStart, +iEnd:b?a._iDisplayLength:a._iDisplayEnd,iLength:a._iDisplayLength,aaSorting:i.extend(!0,[],a.aaSorting),oSearch:i.extend(!0,{},a.oPreviousSearch),aoSearchCols:i.extend(!0,[],a.aoPreSearchCols),abVisCols:[]};b=0;for(c=a.aoColumns.length;b
    ')[0];l.body.appendChild(b);a.oBrowser.bScrollOversize=100===i("#DT_BrowserTest", +b)[0].offsetWidth?!0:!1;l.body.removeChild(b)}function Xa(a){return function(){var b=[u(this[j.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return j.ext.oApi[a].apply(this,b)}}var V=/\[.*?\]$/,Ya=O.JSON?JSON.stringify:function(a){var b=typeof a;if("object"!==b||null===a)return"string"===b&&(a='"'+a+'"'),a+"";var c,d,h=[],e=i.isArray(a);for(c in a)d=a[c],b=typeof d,"string"===b?d='"'+d+'"':"object"===b&&null!==d&&(d=Ya(d)),h.push((e?"":'"'+c+'":')+d);return(e?"[":"{")+h+(e?"]":"}")}; +this.$=function(a,b){var c,d,h=[],e;d=u(this[j.ext.iApiIndex]);var g=d.aoData,o=d.aiDisplay,k=d.aiDisplayMaster;b||(b={});b=i.extend({},{filter:"none",order:"current",page:"all"},b);if("current"==b.page){c=d._iDisplayStart;for(d=d.fnDisplayEnd();c=d.fnRecordsDisplay()&&(d._iDisplayStart-=d._iDisplayLength,0>d._iDisplayStart&&(d._iDisplayStart=0));if(c===n||c)A(d),z(d);return g};this.fnDestroy=function(a){var b=u(this[j.ext.iApiIndex]),c=b.nTableWrapper.parentNode,d=b.nTBody,e,f,a=a===n?!1:!0;b.bDestroying=!0;C(b,"aoDestroyCallback","destroy",[b]);e=0;for(f=b.aoColumns.length;etr>td."+b.oClasses.sRowEmpty,b.nTable).parent().remove();b.nTable!=b.nTHead.parentNode&&(i(b.nTable).children("thead").remove(),b.nTable.appendChild(b.nTHead));b.nTFoot&&b.nTable!=b.nTFoot.parentNode&&(i(b.nTable).children("tfoot").remove(),b.nTable.appendChild(b.nTFoot));b.nTable.parentNode.removeChild(b.nTable);i(b.nTableWrapper).remove();b.aaSorting=[];b.aaSortingFixed=[];R(b);i(U(b)).removeClass(b.asStripeClasses.join(" "));i("th, td",b.nTHead).removeClass([b.oClasses.sSortable,b.oClasses.sSortableAsc, +b.oClasses.sSortableDesc,b.oClasses.sSortableNone].join(" "));b.bJUI&&(i("th span."+b.oClasses.sSortIcon+", td span."+b.oClasses.sSortIcon,b.nTHead).remove(),i("th, td",b.nTHead).each(function(){var a=i("div."+b.oClasses.sSortJUIWrapper,this),c=a.contents();i(this).append(c);a.remove()}));!a&&b.nTableReinsertBefore?c.insertBefore(b.nTable,b.nTableReinsertBefore):a||c.appendChild(b.nTable);e=0;for(f=b.aoData.length;e=w(d);if(!m)for(e=a;et<"F"ip>')):i.extend(g.oClasses,j.ext.oStdClasses);i(this).addClass(g.oClasses.sTable);if(""!==g.oScroll.sX||""!== +g.oScroll.sY)g.oScroll.iBarWidth=Ra();g.iInitDisplayStart===n&&(g.iInitDisplayStart=e.iDisplayStart,g._iDisplayStart=e.iDisplayStart);e.bStateSave&&(g.oFeatures.bStateSave=!0,Ta(g,e),B(g,"aoDrawCallback",qa,"state_save"));null!==e.iDeferLoading&&(g.bDeferLoading=!0,a=i.isArray(e.iDeferLoading),g._iRecordsDisplay=a?e.iDeferLoading[0]:e.iDeferLoading,g._iRecordsTotal=a?e.iDeferLoading[1]:e.iDeferLoading);null!==e.aaData&&(f=!0);""!==e.oLanguage.sUrl?(g.oLanguage.sUrl=e.oLanguage.sUrl,i.getJSON(g.oLanguage.sUrl, +null,function(a){oa(a);i.extend(true,g.oLanguage,e.oLanguage,a);ba(g)}),h=!0):i.extend(!0,g.oLanguage,e.oLanguage);null===e.asStripeClasses&&(g.asStripeClasses=[g.oClasses.sStripeOdd,g.oClasses.sStripeEven]);c=!1;d=i(this).children("tbody").children("tr");a=0;for(b=g.asStripeClasses.length;a=g.aoColumns.length&&(g.aaSorting[a][0]=0);var k=g.aoColumns[g.aaSorting[a][0]];g.aaSorting[a][2]===n&&(g.aaSorting[a][2]=0);e.aaSorting===n&&g.saved_aaSorting===n&&(g.aaSorting[a][1]=k.asSorting[0]);c=0;for(d=k.asSorting.length;c=parseInt(l,10)}; +j.fnIsDataTable=function(e){for(var i=j.settings,r=0;re)return e;for(var i=e+"",e=i.split(""),j="",i=i.length,k=0;k'+k.sPrevious+''+k.sNext+"":'';i(j).append(k);var t=i("a",j),k=t[0],t=t[1];e.oApi._fnBindAction(k,{action:"previous"},s);e.oApi._fnBindAction(t,{action:"next"},s);if(!e.aanFeatures.p)j.id=e.sTableId+"_paginate",k.id=e.sTableId+"_previous",t.id=e.sTableId+"_next",k.setAttribute("aria-controls",e.sTableId),t.setAttribute("aria-controls",e.sTableId)},fnUpdate:function(e){if(e.aanFeatures.p)for(var i=e.oClasses,j=e.aanFeatures.p,l=0, -k=j.length;l'+k.sFirst+ -''+k.sPrevious+''+k.sNext+''+k.sLast+"");var v=i("a",j),k=v[0],s=v[1],B=v[2],v=v[3];e.oApi._fnBindAction(k,{action:"first"},t);e.oApi._fnBindAction(s,{action:"previous"},t);e.oApi._fnBindAction(B,{action:"next"},t);e.oApi._fnBindAction(v,{action:"last"}, -t);if(!e.aanFeatures.p)j.id=e.sTableId+"_paginate",k.id=e.sTableId+"_first",s.id=e.sTableId+"_previous",B.id=e.sTableId+"_next",v.id=e.sTableId+"_last"},fnUpdate:function(e,l){if(e.aanFeatures.p){var k=j.ext.oPagination.iFullNumbersShowPages,r=Math.floor(k/2),s=Math.ceil(e.fnRecordsDisplay()/e._iDisplayLength),t=Math.ceil(e._iDisplayStart/e._iDisplayLength)+1,v="",B,D=e.oClasses,x,I=e.aanFeatures.p,G=function(i){e.oApi._fnBindAction(this,{page:i+B-1},function(i){e.oApi._fnPageChange(e,i.data.page); -l(e);i.preventDefault()})};s=s-r?(B=s-k+1,r=s):(B=t-Math.ceil(k/2)+1,r=B+k-1);for(k=B;k<=r;k++)v+=t!==k?''+e.fnFormatNumber(k)+"":''+e.fnFormatNumber(k)+"";for(k=0,r=I.length;ki?1:0},"string-desc":function(e,i){return ei?-1:0},"html-pre":function(e){return e.replace(/<.*?>/g,"").toLowerCase()}, -"html-asc":function(e,i){return ei?1:0},"html-desc":function(e,i){return ei?-1:0},"date-pre":function(e){e=Date.parse(e);if(isNaN(e)||""===e)e=Date.parse("01/01/1970 00:00:00");return e},"date-asc":function(e,i){return e-i},"date-desc":function(e,i){return i-e},"numeric-pre":function(e){return"-"==e||""===e?0:1*e},"numeric-asc":function(e,i){return e-i},"numeric-desc":function(e,i){return i-e}});i.extend(j.ext.aTypes,[function(e){if("number"===typeof e)return"numeric";if("string"!== -typeof e)return null;var i,j=!1;i=e.charAt(0);if(-1=="0123456789-".indexOf(i))return null;for(var k=1;k")?"html":null}]);i.fn.DataTable=j;i.fn.dataTable=j;i.fn.dataTableSettings=j.settings;i.fn.dataTableExt= -j.ext})(jQuery,window,document,void 0); +sScrollXInner:"",sScrollY:"",sServerMethod:"GET"};j.defaults.columns={aDataSort:null,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bUseRendered:!0,bVisible:!0,fnCreatedCell:null,fnRender:null,iDataSort:-1,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};j.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null, +bServerSide:null,bSort:null,bSortClasses:null,bStateSave:null},oScroll:{bAutoCss:null,bCollapse:null,bInfinite:null,iBarWidth:0,iLoadGap:null,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1},aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],asDataSearch:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:null,asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[], +aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,sPaginationType:"two_button",iCookieDuration:0,sCookiePrefix:"",fnCookieCallback:null,aoStateSave:[],aoStateLoad:[],oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,fnServerData:null, +aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iDisplayEnd:10,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length},fnRecordsDisplay:function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay, +10):this.aiDisplay.length},fnDisplayEnd:function(){return this.oFeatures.bServerSide?!1===this.oFeatures.bPaginate||-1==this._iDisplayLength?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null};j.ext=i.extend(!0,{},j.models.ext);i.extend(j.ext.oStdClasses,{sTable:"dataTable",sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous", +sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active",sPageButtonStaticDisabled:"paginate_button paginate_button_disabled",sPageFirst:"first",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_", +sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody", +sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:"",sJUIHeader:"",sJUIFooter:""});i.extend(j.ext.oJUIClasses,j.ext.oStdClasses,{sPagePrevEnabled:"fg-button ui-button ui-state-default ui-corner-left",sPagePrevDisabled:"fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",sPageNextEnabled:"fg-button ui-button ui-state-default ui-corner-right",sPageNextDisabled:"fg-button ui-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",sPageButton:"fg-button ui-button ui-state-default",sPageButtonActive:"fg-button ui-button ui-state-default ui-state-disabled",sPageButtonStaticDisabled:"fg-button ui-button ui-state-default ui-state-disabled",sPageFirst:"first ui-corner-tl ui-corner-bl",sPageLast:"last ui-corner-tr ui-corner-br",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:"ui-state-default",sSortDesc:"ui-state-default", +sSortable:"ui-state-default",sSortableAsc:"ui-state-default",sSortableDesc:"ui-state-default",sSortableNone:"ui-state-default",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-carat-2-n-s",sSortJUIAscAllowed:"css_right ui-icon ui-icon-carat-1-n",sSortJUIDescAllowed:"css_right ui-icon ui-icon-carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead ui-state-default", +sScrollFoot:"dataTables_scrollFoot ui-state-default",sFooterTH:"ui-state-default",sJUIHeader:"fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix",sJUIFooter:"fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"});i.extend(j.ext.oPagination,{two_button:{fnInit:function(e,j,n){var k=e.oLanguage.oPaginate,l=function(i){e.oApi._fnPageChange(e,i.data.action)&&n(e)},k=!e.bJUI?''+k.sPrevious+''+k.sNext+"":'';i(j).append(k);var t=i("a",j),k=t[0],t=t[1];e.oApi._fnBindAction(k,{action:"previous"}, +l);e.oApi._fnBindAction(t,{action:"next"},l);e.aanFeatures.p||(j.id=e.sTableId+"_paginate",k.id=e.sTableId+"_previous",t.id=e.sTableId+"_next",k.setAttribute("aria-controls",e.sTableId),t.setAttribute("aria-controls",e.sTableId))},fnUpdate:function(e){if(e.aanFeatures.p)for(var i=e.oClasses,j=e.aanFeatures.p,k=0,n=j.length;k'+k.sFirst+''+k.sPrevious+''+k.sNext+''+k.sLast+"");var w=i("a",j),k=w[0],l=w[1],v=w[2],w=w[3];e.oApi._fnBindAction(k,{action:"first"},t);e.oApi._fnBindAction(l,{action:"previous"},t);e.oApi._fnBindAction(v,{action:"next"},t);e.oApi._fnBindAction(w,{action:"last"},t);e.aanFeatures.p||(j.id=e.sTableId+"_paginate",k.id=e.sTableId+"_first",l.id=e.sTableId+"_previous",v.id=e.sTableId+"_next",w.id=e.sTableId+"_last")},fnUpdate:function(e,o){if(e.aanFeatures.p){var l=j.ext.oPagination.iFullNumbersShowPages, +k=Math.floor(l/2),n=Math.ceil(e.fnRecordsDisplay()/e._iDisplayLength),t=Math.ceil(e._iDisplayStart/e._iDisplayLength)+1,w="",v,D=e.oClasses,y,H=e.aanFeatures.p,O=function(i){e.oApi._fnBindAction(this,{page:i+v-1},function(i){e.oApi._fnPageChange(e,i.data.page);o(e);i.preventDefault()})};-1===e._iDisplayLength?t=k=v=1:n=n-k?(v=n-l+1,k=n):(v=t-Math.ceil(l/2)+1,k=v+l-1);for(l=v;l<=k;l++)w+=t!==l?''+e.fnFormatNumber(l)+ +"":''+e.fnFormatNumber(l)+"";l=0;for(k=H.length;li?1:0},"string-desc":function(e,i){return ei?-1:0},"html-pre":function(e){return e.replace(/<.*?>/g,"").toLowerCase()},"html-asc":function(e,i){return ei?1:0},"html-desc":function(e,i){return ei?-1:0},"date-pre":function(e){e=Date.parse(e);if(isNaN(e)||""=== +e)e=Date.parse("01/01/1970 00:00:00");return e},"date-asc":function(e,i){return e-i},"date-desc":function(e,i){return i-e},"numeric-pre":function(e){return"-"==e||""===e?0:1*e},"numeric-asc":function(e,i){return e-i},"numeric-desc":function(e,i){return i-e}});i.extend(j.ext.aTypes,[function(e){if("number"===typeof e)return"numeric";if("string"!==typeof e)return null;var i,j=!1;i=e.charAt(0);if(-1=="0123456789-".indexOf(i))return null;for(var k=1;k")?"html":null}]);i.fn.DataTable=j;i.fn.dataTable=j;i.fn.dataTableSettings=j.settings;i.fn.dataTableExt=j.ext})(jQuery,window,document,void 0); diff --git a/NzbDrone.Web/Scripts/NzbDrone/common.js b/NzbDrone.Web/Scripts/NzbDrone/common.js new file mode 100644 index 000000000..98c481dc1 --- /dev/null +++ b/NzbDrone.Web/Scripts/NzbDrone/common.js @@ -0,0 +1,16 @@ +(function ($) { + $.fn.enableCheckboxRangeSelection = function () { + var lastCheckbox = null; + var $spec = this; + $spec.unbind("click.checkboxrange"); + $spec.bind("click.checkboxrange", function (e) { + if (lastCheckbox != null && (e.shiftKey || e.metaKey)) { + $spec.slice( + Math.min($spec.index(lastCheckbox), $spec.index(e.target)), + Math.max($spec.index(lastCheckbox), $spec.index(e.target)) + 1 + ).prop('checked', e.target.checked); + } + lastCheckbox = e.target; + }); + }; +})(jQuery); \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/Editor.cshtml b/NzbDrone.Web/Views/Series/Editor.cshtml index a76ad5ae0..3b58995b0 100644 --- a/NzbDrone.Web/Views/Series/Editor.cshtml +++ b/NzbDrone.Web/Views/Series/Editor.cshtml @@ -5,53 +5,7 @@ @section HeaderContent { @Html.IncludeCss("Settings.css") - - + @Html.IncludeCss("SeriesEditor.css") } @section ActionMenu @@ -64,15 +18,17 @@ @using (Html.BeginForm("SaveEditor", "Series", FormMethod.Post, new { id = "SeriesEditor", name = "SeriesEditor" })) { - +
    - - - - - - - + + + + + + + + + @@ -81,38 +37,31 @@ Html.RenderPartial("EditorItem", series); } + + + + + + + + + + + +
    @Html.CheckBox("editToggleMaster", false, new { @class = "editToggleMaster" })TitleQualityMonitoredSeason FolderBacklog StatusPath
    @Html.CheckBox("editToggleMaster", false, new { @class = "editToggleMaster" })TitleQualityMonitoredSeason FolderBacklog StatusPath
    + @Html.DropDownList("masterQualitySelector", (SelectList)ViewData["MasterProfileSelectList"], new { @class = "footer-control-quality masterSelector master-quality", disabled = true }) + + @Html.DropDownList("masterMonitored", (SelectList)ViewData["BoolSelectList"], new { @class = "footer-control masterSelector master-monitored", disabled = true }) + + @Html.DropDownList("masterSeasonFolder", (SelectList)ViewData["BoolSelectList"], new { @class = "footer-control masterSelector master-season-folder", disabled = true }) + + @Html.DropDownList("masterBacklogSetting", (SelectList)ViewData["MasterBacklogSettingSelectList"], new { @class = "footer-control masterSelector master-backlog-setting", disabled = true }) + + +
    - -
    -
    -
    - - @Html.DropDownList("masterQualitySelector", (SelectList)ViewData["MasterProfileSelectList"], new { @class = "inputClass masterSelector", disabled = true }) - - @Html.DropDownList("masterMonitored", (SelectList)ViewData["BoolSelectList"], new { @class = "inputClass masterSelector", disabled = true }) - - @Html.DropDownList("masterSeasonFolder", (SelectList)ViewData["BoolSelectList"], new { @class = "inputClass masterSelector", disabled = true }) - - @Html.DropDownList("masterBacklogSetting", (SelectList)ViewData["MasterBacklogSettingSelectList"], new { @class = "inputClass masterSelector", disabled = true }) -
    -
    -
    -
    -
    -
    - -
    - -
    } @section Scripts @@ -130,6 +79,15 @@ "bInfo": false, "bAutoWidth": false }); + + new FixedHeader(oTable, { "top": true, "left": false, "right": false, "bottom": true }); + + $('.editToggle').enableCheckboxRangeSelection(); + + //$('.master-quality option[value=-10]').text('Quality...'); + //$('.master-monitored option[value=-10]').text('Monitored...'); + //$('.master-season-folder option[value=-10]').text('Season Folder...'); + //$('.master-backlog-setting option[value=-10]').text('Backlog Setting...'); }); $(document).on('change', '.editToggleMaster', function () { @@ -147,55 +105,83 @@ $(this).attr("disabled", false); }); - $('#editingCount').text(selectedCount.length + ' series have been selected for editing'); + $('.editing-count').text(selectedCount.length + ' series have been selected for editing'); if (selectedCount.length === 1) { - $('#editingCount').text(selectedCount.length + ' series has been selected for editing'); + $('.editing-count').text(selectedCount.length + ' series has been selected for editing'); } } else { $('.masterSelector').each(function () { $(this).attr("disabled", true); - $('#editingCount').text(''); + $('.editing-count').text(''); }); } }); + + $(document).on('change', '#masterQualitySelector', function () { + var profileId = $(this).val(); + + if (profileId === -10) + return; + + var selected = $('.editToggle:checked'); + + selected.each(function () { + $(this).parent('td').parent('.seriesEditRow').find('.quality').val(profileId); + }); + }); + + $(document).on('change', '#masterMonitored', function () { + var monitored = $(this).val(); - $(document).on('change', '.masterSelector', function () { - //Find selected values - var profileId = $('#masterQualitySelector').val(); - var monitored = $('#masterMonitored').val(); - var seasonFolder = $('#masterSeasonFolder').val(); - var backlogStatus = $('#masterBacklogSetting').val(); + if (monitored === -10) + return; + + var monitoredBool = true; + if (monitored != 1) + monitoredBool = false; var selected = $('.editToggle:checked'); - selected.each(function() { - if (profileId != -10) { - $(this).parent('td').parent('.seriesEditRow').find('.quality').val(profileId); - } - - if (monitored != -10) { - var monitoredBool = true; - if (monitored != 1) - monitoredBool = false; + selected.each(function () { + $(this).parent('td').parent('.seriesEditRow').find('.monitored').prop('checked', monitoredBool); + }); + }); + + $(document).on('change', '#masterSeasonFolder', function () { + var seasonFolder = $(this).val(); - $(this).parent('td').parent('.seriesEditRow').find('.monitored').prop('checked', monitoredBool); - } + if (seasonFolder === -10) + return; - if (seasonFolder != -10) { - var seasonFolderBool = true; - if (seasonFolder != 1) - seasonFolderBool = false; + var seasonFolderBool = true; + if (seasonFolder != 1) + seasonFolderBool = false; - $(this).parent('td').parent('.seriesEditRow').find('.seasonFolder').prop('checked', seasonFolderBool); - } + var selected = $('.editToggle:checked'); - if (backlogStatus != -10) { - $(this).parent('td').parent('.seriesEditRow').find('.backlogSetting').val(backlogStatus); - } + selected.each(function () { + $(this).parent('td').parent('.seriesEditRow').find('.seasonFolder').prop('checked', seasonFolderBool); }); }); + + $(document).on('change', '#masterBacklogSetting', function () { + var backlogStatus = $(this).val(); + + if (backlogStatus === -10) + return; + + var selected = $('.editToggle:checked'); + + selected.each(function () { + $(this).parent('td').parent('.seriesEditRow').find('.backlogSetting').val(backlogStatus); + }); + }); + + $(document).on('click', '#series-editor-save', function () { + $('#SeriesEditor').submit(); + }); } \ No newline at end of file diff --git a/NzbDrone.Web/Views/Shared/_ReferenceLayout.cshtml b/NzbDrone.Web/Views/Shared/_ReferenceLayout.cshtml index cfa4ff0d2..e6abd6f54 100644 --- a/NzbDrone.Web/Views/Shared/_ReferenceLayout.cshtml +++ b/NzbDrone.Web/Views/Shared/_ReferenceLayout.cshtml @@ -46,7 +46,9 @@ @Html.IncludeScript("DataTables-1.9.0/media/js/jquery.dataTables.editable.js") @Html.IncludeScript("DataTables-1.9.0/media/js/jquery.jeditable.js") @Html.IncludeScript("DataTables-1.9.0/media/js/jquery.dataTablesFilteringDelay.js") + @Html.IncludeScript("DataTables-1.9.0/media/js/FixedHeader.min.js") @Html.IncludeScript("jquery.dataTables.4button.pagination.js") + @Html.IncludeScript("NzbDrone/common.js") @RenderSection("Scripts", required: false) \ No newline at end of file diff --git a/NzbDrone.Web/Web.config b/NzbDrone.Web/Web.config index 8b79e67a7..fbb616eec 100644 --- a/NzbDrone.Web/Web.config +++ b/NzbDrone.Web/Web.config @@ -10,6 +10,7 @@ +