1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-12-16 11:37:58 +02:00

Merge branch 'backgrid' into develop

This commit is contained in:
Mark McDowall 2014-01-06 17:17:04 -08:00
commit a3d67b47f9
30 changed files with 1451 additions and 767 deletions

View File

@ -33,6 +33,13 @@ private PagingResource<HistoryResource> GetHistory(PagingResource<HistoryResourc
SortDirection = pagingResource.SortDirection SortDirection = pagingResource.SortDirection
}; };
//This is a hack to deal with backgrid setting the sortKey to the column name instead of sortValue
if (pagingSpec.SortKey.Equals("series", StringComparison.InvariantCultureIgnoreCase))
{
pagingSpec.SortKey = "series.title";
}
if (episodeId.HasValue) if (episodeId.HasValue)
{ {
int i = (int)episodeId; int i = (int)episodeId;

View File

@ -1,69 +0,0 @@
'use strict';
define(
[
'backgrid',
'Shared/Grid/HeaderCell'
], function (Backgrid, NzbDroneHeaderCell) {
Backgrid.QualityHeaderCell = NzbDroneHeaderCell.extend({
events: {
'click': 'onClick'
},
onClick: function (e) {
e.preventDefault();
var self = this;
var columnName = this.column.get('name');
if (this.column.get('sortable')) {
if (this.direction() === 'ascending') {
this.sort(columnName, 'descending', function (left, right) {
var leftVal = left.get(columnName);
var rightVal = right.get(columnName);
return self._comparator(leftVal, rightVal);
});
}
else {
this.sort(columnName, 'ascending', function (left, right) {
var leftVal = left.get(columnName);
var rightVal = right.get(columnName);
return self._comparator(rightVal, leftVal);
});
}
}
},
_comparator: function (leftVal, rightVal) {
var leftWeight = leftVal.quality.weight;
var rightWeight = rightVal.quality.weight;
if (!leftWeight && !rightWeight) {
return 0;
}
if (!leftWeight) {
return -1;
}
if (!rightWeight) {
return 1;
}
if (leftWeight === rightWeight) {
return 0;
}
if (leftWeight > rightWeight) {
return -1;
}
return 1;
}
});
return Backgrid.QualityHeaderCell;
});

View File

@ -8,8 +8,11 @@ define(
className : 'season-folder-cell', className : 'season-folder-cell',
render: function () { render: function () {
var seasonFolder = this.model.get('seasonFolder'); this.$el.empty();
var seasonFolder = this.model.get(this.column.get('name'));
this.$el.html(seasonFolder.toString()); this.$el.html(seasonFolder.toString());
return this; return this;
} }
}); });

View File

