diff --git a/NzbDrone.Api/Bootstrapper.cs b/NzbDrone.Api/Bootstrapper.cs index 50c614198..5e1170b8f 100644 --- a/NzbDrone.Api/Bootstrapper.cs +++ b/NzbDrone.Api/Bootstrapper.cs @@ -56,7 +56,7 @@ namespace NzbDrone.Api .ForMember(dest => dest.QualityTypeId, opt => opt.MapFrom(src => src.Id)); //Series - Mapper.CreateMap() + Mapper.CreateMap() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.SeriesId)) .ForMember(dest => dest.CustomStartDate, opt => opt.ResolveUsing().FromMember(src => src.CustomStartDate)) .ForMember(dest => dest.BacklogSetting, opt => opt.MapFrom(src => (Int32)src.BacklogSetting)); diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 8c0150e64..9ca354aa4 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -97,7 +97,7 @@ - + diff --git a/NzbDrone.Api/Series/SeriesModule.cs b/NzbDrone.Api/Series/SeriesModule.cs index 7ba929cda..9e436eec1 100644 --- a/NzbDrone.Api/Series/SeriesModule.cs +++ b/NzbDrone.Api/Series/SeriesModule.cs @@ -37,7 +37,7 @@ namespace NzbDrone.Api.Series private Response AllSeries() { var series = _seriesProvider.GetAllSeriesWithEpisodeCount().ToList(); - var seriesModels = Mapper.Map, List>(series); + var seriesModels = Mapper.Map, List>(series); return seriesModels.AsResponse(); } @@ -45,7 +45,7 @@ namespace NzbDrone.Api.Series private Response GetSeries(int id) { var series = _seriesProvider.GetSeries(id); - var seriesModels = Mapper.Map(series); + var seriesModels = Mapper.Map(series); return seriesModels.AsResponse(); } @@ -67,7 +67,7 @@ namespace NzbDrone.Api.Series private Response UpdateSeries() { - var request = Request.Body.FromJson(); + var request = Request.Body.FromJson(); var series = _seriesProvider.GetSeries(request.Id); diff --git a/NzbDrone.Api/Series/SeriesModel.cs b/NzbDrone.Api/Series/SeriesResource.cs similarity index 98% rename from NzbDrone.Api/Series/SeriesModel.cs rename to NzbDrone.Api/Series/SeriesResource.cs index 5c42d0305..f4f28103f 100644 --- a/NzbDrone.Api/Series/SeriesModel.cs +++ b/NzbDrone.Api/Series/SeriesResource.cs @@ -7,7 +7,7 @@ using NzbDrone.Core.Model; namespace NzbDrone.Api.Series { - public class SeriesModel + public class SeriesResource { public Int32 Id { get; set; } diff --git a/NzbDrone.Core/Repository/Series.cs b/NzbDrone.Core/Repository/Series.cs index 71f738969..a210a6ab4 100644 --- a/NzbDrone.Core/Repository/Series.cs +++ b/NzbDrone.Core/Repository/Series.cs @@ -61,6 +61,7 @@ namespace NzbDrone.Core.Repository public string TvRageTitle { get; set; } + //Todo: This should be a double since there are timezones that aren't on a full hour offset public int UtcOffset { get; set; } public DateTime? FirstAired { get; set; } diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index f3bf22022..3700bdc59 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -209,6 +209,7 @@ + @@ -216,6 +217,9 @@ + + + diff --git a/NzbDrone.Web/_backboneApp/CassetteConfiguration.cs b/NzbDrone.Web/_backboneApp/CassetteConfiguration.cs index 31e1342b8..65e339ef0 100644 --- a/NzbDrone.Web/_backboneApp/CassetteConfiguration.cs +++ b/NzbDrone.Web/_backboneApp/CassetteConfiguration.cs @@ -23,7 +23,8 @@ namespace NzbDrone.Web.Backbone.NzbDrone APP_PATH + "\\Content\\Bootstrap\\bootstrap.less", APP_PATH + "\\Content\\base.css", APP_PATH + "\\Content\\menu.css", - APP_PATH + "\\AddSeries\\addSeries.css" + APP_PATH + "\\AddSeries\\addSeries.css", + APP_PATH + "\\Content\\jquery.dataTables.bootstrap.css" }, bundle => bundle.AddReference("/" + FONTS)); diff --git a/NzbDrone.Web/_backboneApp/Content/Bootstrap/mixins.less b/NzbDrone.Web/_backboneApp/Content/Bootstrap/mixins.less index a9f313b54..a35492a28 100644 --- a/NzbDrone.Web/_backboneApp/Content/Bootstrap/mixins.less +++ b/NzbDrone.Web/_backboneApp/Content/Bootstrap/mixins.less @@ -579,6 +579,7 @@ .row { margin-left: @gridGutterWidth * -1; + margin-right: @gridGutterWidth * -1; .clearfix(); } diff --git a/NzbDrone.Web/_backboneApp/Content/Images/sort_asc.png b/NzbDrone.Web/_backboneApp/Content/Images/sort_asc.png new file mode 100644 index 000000000..6689409ed Binary files /dev/null and b/NzbDrone.Web/_backboneApp/Content/Images/sort_asc.png differ diff --git a/NzbDrone.Web/_backboneApp/Content/Images/sort_asc_disabled.png b/NzbDrone.Web/_backboneApp/Content/Images/sort_asc_disabled.png new file mode 100644 index 000000000..965040f75 Binary files /dev/null and b/NzbDrone.Web/_backboneApp/Content/Images/sort_asc_disabled.png differ diff --git a/NzbDrone.Web/_backboneApp/Content/Images/sort_both.png b/NzbDrone.Web/_backboneApp/Content/Images/sort_both.png new file mode 100644 index 000000000..3eea3fdc2 Binary files /dev/null and b/NzbDrone.Web/_backboneApp/Content/Images/sort_both.png differ diff --git a/NzbDrone.Web/_backboneApp/Content/Images/sort_desc.png b/NzbDrone.Web/_backboneApp/Content/Images/sort_desc.png new file mode 100644 index 000000000..794f1fc14 Binary files /dev/null and b/NzbDrone.Web/_backboneApp/Content/Images/sort_desc.png differ diff --git a/NzbDrone.Web/_backboneApp/Content/Images/sort_desc_disabled.png b/NzbDrone.Web/_backboneApp/Content/Images/sort_desc_disabled.png new file mode 100644 index 000000000..639bdb228 Binary files /dev/null and b/NzbDrone.Web/_backboneApp/Content/Images/sort_desc_disabled.png differ diff --git a/NzbDrone.Web/_backboneApp/Content/jquery.dataTables.bootstrap.css b/NzbDrone.Web/_backboneApp/Content/jquery.dataTables.bootstrap.css new file mode 100644 index 000000000..a88f22dab --- /dev/null +++ b/NzbDrone.Web/_backboneApp/Content/jquery.dataTables.bootstrap.css @@ -0,0 +1,110 @@ +div.dataTables_length { + /*float: right; + margin: 10px;*/ + display: inline-block; + margin-right: 30px; + padding-top: 5px; +} + +div.bottomRight { + float: right; +} + +div.dataTables_length label { + text-align: left; + display: inline-block; + padding-bottom: 0px; +} + +div.dataTables_length select { + width: 75px; +} + +div.dataTables_filter label { + float: right; +} + +div.dataTables_info { + padding-top: 8px; + display: inline-block; +} + +div.dataTables_paginate { + float: right; + margin: 0; + display: inline-block; +} + +table.table { + clear: both; + margin-bottom: 6px !important; +} + +.dataTables_wrapper { + position: relative; +} + +table.table thead .sorting, +table.table thead .sorting_asc, +table.table thead .sorting_desc, +table.table thead .sorting_asc_disabled, +table.table thead .sorting_desc_disabled { + cursor: pointer; + *cursor: hand; +} + +/*table.table thead .sorting:after { + width: 12px; + font-family: FontAwesome; + content: "\f106 \f107"; + -webkit-transform: rotate(90deg); + -o-transform: rotate(90deg); + -moz-transform: rotate(90deg); +} + +table.table thead .sorting_asc:after { + font-family: FontAwesome; + content: "\f106"; +} + +table.table thead .sorting_desc:after { + font-family: FontAwesome; + content: "\f107"; +}*/ + +table.table thead .sorting { background: url('./images/sort_both.png') no-repeat center right; } +table.table thead .sorting_asc { background: url('./images/sort_asc.png') no-repeat center right; } +table.table thead .sorting_desc { background: url('./images/sort_desc.png') no-repeat center right; } + +table.table thead .sorting_asc_disabled { background: url('./images/sort_asc_disabled.png') no-repeat center right; } +table.table thead .sorting_desc_disabled { background: url('./images/sort_desc_disabled.png') no-repeat center right; } + +table.dataTable th:active { + outline: none; +} + +/*table.dataTable tr.odd { background-color: #F9F9F9; }*/ + +/* + * Processing indicator + */ +.dataTables_processing { + position: absolute; + top: 50px; + left: 50%; + width: 250px; + height: 30px; + line-height: 30px; + margin-left: -125px; + padding: 0px 0 0px 0; + border: 1px solid #ddd; + text-align: center; + color: #999; + font-size: 14px; + background-color: white; + clear: both; +} + +.dataTables_searchButton { + margin-left: 4px; +} \ No newline at end of file diff --git a/NzbDrone.Web/_backboneApp/JsLibraries/jquery.dataTables-1.10.0-dev.js b/NzbDrone.Web/_backboneApp/JsLibraries/jquery.dataTables-1.10.0-dev.js new file mode 100644 index 000000000..3c810653a --- /dev/null +++ b/NzbDrone.Web/_backboneApp/JsLibraries/jquery.dataTables-1.10.0-dev.js @@ -0,0 +1,11979 @@ +/** + * @summary DataTables + * @description Paginate, search and sort HTML tables + * @version 1.10.0-dev + * @file jquery.dataTables.js + * @author Allan Jardine (www.sprymedia.co.uk) + * @contact www.sprymedia.co.uk/contact + * + * @copyright Copyright 2008-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 + * + * 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 please refer to: http://www.datatables.net + */ + +/*jslint evil: true, undef: true, browser: true */ +/*globals $,require,jQuery,define,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns,_fnHungarianMap,_fnCamelToHungarian*/ + +(/** @lends */function( window, document, undefined ) { + +(function( factory ) { + "use strict"; + + // Define as an AMD module if possible + if ( typeof define === 'function' && define.amd ) + { + define( ['jquery'], factory ); + } + /* Define using browser globals otherwise + * Prevent multiple instantiations if the script is loaded twice + */ + else if ( jQuery && !jQuery.fn.dataTable ) + { + factory( jQuery ); + } +} +(/** @lends */function( $ ) { + "use strict"; + + /** + * DataTables is a plug-in for the jQuery Javascript library. It is a highly + * flexible tool, based upon the foundations of progressive enhancement, + * which will add advanced interaction controls to any HTML table. For a + * full list of features please refer to + * [DataTables.net](href="http://datatables.net). + * + * Note that the `DataTable` object is not a global variable but is aliased + * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may + * be accessed. + * + * @class + * @param {object} [init={}] Configuration object for DataTables. Options + * are defined by {@link DataTable.defaults} + * @requires jQuery 1.3+ + * + * @example + * // Basic initialisation + * $(document).ready( function { + * $('#example').dataTable(); + * } ); + * + * @example + * // Initialisation with configuration options - in this case, disable + * // pagination and sorting. + * $(document).ready( function { + * $('#example').dataTable( { + * "paginate": false, + * "sort": false + * } ); + * } ); + */ + var DataTable; + + + + /** + * Create a mapping object that allows camel case parameters to be looked up + * for their Hungarian counterparts. The mapping is stored in a private + * parameter called `_hungaianMap` which can be accessed on the source object. + * @param {object} o + * @memberof DataTable#oApi + */ + function _fnHungarianMap ( o ) + { + var + hungarian = 'a aa ao as b fn i m o s ', + match, + newKey, + map = {}; + + $.each( o, function (key, val) { + match = key.match(/^([^A-Z]+?)([A-Z])/); + + if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) + { + newKey = key.replace( match[0], match[2].toLowerCase() ); + map[ newKey ] = key; + + if ( match[1] === 'o' ) + { + _fnHungarianMap( o[key] ); + } + } + } ); + + o._hungaianMap = map; + } + + + /** + * Convert from camel case parameters to Hungarian, based on a Hungarian map + * created by _fnHungarianMap. + * @param {object} src The model object which holds all parameters that can be + * mapped. + * @param {object} user The object to convert from camel case to Hungarian. + * @param {boolean} force When set to `true`, properties which already have a + * Hungarian value in the `user` object will be overwritten. Otherwise they + * won't be. + * @memberof DataTable#oApi + */ + function _fnCamelToHungarian ( src, user, force ) + { + if ( ! src._hungaianMap ) + { + _fnHungarianMap( src ); + } + + var hungarianKey; + + $.each( user, function (key, val) { + hungarianKey = src._hungaianMap[ key ]; + + if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) + { + user[hungarianKey] = user[ key ]; + + if ( hungarianKey.charAt(0) === 'o' ) + { + _fnCamelToHungarian( src[hungarianKey], user[key] ); + } + } + } ); + } + + + /** + * Language compatibility - when certain options are given, and others aren't, we + * need to duplicate the values over, in order to provide backwards compatibility + * with older language files. + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + 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 && + oDefaults.sEmptyTable === "No data available in table" ) + { + _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' ); + } + + /* Likewise with loading records */ + if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords && + oDefaults.sLoadingRecords === "Loading..." ) + { + _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sLoadingRecords' ); + } + } + + + /** + * Browser feature detection for capabilities, quirks + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnBrowserDetect( oSettings ) + { + // Scrolling feature / quirks detection + var n = $( + '
'+ + '
'+ + '
'+ + '
'+ + '
')[0]; + + document.body.appendChild( n ); + // 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 + oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false; + + // In rtl text layout, some browsers (most, but not all) will place the + // scrollbar on the left, rather than the right. + oSettings.oBrowser.bScrollbarLeft = $('#DT_BrowserTest', n).offset().left !== 1 ? true : false; + document.body.removeChild( n ); + } + + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + var oDefaults = DataTable.defaults.column; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "sSortingClass": oSettings.oClasses.sSortable, + "sSortingClassJUI": oSettings.oClasses.sSortJUI, + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.oDefaults : iCol + } ); + oSettings.aoColumns.push( oCol ); + + /* Add a column specific filter */ + if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null ) + { + oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch ); + } + else + { + var oPre = oSettings.aoPreSearchCols[ iCol ]; + + /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ + if ( oPre.bRegex === undefined ) + { + oPre.bRegex = true; + } + + if ( oPre.bSmart === undefined ) + { + oPre.bSmart = true; + } + + if ( oPre.bCaseInsensitive === undefined ) + { + oPre.bCaseInsensitive = true; + } + } + + /* Use the column options function to initialise classes etc */ + _fnColumnOptions( oSettings, iCol, null ); + } + + + /** + * 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 etc + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + // Map camel case parameters to their Hungarian counterparts + _fnCamelToHungarian( DataTable.defaults.column, oOptions ); + + /* Backwards compatibility for mDataProp */ + if ( oOptions.mDataProp !== undefined && !oOptions.mData ) + { + oOptions.mData = oOptions.mDataProp; + } + + if ( oOptions.sType !== undefined ) + { + oCol.sType = oOptions.sType; + oCol._bAutoType = false; + } + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + 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 ) + { + oCol.bSortable = false; + } + + /* Check that the class assignment is correct for sorting */ + if ( !oCol.bSortable || + ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortable; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI; + } + else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableAsc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableDesc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( oSettings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( oSettings.oFeatures.bAutoWidth === false ) + { + return false; + } + + _fnCalculateColumnWidths( oSettings ); + for ( var i=0 , iLen=oSettings.aoColumns.length ; i