mirror of
https://git.deluge-torrent.org/deluge
synced 2025-04-20 19:44:52 +00:00
initial commit of the big js layout overhall (ui loads but there are parts missing)
This commit is contained in:
parent
b4b95c9423
commit
92d19fd58d
64 changed files with 12705 additions and 1095 deletions
99
deluge/ui/web/build
Executable file
99
deluge/ui/web/build
Executable file
|
@ -0,0 +1,99 @@
|
|||
#!/bin/sh
|
||||
|
||||
BASE_DIR="${PWD}/`dirname ${0}`"
|
||||
BASE_DIR=`readlink -f "$BASE_DIR"`
|
||||
SOURCE_DIR=`readlink -f "${BASE_DIR}/${1}"`
|
||||
BUILD_NAME=`basename $1`
|
||||
BUILD_FILE="${SOURCE_DIR}/.build"
|
||||
BUILD_DATA="${SOURCE_DIR}/.build_data"
|
||||
BUILD_DIR=`dirname $SOURCE_DIR`
|
||||
BUILD_DATA_GENERATED=0
|
||||
|
||||
YUI_COMPRESSOR=`which yuicompressor`
|
||||
#JSDOC=`which jsdoc-toolkit`
|
||||
BUILD_TYPE="js"
|
||||
|
||||
add_file()
|
||||
{
|
||||
FILES="${FILES} ${SOURCE_DIR}/$1"
|
||||
}
|
||||
|
||||
check_file()
|
||||
{
|
||||
# No build data is stored so return 1 since we can't make any guesses.
|
||||
[ ! -e $BUILD_DATA ] && return 1;
|
||||
|
||||
FILE=$1
|
||||
LAST_BUILD=`grep $FILE $BUILD_DATA`
|
||||
if [ $? == 1 ]; then return 1; fi;
|
||||
|
||||
CURRENT=`grep $FILE $BUILD_DATA.tmp`
|
||||
|
||||
[ "$CURRENT" != "$LAST_BUILD" ] && return 1
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gen_build_data()
|
||||
{
|
||||
for FILE in $FILES; do
|
||||
md5sum $FILE >> $BUILD_DATA.tmp
|
||||
done;
|
||||
BUILD_DATA_GENERATED=1
|
||||
}
|
||||
|
||||
check()
|
||||
{
|
||||
# no build data exists so we can't make guesses
|
||||
[ -e $BUILD_DATA ] || return 1
|
||||
|
||||
# remove an existing temp build data file
|
||||
[ -e $BUILD_DATA.tmp ] && rm $BUILD_DATA.tmp
|
||||
|
||||
# generate new build data
|
||||
gen_build_data
|
||||
|
||||
# check files for building
|
||||
NEEDS_BUILDING=0
|
||||
for FILE in $FILES; do
|
||||
check_file $FILE || NEEDS_BUILDING=1
|
||||
done;
|
||||
|
||||
# return the result
|
||||
return $NEEDS_BUILDING
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
echo "building $BUILD_NAME"
|
||||
|
||||
# generated the unminified version
|
||||
cat $FILES > $BUILD_DIR/$BUILD_NAME-debug.js
|
||||
|
||||
# generated the minified version
|
||||
$YUI_COMPRESSOR --type=$BUILD_TYPE -o "$BUILD_DIR/$BUILD_NAME.js" \
|
||||
$BUILD_DIR/$BUILD_NAME-debug.js
|
||||
|
||||
# generate build data incase there hasn't been any
|
||||
[ $BUILD_DATA_GENERATED = 1 ] || gen_build_data
|
||||
|
||||
# move new build data to take the place of the old data
|
||||
mv $BUILD_DATA.tmp $BUILD_DATA
|
||||
}
|
||||
|
||||
do_exit()
|
||||
{
|
||||
echo $1
|
||||
exit $2
|
||||
}
|
||||
|
||||
# check to see if the folders build file exists
|
||||
[ -e "$BUILD_FILE" ] || do_exit "no build file found" 1
|
||||
|
||||
# include the folders build file
|
||||
. $BUILD_FILE
|
||||
|
||||
# check if the build needs to take place
|
||||
check && do_exit "no changes have been made" 0
|
||||
|
||||
build
|
|
@ -1,66 +0,0 @@
|
|||
DELUGE_FILES="Deluge.js Deluge.Formatters.js Deluge.Menus.js Deluge.Events.js Deluge.OptionsManager.js Deluge.MultiOptionsManager.js Deluge.Add.js Deluge.Add.File.js Deluge.Add.Url.js Deluge.Client.js Deluge.ConnectionManager.js Deluge.Details.js Deluge.Details.Status.js Deluge.Details.Details.js Deluge.Details.Files.js Deluge.Details.Peers.js Deluge.Details.Options.js Deluge.EditTrackers.js Deluge.Keys.js Deluge.Login.js Deluge.MoveStorage.js Deluge.Plugin.js Deluge.Preferences.js Deluge.Preferences.Downloads.js Deluge.Preferences.Network.js Deluge.Preferences.Encryption.js Deluge.Preferences.Bandwidth.js Deluge.Preferences.Interface.js Deluge.Preferences.Other.js Deluge.Preferences.Daemon.js Deluge.Preferences.Queue.js Deluge.Preferences.Proxy.js Deluge.Preferences.Notification.js Deluge.Preferences.Cache.js Deluge.Preferences.Plugins.js Deluge.Remove.js Deluge.Sidebar.js Deluge.Statusbar.js Deluge.Toolbar.js Deluge.Torrents.js Deluge.UI.js"
|
||||
ALL_FILES="ext-extensions-debug.js $DELUGE_FILES"
|
||||
|
||||
DELUGE="deluge-all.js"
|
||||
EXT_EXTENSIONS="ext-extensions.js"
|
||||
|
||||
[ -e /usr/bin/jsdoc_toolkit ] && JSDOC="/usr/bin/jsdoc_toolkit"
|
||||
[ -e /usr/local/bin/jsdoc-toolkit ] && JSDOC="/usr/local/bin/jsdoc-toolkit"
|
||||
|
||||
scan() {
|
||||
cat /dev/null > .build_data.tmp
|
||||
for FILE in $ALL_FILES; do
|
||||
md5sum $FILE >> .build_data.tmp
|
||||
done;
|
||||
}
|
||||
|
||||
check_file() {
|
||||
# No build data is stored so return 1 since we can't make any guesses.
|
||||
[ ! -e .build_data ] && return 1;
|
||||
|
||||
FILE=$1
|
||||
LAST_BUILD=`grep $FILE .build_data`
|
||||
if [ $? == 1 ]; then return 1; fi;
|
||||
|
||||
CURRENT=`grep $FILE .build_data.tmp`
|
||||
|
||||
[ "$CURRENT" != "$LAST_BUILD" ] && return 1
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
build_deluge() {
|
||||
NEEDS_BUILD=false;
|
||||
for FILE in $DELUGE_FILES; do
|
||||
check_file $FILE
|
||||
[ $? == 1 ] && NEEDS_BUILD=true
|
||||
done;
|
||||
|
||||
[ $NEEDS_BUILD == false ] && return 0
|
||||
|
||||
echo "Building $DELUGE"
|
||||
cat $DELUGE_FILES > $DELUGE.tmp
|
||||
yuicompressor --type=js -o "$DELUGE" "$DELUGE.tmp" && rm "$DELUGE.tmp"
|
||||
}
|
||||
|
||||
build_docs() {
|
||||
echo "building docs";
|
||||
$JSDOC -d="../docs/" $ALL_FILES
|
||||
}
|
||||
|
||||
build_ext() {
|
||||
check_file "ext-extensions-debug.js"
|
||||
if [ $? == 1 ]; then
|
||||
echo "Building $EXT_EXTENSIONS"
|
||||
yuicompressor --type=js -o "$EXT_EXTENSIONS" "ext-extensions-debug.js"
|
||||
fi;
|
||||
}
|
||||
|
||||
if [ "$1" = "docs" ]; then
|
||||
build_docs
|
||||
else
|
||||
scan
|
||||
build_ext
|
||||
build_deluge
|
||||
mv .build_data.tmp .build_data
|
||||
fi;
|
8026
deluge/ui/web/js/deluge-all-debug.js
Normal file
8026
deluge/ui/web/js/deluge-all-debug.js
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
43
deluge/ui/web/js/deluge-all/.build
Normal file
43
deluge/ui/web/js/deluge-all/.build
Normal file
|
@ -0,0 +1,43 @@
|
|||
#!/bin/sh
|
||||
|
||||
add_file "Deluge.js"
|
||||
add_file "Deluge.Formatters.js"
|
||||
add_file "Deluge.Keys.js"
|
||||
add_file "Deluge.Menus.js"
|
||||
add_file "Deluge.Events.js"
|
||||
add_file "Deluge.OptionsManager.js"
|
||||
add_file "Deluge.MultiOptionsManager.js"
|
||||
add_file "Deluge.Add.js"
|
||||
add_file "Deluge.Add.File.js"
|
||||
add_file "Deluge.Add.Url.js"
|
||||
add_file "Deluge.Client.js"
|
||||
add_file "Deluge.ConnectionManager.js"
|
||||
add_file "Deluge.Details.js"
|
||||
add_file "Deluge.Details.Status.js"
|
||||
add_file "Deluge.Details.Details.js"
|
||||
add_file "Deluge.Details.Files.js"
|
||||
add_file "Deluge.Details.Peers.js"
|
||||
add_file "Deluge.Details.Options.js"
|
||||
add_file "Deluge.EditTrackers.js"
|
||||
add_file "Deluge.Login.js"
|
||||
add_file "Deluge.MoveStorage.js"
|
||||
add_file "Deluge.Plugin.js"
|
||||
add_file "Deluge.Preferences.js"
|
||||
add_file "Deluge.Preferences.Downloads.js"
|
||||
add_file "Deluge.Preferences.Network.js"
|
||||
add_file "Deluge.Preferences.Encryption.js"
|
||||
add_file "Deluge.Preferences.Bandwidth.js"
|
||||
add_file "Deluge.Preferences.Interface.js"
|
||||
add_file "Deluge.Preferences.Other.js"
|
||||
add_file "Deluge.Preferences.Daemon.js"
|
||||
add_file "Deluge.Preferences.Queue.js"
|
||||
add_file "Deluge.Preferences.Proxy.js"
|
||||
add_file "Deluge.Preferences.Notification.js"
|
||||
add_file "Deluge.Preferences.Cache.js"
|
||||
add_file "Deluge.Preferences.Plugins.js"
|
||||
add_file "Deluge.Remove.js"
|
||||
add_file "Deluge.Sidebar.js"
|
||||
add_file "Deluge.Statusbar.js"
|
||||
add_file "Deluge.Toolbar.js"
|
||||
add_file "Deluge.Torrents.js"
|
||||
add_file "Deluge.UI.js"
|
|
@ -560,4 +560,4 @@ Ext.deluge.add.AddWindow = Ext.extend(Ext.deluge.add.Window, {
|
|||
this.url.show();
|
||||
}
|
||||
});
|
||||
Deluge.Add = new Ext.deluge.add.AddWindow();
|
||||
//Deluge.Add = new Ext.deluge.add.AddWindow();
|
|
@ -39,7 +39,7 @@ Ext.deluge.details.StatusTab = Ext.extend(Ext.Panel, {
|
|||
Ext.deluge.details.StatusTab.superclass.onRender.call(this, ct, position);
|
||||
|
||||
this.progressBar = this.add({
|
||||
xtype: 'fullprogressbar',
|
||||
xtype: 'progress',
|
||||
cls: 'x-deluge-status-progressbar'
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
15
deluge/ui/web/js/ext-extensions/.build
Normal file
15
deluge/ui/web/js/ext-extensions/.build
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
add_file "BufferView.js"
|
||||
add_file "FileUploadField.js"
|
||||
add_file "JSLoader.js"
|
||||
add_file "Spinner.js"
|
||||
add_file "SpinnerField.js"
|
||||
add_file "StatusBar.js"
|
||||
add_file "ToggleField.js"
|
||||
add_file "TreeGridSorter.js"
|
||||
add_file "TreeGridColumnResizer.js"
|
||||
add_file "TreeGridNodeUI.js"
|
||||
add_file "TreeGridLoader.js"
|
||||
add_file "TreeGridColumns.js"
|
||||
add_file "TreeGrid.js"
|
219
deluge/ui/web/js/ext-extensions/BufferView.js
Normal file
219
deluge/ui/web/js/ext-extensions/BufferView.js
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
Ext.ns('Ext.ux.grid');
|
||||
|
||||
/**
|
||||
* @class Ext.ux.grid.BufferView
|
||||
* @extends Ext.grid.GridView
|
||||
* A custom GridView which renders rows on an as-needed basis.
|
||||
*/
|
||||
Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, {
|
||||
/**
|
||||
* @cfg {Number} rowHeight
|
||||
* The height of a row in the grid.
|
||||
*/
|
||||
rowHeight: 19,
|
||||
|
||||
/**
|
||||
* @cfg {Number} borderHeight
|
||||
* The combined height of border-top and border-bottom of a row.
|
||||
*/
|
||||
borderHeight: 2,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean/Number} scrollDelay
|
||||
* The number of milliseconds before rendering rows out of the visible
|
||||
* viewing area. Defaults to 100. Rows will render immediately with a config
|
||||
* of false.
|
||||
*/
|
||||
scrollDelay: 100,
|
||||
|
||||
/**
|
||||
* @cfg {Number} cacheSize
|
||||
* The number of rows to look forward and backwards from the currently viewable
|
||||
* area. The cache applies only to rows that have been rendered already.
|
||||
*/
|
||||
cacheSize: 20,
|
||||
|
||||
/**
|
||||
* @cfg {Number} cleanDelay
|
||||
* The number of milliseconds to buffer cleaning of extra rows not in the
|
||||
* cache.
|
||||
*/
|
||||
cleanDelay: 500,
|
||||
|
||||
initTemplates : function(){
|
||||
Ext.ux.grid.BufferView.superclass.initTemplates.call(this);
|
||||
var ts = this.templates;
|
||||
// empty div to act as a place holder for a row
|
||||
ts.rowHolder = new Ext.Template(
|
||||
'<div class="x-grid3-row {alt}" style="{tstyle}"></div>'
|
||||
);
|
||||
ts.rowHolder.disableFormats = true;
|
||||
ts.rowHolder.compile();
|
||||
|
||||
ts.rowBody = new Ext.Template(
|
||||
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
|
||||
'<tbody><tr>{cells}</tr>',
|
||||
(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
|
||||
'</tbody></table>'
|
||||
);
|
||||
ts.rowBody.disableFormats = true;
|
||||
ts.rowBody.compile();
|
||||
},
|
||||
|
||||
getStyleRowHeight : function(){
|
||||
return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
|
||||
},
|
||||
|
||||
getCalculatedRowHeight : function(){
|
||||
return this.rowHeight + this.borderHeight;
|
||||
},
|
||||
|
||||
getVisibleRowCount : function(){
|
||||
var rh = this.getCalculatedRowHeight();
|
||||
var visibleHeight = this.scroller.dom.clientHeight;
|
||||
return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
|
||||
},
|
||||
|
||||
getVisibleRows: function(){
|
||||
var count = this.getVisibleRowCount();
|
||||
var sc = this.scroller.dom.scrollTop;
|
||||
var start = (sc == 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1);
|
||||
return {
|
||||
first: Math.max(start, 0),
|
||||
last: Math.min(start + count + 2, this.ds.getCount()-1)
|
||||
};
|
||||
},
|
||||
|
||||
doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
|
||||
var ts = this.templates, ct = ts.cell, rt = ts.row, rb = ts.rowBody, last = colCount-1;
|
||||
var rh = this.getStyleRowHeight();
|
||||
var vr = this.getVisibleRows();
|
||||
var tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;';
|
||||
// buffers
|
||||
var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
|
||||
for (var j = 0, len = rs.length; j < len; j++) {
|
||||
r = rs[j]; cb = [];
|
||||
var rowIndex = (j+startRow);
|
||||
var visible = rowIndex >= vr.first && rowIndex <= vr.last;
|
||||
if (visible) {
|
||||
for (var i = 0; i < colCount; i++) {
|
||||
c = cs[i];
|
||||
p.id = c.id;
|
||||
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
|
||||
p.attr = p.cellAttr = "";
|
||||
p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
|
||||
p.style = c.style;
|
||||
if (p.value == undefined || p.value === "") {
|
||||
p.value = " ";
|
||||
}
|
||||
if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
|
||||
p.css += ' x-grid3-dirty-cell';
|
||||
}
|
||||
cb[cb.length] = ct.apply(p);
|
||||
}
|
||||
}
|
||||
var alt = [];
|
||||
if(stripe && ((rowIndex+1) % 2 == 0)){
|
||||
alt[0] = "x-grid3-row-alt";
|
||||
}
|
||||
if(r.dirty){
|
||||
alt[1] = " x-grid3-dirty-row";
|
||||
}
|
||||
rp.cols = colCount;
|
||||
if(this.getRowClass){
|
||||
alt[2] = this.getRowClass(r, rowIndex, rp, ds);
|
||||
}
|
||||
rp.alt = alt.join(" ");
|
||||
rp.cells = cb.join("");
|
||||
buf[buf.length] = !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
|
||||
}
|
||||
return buf.join("");
|
||||
},
|
||||
|
||||
isRowRendered: function(index){
|
||||
var row = this.getRow(index);
|
||||
return row && row.childNodes.length > 0;
|
||||
},
|
||||
|
||||
syncScroll: function(){
|
||||
Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments);
|
||||
this.update();
|
||||
},
|
||||
|
||||
// a (optionally) buffered method to update contents of gridview
|
||||
update: function(){
|
||||
if (this.scrollDelay) {
|
||||
if (!this.renderTask) {
|
||||
this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this);
|
||||
}
|
||||
this.renderTask.delay(this.scrollDelay);
|
||||
}else{
|
||||
this.doUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
onRemove : function(ds, record, index, isUpdate){
|
||||
Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments);
|
||||
if(isUpdate !== true){
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
|
||||
doUpdate: function(){
|
||||
if (this.getVisibleRowCount() > 0) {
|
||||
var g = this.grid, cm = g.colModel, ds = g.store;
|
||||
var cs = this.getColumnData();
|
||||
|
||||
var vr = this.getVisibleRows();
|
||||
for (var i = vr.first; i <= vr.last; i++) {
|
||||
// if row is NOT rendered and is visible, render it
|
||||
if(!this.isRowRendered(i)){
|
||||
var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true);
|
||||
this.getRow(i).innerHTML = html;
|
||||
}
|
||||
}
|
||||
this.clean();
|
||||
}
|
||||
},
|
||||
|
||||
// a buffered method to clean rows
|
||||
clean : function(){
|
||||
if(!this.cleanTask){
|
||||
this.cleanTask = new Ext.util.DelayedTask(this.doClean, this);
|
||||
}
|
||||
this.cleanTask.delay(this.cleanDelay);
|
||||
},
|
||||
|
||||
doClean: function(){
|
||||
if (this.getVisibleRowCount() > 0) {
|
||||
var vr = this.getVisibleRows();
|
||||
vr.first -= this.cacheSize;
|
||||
vr.last += this.cacheSize;
|
||||
|
||||
var i = 0, rows = this.getRows();
|
||||
// if first is less than 0, all rows have been rendered
|
||||
// so lets clean the end...
|
||||
if(vr.first <= 0){
|
||||
i = vr.last + 1;
|
||||
}
|
||||
for(var len = this.ds.getCount(); i < len; i++){
|
||||
// if current row is outside of first and last and
|
||||
// has content, update the innerHTML to nothing
|
||||
if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
|
||||
rows[i].innerHTML = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
layout: function(){
|
||||
Ext.ux.grid.BufferView.superclass.layout.call(this);
|
||||
this.update();
|
||||
}
|
||||
});
|
182
deluge/ui/web/js/ext-extensions/FileUploadField.js
Normal file
182
deluge/ui/web/js/ext-extensions/FileUploadField.js
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
Ext.ns('Ext.ux.form');
|
||||
|
||||
/**
|
||||
* @class Ext.ux.form.FileUploadField
|
||||
* @extends Ext.form.TextField
|
||||
* Creates a file upload field.
|
||||
* @xtype fileuploadfield
|
||||
*/
|
||||
Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, {
|
||||
/**
|
||||
* @cfg {String} buttonText The button text to display on the upload button (defaults to
|
||||
* 'Browse...'). Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
|
||||
* value will be used instead if available.
|
||||
*/
|
||||
buttonText: 'Browse...',
|
||||
/**
|
||||
* @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
|
||||
* text field (defaults to false). If true, all inherited TextField members will still be available.
|
||||
*/
|
||||
buttonOnly: false,
|
||||
/**
|
||||
* @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
|
||||
* (defaults to 3). Note that this only applies if {@link #buttonOnly} = false.
|
||||
*/
|
||||
buttonOffset: 3,
|
||||
/**
|
||||
* @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.
|
||||
*/
|
||||
|
||||
// private
|
||||
readOnly: true,
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @method autoSize
|
||||
*/
|
||||
autoSize: Ext.emptyFn,
|
||||
|
||||
// private
|
||||
initComponent: function(){
|
||||
Ext.ux.form.FileUploadField.superclass.initComponent.call(this);
|
||||
|
||||
this.addEvents(
|
||||
/**
|
||||
* @event fileselected
|
||||
* Fires when the underlying file input field's value has changed from the user
|
||||
* selecting a new file from the system file selection dialog.
|
||||
* @param {Ext.ux.form.FileUploadField} this
|
||||
* @param {String} value The file value returned by the underlying file input field
|
||||
*/
|
||||
'fileselected'
|
||||
);
|
||||
},
|
||||
|
||||
// private
|
||||
onRender : function(ct, position){
|
||||
Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position);
|
||||
|
||||
this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});
|
||||
this.el.addClass('x-form-file-text');
|
||||
this.el.dom.removeAttribute('name');
|
||||
this.createFileInput();
|
||||
|
||||
var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
|
||||
text: this.buttonText
|
||||
});
|
||||
this.button = new Ext.Button(Ext.apply(btnCfg, {
|
||||
renderTo: this.wrap,
|
||||
cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '')
|
||||
}));
|
||||
|
||||
if(this.buttonOnly){
|
||||
this.el.hide();
|
||||
this.wrap.setWidth(this.button.getEl().getWidth());
|
||||
}
|
||||
|
||||
this.bindListeners();
|
||||
this.resizeEl = this.positionEl = this.wrap;
|
||||
},
|
||||
|
||||
bindListeners: function(){
|
||||
this.fileInput.on({
|
||||
scope: this,
|
||||
mouseenter: function() {
|
||||
this.button.addClass(['x-btn-over','x-btn-focus'])
|
||||
},
|
||||
mouseleave: function(){
|
||||
this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])
|
||||
},
|
||||
mousedown: function(){
|
||||
this.button.addClass('x-btn-click')
|
||||
},
|
||||
mouseup: function(){
|
||||
this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])
|
||||
},
|
||||
change: function(){
|
||||
var v = this.fileInput.dom.value;
|
||||
this.setValue(v);
|
||||
this.fireEvent('fileselected', this, v);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createFileInput : function() {
|
||||
this.fileInput = this.wrap.createChild({
|
||||
id: this.getFileInputId(),
|
||||
name: this.name||this.getId(),
|
||||
cls: 'x-form-file',
|
||||
tag: 'input',
|
||||
type: 'file',
|
||||
size: 1
|
||||
});
|
||||
},
|
||||
|
||||
reset : function(){
|
||||
this.fileInput.remove();
|
||||
this.createFileInput();
|
||||
this.bindListeners();
|
||||
Ext.ux.form.FileUploadField.superclass.reset.call(this);
|
||||
},
|
||||
|
||||
// private
|
||||
getFileInputId: function(){
|
||||
return this.id + '-file';
|
||||
},
|
||||
|
||||
// private
|
||||
onResize : function(w, h){
|
||||
Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h);
|
||||
|
||||
this.wrap.setWidth(w);
|
||||
|
||||
if(!this.buttonOnly){
|
||||
var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;
|
||||
this.el.setWidth(w);
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
onDestroy: function(){
|
||||
Ext.ux.form.FileUploadField.superclass.onDestroy.call(this);
|
||||
Ext.destroy(this.fileInput, this.button, this.wrap);
|
||||
},
|
||||
|
||||
onDisable: function(){
|
||||
Ext.ux.form.FileUploadField.superclass.onDisable.call(this);
|
||||
this.doDisable(true);
|
||||
},
|
||||
|
||||
onEnable: function(){
|
||||
Ext.ux.form.FileUploadField.superclass.onEnable.call(this);
|
||||
this.doDisable(false);
|
||||
|
||||
},
|
||||
|
||||
// private
|
||||
doDisable: function(disabled){
|
||||
this.fileInput.dom.disabled = disabled;
|
||||
this.button.setDisabled(disabled);
|
||||
},
|
||||
|
||||
|
||||
// private
|
||||
preFocus : Ext.emptyFn,
|
||||
|
||||
// private
|
||||
alignErrorIcon : function(){
|
||||
this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField);
|
||||
|
||||
// backwards compat
|
||||
Ext.form.FileUploadField = Ext.ux.form.FileUploadField;
|
38
deluge/ui/web/js/ext-extensions/JSLoader.js
Normal file
38
deluge/ui/web/js/ext-extensions/JSLoader.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
Ext.ux.JSLoader = function(options) {
|
||||
Ext.ux.JSLoader.scripts[++Ext.ux.JSLoader.index] = {
|
||||
url: options.url,
|
||||
success: true,
|
||||
jsLoadObj: null,
|
||||
options: options,
|
||||
onLoad: options.onLoad || Ext.emptyFn,
|
||||
onError: options.onError || Ext.ux.JSLoader.stdError,
|
||||
scope: options.scope || this
|
||||
};
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: options.url,
|
||||
scriptIndex: Ext.ux.JSLoader.index,
|
||||
success: function(response, options) {
|
||||
var script = Ext.ux.JSLoader.scripts[options.scriptIndex];
|
||||
try {
|
||||
eval(response.responseText);
|
||||
} catch(e) {
|
||||
script.success = false;
|
||||
script.onError(script.options, e);
|
||||
}
|
||||
if (script.success) {
|
||||
script.onLoad.call(script.scope, script.options);
|
||||
}
|
||||
},
|
||||
failure: function(response, options) {
|
||||
var script = Ext.ux.JSLoader.scripts[options.scriptIndex];
|
||||
script.success = false;
|
||||
script.onError(script.options, response.status);
|
||||
}
|
||||
});
|
||||
}
|
||||
Ext.ux.JSLoader.index = 0;
|
||||
Ext.ux.JSLoader.scripts = [];
|
||||
Ext.ux.JSLoader.stdError = function(options, e) {
|
||||
window.alert('Error loading script:\n\n' + options.url + '\n\nstatus: ' + e);
|
||||
}
|
438
deluge/ui/web/js/ext-extensions/Spinner.js
Normal file
438
deluge/ui/web/js/ext-extensions/Spinner.js
Normal file
|
@ -0,0 +1,438 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.Spinner
|
||||
* @extends Ext.util.Observable
|
||||
* Creates a Spinner control utilized by Ext.ux.form.SpinnerField
|
||||
*/
|
||||
Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {
|
||||
incrementValue: 1,
|
||||
alternateIncrementValue: 5,
|
||||
triggerClass: 'x-form-spinner-trigger',
|
||||
splitterClass: 'x-form-spinner-splitter',
|
||||
alternateKey: Ext.EventObject.shiftKey,
|
||||
defaultValue: 0,
|
||||
accelerate: false,
|
||||
|
||||
constructor: function(config){
|
||||
Ext.ux.Spinner.superclass.constructor.call(this, config);
|
||||
Ext.apply(this, config);
|
||||
this.mimicing = false;
|
||||
},
|
||||
|
||||
init: function(field){
|
||||
this.field = field;
|
||||
|
||||
field.afterMethod('onRender', this.doRender, this);
|
||||
field.afterMethod('onEnable', this.doEnable, this);
|
||||
field.afterMethod('onDisable', this.doDisable, this);
|
||||
field.afterMethod('afterRender', this.doAfterRender, this);
|
||||
field.afterMethod('onResize', this.doResize, this);
|
||||
field.afterMethod('onFocus', this.doFocus, this);
|
||||
field.beforeMethod('onDestroy', this.doDestroy, this);
|
||||
},
|
||||
|
||||
doRender: function(ct, position){
|
||||
var el = this.el = this.field.getEl();
|
||||
var f = this.field;
|
||||
|
||||
if (!f.wrap) {
|
||||
f.wrap = this.wrap = el.wrap({
|
||||
cls: "x-form-field-wrap"
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.wrap = f.wrap.addClass('x-form-field-wrap');
|
||||
}
|
||||
|
||||
this.trigger = this.wrap.createChild({
|
||||
tag: "img",
|
||||
src: Ext.BLANK_IMAGE_URL,
|
||||
cls: "x-form-trigger " + this.triggerClass
|
||||
});
|
||||
|
||||
if (!f.width) {
|
||||
this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());
|
||||
}
|
||||
|
||||
this.splitter = this.wrap.createChild({
|
||||
tag: 'div',
|
||||
cls: this.splitterClass,
|
||||
style: 'width:13px; height:2px;'
|
||||
});
|
||||
this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();
|
||||
|
||||
this.proxy = this.trigger.createProxy('', this.splitter, true);
|
||||
this.proxy.addClass("x-form-spinner-proxy");
|
||||
this.proxy.setStyle('left', '0px');
|
||||
this.proxy.setSize(14, 1);
|
||||
this.proxy.hide();
|
||||
this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {
|
||||
dragElId: this.proxy.id
|
||||
});
|
||||
|
||||
this.initTrigger();
|
||||
this.initSpinner();
|
||||
},
|
||||
|
||||
doAfterRender: function(){
|
||||
var y;
|
||||
if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {
|
||||
this.el.position();
|
||||
this.el.setY(y);
|
||||
}
|
||||
},
|
||||
|
||||
doEnable: function(){
|
||||
if (this.wrap) {
|
||||
this.wrap.removeClass(this.field.disabledClass);
|
||||
}
|
||||
},
|
||||
|
||||
doDisable: function(){
|
||||
if (this.wrap) {
|
||||
this.wrap.addClass(this.field.disabledClass);
|
||||
this.el.removeClass(this.field.disabledClass);
|
||||
}
|
||||
},
|
||||
|
||||
doResize: function(w, h){
|
||||
if (typeof w == 'number') {
|
||||
this.el.setWidth(w - this.trigger.getWidth());
|
||||
}
|
||||
this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());
|
||||
},
|
||||
|
||||
doFocus: function(){
|
||||
if (!this.mimicing) {
|
||||
this.wrap.addClass('x-trigger-wrap-focus');
|
||||
this.mimicing = true;
|
||||
Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {
|
||||
delay: 10
|
||||
});
|
||||
this.el.on('keydown', this.checkTab, this);
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
checkTab: function(e){
|
||||
if (e.getKey() == e.TAB) {
|
||||
this.triggerBlur();
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
mimicBlur: function(e){
|
||||
if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {
|
||||
this.triggerBlur();
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
triggerBlur: function(){
|
||||
this.mimicing = false;
|
||||
Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
|
||||
this.el.un("keydown", this.checkTab, this);
|
||||
this.field.beforeBlur();
|
||||
this.wrap.removeClass('x-trigger-wrap-focus');
|
||||
this.field.onBlur.call(this.field);
|
||||
},
|
||||
|
||||
initTrigger: function(){
|
||||
this.trigger.addClassOnOver('x-form-trigger-over');
|
||||
this.trigger.addClassOnClick('x-form-trigger-click');
|
||||
},
|
||||
|
||||
initSpinner: function(){
|
||||
this.field.addEvents({
|
||||
'spin': true,
|
||||
'spinup': true,
|
||||
'spindown': true
|
||||
});
|
||||
|
||||
this.keyNav = new Ext.KeyNav(this.el, {
|
||||
"up": function(e){
|
||||
e.preventDefault();
|
||||
this.onSpinUp();
|
||||
},
|
||||
|
||||
"down": function(e){
|
||||
e.preventDefault();
|
||||
this.onSpinDown();
|
||||
},
|
||||
|
||||
"pageUp": function(e){
|
||||
e.preventDefault();
|
||||
this.onSpinUpAlternate();
|
||||
},
|
||||
|
||||
"pageDown": function(e){
|
||||
e.preventDefault();
|
||||
this.onSpinDownAlternate();
|
||||
},
|
||||
|
||||
scope: this
|
||||
});
|
||||
|
||||
this.repeater = new Ext.util.ClickRepeater(this.trigger, {
|
||||
accelerate: this.accelerate
|
||||
});
|
||||
this.field.mon(this.repeater, "click", this.onTriggerClick, this, {
|
||||
preventDefault: true
|
||||
});
|
||||
|
||||
this.field.mon(this.trigger, {
|
||||
mouseover: this.onMouseOver,
|
||||
mouseout: this.onMouseOut,
|
||||
mousemove: this.onMouseMove,
|
||||
mousedown: this.onMouseDown,
|
||||
mouseup: this.onMouseUp,
|
||||
scope: this,
|
||||
preventDefault: true
|
||||
});
|
||||
|
||||
this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this);
|
||||
|
||||
this.dd.setXConstraint(0, 0, 10)
|
||||
this.dd.setYConstraint(1500, 1500, 10);
|
||||
this.dd.endDrag = this.endDrag.createDelegate(this);
|
||||
this.dd.startDrag = this.startDrag.createDelegate(this);
|
||||
this.dd.onDrag = this.onDrag.createDelegate(this);
|
||||
},
|
||||
|
||||
onMouseOver: function(){
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
var middle = this.getMiddle();
|
||||
this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
|
||||
this.trigger.addClass(this.tmpHoverClass);
|
||||
},
|
||||
|
||||
//private
|
||||
onMouseOut: function(){
|
||||
this.trigger.removeClass(this.tmpHoverClass);
|
||||
},
|
||||
|
||||
//private
|
||||
onMouseMove: function(){
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
var middle = this.getMiddle();
|
||||
if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||
|
||||
((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {
|
||||
}
|
||||
},
|
||||
|
||||
//private
|
||||
onMouseDown: function(){
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
var middle = this.getMiddle();
|
||||
this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
|
||||
this.trigger.addClass(this.tmpClickClass);
|
||||
},
|
||||
|
||||
//private
|
||||
onMouseUp: function(){
|
||||
this.trigger.removeClass(this.tmpClickClass);
|
||||
},
|
||||
|
||||
//private
|
||||
onTriggerClick: function(){
|
||||
if (this.disabled || this.el.dom.readOnly) {
|
||||
return;
|
||||
}
|
||||
var middle = this.getMiddle();
|
||||
var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';
|
||||
this['onSpin' + ud]();
|
||||
},
|
||||
|
||||
//private
|
||||
getMiddle: function(){
|
||||
var t = this.trigger.getTop();
|
||||
var h = this.trigger.getHeight();
|
||||
var middle = t + (h / 2);
|
||||
return middle;
|
||||
},
|
||||
|
||||
//private
|
||||
//checks if control is allowed to spin
|
||||
isSpinnable: function(){
|
||||
if (this.disabled || this.el.dom.readOnly) {
|
||||
Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
handleMouseWheel: function(e){
|
||||
//disable scrolling when not focused
|
||||
if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
var delta = e.getWheelDelta();
|
||||
if (delta > 0) {
|
||||
this.onSpinUp();
|
||||
e.stopEvent();
|
||||
}
|
||||
else
|
||||
if (delta < 0) {
|
||||
this.onSpinDown();
|
||||
e.stopEvent();
|
||||
}
|
||||
},
|
||||
|
||||
//private
|
||||
startDrag: function(){
|
||||
this.proxy.show();
|
||||
this._previousY = Ext.fly(this.dd.getDragEl()).getTop();
|
||||
},
|
||||
|
||||
//private
|
||||
endDrag: function(){
|
||||
this.proxy.hide();
|
||||
},
|
||||
|
||||
//private
|
||||
onDrag: function(){
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
var y = Ext.fly(this.dd.getDragEl()).getTop();
|
||||
var ud = '';
|
||||
|
||||
if (this._previousY > y) {
|
||||
ud = 'Up';
|
||||
} //up
|
||||
if (this._previousY < y) {
|
||||
ud = 'Down';
|
||||
} //down
|
||||
if (ud != '') {
|
||||
this['onSpin' + ud]();
|
||||
}
|
||||
|
||||
this._previousY = y;
|
||||
},
|
||||
|
||||
//private
|
||||
onSpinUp: function(){
|
||||
if (this.isSpinnable() == false) {
|
||||
return;
|
||||
}
|
||||
if (Ext.EventObject.shiftKey == true) {
|
||||
this.onSpinUpAlternate();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.spin(false, false);
|
||||
}
|
||||
this.field.fireEvent("spin", this);
|
||||
this.field.fireEvent("spinup", this);
|
||||
},
|
||||
|
||||
//private
|
||||
onSpinDown: function(){
|
||||
if (this.isSpinnable() == false) {
|
||||
return;
|
||||
}
|
||||
if (Ext.EventObject.shiftKey == true) {
|
||||
this.onSpinDownAlternate();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.spin(true, false);
|
||||
}
|
||||
this.field.fireEvent("spin", this);
|
||||
this.field.fireEvent("spindown", this);
|
||||
},
|
||||
|
||||
//private
|
||||
onSpinUpAlternate: function(){
|
||||
if (this.isSpinnable() == false) {
|
||||
return;
|
||||
}
|
||||
this.spin(false, true);
|
||||
this.field.fireEvent("spin", this);
|
||||
this.field.fireEvent("spinup", this);
|
||||
},
|
||||
|
||||
//private
|
||||
onSpinDownAlternate: function(){
|
||||
if (this.isSpinnable() == false) {
|
||||
return;
|
||||
}
|
||||
this.spin(true, true);
|
||||
this.field.fireEvent("spin", this);
|
||||
this.field.fireEvent("spindown", this);
|
||||
},
|
||||
|
||||
spin: function(down, alternate){
|
||||
var v = parseFloat(this.field.getValue());
|
||||
var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
|
||||
(down == true) ? v -= incr : v += incr;
|
||||
|
||||
v = (isNaN(v)) ? this.defaultValue : v;
|
||||
v = this.fixBoundries(v);
|
||||
this.field.setRawValue(v);
|
||||
},
|
||||
|
||||
fixBoundries: function(value){
|
||||
var v = value;
|
||||
|
||||
if (this.field.minValue != undefined && v < this.field.minValue) {
|
||||
v = this.field.minValue;
|
||||
}
|
||||
if (this.field.maxValue != undefined && v > this.field.maxValue) {
|
||||
v = this.field.maxValue;
|
||||
}
|
||||
|
||||
return this.fixPrecision(v);
|
||||
},
|
||||
|
||||
// private
|
||||
fixPrecision: function(value){
|
||||
var nan = isNaN(value);
|
||||
if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {
|
||||
return nan ? '' : value;
|
||||
}
|
||||
return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));
|
||||
},
|
||||
|
||||
doDestroy: function(){
|
||||
if (this.trigger) {
|
||||
this.trigger.remove();
|
||||
}
|
||||
if (this.wrap) {
|
||||
this.wrap.remove();
|
||||
delete this.field.wrap;
|
||||
}
|
||||
|
||||
if (this.splitter) {
|
||||
this.splitter.remove();
|
||||
}
|
||||
|
||||
if (this.dd) {
|
||||
this.dd.unreg();
|
||||
this.dd = null;
|
||||
}
|
||||
|
||||
if (this.proxy) {
|
||||
this.proxy.remove();
|
||||
}
|
||||
|
||||
if (this.repeater) {
|
||||
this.repeater.purgeListeners();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//backwards compat
|
||||
Ext.form.Spinner = Ext.ux.Spinner;
|
61
deluge/ui/web/js/ext-extensions/SpinnerField.js
Normal file
61
deluge/ui/web/js/ext-extensions/SpinnerField.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
Ext.ns('Ext.ux.form');
|
||||
|
||||
/**
|
||||
* @class Ext.ux.form.SpinnerField
|
||||
* @extends Ext.form.NumberField
|
||||
* Creates a field utilizing Ext.ux.Spinner
|
||||
* @xtype spinnerfield
|
||||
*/
|
||||
Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, {
|
||||
actionMode: 'wrap',
|
||||
deferHeight: true,
|
||||
autoSize: Ext.emptyFn,
|
||||
onBlur: Ext.emptyFn,
|
||||
adjustSize: Ext.BoxComponent.prototype.adjustSize,
|
||||
|
||||
constructor: function(config) {
|
||||
var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass');
|
||||
|
||||
var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig);
|
||||
|
||||
var plugins = config.plugins
|
||||
? (Ext.isArray(config.plugins)
|
||||
? config.plugins.push(spl)
|
||||
: [config.plugins, spl])
|
||||
: spl;
|
||||
|
||||
Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins}));
|
||||
},
|
||||
|
||||
// private
|
||||
getResizeEl: function(){
|
||||
return this.wrap;
|
||||
},
|
||||
|
||||
// private
|
||||
getPositionEl: function(){
|
||||
return this.wrap;
|
||||
},
|
||||
|
||||
// private
|
||||
alignErrorIcon: function(){
|
||||
if (this.wrap) {
|
||||
this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
|
||||
}
|
||||
},
|
||||
|
||||
validateBlur: function(){
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Ext.reg('spinnerfield', Ext.ux.form.SpinnerField);
|
||||
|
||||
//backwards compat
|
||||
Ext.form.SpinnerField = Ext.ux.form.SpinnerField;
|
429
deluge/ui/web/js/ext-extensions/StatusBar.js
Normal file
429
deluge/ui/web/js/ext-extensions/StatusBar.js
Normal file
|
@ -0,0 +1,429 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.StatusBar
|
||||
* <p>Basic status bar component that can be used as the bottom toolbar of any {@link Ext.Panel}. In addition to
|
||||
* supporting the standard {@link Ext.Toolbar} interface for adding buttons, menus and other items, the StatusBar
|
||||
* provides a greedy status element that can be aligned to either side and has convenient methods for setting the
|
||||
* status text and icon. You can also indicate that something is processing using the {@link #showBusy} method.</p>
|
||||
* <pre><code>
|
||||
new Ext.Panel({
|
||||
title: 'StatusBar',
|
||||
// etc.
|
||||
bbar: new Ext.ux.StatusBar({
|
||||
id: 'my-status',
|
||||
|
||||
// defaults to use when the status is cleared:
|
||||
defaultText: 'Default status text',
|
||||
defaultIconCls: 'default-icon',
|
||||
|
||||
// values to set initially:
|
||||
text: 'Ready',
|
||||
iconCls: 'ready-icon',
|
||||
|
||||
// any standard Toolbar items:
|
||||
items: [{
|
||||
text: 'A Button'
|
||||
}, '-', 'Plain Text']
|
||||
})
|
||||
});
|
||||
|
||||
// Update the status bar later in code:
|
||||
var sb = Ext.getCmp('my-status');
|
||||
sb.setStatus({
|
||||
text: 'OK',
|
||||
iconCls: 'ok-icon',
|
||||
clear: true // auto-clear after a set interval
|
||||
});
|
||||
|
||||
// Set the status bar to show that something is processing:
|
||||
sb.showBusy();
|
||||
|
||||
// processing....
|
||||
|
||||
sb.clearStatus(); // once completeed
|
||||
</code></pre>
|
||||
* @extends Ext.Toolbar
|
||||
* @constructor
|
||||
* Creates a new StatusBar
|
||||
* @param {Object/Array} config A config object
|
||||
*/
|
||||
Ext.ux.StatusBar = Ext.extend(Ext.Toolbar, {
|
||||
/**
|
||||
* @cfg {String} statusAlign
|
||||
* The alignment of the status element within the overall StatusBar layout. When the StatusBar is rendered,
|
||||
* it creates an internal div containing the status text and icon. Any additional Toolbar items added in the
|
||||
* StatusBar's {@link #items} config, or added via {@link #add} or any of the supported add* methods, will be
|
||||
* rendered, in added order, to the opposite side. The status element is greedy, so it will automatically
|
||||
* expand to take up all sapce left over by any other items. Example usage:
|
||||
* <pre><code>
|
||||
// Create a left-aligned status bar containing a button,
|
||||
// separator and text item that will be right-aligned (default):
|
||||
new Ext.Panel({
|
||||
title: 'StatusBar',
|
||||
// etc.
|
||||
bbar: new Ext.ux.StatusBar({
|
||||
defaultText: 'Default status text',
|
||||
id: 'status-id',
|
||||
items: [{
|
||||
text: 'A Button'
|
||||
}, '-', 'Plain Text']
|
||||
})
|
||||
});
|
||||
|
||||
// By adding the statusAlign config, this will create the
|
||||
// exact same toolbar, except the status and toolbar item
|
||||
// layout will be reversed from the previous example:
|
||||
new Ext.Panel({
|
||||
title: 'StatusBar',
|
||||
// etc.
|
||||
bbar: new Ext.ux.StatusBar({
|
||||
defaultText: 'Default status text',
|
||||
id: 'status-id',
|
||||
statusAlign: 'right',
|
||||
items: [{
|
||||
text: 'A Button'
|
||||
}, '-', 'Plain Text']
|
||||
})
|
||||
});
|
||||
</code></pre>
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} defaultText
|
||||
* The default {@link #text} value. This will be used anytime the status bar is cleared with the
|
||||
* <tt>useDefaults:true</tt> option (defaults to '').
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} defaultIconCls
|
||||
* The default {@link #iconCls} value (see the iconCls docs for additional details about customizing the icon).
|
||||
* This will be used anytime the status bar is cleared with the <tt>useDefaults:true</tt> option (defaults to '').
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} text
|
||||
* A string that will be <b>initially</b> set as the status message. This string
|
||||
* will be set as innerHTML (html tags are accepted) for the toolbar item.
|
||||
* If not specified, the value set for <code>{@link #defaultText}</code>
|
||||
* will be used.
|
||||
*/
|
||||
/**
|
||||
* @cfg {String} iconCls
|
||||
* A CSS class that will be <b>initially</b> set as the status bar icon and is
|
||||
* expected to provide a background image (defaults to '').
|
||||
* Example usage:<pre><code>
|
||||
// Example CSS rule:
|
||||
.x-statusbar .x-status-custom {
|
||||
padding-left: 25px;
|
||||
background: transparent url(images/custom-icon.gif) no-repeat 3px 2px;
|
||||
}
|
||||
|
||||
// Setting a default icon:
|
||||
var sb = new Ext.ux.StatusBar({
|
||||
defaultIconCls: 'x-status-custom'
|
||||
});
|
||||
|
||||
// Changing the icon:
|
||||
sb.setStatus({
|
||||
text: 'New status',
|
||||
iconCls: 'x-status-custom'
|
||||
});
|
||||
</code></pre>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @cfg {String} cls
|
||||
* The base class applied to the containing element for this component on render (defaults to 'x-statusbar')
|
||||
*/
|
||||
cls : 'x-statusbar',
|
||||
/**
|
||||
* @cfg {String} busyIconCls
|
||||
* The default <code>{@link #iconCls}</code> applied when calling
|
||||
* <code>{@link #showBusy}</code> (defaults to <tt>'x-status-busy'</tt>).
|
||||
* It can be overridden at any time by passing the <code>iconCls</code>
|
||||
* argument into <code>{@link #showBusy}</code>.
|
||||
*/
|
||||
busyIconCls : 'x-status-busy',
|
||||
/**
|
||||
* @cfg {String} busyText
|
||||
* The default <code>{@link #text}</code> applied when calling
|
||||
* <code>{@link #showBusy}</code> (defaults to <tt>'Loading...'</tt>).
|
||||
* It can be overridden at any time by passing the <code>text</code>
|
||||
* argument into <code>{@link #showBusy}</code>.
|
||||
*/
|
||||
busyText : 'Loading...',
|
||||
/**
|
||||
* @cfg {Number} autoClear
|
||||
* The number of milliseconds to wait after setting the status via
|
||||
* <code>{@link #setStatus}</code> before automatically clearing the status
|
||||
* text and icon (defaults to <tt>5000</tt>). Note that this only applies
|
||||
* when passing the <tt>clear</tt> argument to <code>{@link #setStatus}</code>
|
||||
* since that is the only way to defer clearing the status. This can
|
||||
* be overridden by specifying a different <tt>wait</tt> value in
|
||||
* <code>{@link #setStatus}</code>. Calls to <code>{@link #clearStatus}</code>
|
||||
* always clear the status bar immediately and ignore this value.
|
||||
*/
|
||||
autoClear : 5000,
|
||||
|
||||
/**
|
||||
* @cfg {String} emptyText
|
||||
* The text string to use if no text has been set. Defaults to
|
||||
* <tt>' '</tt>). If there are no other items in the toolbar using
|
||||
* an empty string (<tt>''</tt>) for this value would end up in the toolbar
|
||||
* height collapsing since the empty string will not maintain the toolbar
|
||||
* height. Use <tt>''</tt> if the toolbar should collapse in height
|
||||
* vertically when no text is specified and there are no other items in
|
||||
* the toolbar.
|
||||
*/
|
||||
emptyText : ' ',
|
||||
|
||||
// private
|
||||
activeThreadId : 0,
|
||||
|
||||
// private
|
||||
initComponent : function(){
|
||||
if(this.statusAlign=='right'){
|
||||
this.cls += ' x-status-right';
|
||||
}
|
||||
Ext.ux.StatusBar.superclass.initComponent.call(this);
|
||||
},
|
||||
|
||||
// private
|
||||
afterRender : function(){
|
||||
Ext.ux.StatusBar.superclass.afterRender.call(this);
|
||||
|
||||
var right = this.statusAlign == 'right';
|
||||
this.currIconCls = this.iconCls || this.defaultIconCls;
|
||||
this.statusEl = new Ext.Toolbar.TextItem({
|
||||
cls: 'x-status-text ' + (this.currIconCls || ''),
|
||||
text: this.text || this.defaultText || ''
|
||||
});
|
||||
|
||||
if(right){
|
||||
this.add('->');
|
||||
this.add(this.statusEl);
|
||||
}else{
|
||||
this.insert(0, this.statusEl);
|
||||
this.insert(1, '->');
|
||||
}
|
||||
|
||||
// this.statusEl = td.createChild({
|
||||
// cls: 'x-status-text ' + (this.iconCls || this.defaultIconCls || ''),
|
||||
// html: this.text || this.defaultText || ''
|
||||
// });
|
||||
// this.statusEl.unselectable();
|
||||
|
||||
// this.spacerEl = td.insertSibling({
|
||||
// tag: 'td',
|
||||
// style: 'width:100%',
|
||||
// cn: [{cls:'ytb-spacer'}]
|
||||
// }, right ? 'before' : 'after');
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the
|
||||
* status that was set after a specified interval.
|
||||
* @param {Object/String} config A config object specifying what status to set, or a string assumed
|
||||
* to be the status text (and all other options are defaulted as explained below). A config
|
||||
* object containing any or all of the following properties can be passed:<ul>
|
||||
* <li><tt>text</tt> {String} : (optional) The status text to display. If not specified, any current
|
||||
* status text will remain unchanged.</li>
|
||||
* <li><tt>iconCls</tt> {String} : (optional) The CSS class used to customize the status icon (see
|
||||
* {@link #iconCls} for details). If not specified, any current iconCls will remain unchanged.</li>
|
||||
* <li><tt>clear</tt> {Boolean/Number/Object} : (optional) Allows you to set an internal callback that will
|
||||
* automatically clear the status text and iconCls after a specified amount of time has passed. If clear is not
|
||||
* specified, the new status will not be auto-cleared and will stay until updated again or cleared using
|
||||
* {@link #clearStatus}. If <tt>true</tt> is passed, the status will be cleared using {@link #autoClear},
|
||||
* {@link #defaultText} and {@link #defaultIconCls} via a fade out animation. If a numeric value is passed,
|
||||
* it will be used as the callback interval (in milliseconds), overriding the {@link #autoClear} value.
|
||||
* All other options will be defaulted as with the boolean option. To customize any other options,
|
||||
* you can pass an object in the format:<ul>
|
||||
* <li><tt>wait</tt> {Number} : (optional) The number of milliseconds to wait before clearing
|
||||
* (defaults to {@link #autoClear}).</li>
|
||||
* <li><tt>anim</tt> {Number} : (optional) False to clear the status immediately once the callback
|
||||
* executes (defaults to true which fades the status out).</li>
|
||||
* <li><tt>useDefaults</tt> {Number} : (optional) False to completely clear the status text and iconCls
|
||||
* (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).</li>
|
||||
* </ul></li></ul>
|
||||
* Example usage:<pre><code>
|
||||
// Simple call to update the text
|
||||
statusBar.setStatus('New status');
|
||||
|
||||
// Set the status and icon, auto-clearing with default options:
|
||||
statusBar.setStatus({
|
||||
text: 'New status',
|
||||
iconCls: 'x-status-custom',
|
||||
clear: true
|
||||
});
|
||||
|
||||
// Auto-clear with custom options:
|
||||
statusBar.setStatus({
|
||||
text: 'New status',
|
||||
iconCls: 'x-status-custom',
|
||||
clear: {
|
||||
wait: 8000,
|
||||
anim: false,
|
||||
useDefaults: false
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
setStatus : function(o){
|
||||
o = o || {};
|
||||
|
||||
if(typeof o == 'string'){
|
||||
o = {text:o};
|
||||
}
|
||||
if(o.text !== undefined){
|
||||
this.setText(o.text);
|
||||
}
|
||||
if(o.iconCls !== undefined){
|
||||
this.setIcon(o.iconCls);
|
||||
}
|
||||
|
||||
if(o.clear){
|
||||
var c = o.clear,
|
||||
wait = this.autoClear,
|
||||
defaults = {useDefaults: true, anim: true};
|
||||
|
||||
if(typeof c == 'object'){
|
||||
c = Ext.applyIf(c, defaults);
|
||||
if(c.wait){
|
||||
wait = c.wait;
|
||||
}
|
||||
}else if(typeof c == 'number'){
|
||||
wait = c;
|
||||
c = defaults;
|
||||
}else if(typeof c == 'boolean'){
|
||||
c = defaults;
|
||||
}
|
||||
|
||||
c.threadId = this.activeThreadId;
|
||||
this.clearStatus.defer(wait, this, [c]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation.
|
||||
* @param {Object} config (optional) A config object containing any or all of the following properties. If this
|
||||
* object is not specified the status will be cleared using the defaults below:<ul>
|
||||
* <li><tt>anim</tt> {Boolean} : (optional) True to clear the status by fading out the status element (defaults
|
||||
* to false which clears immediately).</li>
|
||||
* <li><tt>useDefaults</tt> {Boolean} : (optional) True to reset the text and icon using {@link #defaultText} and
|
||||
* {@link #defaultIconCls} (defaults to false which sets the text to '' and removes any existing icon class).</li>
|
||||
* </ul>
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
clearStatus : function(o){
|
||||
o = o || {};
|
||||
|
||||
if(o.threadId && o.threadId !== this.activeThreadId){
|
||||
// this means the current call was made internally, but a newer
|
||||
// thread has set a message since this call was deferred. Since
|
||||
// we don't want to overwrite a newer message just ignore.
|
||||
return this;
|
||||
}
|
||||
|
||||
var text = o.useDefaults ? this.defaultText : this.emptyText,
|
||||
iconCls = o.useDefaults ? (this.defaultIconCls ? this.defaultIconCls : '') : '';
|
||||
|
||||
if(o.anim){
|
||||
// animate the statusEl Ext.Element
|
||||
this.statusEl.el.fadeOut({
|
||||
remove: false,
|
||||
useDisplay: true,
|
||||
scope: this,
|
||||
callback: function(){
|
||||
this.setStatus({
|
||||
text: text,
|
||||
iconCls: iconCls
|
||||
});
|
||||
|
||||
this.statusEl.el.show();
|
||||
}
|
||||
});
|
||||
}else{
|
||||
// hide/show the el to avoid jumpy text or icon
|
||||
this.statusEl.hide();
|
||||
this.setStatus({
|
||||
text: text,
|
||||
iconCls: iconCls
|
||||
});
|
||||
this.statusEl.show();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for setting the status text directly. For more flexible options see {@link #setStatus}.
|
||||
* @param {String} text (optional) The text to set (defaults to '')
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
setText : function(text){
|
||||
this.activeThreadId++;
|
||||
this.text = text || '';
|
||||
if(this.rendered){
|
||||
this.statusEl.setText(this.text);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the current status text.
|
||||
* @return {String} The status text
|
||||
*/
|
||||
getText : function(){
|
||||
return this.text;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for setting the status icon directly. For more flexible options see {@link #setStatus}.
|
||||
* See {@link #iconCls} for complete details about customizing the icon.
|
||||
* @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed)
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
setIcon : function(cls){
|
||||
this.activeThreadId++;
|
||||
cls = cls || '';
|
||||
|
||||
if(this.rendered){
|
||||
if(this.currIconCls){
|
||||
this.statusEl.removeClass(this.currIconCls);
|
||||
this.currIconCls = null;
|
||||
}
|
||||
if(cls.length > 0){
|
||||
this.statusEl.addClass(cls);
|
||||
this.currIconCls = cls;
|
||||
}
|
||||
}else{
|
||||
this.currIconCls = cls;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for setting the status text and icon to special values that are pre-configured to indicate
|
||||
* a "busy" state, usually for loading or processing activities.
|
||||
* @param {Object/String} config (optional) A config object in the same format supported by {@link #setStatus}, or a
|
||||
* string to use as the status text (in which case all other options for setStatus will be defaulted). Use the
|
||||
* <tt>text</tt> and/or <tt>iconCls</tt> properties on the config to override the default {@link #busyText}
|
||||
* and {@link #busyIconCls} settings. If the config argument is not specified, {@link #busyText} and
|
||||
* {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}.
|
||||
* @return {Ext.ux.StatusBar} this
|
||||
*/
|
||||
showBusy : function(o){
|
||||
if(typeof o == 'string'){
|
||||
o = {text:o};
|
||||
}
|
||||
o = Ext.applyIf(o || {}, {
|
||||
text: this.busyText,
|
||||
iconCls: this.busyIconCls
|
||||
});
|
||||
return this.setStatus(o);
|
||||
}
|
||||
});
|
||||
Ext.reg('statusbar', Ext.ux.StatusBar);
|
64
deluge/ui/web/js/ext-extensions/ToggleField.js
Normal file
64
deluge/ui/web/js/ext-extensions/ToggleField.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
Ext.namespace("Ext.ux.form");
|
||||
|
||||
/**
|
||||
* Ext.ux.form.ToggleField class
|
||||
*
|
||||
* @author Damien Churchill
|
||||
* @version v0.1
|
||||
*
|
||||
* @class Ext.ux.form.ToggleField
|
||||
* @extends Ext.form.TriggerField
|
||||
*/
|
||||
Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, {
|
||||
|
||||
cls: 'x-toggle-field',
|
||||
|
||||
initComponent: function() {
|
||||
Ext.ux.form.ToggleField.superclass.initComponent.call(this);
|
||||
|
||||
this.toggle = new Ext.form.Checkbox();
|
||||
this.toggle.on('check', this.onToggleCheck, this);
|
||||
|
||||
this.input = new Ext.form.TextField({
|
||||
disabled: true
|
||||
});
|
||||
},
|
||||
|
||||
onRender: function(ct, position) {
|
||||
if (!this.el) {
|
||||
this.panel = new Ext.Panel({
|
||||
cls: this.groupCls,
|
||||
layout: 'table',
|
||||
layoutConfig: {
|
||||
columns: 2
|
||||
},
|
||||
border: false,
|
||||
renderTo: ct
|
||||
});
|
||||
this.panel.ownerCt = this;
|
||||
this.el = this.panel.getEl();
|
||||
|
||||
this.panel.add(this.toggle);
|
||||
this.panel.add(this.input);
|
||||
this.panel.doLayout();
|
||||
|
||||
this.toggle.getEl().parent().setStyle('padding-right', '10px');
|
||||
}
|
||||
Ext.ux.form.ToggleField.superclass.onRender.call(this, ct, position);
|
||||
},
|
||||
|
||||
// private
|
||||
onResize: function(w, h) {
|
||||
this.panel.setSize(w, h);
|
||||
this.panel.doLayout();
|
||||
|
||||
// we substract 10 for the padding :-)
|
||||
var inputWidth = w - this.toggle.getSize().width - 10;
|
||||
this.input.setSize(inputWidth, h);
|
||||
},
|
||||
|
||||
onToggleCheck: function(toggle, checked) {
|
||||
this.input.setDisabled(!checked);
|
||||
}
|
||||
});
|
||||
Ext.reg('togglefield', Ext.ux.form.ToggleField);
|
407
deluge/ui/web/js/ext-extensions/TreeGrid.js
Normal file
407
deluge/ui/web/js/ext-extensions/TreeGrid.js
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.tree.TreeGrid
|
||||
* @extends Ext.tree.TreePanel
|
||||
*
|
||||
* @xtype treegrid
|
||||
*/
|
||||
Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, {
|
||||
rootVisible : false,
|
||||
useArrows : true,
|
||||
lines : false,
|
||||
borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
|
||||
cls : 'x-treegrid',
|
||||
|
||||
columnResize : true,
|
||||
enableSort : true,
|
||||
reserveScrollOffset : true,
|
||||
enableHdMenu : true,
|
||||
|
||||
columnsText : 'Columns',
|
||||
|
||||
initComponent : function() {
|
||||
if(!this.root) {
|
||||
this.root = new Ext.tree.AsyncTreeNode({text: 'Root'});
|
||||
}
|
||||
|
||||
// initialize the loader
|
||||
var l = this.loader;
|
||||
if(!l){
|
||||
l = new Ext.ux.tree.TreeGridLoader({
|
||||
dataUrl: this.dataUrl,
|
||||
requestMethod: this.requestMethod,
|
||||
store: this.store
|
||||
});
|
||||
}else if(Ext.isObject(l) && !l.load){
|
||||
l = new Ext.ux.tree.TreeGridLoader(l);
|
||||
}
|
||||
else if(l) {
|
||||
l.createNode = function(attr) {
|
||||
if (!attr.uiProvider) {
|
||||
attr.uiProvider = Ext.ux.tree.TreeGridNodeUI;
|
||||
}
|
||||
return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
|
||||
}
|
||||
}
|
||||
this.loader = l;
|
||||
|
||||
Ext.ux.tree.TreeGrid.superclass.initComponent.call(this);
|
||||
|
||||
this.initColumns();
|
||||
|
||||
if(this.enableSort) {
|
||||
this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort);
|
||||
}
|
||||
|
||||
if(this.columnResize){
|
||||
this.colResizer = new Ext.tree.ColumnResizer(this.columnResize);
|
||||
this.colResizer.init(this);
|
||||
}
|
||||
|
||||
var c = this.columns;
|
||||
if(!this.internalTpl){
|
||||
this.internalTpl = new Ext.XTemplate(
|
||||
'<div class="x-grid3-header">',
|
||||
'<div class="x-treegrid-header-inner">',
|
||||
'<div class="x-grid3-header-offset">',
|
||||
'<table cellspacing="0" cellpadding="0" border="0"><colgroup><tpl for="columns"><col /></tpl></colgroup>',
|
||||
'<thead><tr class="x-grid3-hd-row">',
|
||||
'<tpl for="columns">',
|
||||
'<td class="x-grid3-hd x-grid3-cell x-treegrid-hd" style="text-align: {align};" id="', this.id, '-xlhd-{#}">',
|
||||
'<div class="x-grid3-hd-inner x-treegrid-hd-inner" unselectable="on">',
|
||||
this.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
|
||||
'{header}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
|
||||
'</div>',
|
||||
'</td></tpl>',
|
||||
'</tr></thead>',
|
||||
'</div></table>',
|
||||
'</div></div>',
|
||||
'</div>',
|
||||
'<div class="x-treegrid-root-node">',
|
||||
'<table class="x-treegrid-root-table" cellpadding="0" cellspacing="0" style="table-layout: fixed;"></table>',
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
|
||||
if(!this.colgroupTpl) {
|
||||
this.colgroupTpl = new Ext.XTemplate(
|
||||
'<colgroup><tpl for="columns"><col style="width: {width}px"/></tpl></colgroup>'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
initColumns : function() {
|
||||
var cs = this.columns,
|
||||
len = cs.length,
|
||||
columns = [],
|
||||
i, c;
|
||||
|
||||
for(i = 0; i < len; i++){
|
||||
c = cs[i];
|
||||
if(!c.isColumn) {
|
||||
c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn';
|
||||
c = Ext.create(c);
|
||||
}
|
||||
c.init(this);
|
||||
columns.push(c);
|
||||
|
||||
if(this.enableSort !== false && c.sortable !== false) {
|
||||
c.sortable = true;
|
||||
this.enableSort = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.columns = columns;
|
||||
},
|
||||
|
||||
onRender : function(){
|
||||
Ext.tree.TreePanel.superclass.onRender.apply(this, arguments);
|
||||
|
||||
this.el.addClass('x-treegrid');
|
||||
|
||||
this.outerCt = this.body.createChild({
|
||||
cls:'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')
|
||||
});
|
||||
|
||||
this.internalTpl.overwrite(this.outerCt, {columns: this.columns});
|
||||
|
||||
this.mainHd = Ext.get(this.outerCt.dom.firstChild);
|
||||
this.innerHd = Ext.get(this.mainHd.dom.firstChild);
|
||||
this.innerBody = Ext.get(this.outerCt.dom.lastChild);
|
||||
this.innerCt = Ext.get(this.innerBody.dom.firstChild);
|
||||
|
||||
this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
|
||||
|
||||
if(this.hideHeaders){
|
||||
this.header.dom.style.display = 'none';
|
||||
}
|
||||
else if(this.enableHdMenu !== false){
|
||||
this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'});
|
||||
if(this.enableColumnHide !== false){
|
||||
this.colMenu = new Ext.menu.Menu({id: this.id + '-hcols-menu'});
|
||||
this.colMenu.on({
|
||||
scope: this,
|
||||
beforeshow: this.beforeColMenuShow,
|
||||
itemclick: this.handleHdMenuClick
|
||||
});
|
||||
this.hmenu.add({
|
||||
itemId:'columns',
|
||||
hideOnClick: false,
|
||||
text: this.columnsText,
|
||||
menu: this.colMenu,
|
||||
iconCls: 'x-cols-icon'
|
||||
});
|
||||
}
|
||||
this.hmenu.on('itemclick', this.handleHdMenuClick, this);
|
||||
}
|
||||
},
|
||||
|
||||
setRootNode : function(node){
|
||||
node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI;
|
||||
node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node);
|
||||
if(this.innerCt) {
|
||||
this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
|
||||
}
|
||||
return node;
|
||||
},
|
||||
|
||||
initEvents : function() {
|
||||
Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments);
|
||||
|
||||
this.mon(this.innerBody, 'scroll', this.syncScroll, this);
|
||||
this.mon(this.innerHd, 'click', this.handleHdDown, this);
|
||||
this.mon(this.mainHd, {
|
||||
scope: this,
|
||||
mouseover: this.handleHdOver,
|
||||
mouseout: this.handleHdOut
|
||||
});
|
||||
},
|
||||
|
||||
onResize : function(w, h) {
|
||||
Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments);
|
||||
|
||||
var bd = this.innerBody.dom;
|
||||
var hd = this.innerHd.dom;
|
||||
|
||||
if(!bd){
|
||||
return;
|
||||
}
|
||||
|
||||
if(Ext.isNumber(h)){
|
||||
bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px';
|
||||
}
|
||||
|
||||
if(Ext.isNumber(w)){
|
||||
var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
|
||||
if(this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)){
|
||||
this.setScrollOffset(sw);
|
||||
}else{
|
||||
var me = this;
|
||||
setTimeout(function(){
|
||||
me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0);
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateColumnWidths : function() {
|
||||
var cols = this.columns,
|
||||
colCount = cols.length,
|
||||
groups = this.outerCt.query('colgroup'),
|
||||
groupCount = groups.length,
|
||||
c, g, i, j;
|
||||
|
||||
for(i = 0; i<colCount; i++) {
|
||||
c = cols[i];
|
||||
for(j = 0; j<groupCount; j++) {
|
||||
g = groups[j];
|
||||
g.childNodes[i].style.width = (c.hidden ? 0 : c.width) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0, groups = this.innerHd.query('td'), len = groups.length; i<len; i++) {
|
||||
c = Ext.fly(groups[i]);
|
||||
if(cols[i] && cols[i].hidden) {
|
||||
c.addClass('x-treegrid-hd-hidden');
|
||||
}
|
||||
else {
|
||||
c.removeClass('x-treegrid-hd-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
var tcw = this.getTotalColumnWidth();
|
||||
Ext.fly(this.innerHd.dom.firstChild).setWidth(tcw + (this.scrollOffset || 0));
|
||||
this.outerCt.select('table').setWidth(tcw);
|
||||
this.syncHeaderScroll();
|
||||
},
|
||||
|
||||
getVisibleColumns : function() {
|
||||
var columns = [],
|
||||
cs = this.columns,
|
||||
len = cs.length,
|
||||
i;
|
||||
|
||||
for(i = 0; i<len; i++) {
|
||||
if(!cs[i].hidden) {
|
||||
columns.push(cs[i]);
|
||||
}
|
||||
}
|
||||
return columns;
|
||||
},
|
||||
|
||||
getTotalColumnWidth : function() {
|
||||
var total = 0;
|
||||
for(var i = 0, cs = this.getVisibleColumns(), len = cs.length; i<len; i++) {
|
||||
total += cs[i].width;
|
||||
}
|
||||
return total;
|
||||
},
|
||||
|
||||
setScrollOffset : function(scrollOffset) {
|
||||
this.scrollOffset = scrollOffset;
|
||||
this.updateColumnWidths();
|
||||
},
|
||||
|
||||
// private
|
||||
handleHdDown : function(e, t){
|
||||
var hd = e.getTarget('.x-treegrid-hd');
|
||||
|
||||
if(hd && Ext.fly(t).hasClass('x-grid3-hd-btn')){
|
||||
var ms = this.hmenu.items,
|
||||
cs = this.columns,
|
||||
index = this.findHeaderIndex(hd),
|
||||
c = cs[index],
|
||||
sort = c.sortable;
|
||||
|
||||
e.stopEvent();
|
||||
Ext.fly(hd).addClass('x-grid3-hd-menu-open');
|
||||
this.hdCtxIndex = index;
|
||||
|
||||
this.fireEvent('headerbuttonclick', ms, c, hd, index);
|
||||
|
||||
this.hmenu.on('hide', function(){
|
||||
Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
|
||||
}, this, {single:true});
|
||||
|
||||
this.hmenu.show(t, 'tl-bl?');
|
||||
}
|
||||
else if(hd) {
|
||||
var index = this.findHeaderIndex(hd);
|
||||
this.fireEvent('headerclick', this.columns[index], hd, index);
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
handleHdOver : function(e, t){
|
||||
var hd = e.getTarget('.x-treegrid-hd');
|
||||
if(hd && !this.headersDisabled){
|
||||
index = this.findHeaderIndex(hd);
|
||||
this.activeHdRef = t;
|
||||
this.activeHdIndex = index;
|
||||
var el = Ext.get(hd);
|
||||
this.activeHdRegion = el.getRegion();
|
||||
el.addClass('x-grid3-hd-over');
|
||||
this.activeHdBtn = el.child('.x-grid3-hd-btn');
|
||||
if(this.activeHdBtn){
|
||||
this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
handleHdOut : function(e, t){
|
||||
var hd = e.getTarget('.x-treegrid-hd');
|
||||
if(hd && (!Ext.isIE || !e.within(hd, true))){
|
||||
this.activeHdRef = null;
|
||||
Ext.fly(hd).removeClass('x-grid3-hd-over');
|
||||
hd.style.cursor = '';
|
||||
}
|
||||
},
|
||||
|
||||
findHeaderIndex : function(hd){
|
||||
hd = hd.dom || hd;
|
||||
var cs = hd.parentNode.childNodes;
|
||||
for(var i = 0, c; c = cs[i]; i++){
|
||||
if(c == hd){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
// private
|
||||
beforeColMenuShow : function(){
|
||||
var cols = this.columns,
|
||||
colCount = cols.length,
|
||||
i, c;
|
||||
this.colMenu.removeAll();
|
||||
for(i = 1; i < colCount; i++){
|
||||
c = cols[i];
|
||||
if(c.hideable !== false){
|
||||
this.colMenu.add(new Ext.menu.CheckItem({
|
||||
itemId: 'col-' + i,
|
||||
text: c.header,
|
||||
checked: !c.hidden,
|
||||
hideOnClick:false,
|
||||
disabled: c.hideable === false
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
handleHdMenuClick : function(item){
|
||||
var index = this.hdCtxIndex,
|
||||
id = item.getItemId();
|
||||
|
||||
if(this.fireEvent('headermenuclick', this.columns[index], id, index) !== false) {
|
||||
index = id.substr(4);
|
||||
if(index > 0 && this.columns[index]) {
|
||||
this.setColumnVisible(index, !item.checked);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
setColumnVisible : function(index, visible) {
|
||||
this.columns[index].hidden = !visible;
|
||||
this.updateColumnWidths();
|
||||
},
|
||||
|
||||
/**
|
||||
* Scrolls the grid to the top
|
||||
*/
|
||||
scrollToTop : function(){
|
||||
this.innerBody.dom.scrollTop = 0;
|
||||
this.innerBody.dom.scrollLeft = 0;
|
||||
},
|
||||
|
||||
// private
|
||||
syncScroll : function(){
|
||||
this.syncHeaderScroll();
|
||||
var mb = this.innerBody.dom;
|
||||
this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
|
||||
},
|
||||
|
||||
// private
|
||||
syncHeaderScroll : function(){
|
||||
var mb = this.innerBody.dom;
|
||||
this.innerHd.dom.scrollLeft = mb.scrollLeft;
|
||||
this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
|
||||
},
|
||||
|
||||
registerNode : function(n) {
|
||||
Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n);
|
||||
if(!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) {
|
||||
n.ui = new Ext.ux.tree.TreeGridNodeUI(n);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ext.reg('treegrid', Ext.ux.tree.TreeGrid);
|
117
deluge/ui/web/js/ext-extensions/TreeGridColumnResizer.js
Normal file
117
deluge/ui/web/js/ext-extensions/TreeGridColumnResizer.js
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.tree.ColumnResizer
|
||||
* @extends Ext.util.Observable
|
||||
*/
|
||||
Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, {
|
||||
/**
|
||||
* @cfg {Number} minWidth The minimum width the column can be dragged to.
|
||||
* Defaults to <tt>14</tt>.
|
||||
*/
|
||||
minWidth: 14,
|
||||
|
||||
constructor: function(config){
|
||||
Ext.apply(this, config);
|
||||
Ext.tree.ColumnResizer.superclass.constructor.call(this);
|
||||
},
|
||||
|
||||
init : function(tree){
|
||||
this.tree = tree;
|
||||
tree.on('render', this.initEvents, this);
|
||||
},
|
||||
|
||||
initEvents : function(tree){
|
||||
tree.mon(tree.innerHd, 'mousemove', this.handleHdMove, this);
|
||||
this.tracker = new Ext.dd.DragTracker({
|
||||
onBeforeStart: this.onBeforeStart.createDelegate(this),
|
||||
onStart: this.onStart.createDelegate(this),
|
||||
onDrag: this.onDrag.createDelegate(this),
|
||||
onEnd: this.onEnd.createDelegate(this),
|
||||
tolerance: 3,
|
||||
autoStart: 300
|
||||
});
|
||||
this.tracker.initEl(tree.innerHd);
|
||||
tree.on('beforedestroy', this.tracker.destroy, this.tracker);
|
||||
},
|
||||
|
||||
handleHdMove : function(e, t){
|
||||
var hw = 5,
|
||||
x = e.getPageX(),
|
||||
hd = e.getTarget('.x-treegrid-hd', 3, true);
|
||||
|
||||
if(hd){
|
||||
var r = hd.getRegion(),
|
||||
ss = hd.dom.style,
|
||||
pn = hd.dom.parentNode;
|
||||
|
||||
if(x - r.left <= hw && hd.dom !== pn.firstChild) {
|
||||
var ps = hd.dom.previousSibling;
|
||||
while(ps && Ext.fly(ps).hasClass('x-treegrid-hd-hidden')) {
|
||||
ps = ps.previousSibling;
|
||||
}
|
||||
if(ps) {
|
||||
this.activeHd = Ext.get(ps);
|
||||
ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
|
||||
}
|
||||
} else if(r.right - x <= hw) {
|
||||
var ns = hd.dom;
|
||||
while(ns && Ext.fly(ns).hasClass('x-treegrid-hd-hidden')) {
|
||||
ns = ns.previousSibling;
|
||||
}
|
||||
if(ns) {
|
||||
this.activeHd = Ext.get(ns);
|
||||
ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
|
||||
}
|
||||
} else{
|
||||
delete this.activeHd;
|
||||
ss.cursor = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeStart : function(e){
|
||||
this.dragHd = this.activeHd;
|
||||
return !!this.dragHd;
|
||||
},
|
||||
|
||||
onStart : function(e){
|
||||
this.tree.headersDisabled = true;
|
||||
this.proxy = this.tree.body.createChild({cls:'x-treegrid-resizer'});
|
||||
this.proxy.setHeight(this.tree.body.getHeight());
|
||||
|
||||
var x = this.tracker.getXY()[0];
|
||||
|
||||
this.hdX = this.dragHd.getX();
|
||||
this.hdIndex = this.tree.findHeaderIndex(this.dragHd);
|
||||
|
||||
this.proxy.setX(this.hdX);
|
||||
this.proxy.setWidth(x-this.hdX);
|
||||
|
||||
this.maxWidth = this.tree.outerCt.getWidth() - this.tree.innerBody.translatePoints(this.hdX).left;
|
||||
},
|
||||
|
||||
onDrag : function(e){
|
||||
var cursorX = this.tracker.getXY()[0];
|
||||
this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));
|
||||
},
|
||||
|
||||
onEnd : function(e){
|
||||
var nw = this.proxy.getWidth(),
|
||||
tree = this.tree;
|
||||
|
||||
this.proxy.remove();
|
||||
delete this.dragHd;
|
||||
|
||||
tree.columns[this.hdIndex].width = nw;
|
||||
tree.updateColumnWidths();
|
||||
|
||||
setTimeout(function(){
|
||||
tree.headersDisabled = false;
|
||||
}, 100);
|
||||
}
|
||||
});
|
45
deluge/ui/web/js/ext-extensions/TreeGridColumns.js
Normal file
45
deluge/ui/web/js/ext-extensions/TreeGridColumns.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
(function() {
|
||||
Ext.override(Ext.list.Column, {
|
||||
init : function() {
|
||||
if(!this.type){
|
||||
this.type = "auto";
|
||||
}
|
||||
|
||||
var st = Ext.data.SortTypes;
|
||||
// named sortTypes are supported, here we look them up
|
||||
if(typeof this.sortType == "string"){
|
||||
this.sortType = st[this.sortType];
|
||||
}
|
||||
|
||||
// set default sortType for strings and dates
|
||||
if(!this.sortType){
|
||||
switch(this.type){
|
||||
case "string":
|
||||
this.sortType = st.asUCString;
|
||||
break;
|
||||
case "date":
|
||||
this.sortType = st.asDate;
|
||||
break;
|
||||
default:
|
||||
this.sortType = st.none;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ext.tree.Column = Ext.extend(Ext.list.Column, {});
|
||||
Ext.tree.NumberColumn = Ext.extend(Ext.list.NumberColumn, {});
|
||||
Ext.tree.DateColumn = Ext.extend(Ext.list.DateColumn, {});
|
||||
Ext.tree.BooleanColumn = Ext.extend(Ext.list.BooleanColumn, {});
|
||||
|
||||
Ext.reg('tgcolumn', Ext.tree.Column);
|
||||
Ext.reg('tgnumbercolumn', Ext.tree.NumberColumn);
|
||||
Ext.reg('tgdatecolumn', Ext.tree.DateColumn);
|
||||
Ext.reg('tgbooleancolumn', Ext.tree.BooleanColumn);
|
||||
})();
|
18
deluge/ui/web/js/ext-extensions/TreeGridLoader.js
Normal file
18
deluge/ui/web/js/ext-extensions/TreeGridLoader.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.tree.TreeGridLoader
|
||||
* @extends Ext.tree.TreeLoader
|
||||
*/
|
||||
Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, {
|
||||
createNode : function(attr) {
|
||||
if (!attr.uiProvider) {
|
||||
attr.uiProvider = Ext.ux.tree.TreeGridNodeUI;
|
||||
}
|
||||
return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
|
||||
}
|
||||
});
|
107
deluge/ui/web/js/ext-extensions/TreeGridNodeUI.js
Normal file
107
deluge/ui/web/js/ext-extensions/TreeGridNodeUI.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
/**
|
||||
* @class Ext.ux.tree.TreeGridNodeUI
|
||||
* @extends Ext.tree.TreeNodeUI
|
||||
*/
|
||||
Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
|
||||
isTreeGridNodeUI: true,
|
||||
|
||||
renderElements : function(n, a, targetNode, bulkRender){
|
||||
var t = n.getOwnerTree(),
|
||||
cols = t.columns,
|
||||
c = cols[0],
|
||||
i, buf, len;
|
||||
|
||||
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
|
||||
|
||||
buf = [
|
||||
'<tbody class="x-tree-node">',
|
||||
'<tr ext:tree-node-id="', n.id ,'" class="x-tree-node-el ', a.cls, '">',
|
||||
'<td class="x-treegrid-col">',
|
||||
'<span class="x-tree-node-indent">', this.indentMarkup, "</span>",
|
||||
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
|
||||
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon', (a.icon ? " x-tree-node-inline-icon" : ""), (a.iconCls ? " "+a.iconCls : ""), '" unselectable="on">',
|
||||
'<a hidefocus="on" class="x-tree-node-anchor" href="', a.href ? a.href : '#', '" tabIndex="1" ',
|
||||
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : '', '>',
|
||||
'<span unselectable="on">', (c.tpl ? c.tpl.apply(a) : a[c.dataIndex] || c.text), '</span></a>',
|
||||
'</td>'
|
||||
];
|
||||
|
||||
for(i = 1, len = cols.length; i < len; i++){
|
||||
c = cols[i];
|
||||
buf.push(
|
||||
'<td class="x-treegrid-col ', (c.cls ? c.cls : ''), '">',
|
||||
'<div unselectable="on" class="x-treegrid-text"', (c.align ? ' style="text-align: ' + c.align + ';"' : ''), '>',
|
||||
(c.tpl ? c.tpl.apply(a) : a[c.dataIndex]),
|
||||
'</div>',
|
||||
'</td>'
|
||||
);
|
||||
}
|
||||
|
||||
buf.push(
|
||||
'</tr><tr class="x-tree-node-ct"><td colspan="', cols.length, '">',
|
||||
'<table class="x-treegrid-node-ct-table" cellpadding="0" cellspacing="0" style="table-layout: fixed; display: none; width: ', t.innerCt.getWidth() ,'px;"><colgroup>'
|
||||
);
|
||||
for(i = 0, len = cols.length; i<len; i++) {
|
||||
buf.push('<col style="width: ', (cols[i].hidden ? 0 : cols[i].width) ,'px;" />');
|
||||
}
|
||||
buf.push('</colgroup></table></td></tr></tbody>');
|
||||
|
||||
if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
|
||||
this.wrap = Ext.DomHelper.insertHtml("beforeBegin", n.nextSibling.ui.getEl(), buf.join(''));
|
||||
}else{
|
||||
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(''));
|
||||
}
|
||||
|
||||
this.elNode = this.wrap.childNodes[0];
|
||||
this.ctNode = this.wrap.childNodes[1].firstChild.firstChild;
|
||||
var cs = this.elNode.firstChild.childNodes;
|
||||
this.indentNode = cs[0];
|
||||
this.ecNode = cs[1];
|
||||
this.iconNode = cs[2];
|
||||
this.anchor = cs[3];
|
||||
this.textNode = cs[3].firstChild;
|
||||
},
|
||||
|
||||
// private
|
||||
animExpand : function(cb){
|
||||
this.ctNode.style.display = "";
|
||||
Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, cb);
|
||||
}
|
||||
});
|
||||
|
||||
Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
|
||||
isTreeGridNodeUI: true,
|
||||
|
||||
// private
|
||||
render : function(){
|
||||
if(!this.rendered){
|
||||
this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom;
|
||||
this.node.expanded = true;
|
||||
}
|
||||
|
||||
if(Ext.isWebKit) {
|
||||
// weird table-layout: fixed issue in webkit
|
||||
var ct = this.ctNode;
|
||||
ct.style.tableLayout = null;
|
||||
(function() {
|
||||
ct.style.tableLayout = 'fixed';
|
||||
}).defer(1);
|
||||
}
|
||||
},
|
||||
|
||||
destroy : function(){
|
||||
if(this.elNode){
|
||||
Ext.dd.Registry.unregister(this.elNode.id);
|
||||
}
|
||||
delete this.node;
|
||||
},
|
||||
|
||||
collapse : Ext.emptyFn,
|
||||
expand : Ext.emptyFn
|
||||
});
|
116
deluge/ui/web/js/ext-extensions/TreeGridSorter.js
Normal file
116
deluge/ui/web/js/ext-extensions/TreeGridSorter.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*!
|
||||
* Ext JS Library 3.1.0
|
||||
* Copyright(c) 2006-2009 Ext JS, LLC
|
||||
* licensing@extjs.com
|
||||
* http://www.extjs.com/license
|
||||
*/
|
||||
Ext.ns('Ext.ux.tree');
|
||||
|
||||
/**
|
||||
* @class Ext.ux.tree.TreeGridSorter
|
||||
* @extends Ext.tree.TreeSorter
|
||||
*/
|
||||
Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, {
|
||||
/**
|
||||
* @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
|
||||
*/
|
||||
sortClasses : ['sort-asc', 'sort-desc'],
|
||||
/**
|
||||
* @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
|
||||
*/
|
||||
sortAscText : 'Sort Ascending',
|
||||
/**
|
||||
* @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
|
||||
*/
|
||||
sortDescText : 'Sort Descending',
|
||||
|
||||
constructor : function(tree, config) {
|
||||
if(!Ext.isObject(config)) {
|
||||
config = {
|
||||
property: tree.columns[0].dataIndex || 'text',
|
||||
folderSort: true
|
||||
}
|
||||
}
|
||||
|
||||
Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this, arguments);
|
||||
|
||||
this.tree = tree;
|
||||
tree.on('headerclick', this.onHeaderClick, this);
|
||||
tree.ddAppendOnly = true;
|
||||
|
||||
me = this;
|
||||
this.defaultSortFn = function(n1, n2){
|
||||
|
||||
var dsc = me.dir && me.dir.toLowerCase() == 'desc';
|
||||
var p = me.property || 'text';
|
||||
var sortType = me.sortType;
|
||||
var fs = me.folderSort;
|
||||
var cs = me.caseSensitive === true;
|
||||
var leafAttr = me.leafAttr || 'leaf';
|
||||
|
||||
if(fs){
|
||||
if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
|
||||
return 1;
|
||||
}
|
||||
if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
var v1 = sortType ? sortType(n1.attributes[p]) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
|
||||
var v2 = sortType ? sortType(n2.attributes[p]) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
|
||||
if(v1 < v2){
|
||||
return dsc ? +1 : -1;
|
||||
}else if(v1 > v2){
|
||||
return dsc ? -1 : +1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
tree.on('afterrender', this.onAfterTreeRender, this, {single: true});
|
||||
tree.on('headermenuclick', this.onHeaderMenuClick, this);
|
||||
},
|
||||
|
||||
onAfterTreeRender : function() {
|
||||
var hmenu = this.tree.hmenu;
|
||||
hmenu.insert(0,
|
||||
{itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
|
||||
{itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
|
||||
);
|
||||
this.updateSortIcon(0, 'asc');
|
||||
},
|
||||
|
||||
onHeaderMenuClick : function(c, id, index) {
|
||||
if(id === 'asc' || id === 'desc') {
|
||||
this.onHeaderClick(c, null, index);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onHeaderClick : function(c, el, i) {
|
||||
if(c && !this.tree.headersDisabled){
|
||||
var me = this;
|
||||
|
||||
me.property = c.dataIndex;
|
||||
me.dir = c.dir = (c.dir === 'desc' ? 'asc' : 'desc');
|
||||
me.sortType = c.sortType;
|
||||
me.caseSensitive === Ext.isBoolean(c.caseSensitive) ? c.caseSensitive : this.caseSensitive;
|
||||
me.sortFn = c.sortFn || this.defaultSortFn;
|
||||
|
||||
this.tree.root.cascade(function(n) {
|
||||
if(!n.isLeaf()) {
|
||||
me.updateSort(me.tree, n);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateSortIcon(i, c.dir);
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
updateSortIcon : function(col, dir){
|
||||
var sc = this.sortClasses;
|
||||
var hds = this.tree.innerHd.select('td').removeClass(sc);
|
||||
hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]);
|
||||
}
|
||||
});
|
|
@ -114,11 +114,11 @@ OLD_CONFIG_KEYS = (
|
|||
"https"
|
||||
)
|
||||
|
||||
def rpath(path):
|
||||
def rpath(*paths):
|
||||
"""Convert a relative path into an absolute path relative to the location
|
||||
of this script.
|
||||
"""
|
||||
return os.path.join(current_dir, path)
|
||||
return os.path.join(current_dir, *paths)
|
||||
|
||||
class Config(resource.Resource):
|
||||
"""
|
||||
|
@ -255,9 +255,8 @@ class LookupResource(resource.Resource, component.Component):
|
|||
|
||||
def addDirectory(self, directory, path=""):
|
||||
log.debug("Adding directory `%s` with path `%s`", directory, path)
|
||||
paths = self.__paths.get(path, [])
|
||||
paths = self.__paths.setdefault(path, [])
|
||||
paths.append(directory)
|
||||
self.__paths[path] = paths
|
||||
|
||||
def removeDirectory(self, directory, path=""):
|
||||
log.debug("Removing directory `%s`", directory)
|
||||
|
@ -265,7 +264,7 @@ class LookupResource(resource.Resource, component.Component):
|
|||
|
||||
def getChild(self, path, request):
|
||||
if hasattr(request, 'lookup_path'):
|
||||
request.lookup_path += '/' + path
|
||||
request.lookup_path = os.path.join(request.lookup_path, path)
|
||||
else:
|
||||
request.lookup_path = path
|
||||
return self
|
||||
|
@ -314,48 +313,69 @@ class TopLevel(resource.Resource):
|
|||
"/js/ext-extensions-debug.js",
|
||||
"/config.js",
|
||||
"/gettext.js",
|
||||
"/js/Deluge.js",
|
||||
"/js/Deluge.Formatters.js",
|
||||
"/js/Deluge.Menus.js",
|
||||
"/js/Deluge.Events.js",
|
||||
"/js/Deluge.OptionsManager.js",
|
||||
"/js/Deluge.MultiOptionsManager.js",
|
||||
"/js/Deluge.Add.js",
|
||||
"/js/Deluge.Add.File.js",
|
||||
"/js/Deluge.Add.Url.js",
|
||||
"/js/Deluge.Add.Infohash.js",
|
||||
"/js/Deluge.Client.js",
|
||||
"/js/Deluge.ConnectionManager.js",
|
||||
"/js/Deluge.Details.js",
|
||||
"/js/Deluge.Details.Status.js",
|
||||
"/js/Deluge.Details.Details.js",
|
||||
"/js/Deluge.Details.Files.js",
|
||||
"/js/Deluge.Details.Peers.js",
|
||||
"/js/Deluge.Details.Options.js",
|
||||
"/js/Deluge.EditTrackers.js",
|
||||
"/js/Deluge.Keys.js",
|
||||
"/js/Deluge.Login.js",
|
||||
"/js/Deluge.MoveStorage.js",
|
||||
"/js/Deluge.Plugin.js",
|
||||
"/js/Deluge.Preferences.js",
|
||||
"/js/Deluge.Preferences.Downloads.js",
|
||||
"/js/Deluge.Preferences.Network.js",
|
||||
"/js/Deluge.Preferences.Encryption.js",
|
||||
"/js/Deluge.Preferences.Bandwidth.js",
|
||||
"/js/Deluge.Preferences.Interface.js",
|
||||
"/js/Deluge.Preferences.Other.js",
|
||||
"/js/Deluge.Preferences.Daemon.js",
|
||||
"/js/Deluge.Preferences.Queue.js",
|
||||
"/js/Deluge.Preferences.Proxy.js",
|
||||
"/js/Deluge.Preferences.Notification.js",
|
||||
"/js/Deluge.Preferences.Cache.js",
|
||||
"/js/Deluge.Preferences.Plugins.js",
|
||||
"/js/Deluge.Remove.js",
|
||||
"/js/Deluge.Sidebar.js",
|
||||
"/js/Deluge.Statusbar.js",
|
||||
"/js/Deluge.Toolbar.js",
|
||||
"/js/Deluge.Torrents.js",
|
||||
"/js/Deluge.UI.js"
|
||||
"/js/deluge-all-debug.js"
|
||||
]
|
||||
|
||||
__dev_scripts = [
|
||||
"/js/ext-base-debug.js",
|
||||
"/js/ext-all-debug.js",
|
||||
"/js/ext-extensions/BufferView.js",
|
||||
"/js/ext-extensions/FileUploadField.js",
|
||||
"/js/ext-extensions/JSLoader.js",
|
||||
"/js/ext-extensions/Spinner.js",
|
||||
"/js/ext-extensions/SpinnerField.js",
|
||||
"/js/ext-extensions/StatusBar.js",
|
||||
"/js/ext-extensions/ToggleField.js",
|
||||
"/js/ext-extensions/TreeGridSorter.js",
|
||||
"/js/ext-extensions/TreeGridColumnResizer.js",
|
||||
"/js/ext-extensions/TreeGridNodeUI.js",
|
||||
"/js/ext-extensions/TreeGridLoader.js",
|
||||
"/js/ext-extensions/TreeGridColumns.js",
|
||||
"/js/ext-extensions/TreeGrid.js",
|
||||
"/config.js",
|
||||
"/gettext.js",
|
||||
"/js/deluge-all/Deluge.js",
|
||||
"/js/deluge-all/Deluge.Formatters.js",
|
||||
"/js/deluge-all/Deluge.Menus.js",
|
||||
"/js/deluge-all/Deluge.Events.js",
|
||||
"/js/deluge-all/Deluge.OptionsManager.js",
|
||||
"/js/deluge-all/Deluge.MultiOptionsManager.js",
|
||||
"/js/deluge-all/Deluge.Add.js",
|
||||
"/js/deluge-all/Deluge.Add.File.js",
|
||||
"/js/deluge-all/Deluge.Add.Url.js",
|
||||
"/js/deluge-all/Deluge.Add.Infohash.js",
|
||||
"/js/deluge-all/Deluge.Client.js",
|
||||
"/js/deluge-all/Deluge.ConnectionManager.js",
|
||||
"/js/deluge-all/Deluge.Details.js",
|
||||
"/js/deluge-all/Deluge.Details.Status.js",
|
||||
"/js/deluge-all/Deluge.Details.Details.js",
|
||||
"/js/deluge-all/Deluge.Details.Files.js",
|
||||
"/js/deluge-all/Deluge.Details.Peers.js",
|
||||
#"/js/deluge-all/Deluge.Details.Options.js",
|
||||
"/js/deluge-all/Deluge.EditTrackers.js",
|
||||
"/js/deluge-all/Deluge.Keys.js",
|
||||
"/js/deluge-all/Deluge.Login.js",
|
||||
"/js/deluge-all/Deluge.MoveStorage.js",
|
||||
"/js/deluge-all/Deluge.Plugin.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Downloads.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Network.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Encryption.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Bandwidth.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Interface.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Other.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Daemon.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Queue.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Proxy.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Notification.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Cache.js",
|
||||
#"/js/deluge-all/Deluge.Preferences.Plugins.js",
|
||||
"/js/deluge-all/Deluge.Remove.js",
|
||||
"/js/deluge-all/Deluge.Sidebar.js",
|
||||
"/js/deluge-all/Deluge.Statusbar.js",
|
||||
"/js/deluge-all/Deluge.Toolbar.js",
|
||||
"/js/deluge-all/Deluge.Torrents.js",
|
||||
"/js/deluge-all/Deluge.UI.js"
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -366,7 +386,13 @@ class TopLevel(resource.Resource):
|
|||
self.putChild("flag", Flag())
|
||||
self.putChild("icons", LookupResource("Icons", rpath("icons")))
|
||||
self.putChild("images", LookupResource("Images", rpath("images")))
|
||||
self.putChild("js", LookupResource("Javascript", rpath("js")))
|
||||
|
||||
# Add the javascript resource with the development paths
|
||||
js = LookupResource("Javascript", rpath("js"))
|
||||
js.addDirectory(rpath("js", "ext-extensions"), "ext-extensions")
|
||||
js.addDirectory(rpath("js", "deluge-all"), "deluge-all")
|
||||
self.putChild("js", js)
|
||||
|
||||
self.putChild("json", JSON())
|
||||
self.putChild("upload", Upload())
|
||||
self.putChild("render", Render())
|
||||
|
@ -384,6 +410,10 @@ class TopLevel(resource.Resource):
|
|||
def debug_scripts(self):
|
||||
return self.__debug_scripts
|
||||
|
||||
@property
|
||||
def dev_scripts(self):
|
||||
return self.__dev_scripts
|
||||
|
||||
@property
|
||||
def stylesheets(self):
|
||||
return self.__stylesheets
|
||||
|
@ -418,15 +448,26 @@ class TopLevel(resource.Resource):
|
|||
return resource.Resource.getChild(self, path, request)
|
||||
|
||||
def render(self, request):
|
||||
debug = 'dev' in common.get_version()
|
||||
|
||||
debug = False
|
||||
if 'debug' in request.args:
|
||||
debug_arg = request.args.get('debug')[-1]
|
||||
if debug_arg == 'true':
|
||||
if debug_arg in ('true', 'yes', '1'):
|
||||
debug = True
|
||||
elif debug_arg == 'false':
|
||||
else:
|
||||
debug = False
|
||||
|
||||
if debug:
|
||||
dev = 'dev' in common.get_version()
|
||||
if 'dev' in request.args:
|
||||
dev_arg = request.args.get('dev')[-1]
|
||||
if dev_arg in ('true', 'yes' '1'):
|
||||
dev = True
|
||||
else:
|
||||
dev = False
|
||||
|
||||
if dev:
|
||||
scripts = self.dev_scripts[:]
|
||||
elif debug:
|
||||
scripts = self.debug_scripts[:]
|
||||
else:
|
||||
scripts = self.scripts[:]
|
||||
|
|
Loading…
Add table
Reference in a new issue