@ -46,6 +46,17 @@
.page-toolbar { .page-toolbar {
margin-top : 10px; margin-top : 10px;
margin-bottom : 30px; margin-bottom : 30px;
.toolbar-group {
display: inline-block;
}
.sorting-buttons {
.sorting-title {
display: inline-block;
width: 110px;
}
}
} }
.page-container { .page-container {

View File

@ -47,7 +47,7 @@ define(
this.model = options.model; this.model = options.model;
this.series = options.series; this.series = options.series;
this.collection = new HistoryCollection({ episodeId: this.model.id }); this.collection = new HistoryCollection({ episodeId: this.model.id, tableName: 'episodeActivity' });
this.collection.fetch(); this.collection.fetch();
this.listenTo(this.collection, 'sync', this._showTable); this.listenTo(this.collection, 'sync', this._showTable);
}, },

View File

@ -6,10 +6,8 @@ define(
'Cells/FileSizeCell', 'Cells/FileSizeCell',
'Cells/QualityCell', 'Cells/QualityCell',
'Cells/ApprovalStatusCell', 'Cells/ApprovalStatusCell',
'Release/DownloadReportCell', 'Release/DownloadReportCell'
'Cells/Header/QualityHeaderCell' ], function (Marionette, Backgrid, FileSizeCell, QualityCell, ApprovalStatusCell, DownloadReportCell) {
], function (Marionette, Backgrid, FileSizeCell, QualityCell, ApprovalStatusCell, DownloadReportCell, QualityHeaderCell) {
return Marionette.Layout.extend({ return Marionette.Layout.extend({
template: 'Episode/Search/ManualLayoutTemplate', template: 'Episode/Search/ManualLayoutTemplate',
@ -49,7 +47,9 @@ define(
label : 'Quality', label : 'Quality',
sortable : true, sortable : true,
cell : QualityCell, cell : QualityCell,
headerCell: QualityHeaderCell sortValue : function (model) {
return model.get('quality').quality.weight;
}
}, },
{ {

View File

@ -2,9 +2,10 @@
define( define(
[ [
'History/HistoryModel', 'History/HistoryModel',
'backbone.pageable' 'backbone.pageable',
], function (HistoryModel, PageableCollection) { 'Mixins/AsPersistedStateCollection'
return PageableCollection.extend({ ], function (HistoryModel, PageableCollection, AsPersistedStateCollection) {
var collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/history', url : window.NzbDrone.ApiRoot + '/history',
model: HistoryModel, model: HistoryModel,
@ -48,4 +49,6 @@ define(
return resp; return resp;
} }
}); });
return AsPersistedStateCollection.apply(collection);
}); });

View File

@ -45,7 +45,8 @@ define(
{ {
name : 'series', name : 'series',
label: 'Series', label: 'Series',
cell : SeriesTitleCell cell : SeriesTitleCell,
sortValue: 'series.title'
}, },
{ {
name : 'episode', name : 'episode',
@ -80,7 +81,7 @@ define(
initialize: function () { initialize: function () {
this.collection = new HistoryCollection(); this.collection = new HistoryCollection({ tableName: 'history' });
this.listenTo(this.collection, 'sync', this._showTable); this.listenTo(this.collection, 'sync', this._showTable);
}, },

File diff suppressed because it is too large Load Diff

View File

@ -5,19 +5,202 @@
Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
Licensed under the MIT @license. Licensed under the MIT @license.
*/ */
(function (factory) {
(function ($, _, Backbone, Backgrid) { // CommonJS
if (typeof exports == "object") {
module.exports = factory(require("underscore"),
require("backbone"),
require("backgrid"),
require("backbone-pageable"));
}
// Browser
else if (typeof _ !== "undefined" &&
typeof Backbone !== "undefined" &&
typeof Backgrid !== "undefined") {
factory(_, Backbone, Backgrid);
}
}(function (_, Backbone, Backgrid) {
"use strict"; "use strict";
/**
PageHandle is a class that renders the actual page handles and reacts to
click events for pagination.
This class acts in two modes - control or discrete page handle modes. If
one of the `is*` flags is `true`, an instance of this class is under
control page handle mode. Setting a `pageIndex` to an instance of this
class under control mode has no effect and the correct page index will
always be inferred from the `is*` flag. Only one of the `is*` flags should
be set to `true` at a time. For example, an instance of this class cannot
simultaneously be a rewind control and a fast forward control. A `label`
and a `title` template or a string are required to be passed to the
constuctor under this mode. If a `title` template is provided, it __MUST__
accept a parameter `label`. When the `label` is provided to the `title`
template function, its result will be used to render the generated anchor's
title attribute.
If all of the `is*` flags is set to `false`, which is the default, an
instance of this class will be in discrete page handle mode. An instance
under this mode requires the `pageIndex` to be passed from the constructor
as an option and it __MUST__ be a 0-based index of the list of page numbers
to render. The constuctor will normalize the base to the same base the
underlying PageableCollection collection instance uses. A `label` is not
required under this mode, which will default to the equivalent 1-based page
index calculated from `pageIndex` and the underlying PageableCollection
instance. A provided `label` will still be honored however. The `title`
parameter is also not required under this mode, in which case the default
`title` template will be used. You are encouraged to provide your own
`title` template however if you wish to localize the title strings.
If this page handle represents the current page, an `active` class will be
placed on the root list element.
if this page handle is at the border of the list of pages, a `disabled`
class will be placed on the root list element.
Only page handles that are neither `active` nor `disabled` will respond to
click events and triggers pagination.
@class Backgrid.Extension.PageHandle
*/
var PageHandle = Backgrid.Extension.PageHandle = Backbone.View.extend({
/** @property */
tagName: "li",
/** @property */
events: {
"click a": "changePage"
},
/**
@property {string|function(Object.<string, string>): string} title
The title to use for the `title` attribute of the generated page handle
anchor elements. It can be a string or an Underscore template function
that takes a mandatory `label` parameter.
*/
title: _.template('Page <%- label %>', null, {variable: null}),
/**
@property {boolean} isRewind Whether this handle represents a rewind
control
*/
isRewind: false,
/**
@property {boolean} isBack Whether this handle represents a back
control
*/
isBack: false,
/**
@property {boolean} isForward Whether this handle represents a forward
control
*/
isForward: false,
/**
@property {boolean} isFastForward Whether this handle represents a fast
forward control
*/
isFastForward: false,
/**
Initializer.
@param {Object} options
@param {Backbone.Collection} options.collection
@param {number} pageIndex 0-based index of the page number this handle
handles. This parameter will be normalized to the base the underlying
PageableCollection uses.
@param {string} [options.label] If provided it is used to render the
anchor text, otherwise the normalized pageIndex will be used
instead. Required if any of the `is*` flags is set to `true`.
@param {string} [options.title]
@param {boolean} [options.isRewind=false]
@param {boolean} [options.isBack=false]
@param {boolean} [options.isForward=false]
@param {boolean} [options.isFastForward=false]
*/
initialize: function (options) {
Backbone.View.prototype.initialize.apply(this, arguments);
var collection = this.collection;
var state = collection.state;
var currentPage = state.currentPage;
var firstPage = state.firstPage;
var lastPage = state.lastPage;
_.extend(this, _.pick(options,
["isRewind", "isBack", "isForward", "isFastForward"]));
var pageIndex;
if (this.isRewind) pageIndex = firstPage;
else if (this.isBack) pageIndex = Math.max(firstPage, currentPage - 1);
else if (this.isForward) pageIndex = Math.min(lastPage, currentPage + 1);
else if (this.isFastForward) pageIndex = lastPage;
else {
pageIndex = +options.pageIndex;
pageIndex = (firstPage ? pageIndex + 1 : pageIndex);
}
this.pageIndex = pageIndex;
if (((this.isRewind || this.isBack) && currentPage == firstPage) ||
((this.isForward || this.isFastForward) && currentPage == lastPage)) {
this.$el.addClass("disabled");
}
else if (!(this.isRewind ||
this.isBack ||
this.isForward ||
this.isFastForward) &&
currentPage == pageIndex) {
this.$el.addClass("active");
}
this.label = (options.label || (firstPage ? pageIndex : pageIndex + 1)) + '';
var title = options.title || this.title;
this.title = _.isFunction(title) ? title({label: this.label}) : title;
},
/**
Renders a clickable anchor element under a list item.
*/
render: function () {
this.$el.empty();
var anchor = document.createElement("a");
anchor.href = '#';
if (this.title) anchor.title = this.title;
anchor.innerHTML = this.label;
this.el.appendChild(anchor);
this.delegateEvents();
return this;
},
/**
jQuery click event handler. Goes to the page this PageHandle instance
represents. No-op if this page handle is currently active or disabled.
*/
changePage: function (e) {
e.preventDefault();
var $el = this.$el;
if (!$el.hasClass("active") && !$el.hasClass("disabled")) {
this.collection.getPage(this.pageIndex);
}
return this;
}
});
/** /**
Paginator is a Backgrid extension that renders a series of configurable Paginator is a Backgrid extension that renders a series of configurable
pagination handles. This extension is best used for splitting a large data pagination handles. This extension is best used for splitting a large data
set across multiple pages. If the number of pages is larger then a set across multiple pages. If the number of pages is larger then a
threshold, which is set to 10 by default, the page handles are rendered threshold, which is set to 10 by default, the page handles are rendered
within a sliding window, plus the fast forward, fast backward, previous and within a sliding window, plus the rewind, back, forward and fast forward
next page handles. The fast forward, fast backward, previous and next page control handles. The individual control handles can be turned off.
handles can be turned off.
@class Backgrid.Extension.Paginator @class Backgrid.Extension.Paginator
*/ */
@ -30,97 +213,63 @@
windowSize: 10, windowSize: 10,
/** /**
@property {Object} fastForwardHandleLabels You can disable specific @property {Object.<string, Object.<string, string>>} controls You can
handles by setting its value to `null`. disable specific control handles by omitting certain keys.
*/ */
fastForwardHandleLabels: { controls: {
first: "《", rewind: {
prev: "〈", label: "《",
next: "〉", title: "First"
last: "》" },
back: {
label: "〈",
title: "Previous"
},
forward: {
label: "〉",
title: "Next"
},
fastForward: {
label: "》",
title: "Last"
}
}, },
/** @property */ /**
template: _.template('<ul><% _.each(handles, function (handle) { %><li <% if (handle.className) { %>class="<%= handle.className %>"<% } %>><a href="#" <% if (handle.title) {%> title="<%= handle.title %>"<% } %>><%= handle.label %></a></li><% }); %></ul>'), @property {Backgrid.Extension.PageHandle} pageHandle. The PageHandle
class to use for rendering individual handles
*/
pageHandle: PageHandle,
/** @property */ /** @property */
events: { goBackFirstOnSort: true,
"click a": "changePage"
},
/** /**
Initializer. Initializer.
@param {Object} options @param {Object} options
@param {Backbone.Collection} options.collection @param {Backbone.Collection} options.collection
@param {boolean} [options.fastForwardHandleLabels] Whether to render fast forward buttons. @param {boolean} [options.controls]
@param {boolean} [options.pageHandle=Backgrid.Extension.PageHandle]
@param {boolean} [options.goBackFirstOnSort=true]
*/ */
initialize: function (options) { initialize: function (options) {
Backgrid.requireOptions(options, ["collection"]); this.controls = options.controls || this.controls;
this.pageHandle = options.pageHandle || this.pageHandle;
var collection = this.collection; var collection = this.collection;
var fullCollection = collection.fullCollection; this.listenTo(collection, "add", this.render);
if (fullCollection) { this.listenTo(collection, "remove", this.render);
this.listenTo(fullCollection, "add", this.render); this.listenTo(collection, "reset", this.render);
this.listenTo(fullCollection, "remove", this.render); if ((options.goBackFirstOnSort || this.goBackFirstOnSort) &&
this.listenTo(fullCollection, "reset", this.render); collection.fullCollection) {
} this.listenTo(collection.fullCollection, "sort", function () {
else { collection.getFirstPage();
this.listenTo(collection, "add", this.render); });
this.listenTo(collection, "remove", this.render);
this.listenTo(collection, "reset", this.render);
} }
}, },
/** _calculateWindow: function () {
jQuery event handler for the page handlers. Goes to the right page upon
clicking.
@param {Event} e
*/
changePage: function (e) {
e.preventDefault();
var $li = $(e.target).parent();
if (!$li.hasClass("active") && !$li.hasClass("disabled")) {
var label = $(e.target).text();
var ffLabels = this.fastForwardHandleLabels;
var collection = this.collection;
if (ffLabels) {
switch (label) {
case ffLabels.first:
collection.getFirstPage();
return;
case ffLabels.prev:
collection.getPreviousPage();
return;
case ffLabels.next:
collection.getNextPage();
return;
case ffLabels.last:
collection.getLastPage();
return;
}
}
var state = collection.state;
var pageIndex = +label;
collection.getPage(state.firstPage === 0 ? pageIndex - 1 : pageIndex);
}
},
/**
Internal method to create a list of page handle objects for the template
to render them.
@return {Array.<Object>} an array of page handle objects hashes
*/
makeHandles: function () {
var handles = [];
var collection = this.collection; var collection = this.collection;
var state = collection.state; var state = collection.state;
@ -132,48 +281,44 @@
currentPage = firstPage ? currentPage - 1 : currentPage; currentPage = firstPage ? currentPage - 1 : currentPage;
var windowStart = Math.floor(currentPage / this.windowSize) * this.windowSize; var windowStart = Math.floor(currentPage / this.windowSize) * this.windowSize;
var windowEnd = Math.min(lastPage + 1, windowStart + this.windowSize); var windowEnd = Math.min(lastPage + 1, windowStart + this.windowSize);
return [windowStart, windowEnd];
},
if (collection.mode !== "infinite") { /**
for (var i = windowStart; i < windowEnd; i++) { Creates a list of page handle objects for rendering.
handles.push({
label: i + 1, @return {Array.<Object>} an array of page handle objects hashes
title: "No. " + (i + 1), */
className: currentPage === i ? "active" : undefined makeHandles: function () {
});
} var handles = [];
var collection = this.collection;
var window = this._calculateWindow();
var winStart = window[0], winEnd = window[1];
for (var i = winStart; i < winEnd; i++) {
handles.push(new this.pageHandle({
collection: collection,
pageIndex: i
}));
} }
var ffLabels = this.fastForwardHandleLabels; var controls = this.controls;
if (ffLabels) { _.each(["back", "rewind", "forward", "fastForward"], function (key) {
var value = controls[key];
if (ffLabels.prev) { if (value) {
handles.unshift({ var handleCtorOpts = {
label: ffLabels.prev, collection: collection,
className: collection.hasPrevious() ? void 0 : "disabled" title: value.title,
}); label: value.label
};
handleCtorOpts["is" + key.slice(0, 1).toUpperCase() + key.slice(1)] = true;
var handle = new this.pageHandle(handleCtorOpts);
if (key == "rewind" || key == "back") handles.unshift(handle);
else handles.push(handle);
} }
}, this);
if (ffLabels.first) {
handles.unshift({
label: ffLabels.first,
className: collection.hasPrevious() ? void 0 : "disabled"
});
}
if (ffLabels.next) {
handles.push({
label: ffLabels.next,
className: collection.hasNext() ? void 0 : "disabled"
});
}
if (ffLabels.last) {
handles.push({
label: ffLabels.last,
className: collection.hasNext() ? void 0 : "disabled"
});
}
}
return handles; return handles;
}, },
@ -184,15 +329,24 @@
render: function () { render: function () {
this.$el.empty(); this.$el.empty();
this.$el.append(this.template({ if (this.handles) {
handles: this.makeHandles() for (var i = 0, l = this.handles.length; i < l; i++) {
})); this.handles[i].remove();
}
}
this.delegateEvents(); var handles = this.handles = this.makeHandles();
var ul = document.createElement("ul");
for (var i = 0; i < handles.length; i++) {
ul.appendChild(handles[i].render().el);
}
this.el.appendChild(ul);
return this; return this;
} }
}); });
}(jQuery, _, Backbone, Backgrid)); }));

