Add scheduler plugin page to webui

This commit is contained in:
samuel337 2012-01-25 00:14:26 +00:00 committed by Calum Lind
parent 101ad99c14
commit 4a7876f203
2 changed files with 619 additions and 100 deletions

View File

@ -1,106 +1,624 @@
ScheduleSelectPanel = Ext.extend(Ext.form.FieldSet, { /*
constructor: function(config) { Script: scheduler.js
config = Ext.apply({ The client-side javascript code for the Scheduler plugin.
title: _('Schedule'),
autoHeight: true
}, config);
ScheduleSelectPanel.superclass.constructor.call(this, config);
},
onRender: function(ct, position) { Copyright:
ScheduleSelectPanel.superclass.onRender.call(this, ct, position); (C) samuel337 2011
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
var dom = this.body.dom; This program is distributed in the hope that it will be useful,
var table = createEl(dom, 'table'); but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
function createEl(parent, type) { You should have received a copy of the GNU General Public License
var el = document.createElement(type); along with this program. If not, write to:
parent.appendChild(el); The Free Software Foundation, Inc.,
return el; 51 Franklin Street, Fifth Floor
} Boston, MA 02110-1301, USA.
Ext.each(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], function(day) { In addition, as a special exception, the copyright holders give
var row = createEl(table, 'tr'); permission to link the code of portions of this program with the OpenSSL
var label = createEl(row, 'th'); library.
label.setAttribute('style', 'font-weight: bold; padding-right: 5px;'); You must obey the GNU General Public License in all respects for all of
label.innerHTML = day; the code used other than OpenSSL. If you modify file(s) with this
for (var hour = 0; hour < 24; hour++) { exception, you may extend this exception to your version of the file(s),
var cell = createEl(row, 'td'); but you are not obligated to do so. If you do not wish to do so, delete
cell.setAttribute('style', 'border: 1px solid Green; width: 16px; height: 20px; background: LightGreen;'); this exception statement from your version. If you delete this exception
} statement from all source files in the program, then also delete it here.
}); */
}
Ext.ns('Deluge.ux');
Deluge.ux.ScheduleSelector = Ext.extend(Ext.form.FieldSet, {
title: _('Schedule'),
autoHeight: true,
style: 'margin-bottom: 0px; padding-bottom: 0px;',
border: false,
states: [
{
name: 'Normal',
backgroundColor: 'LightGreen',
borderColor: 'DarkGreen',
value: 0
},
{
name: 'Throttled',
backgroundColor: 'Yellow',
borderColor: 'Gold',
value: 1
},
{
name: 'Paused',
backgroundColor: 'OrangeRed',
borderColor: 'FireBrick',
value: 2
}
],
daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
initComponent: function() {
Deluge.ux.ScheduleSelector.superclass.initComponent.call(this);
// ExtJS' radiogroup implementation is very broken for styling.
/*this.stateBrush = this.add({
xtype: 'radiogroup',
fieldLabel: _('State Brush'),
name: 'current_state_brush',
submitValue: false,
items: [
{ boxLabel: 'Normal', name: 'current_state_brush', inputValue: 0 },
{ boxLabel: 'Throttled', name: 'current_state_brush', inputValue: 1, checked: true },
{ boxLabel: 'Paused', name: 'current_state_brush', inputValue: 2 },
]
});*/
},
onRender: function(ct, position) {
Deluge.ux.ScheduleSelector.superclass.onRender.call(this, ct, position);
var dom = this.body.dom;
function createEl(parent, type) {
var el = document.createElement(type);
parent.appendChild(el);
return el;
}
// create state brushes
// tack a random number to the end to avoid clashes
this.stateBrushName = 'schedule-state-brush-' + Math.round(Math.random() * 10000);
var el1 = createEl(dom, 'div');
var el2 = createEl(el1, 'div');
this.stateBrush = el2;
el2.id = this.stateBrushName;
// for webkit
var floatAttr = 'float';
if (el2.style.float == undefined) {
// for firefox
if (el2.style.cssFloat != undefined) floatAttr = 'cssFloat';
// for IE
if (el2.style.styleFloat != undefined) floatAttr = 'styleFloat';
}
el2.style[floatAttr] = 'right';
for (var i=0; i < this.states.length; i++) {
var el3 = createEl(el2, 'input');
el3.type = 'radio';
el3.value = this.states[i].value;
el3.name = this.stateBrushName;
el3.id = this.stateBrushName + '-' + this.states[i].name;
// isn't the first one
if (i > 0) el3.style.marginLeft = '7px';
// assume the first is the default state, so make the 2nd one the default brush
if (i == 1) el3.checked = true;
var el4 = createEl(el2, 'label');
el4.appendChild(document.createTextNode(this.states[i].name));
el4.htmlFor = el3.id;
el4.style.backgroundColor = this.states[i].backgroundColor;
el4.style.borderBottom = '2px solid ' + this.states[i].borderColor;
el4.style.padding = '2px 3px';
el4.style.marginLeft = '3px';
}
el1.appendChild(document.createTextNode('Select a state brush:'));
el1.style.marginBottom = '10px';
// keep the radio buttons separate from the time bars
createEl(dom, 'div').style.clear = 'both';
var table = createEl(dom, 'table');
table.cellSpacing = 0;
// cache access to cells for easier access later
this.scheduleCells = { };
Ext.each(this.daysOfWeek, function(day) {
var cells = [ ];
var row = createEl(table, 'tr');
var label = createEl(row, 'th');
label.setAttribute('style', 'font-weight: bold; padding-right: 5px;');
label.appendChild(document.createTextNode(day));
for (var hour = 0; hour < 24; hour++) {
var cell = createEl(row, 'td');
// assume the first state is the default state
cell.currentValue = cell.oldValue = this.states[0].value;
cell.day = day;
cell.hour = hour;
cell.width = '16px';
cell.height = '20px';
cell.style.border = '1px solid #999999';
// don't repeat borders in between cells
if (hour != 23) // not the last cell
cell.style.borderRight = 'none';
this.updateCell(cell);
cells.push(cell);
cell = Ext.get(cell);
cell.on('click', this.onCellClick, this);
cell.on('mouseover', this.onCellMouseOver, this);
cell.on('mouseout', this.onCellMouseOut, this);
cell.on('mousedown', this.onCellMouseDown, this);
cell.on('mouseup', this.onCellMouseUp, this);
}
// insert gap row to provide visual separation
row = createEl(table, 'tr');
// blank cell to create gap
createEl(row, 'td').height = '3px';
this.scheduleCells[day] = cells;
}, this);
},
updateCell: function(cell) {
// sanity check
if (cell.currentValue == undefined) return;
for (var i in this.states) {
var curState = this.states[i];
if (curState.value == cell.currentValue) {
cell.style.background = curState.backgroundColor;
break;
}
}
},
getCurrentBrushValue: function() {
var v = null;
var brushes = Ext.get(this.body.dom).findParent('form').elements[this.stateBrushName];
Ext.each(brushes, function(b) {
if (b.checked)
v = b.value;
});
return v;
},
onCellClick: function(event, cell) {
cell.oldValue = cell.currentValue;
this.dragAnchor = null;
},
onCellMouseDown: function(event, cell) {
this.dragAnchor = cell;
},
onCellMouseUp: function(event, cell) {
// if we're dragging...
if (this.dragAnchor) {
// set all those between here and the anchor to the new values
if (cell.hour > this.dragAnchor.hour)
this.confirmCells(cell.day, this.dragAnchor.hour, cell.hour);
else if (cell.hour < this.dragAnchor.hour)
this.confirmCells(cell.day, cell.hour, this.dragAnchor.hour);
else
this.confirmCells(cell.day, cell.hour, cell.hour);
this.hideCellLeftTooltip();
this.hideCellRightTooltip();
this.dragAnchor = null;
}
},
onCellMouseOver: function(event, cell) {
// LEFT TOOL TIP
// if it isn't showing and we're dragging, show it.
// otherwise if dragging, leave it alone unless we're dragging to the left.
// if we're not dragging, show it.
var leftTooltipCell = null;
if (!this.dragAnchor)
leftTooltipCell = cell;
else if ((this.dragAnchor && this.isCellLeftTooltipHidden()) ||
(this.dragAnchor && this.dragAnchor.hour > cell.hour))
leftTooltipCell = this.dragAnchor;
if (leftTooltipCell) {
var hour = leftTooltipCell.hour;
var pm = false;
// convert to 12-hour time
if (hour >= 12) {
pm = true;
if (hour > 12) hour -= 12;
}
// change 0 hour to 12am
else if (hour == 0) {
hour = 12;
}
this.showCellLeftTooltip(hour + ' ' + (pm ? 'pm' : 'am'), leftTooltipCell);
}
// RIGHT TOOL TIP
var rightTooltipCell = null;
if (this.dragAnchor) {
if (this.dragAnchor.hour == cell.hour)
this.hideCellRightTooltip();
else if (this.dragAnchor.hour > cell.hour && this.isCellRightTooltipHidden())
rightTooltipCell = this.dragAnchor;
else // cell.hour > this.dragAnchor.hour
rightTooltipCell = cell;
}
if (rightTooltipCell) {
var hour = rightTooltipCell.hour;
var pm = false;
// convert to 12-hour time
if (hour >= 12) {
pm = true;
if (hour > 12) hour -= 12;
}
// change 0 hour to 12am
else if (hour == 0) {
hour = 12;
}
this.showCellRightTooltip(hour + ' ' + (pm ? 'pm' : 'am'), rightTooltipCell);
}
// preview colour change and
// revert state for all those on the outer side of the drag if dragging
if (this.dragAnchor) {
if (cell.day != this.dragAnchor.day) {
// dragged into another day. Abort! Abort!
Ext.each(this.daysOfWeek, function(day) {
this.revertCells(day, 0, 23);
}, this);
this.dragAnchor = null;
this.hideCellLeftTooltip();
this.hideCellRightTooltip();
}
else if (cell.hour > this.dragAnchor.hour) {
// dragging right
this.revertCells(cell.day, cell.hour+1, 23);
this.previewCells(cell.day, this.dragAnchor.hour, cell.hour);
}
else if (cell.hour < this.dragAnchor.hour) {
// dragging left
this.revertCells(cell.day, 0, cell.hour-1);
this.previewCells(cell.day, cell.hour, this.dragAnchor.hour);
}
else {
// back to anchor cell
// don't know if it is from right or left, so revert all except this
this.revertCells(cell.day, cell.hour+1, 23);
this.revertCells(cell.day, 0, cell.hour-1);
}
}
else {
// not dragging, just preview this cell
this.previewCells(cell.day, cell.hour, cell.hour);
}
},
onCellMouseOut: function(event, cell) {
if (!this.dragAnchor)
this.hideCellLeftTooltip();
// revert state. If new state has been set, old and new will be equal.
// if dragging, this will be handled by the next mouse over
if (this.dragAnchor == null && cell.oldValue != cell.currentValue) {
this.revertCells(cell.day, cell.hour, cell.hour);
}
},
previewCells: function(day, fromHour, toHour) {
var cells = this.scheduleCells[day];
var curBrushValue = this.getCurrentBrushValue();
if (toHour > cells.length) toHour = cells.length;
for (var i=fromHour; i <= toHour; i++) {
if (cells[i].currentValue != curBrushValue) {
cells[i].oldValue = cells[i].currentValue;
cells[i].currentValue = curBrushValue;
this.updateCell(cells[i]);
}
}
},
revertCells: function(day, fromHour, toHour) {
var cells = this.scheduleCells[day];
if (toHour > cells.length) toHour = cells.length;
for (var i=fromHour; i <= toHour; i++) {
cells[i].currentValue = cells[i].oldValue;
this.updateCell(cells[i]);
}
},
confirmCells: function(day, fromHour, toHour) {
var cells = this.scheduleCells[day];
if (toHour > cells.length) toHour = cells.length;
for (var i=fromHour; i <= toHour; i++) {
if (cells[i].currentValue != cells[i].oldValue) {
cells[i].oldValue = cells[i].currentValue;
}
}
},
showCellLeftTooltip: function(text, cell) {
var tooltip = this.cellLeftTooltip;
if (!tooltip) {
// no cached left tooltip exists, create one
tooltip = document.createElement('div');
this.cellLeftTooltip = tooltip;
this.body.dom.appendChild(tooltip);
tooltip.style.position = 'absolute';
tooltip.style.backgroundColor = '#F2F2F2';
tooltip.style.border = '1px solid #333333';
tooltip.style.padding = '1px 3px';
tooltip.style.opacity = 0.8;
}
// remove all existing children
while (tooltip.childNodes.length > 0) {
tooltip.removeChild(tooltip.firstChild);
}
// add the requested text
tooltip.appendChild(document.createTextNode(text));
// place the tooltip
Ext.get(tooltip).alignTo(cell, 'br-tr');
// make it visible
tooltip.style.visibility = 'visible';
},
hideCellLeftTooltip: function() {
if (this.cellLeftTooltip) {
this.cellLeftTooltip.style.visibility = 'hidden';
}
},
isCellLeftTooltipHidden: function() {
if (this.cellLeftTooltip)
return this.cellLeftTooltip.style.visibility == 'hidden';
else
return true;
},
showCellRightTooltip: function(text, cell) {
var tooltip = this.cellRightTooltip;
if (!tooltip) {
// no cached left tooltip exists, create one
tooltip = document.createElement('div');
this.cellRightTooltip = tooltip;
this.body.dom.appendChild(tooltip);
tooltip.style.position = 'absolute';
tooltip.style.backgroundColor = '#F2F2F2';
tooltip.style.border = '1px solid #333333';
tooltip.style.padding = '1px 3px';
tooltip.style.opacity = 0.8;
}
// remove all existing children
while (tooltip.childNodes.length > 0) {
tooltip.removeChild(tooltip.firstChild);
}
// add the requested text
tooltip.appendChild(document.createTextNode(text));
// place the tooltip
Ext.get(tooltip).alignTo(cell, 'bl-tl');
// make it visible
tooltip.style.visibility = 'visible';
},
hideCellRightTooltip: function() {
if (this.cellRightTooltip) {
this.cellRightTooltip.style.visibility = 'hidden';
}
},
isCellRightTooltipHidden: function() {
if (this.cellRightTooltip)
return this.cellRightTooltip.style.visibility == 'hidden';
else
return true;
},
getConfig: function() {
var config = [ ];
for (var i=0; i < 24; i++) {
var hourConfig = [ 0, 0, 0, 0, 0, 0, 0 ];
for (var j=0; j < this.daysOfWeek.length; j++) {
hourConfig[j] = parseInt(this.scheduleCells[this.daysOfWeek[j]][i].currentValue);
}
config.push(hourConfig);
}
return config;
},
setConfig: function(config) {
for (var i=0; i < 24; i++) {
var hourConfig = config[i];
for (var j=0; j < this.daysOfWeek.length; j++) {
var cell = this.scheduleCells[this.daysOfWeek[j]][i];
cell.currentValue = cell.oldValue = hourConfig[j];
this.updateCell(cell);
}
}
}
}); });
SchedulerPreferences = Ext.extend(Ext.Panel, { Ext.ns('Deluge.ux.preferences');
constructor: function(config) {
config = Ext.apply({
border: false,
title: _('Scheduler')
}, config);
SchedulerPreferences.superclass.constructor.call(this, config);
},
initComponent: function() { Deluge.ux.preferences.SchedulerPage = Ext.extend(Ext.Panel, {
SchedulerPreferences.superclass.initComponent.call(this);
this.form = this.add({ border: false,
xtype: 'form', title: _('Scheduler'),
layout: 'form', layout: 'fit',
border: false,
autoHeight: true
});
this.schedule = this.form.add(new ScheduleSelectPanel()); initComponent: function() {
Deluge.ux.preferences.SchedulerPage.superclass.initComponent.call(this);
this.slowSettings = this.form.add({ this.form = this.add({
xtype: 'fieldset', xtype: 'form',
title: _('Slow Settings'), layout: 'form',
autoHeight: true, border: false,
defaultType: 'uxspinner' autoHeight: true
}); });
this.downloadLimit = this.slowSettings.add({ this.schedule = this.form.add(new Deluge.ux.ScheduleSelector());
fieldLabel: _('Download Limit'),
name: 'download_limit'
});
this.uploadLimit = this.slowSettings.add({
fieldLabel: _('Upload Limit'),
name: 'upload_limit'
});
this.activeTorrents = this.slowSettings.add({
fieldLabel: _('Active Torrents'),
name: 'active_torrents'
});
},
onRender: function(ct, position) { this.slowSettings = this.form.add({
SchedulerPreferences.superclass.onRender.call(this, ct, position); xtype: 'fieldset',
this.form.layout = new Ext.layout.FormLayout(); border: false,
this.form.layout.setContainer(this); title: _('Throttled Settings'),
this.form.doLayout(); autoHeight: true,
}, defaultType: 'spinnerfield',
defaults: {
minValue: -1,
maxValue: 99999
},
style: 'margin-top: 5px; margin-bottom: 0px; padding-bottom: 0px;',
labelWidth: 200
});
onShow: function() { this.downloadLimit = this.slowSettings.add({
SchedulerPreferences.superclass.onShow.call(this); fieldLabel: _('Maximum Download Speed (KiB/s)'),
} name: 'download_limit',
width: 80,
value: -1,
decimalPrecision: 0
});
this.uploadLimit = this.slowSettings.add({
fieldLabel: _('Maximum Upload Speed (KiB/s)'),
name: 'upload_limit',
width: 80,
value: -1,
decimalPrecision: 0
});
this.activeTorrents = this.slowSettings.add({
fieldLabel: _('Active Torrents'),
name: 'active_torrents',
width: 80,
value: -1,
decimalPrecision: 0
});
this.activeDownloading = this.slowSettings.add({
fieldLabel: _('Active Downloading'),
name: 'active_downloading',
width: 80,
value: -1,
decimalPrecision: 0
});
this.activeSeeding = this.slowSettings.add({
fieldLabel: _('Active Seeding'),
name: 'active_seeding',
width: 80,
value: -1,
decimalPrecision: 0
});
deluge.preferences.on('show', this.updateConfig, this);
},
onRender: function(ct, position) {
Deluge.ux.preferences.SchedulerPage.superclass.onRender.call(this, ct, position);
this.form.layout = new Ext.layout.FormLayout();
this.form.layout.setContainer(this);
this.form.doLayout();
},
onApply: function() {
// build settings object
var config = { }
config['button_state'] = this.schedule.getConfig();
config['low_down'] = this.downloadLimit.getValue();
config['low_up'] = this.uploadLimit.getValue();
config['low_active'] = this.activeTorrents.getValue();
config['low_active_down'] = this.activeDownloading.getValue();
config['low_active_up'] = this.activeSeeding.getValue();
deluge.client.scheduler.set_config(config);
},
onOk: function() {
this.onApply();
},
afterRender: function() {
Deluge.ux.preferences.SchedulerPage.superclass.afterRender.call(this);
this.updateConfig();
},
updateConfig: function() {
deluge.client.scheduler.get_config({
success: function(config) {
this.schedule.setConfig(config['button_state']);
this.downloadLimit.setValue(config['low_down']);
this.uploadLimit.setValue(config['low_up']);
this.activeTorrents.setValue(config['low_active']);
this.activeDownloading.setValue(config['low_active_down']);
this.activeSeeding.setValue(config['low_active_up']);
},
scope: this
});
}
}); });
SchedulerPlugin = Ext.extend(Deluge.Plugin, { Deluge.plugins.SchedulerPlugin = Ext.extend(Deluge.Plugin, {
constructor: function(config) {
config = Ext.apply({
name: "Scheduler"
}, config);
SchedulerPlugin.superclass.constructor.call(this, config);
},
onDisable: function() { name: 'Scheduler',
Deluge.Preferences.removePage(this.prefsPage);
},
onEnable: function() { onDisable: function() {
this.prefsPage = new SchedulerPreferences(); deluge.preferences.removePage(this.prefsPage);
this.prefsPage = Deluge.Preferences.addPage(this.prefsPage); },
}
onEnable: function() {
this.prefsPage = deluge.preferences.addPage(new Deluge.ux.preferences.SchedulerPage());
}
}); });
new SchedulerPlugin(); Deluge.registerPlugin('Scheduler', Deluge.plugins.SchedulerPlugin);

View File

@ -48,3 +48,4 @@ log = logging.getLogger(__name__)
class WebUI(WebPluginBase): class WebUI(WebPluginBase):
scripts = [get_resource("scheduler.js")] scripts = [get_resource("scheduler.js")]
debug_scripts = scripts