View File

@ -1,5 +1,5 @@
/* /*
backbone-pageable 1.3.2 backbone-pageable 1.4.1
http://github.com/wyuenho/backbone-pageable http://github.com/wyuenho/backbone-pageable
Copyright (c) 2013 Jimmy Yuen Ho Wong Copyright (c) 2013 Jimmy Yuen Ho Wong
@ -83,7 +83,7 @@
for (var i = 0, l = kvps.length; i < l; i++) { for (var i = 0, l = kvps.length; i < l; i++) {
var param = kvps[i]; var param = kvps[i];
kvp = param.split('='), k = kvp[0], v = kvp[1] || true; kvp = param.split('='), k = kvp[0], v = kvp[1] || true;
k = decode(k), ls = params[k]; k = decode(k), v = decode(v), ls = params[k];
if (_isArray(ls)) ls.push(v); if (_isArray(ls)) ls.push(v);
else if (ls) params[k] = [ls, v]; else if (ls) params[k] = [ls, v];
else params[k] = v; else params[k] = v;
@ -91,6 +91,29 @@
return params; return params;
} }
// hack to make sure the whatever event handlers for this event is run
// before func is, and the event handlers that func will trigger.
function runOnceAtLastHandler (col, event, func) {
var eventHandlers = col._events[event];
if (eventHandlers && eventHandlers.length) {
var lastHandler = eventHandlers[eventHandlers.length - 1];
var oldCallback = lastHandler.callback;
lastHandler.callback = function () {
try {
oldCallback.apply(this, arguments);
func();
}
catch (e) {
throw e;
}
finally {
lastHandler.callback = oldCallback;
}
};
}
else func();
}
var PARAM_TRIM_RE = /[\s'"]/g; var PARAM_TRIM_RE = /[\s'"]/g;
var URL_TRIM_RE = /[<>\s'"]/g; var URL_TRIM_RE = /[<>\s'"]/g;
@ -256,7 +279,7 @@
*/ */
constructor: function (models, options) { constructor: function (models, options) {
Backbone.Collection.apply(this, arguments); BBColProto.constructor.apply(this, arguments);
options = options || {}; options = options || {};
@ -299,7 +322,7 @@
var fullCollection = this.fullCollection; var fullCollection = this.fullCollection;
if (comparator && options.full) { if (comparator && options.full) {
delete this.comparator; this.comparator = null;
fullCollection.comparator = comparator; fullCollection.comparator = comparator;
} }
@ -308,6 +331,7 @@
// make sure the models in the current page and full collection have the // make sure the models in the current page and full collection have the
// same references // same references
if (models && !_isEmpty(models)) { if (models && !_isEmpty(models)) {
this.reset([].slice.call(models), _extend({silent: true}, options));
this.getPage(state.currentPage); this.getPage(state.currentPage);
models.splice.apply(models, [0, models.length].concat(this.models)); models.splice.apply(models, [0, models.length].concat(this.models));
} }
@ -412,22 +436,10 @@
pageCol.at(pageSize) : pageCol.at(pageSize) :
null; null;
if (modelToRemove) { if (modelToRemove) {
var addHandlers = collection._events.add || [], var popOptions = {onAdd: true};
popOptions = {onAdd: true}; runOnceAtLastHandler(collection, event, function () {
if (addHandlers.length) { pageCol.remove(modelToRemove, popOptions);
var lastAddHandler = addHandlers[addHandlers.length - 1]; });
var oldCallback = lastAddHandler.callback;
lastAddHandler.callback = function () {
try {
oldCallback.apply(this, arguments);
pageCol.remove(modelToRemove, popOptions);
}
finally {
lastAddHandler.callback = oldCallback;
}
};
}
else pageCol.remove(modelToRemove, popOptions);
} }
} }
} }
@ -442,20 +454,25 @@
} }
else { else {
var totalPages = state.totalPages = ceil(state.totalRecords / pageSize); var totalPages = state.totalPages = ceil(state.totalRecords / pageSize);
state.lastPage = firstPage === 0 ? totalPages - 1 : totalPages; state.lastPage = firstPage === 0 ? totalPages - 1 : totalPages || firstPage;
if (state.currentPage > totalPages) state.currentPage = state.lastPage; if (state.currentPage > totalPages) state.currentPage = state.lastPage;
} }
pageCol.state = pageCol._checkState(state); pageCol.state = pageCol._checkState(state);
var nextModel, removedIndex = options.index; var nextModel, removedIndex = options.index;
if (collection == pageCol) { if (collection == pageCol) {
if (nextModel = fullCol.at(pageEnd)) pageCol.push(nextModel); if (nextModel = fullCol.at(pageEnd)) {
runOnceAtLastHandler(pageCol, event, function () {
pageCol.push(nextModel);
});
}
fullCol.remove(model); fullCol.remove(model);
} }
else if (removedIndex >= pageStart && removedIndex < pageEnd) { else if (removedIndex >= pageStart && removedIndex < pageEnd) {
pageCol.remove(model); pageCol.remove(model);
nextModel = fullCol.at(currentPage * (pageSize + removedIndex)); var at = removedIndex + 1
if (nextModel) pageCol.push(nextModel); nextModel = fullCol.at(at) || fullCol.last();
if (nextModel) pageCol.add(nextModel, {at: at});
} }
} }
else delete options.onAdd; else delete options.onAdd;
@ -466,13 +483,13 @@
collection = model; collection = model;
// Reset that's not a result of getPage // Reset that's not a result of getPage
if (collection === pageCol && options.from == null && if (collection == pageCol && options.from == null &&
options.to == null) { options.to == null) {
var head = fullCol.models.slice(0, pageStart); var head = fullCol.models.slice(0, pageStart);
var tail = fullCol.models.slice(pageStart + pageCol.models.length); var tail = fullCol.models.slice(pageStart + pageCol.models.length);
fullCol.reset(head.concat(pageCol.models).concat(tail), options); fullCol.reset(head.concat(pageCol.models).concat(tail), options);
} }
else if (collection === fullCol) { else if (collection == fullCol) {
if (!(state.totalRecords = fullCol.models.length)) { if (!(state.totalRecords = fullCol.models.length)) {
state.totalRecords = null; state.totalRecords = null;
state.totalPages = null; state.totalPages = null;
@ -551,7 +568,7 @@
throw new RangeError("`firstPage must be 0 or 1`"); throw new RangeError("`firstPage must be 0 or 1`");
} }
state.lastPage = firstPage === 0 ? max(0, totalPages - 1) : totalPages; state.lastPage = firstPage === 0 ? max(0, totalPages - 1) : totalPages || firstPage;
if (mode == "infinite") { if (mode == "infinite") {
if (!links[currentPage + '']) { if (!links[currentPage + '']) {
@ -561,6 +578,8 @@
else if (currentPage < firstPage || else if (currentPage < firstPage ||
(totalPages > 0 && (totalPages > 0 &&
(firstPage ? currentPage > totalPages : currentPage >= totalPages))) { (firstPage ? currentPage > totalPages : currentPage >= totalPages))) {
var op = firstPage ? ">=" : ">";
throw new RangeError("`currentPage` must be firstPage <= currentPage " + throw new RangeError("`currentPage` must be firstPage <= currentPage " +
(firstPage ? ">" : ">=") + (firstPage ? ">" : ">=") +
" totalPages if " + firstPage + "-based. Got " + " totalPages if " + firstPage + "-based. Got " +
@ -681,7 +700,7 @@
var fullCollection = this.fullCollection; var fullCollection = this.fullCollection;
var handlers = this._handlers = this._handlers || {}, handler; var handlers = this._handlers = this._handlers || {}, handler;
if (mode != "server" && !fullCollection) { if (mode != "server" && !fullCollection) {
fullCollection = this._makeFullCollection(options.models || []); fullCollection = this._makeFullCollection(options.models || [], options);
fullCollection.pageableCollection = this; fullCollection.pageableCollection = this;
this.fullCollection = fullCollection; this.fullCollection = fullCollection;
var allHandler = this._makeCollectionEventHandler(this, fullCollection); var allHandler = this._makeCollectionEventHandler(this, fullCollection);
@ -856,7 +875,8 @@
[]; [];
if ((mode == "client" || (mode == "infinite" && !_isEmpty(pageModels))) && if ((mode == "client" || (mode == "infinite" && !_isEmpty(pageModels))) &&
!options.fetch) { !options.fetch) {
return this.reset(pageModels, _omit(options, "fetch")); this.reset(pageModels, _omit(options, "fetch"));
return this;
} }
if (mode == "infinite") options.url = this.links[pageNum]; if (mode == "infinite") options.url = this.links[pageNum];
@ -1310,8 +1330,8 @@
this.comparator = comparator; this.comparator = comparator;
} }
if (delComp) delete this.comparator; if (delComp) this.comparator = null;
if (delFullComp && fullCollection) delete fullCollection.comparator; if (delFullComp && fullCollection) fullCollection.comparator = null;
return this; return this;
} }

View File

@ -2,11 +2,13 @@
define( define(
[ [
'Series/EpisodeModel', 'Series/EpisodeModel',
'backbone.pageable' 'backbone.pageable',
], function (EpisodeModel, PagableCollection) { 'Mixins/AsPersistedStateCollection'
return PagableCollection.extend({ ], function (EpisodeModel, PagableCollection, AsPersistedStateCollection) {
var collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/missing', url : window.NzbDrone.ApiRoot + '/missing',
model: EpisodeModel, model: EpisodeModel,
tableName: 'missing',
state: { state: {
pageSize: 15, pageSize: 15,
@ -38,4 +40,6 @@ define(
return resp; return resp;
} }
}); });
return AsPersistedStateCollection.call(collection);
}); });

View File

@ -4,7 +4,7 @@ define(
'underscore', 'underscore',
'marionette', 'marionette',
'backgrid', 'backgrid',
'Missing/Collection', 'Missing/MissingCollection',
'Cells/SeriesTitleCell', 'Cells/SeriesTitleCell',
'Cells/EpisodeNumberCell', 'Cells/EpisodeNumberCell',
'Cells/EpisodeTitleCell', 'Cells/EpisodeTitleCell',
@ -121,7 +121,6 @@ define(
] ]
}; };
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
left : left :
[ [

View File

@ -0,0 +1,69 @@
'use strict';
define(
['underscore', 'Config'],
function (_, Config) {
return function () {
var originalInit = this.prototype.initialize;
this.prototype.initialize = function (options) {
options = options || {};
if (options.tableName) {
this.tableName = options.tableName;
}
if (!this.tableName && !options.tableName) {
throw 'tableName is required';
}
_setInitialState.call(this);
this.on('backgrid:sort', _storeStateFromBackgrid, this);
this.on('drone:sort', _storeState, this);
if (originalInit) {
originalInit.call(this, options);
}
};
var _setInitialState = function () {
var key = Config.getValue('{0}.sortKey'.format(this.tableName), this.state.sortKey);
var direction = Config.getValue('{0}.sortDirection'.format(this.tableName), this.state.order);
var order = parseInt(direction, 10);
this.state.sortKey = key;
this.state.order = order;
};
var _storeStateFromBackgrid = function (column, sortDirection) {
var order = _convertDirectionToInt(sortDirection);
var sortKey = column.has('sortValue') && _.isString(column.get('sortValue')) ? column.get('sortValue') : column.get('name');
Config.setValue('{0}.sortKey'.format(this.tableName), sortKey);
Config.setValue('{0}.sortDirection'.format(this.tableName), order);
};
var _storeState = function (sortModel, sortDirection) {
var order = _convertDirectionToInt(sortDirection);
var sortKey = sortModel.get('name');
Config.setValue('{0}.sortKey'.format(this.tableName), sortKey);
Config.setValue('{0}.sortDirection'.format(this.tableName), order);
};
var _convertDirectionToInt = function (dir) {
if (dir === 'ascending') {
return '-1';
}
return '1';
};
return this;
};
}
);

View File

@ -94,6 +94,8 @@ define(
model.set('rootFolderPath', rootFolderPath.get('path')); model.set('rootFolderPath', rootFolderPath.get('path'));
} }
model.edited = true;
}); });
SeriesCollection.save(); SeriesCollection.save();
@ -150,6 +152,7 @@ define(
SeriesCollection.each(function (model) { SeriesCollection.each(function (model) {
model.trigger('backgrid:select', model, false); model.trigger('backgrid:select', model, false);
model.edited = false;
}); });
} }
}); });

View File

@ -68,7 +68,7 @@ define(
cell : QualityProfileCell cell : QualityProfileCell
}, },
{ {
name : 'monitored', name : 'seasonFolder',
label: 'Season Folder', label: 'Season Folder',
cell : SeasonFolderCell cell : SeasonFolderCell
}, },

View File

@ -13,12 +13,12 @@ define(
'Cells/QualityProfileCell', 'Cells/QualityProfileCell',
'Cells/EpisodeProgressCell', 'Cells/EpisodeProgressCell',
'Cells/SeriesActionsCell', 'Cells/SeriesActionsCell',
'Shared/Grid/DateHeaderCell',
'Cells/SeriesStatusCell', 'Cells/SeriesStatusCell',
'Series/Index/FooterView', 'Series/Index/FooterView',
'Series/Index/FooterModel', 'Series/Index/FooterModel',
'Shared/Toolbar/ToolbarLayout', 'Shared/Toolbar/ToolbarLayout',
'underscore' 'underscore',
'moment'
], function (Marionette, ], function (Marionette,
Backgrid, Backgrid,
PosterCollectionView, PosterCollectionView,
@ -31,68 +31,75 @@ define(
QualityProfileCell, QualityProfileCell,
EpisodeProgressCell, EpisodeProgressCell,
SeriesActionsCell, SeriesActionsCell,
DateHeaderCell,
SeriesStatusCell, SeriesStatusCell,
FooterView, FooterView,
FooterModel, FooterModel,
ToolbarLayout, ToolbarLayout,
_) { _,
Moment) {
return Marionette.Layout.extend({ return Marionette.Layout.extend({
template: 'Series/Index/SeriesIndexLayoutTemplate', template: 'Series/Index/SeriesIndexLayoutTemplate',
regions: { regions: {
seriesRegion: '#x-series', seriesRegion : '#x-series',
toolbar : '#x-toolbar', toolbar : '#x-toolbar',
footer : '#x-series-footer' footer : '#x-series-footer'
}, },
columns: columns: [
[ {
{ name : 'statusWeight',
name : 'statusWeight', label : '',
label : '', cell : SeriesStatusCell
cell : SeriesStatusCell },
}, {
{ name : 'title',
name : 'title', label : 'Title',
label : 'Title', cell : SeriesTitleCell,
cell : SeriesTitleCell, cellValue: 'this'
cellValue: 'this' },
}, {
{ name : 'seasonCount',
name : 'seasonCount', label: 'Seasons',
label: 'Seasons', cell : 'integer'
cell : 'integer' },
}, {
{ name : 'qualityProfileId',
name : 'qualityProfileId', label: 'Quality',
label: 'Quality', cell : QualityProfileCell
cell : QualityProfileCell },
}, {
{ name : 'network',
name : 'network', label: 'Network',
label: 'Network', cell : 'string'
cell : 'string' },
}, {
{ name : 'nextAiring',
name : 'nextAiring', label : 'Next Airing',
label : 'Next Airing', cell : RelativeDateCell,
cell : RelativeDateCell, sortValue : function (model) {
headerCell: DateHeaderCell var nextAiring = model.get('nextAiring');
},
{ if (!nextAiring) {
name : 'percentOfEpisodes', return Number.MAX_VALUE;
label : 'Episodes', }
cell : EpisodeProgressCell,
className: 'episode-progress-cell' return Moment(nextAiring).unix();
},
{
name : 'this',
label : '',
sortable: false,
cell : SeriesActionsCell
} }
], },
{
name : 'percentOfEpisodes',
label : 'Episodes',
cell : EpisodeProgressCell,
className: 'episode-progress-cell'
},
{
name : 'this',
label : '',
sortable: false,
cell : SeriesActionsCell
}
],
leftSideButtons: { leftSideButtons: {
type : 'default', type : 'default',
@ -130,25 +137,46 @@ define(
] ]
}, },
_showTable: function () { sortingOptions: {
this.currentView = new Backgrid.Grid({ type : 'sorting',
collection: SeriesCollection, storeState : false,
columns : this.columns, viewCollection: SeriesCollection,
className : 'table table-hover' items :
}); [
{
title: 'Title',
name : 'title'
},
{
title: 'Seasons',
name : 'seasonCount'
},
{
title: 'Quality',
name : 'qualityProfileId'
},
{
title: 'Network',
name : 'network'
},
{
title : 'Next Airing',
name : 'nextAiring',
sortValue : function (model) {
var nextAiring = model.get('nextAiring');
this._renderView(); if (!nextAiring) {
this._fetchCollection(); return Number.MAX_VALUE;
}, }
_showList: function () { return Moment(nextAiring).unix();
this.currentView = new ListCollectionView(); }
this._fetchCollection(); },
}, {
title: 'Episodes',
_showPosters: function () { name : 'percentOfEpisodes'
this.currentView = new PosterCollectionView(); }
this._fetchCollection(); ]
}, },
initialize: function () { initialize: function () {
@ -156,39 +184,8 @@ define(
this.listenTo(SeriesCollection, 'sync', this._renderView); this.listenTo(SeriesCollection, 'sync', this._renderView);
this.listenTo(SeriesCollection, 'remove', this._renderView); this.listenTo(SeriesCollection, 'remove', this._renderView);
},
_renderView: function () { this.viewButtons = {
if (SeriesCollection.length === 0) {
this.seriesRegion.show(new EmptyView());
this.toolbar.close();
}
else {
this.currentView.collection = SeriesCollection;
this.seriesRegion.show(this.currentView);
this._showToolbar();
this._showFooter();
}
},
onShow: function () {
this._showToolbar();
this._renderView();
},
_fetchCollection: function () {
SeriesCollection.fetch();
},
_showToolbar: function () {
if (this.toolbar.currentView) {
return;
}
var viewButtons = {
type : 'radio', type : 'radio',
storeState : true, storeState : true,
menuKey : 'seriesViewMode', menuKey : 'seriesViewMode',
@ -218,12 +215,71 @@ define(
} }
] ]
}; };
},
_showTable: function () {
this.currentView = new Backgrid.Grid({
collection: SeriesCollection,
columns : this.columns,
className : 'table table-hover'
});
this._fetchCollection();
},
_showList: function () {
this.currentView = new ListCollectionView({ collection: SeriesCollection });
this._fetchCollection();
},
_showPosters: function () {
this.currentView = new PosterCollectionView({ collection: SeriesCollection });
this._fetchCollection();
},
_renderView: function () {
if (SeriesCollection.length === 0) {
this.seriesRegion.show(new EmptyView());
this.toolbar.close();
}
else {
this.seriesRegion.show(this.currentView);
this._showToolbar();
this._showFooter();
}
},
onShow: function () {
this._showToolbar();
this._renderView();
},
_fetchCollection: function () {
SeriesCollection.fetch();
},
_showToolbar: function () {
if (this.toolbar.currentView) {
return;
}
var rightButtons = [
this.viewButtons
];
if (this.showSortingButton) {
rightButtons.splice(0, 0, this.sortingOptions);
}
rightButtons.splice(0, 0, this.sortingOptions);
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
right : right : rightButtons,
[
viewButtons
],
left : left :
[ [
this.leftSideButtons this.leftSideButtons

View File

@ -3,22 +3,24 @@ define(
[ [
'underscore', 'underscore',
'backbone', 'backbone',
'backbone.pageable',
'Series/SeriesModel', 'Series/SeriesModel',
'api!series' 'api!series',
], function (_, Backbone, SeriesModel, SeriesData) { 'Mixins/AsPersistedStateCollection'
var Collection = Backbone.Collection.extend({ ], function (_, Backbone, PageableCollection, SeriesModel, SeriesData, AsPersistedStateCollection) {
var Collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/series', url : window.NzbDrone.ApiRoot + '/series',
model: SeriesModel, model: SeriesModel,
tableName: 'series',
comparator: function (model) {
return model.get('title');
},
state: { state: {
sortKey: 'title', sortKey: 'title',
order : -1 order : -1,
pageSize: 1000
}, },
mode: 'client',
save: function () { save: function () {
var self = this; var self = this;
@ -31,7 +33,7 @@ define(
toJSON: function() toJSON: function()
{ {
return self.filter(function (model) { return self.filter(function (model) {
return model.hasChanged(); return model.edited;
}); });
} }
}); });
@ -45,6 +47,7 @@ define(
} }
}); });
var collection = new Collection(SeriesData); var MixedIn = AsPersistedStateCollection.call(Collection);
var collection = new MixedIn(SeriesData);
return collection; return collection;
}); });

View File

@ -1,66 +0,0 @@
'use strict';
define(
[
'backgrid',
'Shared/Grid/HeaderCell'
], function (Backgrid, NzbDroneHeaderCell) {
Backgrid.DateHeaderCell = NzbDroneHeaderCell.extend({
events: {
'click': 'onClick'
},
onClick: function (e) {
e.preventDefault();
var self = this;
var columnName = this.column.get('name');
if (this.column.get('sortable')) {
if (this.direction() === 'ascending') {
this.sort(columnName, 'descending', function (left, right) {
var leftVal = left.get(columnName);
var rightVal = right.get(columnName);
return self._comparator(leftVal, rightVal);
});
}
else {
this.sort(columnName, 'ascending', function (left, right) {
var leftVal = left.get(columnName);
var rightVal = right.get(columnName);
return self._comparator(rightVal, leftVal);
});
}
}
},
_comparator: function (leftVal, rightVal) {
if (!leftVal && !rightVal) {
return 0;
}
if (!leftVal) {
return -1;
}
if (!rightVal) {
return 1;
}
if (leftVal === rightVal) {
return 0;
}
if (leftVal > rightVal) {
return -1;
}
return 1;
}
});
return Backgrid.DateHeaderCell;
});

View File

@ -6,95 +6,134 @@ define(
], function (Backgrid) { ], function (Backgrid) {
Backgrid.NzbDroneHeaderCell = Backgrid.HeaderCell.extend({ Backgrid.NzbDroneHeaderCell = Backgrid.HeaderCell.extend({
events: { events: {
'click': 'onClick' 'click': 'onClick'
}, },
_originalInit: Backgrid.HeaderCell.prototype.initialize,
initialize: function (options) {
this._originalInit.call(this, options);
this.listenTo(this.collection, 'drone:sort', this.render);
},
render: function () { render: function () {
this.$el.empty(); this.$el.empty();
this.$el.append(this.column.get('label')); this.$el.append(this.column.get('label'));
if (this.column.get('sortable')) { var column = this.column;
var sortable = Backgrid.callByNeed(column.sortable(), column, this.collection);
if (sortable)
{
this.$el.addClass('sortable'); this.$el.addClass('sortable');
this.$el.append(' <i class="pull-right"></i>'); this.$el.append(' <i class="pull-right"></i>');
}
if (this.collection.state) { //Do we need this?
var sortKey = this.collection.state.sortKey; this.$el.addClass(column.get('name'));
var sortDir = this._convertIntToDirection(this.collection.state.order);
if (sortKey === this.column.get('name')) { this.delegateEvents();
this.$el.children('i').addClass(this._convertDirectionToIcon(sortDir)); this.direction(column.get('direction'));
this._direction = sortDir;
} if (this.collection.state) {
var key = this.collection.state.sortKey;
var order = this.collection.state.order;
if (key === this.column.get('name')) {
this._setSortIcon(order);
}
else {
this._removeSortIcon();
} }
} }
this.delegateEvents();
return this; return this;
}, },
direction: function (dir) { direction: function (dir) {
this.$el.children('i').removeClass('icon-sort-up icon-sort-down');
if (arguments.length) { if (arguments.length) {
if (this._direction) { if (dir)
this.$el.children('i').removeClass(this._convertDirectionToIcon(this._direction)); {
this._setSortIcon(dir);
} }
if (dir) {
this.$el.children('i').addClass(this._convertDirectionToIcon(dir)); this.column.set('direction', dir);
}
this._direction = dir;
} }
return this._direction; var columnDirection = this.column.get('direction');
if (!columnDirection && this.collection.state) {
var key = this.collection.state.sortKey;
var order = this.collection.state.order;
if (key === this.column.get('name')) {
columnDirection = order;
}
}
return columnDirection;
}, },
onClick: function (e) { onClick: function (e) {
e.preventDefault(); e.preventDefault();
var columnName = this.column.get('name'); var collection = this.collection;
var event = 'backgrid:sort';
if (this.column.get('sortable')) { function toggleSort(header, col) {
if (this.direction() === 'ascending') { collection.state.sortKey = col.get('name');
this.sort(columnName, 'descending', function (left, right) { var direction = header.direction();
var leftVal = left.get(columnName); if (direction === 'ascending' || direction === -1)
var rightVal = right.get(columnName); {
if (leftVal === rightVal) { collection.state.order = 'descending';
return 0; collection.trigger(event, col, 'descending');
}
else if (leftVal > rightVal) {
return -1;
}
return 1;
});
} }
else { else
this.sort(columnName, 'ascending', function (left, right) { {
var leftVal = left.get(columnName); collection.state.order = 'ascending';
var rightVal = right.get(columnName); collection.trigger(event, col, 'ascending');
if (leftVal === rightVal) {
return 0;
}
else if (leftVal < rightVal) {
return -1;
}
return 1;
});
} }
} }
var column = this.column;
var sortable = Backgrid.callByNeed(column.sortable(), column, this.collection);
if (sortable) {
toggleSort(this, column);
}
},
_resetCellDirection: function (columnToSort, direction) {
if (columnToSort !== this.column)
{
this.direction(null);
}
else
{
this.direction(direction);
}
}, },
_convertDirectionToIcon: function (dir) { _convertDirectionToIcon: function (dir) {
if (dir === 'ascending') { if (dir === 'ascending' || dir === -1) {
return 'icon-sort-up'; return 'icon-sort-up';
} }
return 'icon-sort-down'; return 'icon-sort-down';
}, },
_convertIntToDirection: function (dir) { _setSortIcon: function (dir) {
if (dir === '-1') { this._removeSortIcon();
return 'ascending'; this.$el.children('i').addClass(this._convertDirectionToIcon(dir));
} },
return 'descending'; _removeSortIcon: function () {
this.$el.children('i').removeClass('icon-sort-up icon-sort-down');
} }
}); });

View File

@ -1,13 +1,29 @@
'use strict'; 'use strict';
define( define(
[ [
'underscore',
'backbone' 'backbone'
], function (Backbone) { ], function (_, Backbone) {
return Backbone.Model.extend({ return Backbone.Model.extend({
defaults: { defaults: {
'target' : '/nzbdrone/route', 'target' : '/nzbdrone/route',
'title' : '', 'title' : '',
'active' : false, 'active' : false,
'tooltip': undefined } 'tooltip': undefined
},
sortValue: function () {
var sortValue = this.get('sortValue');
if (_.isString(sortValue)) {
return this[sortValue];
}
else if (_.isFunction(sortValue)) {
return sortValue;
}
return function (model, colName) {
return model.get(colName);
};
}
}); });
}); });

View File

@ -13,7 +13,6 @@ define(
'click': 'onClick' 'click': 'onClick'
}, },
initialize: function () { initialize: function () {
this.storageKey = this.model.get('menuKey') + ':' + this.model.get('key'); this.storageKey = this.model.get('menuKey') + ':' + this.model.get('key');
@ -53,7 +52,6 @@ define(
callback.call(this.model.ownerContext); callback.call(this.model.ownerContext);
} }
} }
}); });
}); });

View File

@ -0,0 +1,87 @@
'use strict';
define(
[
'backbone.pageable',
'marionette',
'Shared/Toolbar/Sorting/SortingButtonView'
], function (PageableCollection, Marionette, ButtonView) {
return Marionette.CompositeView.extend({
itemView : ButtonView,
template : 'Shared/Toolbar/Sorting/SortingButtonCollectionViewTemplate',
itemViewContainer: '.dropdown-menu',
initialize: function (options) {
this.viewCollection = options.viewCollection;
this.listenTo(this.viewCollection, 'drone:sort', this.sort);
},
itemViewOptions: function () {
return {
viewCollection: this.viewCollection
};
},
sort: function (sortModel, sortDirection) {
var collection = this.viewCollection;
var order;
if (sortDirection === 'ascending') {
order = -1;
}
else if (sortDirection === 'descending') {
order = 1;
}
else {
order = null;
}
var comparator = this.makeComparator(sortModel.get('name'), order,
order ?
sortModel.sortValue() :
function (model) {
return model.cid;
});
if (PageableCollection &&
collection instanceof PageableCollection) {
collection.setSorting(order && sortModel.get('name'), order,
{sortValue: sortModel.sortValue()});
if (collection.mode === 'client') {
if (collection.fullCollection.comparator === null) {
collection.fullCollection.comparator = comparator;
}
collection.fullCollection.sort();
}
else {
collection.fetch({reset: true});
}
}
else {
collection.comparator = comparator;
collection.sort();
}
return this;
},
makeComparator: function (attr, order, func) {
return function (left, right) {
// extract the values from the models
var l = func(left, attr), r = func(right, attr), t;
// if descending order, swap left and right
if (order === 1) t = l, l = r, r = t;
// compare as usual
if (l === r) return 0;
else if (l < r) return -1;
return 1;
};
}
});
});

View File

@ -0,0 +1,8 @@
<div class="btn-group sorting-buttons">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Sort <span class="caret"></span>
</a>
<ul class="dropdown-menu">
</ul>
</div>

View File

@ -0,0 +1,84 @@
'use strict';
define(
[
'backbone',
'marionette',
'underscore'
], function (Backbone, Marionette, _) {
return Marionette.ItemView.extend({
template : 'Shared/Toolbar/Sorting/SortingButtonViewTemplate',
tagName : 'li',
ui: {
icon: 'i'
},
events: {
'click': 'onClick'
},
initialize: function (options) {
this.viewCollection = options.viewCollection;
this.listenTo(this.viewCollection, 'drone:sort', this.render);
this.listenTo(this.viewCollection, 'backgrid:sort', this.render);
},
onRender: function () {
if (this.viewCollection.state) {
var key = this.viewCollection.state.sortKey;
var order = this.viewCollection.state.order;
if (key === this.model.get('name')) {
this._setSortIcon(order);
}
else {
this._removeSortIcon();
}
}
},
onClick: function (e) {
e.preventDefault();
var collection = this.viewCollection;
var event = 'drone:sort';
collection.state.sortKey = this.model.get('name');
var direction = collection.state.order;
if (direction === 'ascending' || direction === -1)
{
collection.state.order = 'descending';
collection.trigger(event, this.model, 'descending');
}
else
{
collection.state.order = 'ascending';
collection.trigger(event, this.model, 'ascending');
}
},
_convertDirectionToIcon: function (dir) {
if (dir === 'ascending' || dir === -1) {
return 'icon-sort-up';
}
return 'icon-sort-down';
},
_setSortIcon: function (dir) {
this._removeSortIcon();
this.ui.icon.addClass(this._convertDirectionToIcon(dir));
},
_removeSortIcon: function () {
this.ui.icon.removeClass('icon-sort-up icon-sort-down');
}
});
});

View File

@ -0,0 +1,4 @@
<a href="#">
<span class="sorting-title">{{title}}</span>
<i class=""></i>
</a>

View File

@ -6,8 +6,9 @@ define(
'Shared/Toolbar/ButtonModel', 'Shared/Toolbar/ButtonModel',
'Shared/Toolbar/Radio/RadioButtonCollectionView', 'Shared/Toolbar/Radio/RadioButtonCollectionView',
'Shared/Toolbar/Button/ButtonCollectionView', 'Shared/Toolbar/Button/ButtonCollectionView',
'Shared/Toolbar/Sorting/SortingButtonCollectionView',
'underscore' 'underscore'
], function (Marionette, ButtonCollection, ButtonModel, RadioButtonCollectionView, ButtonCollectionView,_) { ], function (Marionette, ButtonCollection, ButtonModel, RadioButtonCollectionView, ButtonCollectionView, SortingButtonCollectionView, _) {
return Marionette.Layout.extend({ return Marionette.Layout.extend({
template: 'Shared/Toolbar/ToolbarLayoutTemplate', template: 'Shared/Toolbar/ToolbarLayoutTemplate',
@ -78,6 +79,15 @@ define(
}); });
break; break;
} }
case 'sorting':
{
buttonGroupView = new SortingButtonCollectionView({
collection : groupCollection,
menu : buttonGroup,
viewCollection: buttonGroup.viewCollection
});
break;
}
default : default :
{ {
buttonGroupView = new ButtonCollectionView({ buttonGroupView = new ButtonCollectionView({

View File

@ -1,8 +1,8 @@
<div class="pull-left page-toolbar"> <div class="pull-left page-toolbar">
<div class="x-toolbar-left-1"/> <div class="toolbar-group x-toolbar-left-1"/>
<div class="x-toolbar-left-2"/> <div class="toolbar-group x-toolbar-left-2"/>
</div> </div>
<div class="pull-right page-toolbar"> <div class="pull-right page-toolbar">
<div class="x-toolbar-right-1"/> <div class="toolbar-group x-toolbar-right-1"/>
<div class="x-toolbar-right-2"/> <div class="toolbar-group x-toolbar-right-2"/>
</div> </div>

View File

@ -1,10 +1,16 @@
'use strict'; 'use strict';
define(['backbone.pageable', 'System/Logs/LogsModel'], define(
function (PagableCollection, LogsModel) { [
return PagableCollection.extend({ 'backbone.pageable',
'System/Logs/LogsModel',
'Mixins/AsPersistedStateCollection'
],
function (PagableCollection, LogsModel, AsPersistedStateCollection) {
var collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/log', url : window.NzbDrone.ApiRoot + '/log',
model: LogsModel, model: LogsModel,
tableName: 'logs',
state: { state: {
pageSize: 50, pageSize: 50,
@ -36,4 +42,6 @@ define(['backbone.pageable', 'System/Logs/LogsModel'],
return resp; return resp;
} }
}); });
return AsPersistedStateCollection.call(collection);
}); });

View File

@ -165,7 +165,8 @@ require.config({
renderable: true, renderable: true,
formatter : undefined, formatter : undefined,
cell : undefined, cell : undefined,
headerCell: 'NzbDrone' headerCell: 'NzbDrone',
sortType : 'toggle'
}; };
}); });