diff --git a/deluge/ui/web/build b/deluge/ui/web/build new file mode 100755 index 000000000..6d878bae4 --- /dev/null +++ b/deluge/ui/web/build @@ -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 diff --git a/deluge/ui/web/js/build.sh b/deluge/ui/web/js/build.sh deleted file mode 100755 index 718ef5380..000000000 --- a/deluge/ui/web/js/build.sh +++ /dev/null @@ -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; diff --git a/deluge/ui/web/js/deluge-all-debug.js b/deluge/ui/web/js/deluge-all-debug.js new file mode 100644 index 000000000..f78a62864 --- /dev/null +++ b/deluge/ui/web/js/deluge-all-debug.js @@ -0,0 +1,8026 @@ +/* +Script: Deluge.js + Contains the keys for get_torrent(s)_status. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +// Create the namespace Ext.deluge +Ext.namespace('Ext.deluge'); + +// Setup the state manager +Ext.state.Manager.setProvider(new Ext.state.CookieProvider()); + +(function() { + /* Add some helper functions to Ext */ + Ext.apply(Function.prototype, { + bind: function(scope) { + var self = this; + return function() { + return self.apply(scope, arguments); + } + } + }); + + Ext.apply(Ext, { + escapeHTML: function(text) { + text = String(text).replace('<', '<').replace('>', '>'); + return text.replace('&', '&'); + }, + + isObjectEmpty: function(obj) { + for(var i in obj) { return false; } + return true; + }, + + keys: function(obj) { + var keys = []; + for (var i in obj) if (obj.hasOwnProperty(i)) + { + keys.push(i); + } + return keys; + }, + + values: function(obj) { + var values = []; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + values.push(obj[i]); + } + } + return values; + }, + + splat: function(obj) { + var type = Ext.type(obj); + return (type) ? ((type != 'array') ? [obj] : obj) : []; + } + }); + Ext.getKeys = Ext.keys; + Ext.BLANK_IMAGE_URL = '/images/s.gif'; + Ext.USE_NATIVE_JSON = true; +})(); + +(function() { + var tpl = '
' + + '
' + + '
' + + '
' + + '
{0}
' + + '
' + + '
' + + '
' + + '
{0}
' + + '
' + + '
' + + '
'; + + Deluge.progressBar = function(progress, width, text, modifier) { + modifier = Ext.value(modifier, 10); + var progressWidth = ((width / 100.0) * progress).toFixed(0); + var barWidth = progressWidth - 1; + var textWidth = ((progressWidth - modifier) > 0 ? progressWidth - modifier : 0); + return String.format(tpl, text, width, barWidth, textWidth); + } + + Deluge.Plugins = {}; +})(); + +// Hinting for gettext_gen.py +// _('Do Not Download') +// _('Normal Priority') +// _('High Priority') +// _('Highest Priority') +FILE_PRIORITY = { + 0: 'Do Not Download', + 1: 'Normal Priority', + 2: 'High Priority', + 5: 'Highest Priority', + 'Do Not Download': 0, + 'Normal Priority': 1, + 'High Priority': 2, + 'Highest Priority': 5 +} + +FILE_PRIORITY_CSS = { + 0: 'x-no-download', + 1: 'x-normal-download', + 2: 'x-high-download', + 5: 'x-highest-download' +} +/* +Script: + Deluge.Formatters.js + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +/** + * @description A collection of functions for string formatting values. + * @namespace Deluge.Formatters + */ +Deluge.Formatters = { + /** + * Formats a date string in the locale's date representation based on the + * systems timezone. + * + * @param {number} timestamp time in seconds since the Epoch + * @returns {string} a string in the locale's date representation or "" + * if seconds < 0 + */ + date: function(timestamp) { + function zeroPad(num, count) { + var numZeropad = num + ''; + while (numZeropad.length < count) { + numZeropad = '0' + numZeropad; + } + return numZeropad; + } + timestamp = timestamp * 1000; + var date = new Date(timestamp); + return String.format('{0}/{1}/{2}', zeroPad(date.getDate(), 2), zeroPad(date.getMonth() + 1, 2), date.getFullYear()); + }, + + /** + * Formats the bytes value into a string with KiB, MiB or GiB units. + * + * @param {number} bytes the filesize in bytes + * @returns {string} formatted string with KiB, MiB or GiB units. + */ + size: function(bytes) { + bytes = bytes / 1024.0; + + if (bytes < 1024) { return bytes.toFixed(1) + ' KiB'; } + else { bytes = bytes / 1024; } + + if (bytes < 1024) { return bytes.toFixed(1) + ' MiB'; } + else { bytes = bytes / 1024; } + + return bytes.toFixed(1) + ' GiB' + }, + + /** + * Formats a string to display a transfer speed utilizing {@link Deluge.Formatters.size} + * + * @param {number} bytes the filesize in bytes + * @returns {string} formatted string with KiB, MiB or GiB units. + */ + speed: function(bits) { + return fsize(bits) + '/s' + }, + + /** + * Formats a string to show time in a human readable form. + * + * @param {number} time the number of seconds + * @returns {string} a formatted time string. will return '' if seconds == 0 + */ + timeRemaining: function(time) { + if (time == 0) { return '∞' } + time = time.toFixed(0); + if (time < 60) { return time + 's'; } + else { time = time / 60; } + + if (time < 60) { + var minutes = Math.floor(time) + var seconds = Math.round(60 * (time - minutes)) + if (seconds > 0) { + return minutes + 'm ' + seconds + 's'; + } else { + return minutes + 'm'; } + } + else { time = time / 60; } + + if (time < 24) { + var hours = Math.floor(time) + var minutes = Math.round(60 * (time - hours)) + if (minutes > 0) { + return hours + 'h ' + minutes + 'm'; + } else { + return hours + 'h'; + } + } + else { time = time / 24; } + + var days = Math.floor(time) + var hours = Math.round(24 * (time - days)) + if (hours > 0) { + return days + 'd ' + hours + 'h'; + } else { + return days + 'd'; + } + }, + + /** + * Simply returns the value untouched, for when no formatting is required. + * + * @param value, the value to be displayed + * @returns the untouched value. + */ + plain: function(value) { + return value; + } +} +var fsize = Deluge.Formatters.size; +var fspeed = Deluge.Formatters.speed; +var ftime = Deluge.Formatters.timeRemaining; +var fdate = Deluge.Formatters.date; +var fplain = Deluge.Formatters.plain; +/* +Script: Deluge.Keys.js + The torrent status keys that are commonly used around the UI. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +/** + * @description The torrent status keys that are commonly used around the UI. + * @namespace Deluge.Keys + */ +Deluge.Keys = { + /** + * @static + */ + Grid: [ + 'queue', 'name', 'total_size', 'state', 'progress', 'num_seeds', + 'total_seeds', 'num_peers', 'total_peers', 'download_payload_rate', + 'upload_payload_rate', 'eta', 'ratio', 'distributed_copies', + 'is_auto_managed', 'time_added', 'tracker_host' + ], + + /** + * @description Keys used in the status tab of the statistics panel. + * These get extended + * by {@link Deluge.Keys.Grid}. + * @static + */ + Status: [ + 'total_done', 'total_payload_download', 'total_uploaded', + 'total_payload_upload', 'next_announce', 'tracker_status', 'num_pieces', + 'piece_length', 'is_auto_managed', 'active_time', 'seeding_time', + 'seed_rank' + ], + + /** + * @static + * @description Keys used in the files tab of the statistics panel. + *
['files', 'file_progress', 'file_priorities']
+ */ + Files: [ + 'files', 'file_progress', 'file_priorities' + ], + + /** + * @description Keys used in the peers tab of the statistics panel. + *
['peers']
+ * @static + */ + Peers: [ + 'peers' + ], + + /** + * @description Keys used in the details tab of the statistics panel. + * @static + */ + Details: [ + 'name', 'save_path', 'total_size', 'num_files', 'tracker_status', + 'tracker', 'comment' + ], + + /** + * @static + * @description Keys used in the options tab of the statistics panel. + *
['max_download_speed', 'max_upload_speed', 'max_connections', 'max_upload_slots',
+	 *  'is_auto_managed', 'stop_at_ratio', 'stop_ratio', 'remove_at_ratio', 'private',
+	 *  'prioritize_first_last']
+ */ + Options: [ + 'max_download_speed', 'max_upload_speed', 'max_connections', + 'max_upload_slots','is_auto_managed', 'stop_at_ratio', 'stop_ratio', + 'remove_at_ratio', 'private', 'prioritize_first_last' + ] +}; + +// Merge the grid and status keys together as the status keys contain all the +// grid ones. +Ext.each(Deluge.Keys.Grid, function(key) { + Deluge.Keys.Status.push(key); +}); +/* +Script: deluge-menus.js + Contains all the menus contained within the UI for easy access and editing. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +Deluge.Menus = { + onTorrentAction: function(item, e) { + var selection = Deluge.Torrents.getSelections(); + var ids = []; + Ext.each(selection, function(record) { + ids.push(record.id); + }); + + switch (item.id) { + case 'pause': + case 'resume': + Deluge.Client.core[item.id + '_torrent'](ids, { + success: function() { + Deluge.UI.update(); + } + }); + break; + case 'top': + case 'up': + case 'down': + case 'bottom': + Deluge.Client.core['queue_' + item.id](ids, { + success: function() { + Deluge.UI.update(); + } + }); + break; + case 'edit_trackers': + Deluge.EditTrackers.show(); + break; + case 'update': + Deluge.Client.core.force_reannounce(ids, { + success: function() { + Deluge.UI.update(); + } + }); + break; + case 'remove': + Deluge.RemoveWindow.show(ids); + break; + case 'recheck': + Deluge.Client.core.force_recheck(ids, { + success: function() { + Deluge.UI.update(); + } + }); + break; + case 'move': + Deluge.MoveStorage.show(ids); + break; + } + } +} + +Deluge.Menus.Torrent = new Ext.menu.Menu({ + id: 'torrentMenu', + items: [{ + id: 'pause', + text: _('Pause'), + icon: '/icons/pause.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }, { + id: 'resume', + text: _('Resume'), + icon: '/icons/start.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }, '-', { + id: 'options', + text: _('Options'), + icon: '/icons/preferences.png', + menu: new Ext.menu.Menu({ + items: [{ + text: _('D/L Speed Limit'), + iconCls: 'x-deluge-downloading', + menu: new Ext.menu.Menu({ + items: [{ + text: _('5 KiB/s') + }, { + text: _('10 KiB/s') + }, { + text: _('30 KiB/s') + }, { + text: _('80 KiB/s') + }, { + text: _('300 KiB/s') + },{ + text: _('Unlimited') + }] + }) + }, { + text: _('U/L Speed Limit'), + iconCls: 'x-deluge-seeding', + menu: new Ext.menu.Menu({ + items: [{ + text: _('5 KiB/s') + }, { + text: _('10 KiB/s') + }, { + text: _('30 KiB/s') + }, { + text: _('80 KiB/s') + }, { + text: _('300 KiB/s') + },{ + text: _('Unlimited') + }] + }) + }, { + text: _('Connection Limit'), + iconCls: 'x-deluge-connections', + menu: new Ext.menu.Menu({ + items: [{ + text: _('50') + }, { + text: _('100') + }, { + text: _('200') + }, { + text: _('300') + }, { + text: _('500') + },{ + text: _('Unlimited') + }] + }) + }, { + text: _('Upload Slot Limit'), + icon: '/icons/upload_slots.png', + menu: new Ext.menu.Menu({ + items: [{ + text: _('0') + }, { + text: _('1') + }, { + text: _('2') + }, { + text: _('3') + }, { + text: _('5') + },{ + text: _('Unlimited') + }] + }) + }, { + id: 'auto_managed', + text: _('Auto Managed'), + checked: false + }] + }) + }, '-', { + text: _('Queue'), + icon: '/icons/queue.png', + menu: new Ext.menu.Menu({ + items: [{ + id: 'top', + text: _('Top'), + icon: '/icons/top.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + },{ + id: 'up', + text: _('Up'), + icon: '/icons/up.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + },{ + id: 'down', + text: _('Down'), + icon: '/icons/down.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + },{ + id: 'bottom', + text: _('Bottom'), + icon: '/icons/bottom.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }] + }) + }, '-', { + id: 'update', + text: _('Update Tracker'), + icon: '/icons/update.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }, { + id: 'edit_trackers', + text: _('Edit Trackers'), + icon: '/icons/edit_trackers.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }, '-', { + id: 'remove', + text: _('Remove Torrent'), + icon: '/icons/remove.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }, '-', { + id: 'recheck', + text: _('Force Recheck'), + icon: '/icons/recheck.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }, { + id: 'move', + text: _('Move Storage'), + icon: '/icons/move.png', + handler: Deluge.Menus.onTorrentAction, + scope: Deluge.Menus + }] +}); + +Ext.deluge.StatusbarMenu = Ext.extend(Ext.menu.Menu, { + + setValue: function(value) { + value = (value == 0) ? -1 : value; + var item = this.items.get(value); + if (!item) item = this.items.get('other') + item.suspendEvents(); + item.setChecked(true); + item.resumeEvents(); + } +}); + +Deluge.Menus.Connections = new Ext.deluge.StatusbarMenu({ + id: 'connectionsMenu', + items: [{ + id: '50', + text: '50', + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + },{ + id: '100', + text: '100', + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + },{ + id: '200', + text: '200', + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + },{ + id: '300', + text: '300', + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + },{ + id: '500', + text: '500', + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + },{ + id: '-1', + text: _('Unlimited'), + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + },'-',{ + id: 'other', + text: _('Other'), + group: 'max_connections_global', + checked: false, + checkHandler: onLimitChanged + }] +}); + +Deluge.Menus.Download = new Ext.deluge.StatusbarMenu({ + id: 'downspeedMenu', + items: [{ + id: '5', + text: '5 KiB/s', + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '10', + text: '10 KiB/s', + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '30', + text: '30 KiB/s', + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '80', + text: '80 KiB/s', + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '300', + text: '300 KiB/s', + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '-1', + text: _('Unlimited'), + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + },'-',{ + id: 'other', + text: _('Other'), + group: 'max_download_speed', + checked: false, + checkHandler: onLimitChanged + }] +}); + +Deluge.Menus.Upload = new Ext.deluge.StatusbarMenu({ + id: 'upspeedMenu', + items: [{ + id: '5', + text: '5 KiB/s', + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '10', + text: '10 KiB/s', + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '30', + text: '30 KiB/s', + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '80', + text: '80 KiB/s', + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '300', + text: '300 KiB/s', + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + },{ + id: '-1', + text: _('Unlimited'), + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + },'-',{ + id: 'other', + text: _('Other'), + group: 'max_upload_speed', + checked: false, + checkHandler: onLimitChanged + }] +}); + +Deluge.Menus.FilePriorities = new Ext.menu.Menu({ + id: 'filePrioritiesMenu', + items: [{ + id: 'expandAll', + text: _('Expand All'), + icon: '/icons/expand_all.png' + }, '-', { + id: 'no_download', + text: _('Do Not Download'), + icon: '/icons/no_download.png', + filePriority: 0 + }, { + id: 'normal', + text: _('Normal Priority'), + icon: '/icons/normal.png', + filePriority: 1 + }, { + id: 'high', + text: _('High Priority'), + icon: '/icons/high.png', + filePriority: 2 + }, { + id: 'highest', + text: _('Highest Priority'), + icon: '/icons/highest.png', + filePriority: 5 + }] +}); + +function onLimitChanged(item, checked) { + if (item.id == "other") { + } else { + config = {} + config[item.group] = item.id + Deluge.Client.core.set_config(config, { + success: function() { + Deluge.UI.update(); + } + }); + } +} +/* +Script: Deluge.Events.js + Class for holding global events that occur within the UI. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +/** + * @namespace Deluge.Events + * @class Deluge.Events + * @name Deluge.Events + * @description Class for holding global events that occur within the UI. + */ + +(function() { + Events = Ext.extend(Ext.util.Observable, { + constructor: function() { + this.toRegister = []; + this.on('login', this.onLogin, this); + Events.superclass.constructor.call(this); + }, + + addListener: function(eventName, fn, scope, o) { + this.addEvents(eventName); + if (/[A-Z]/.test(eventName.substring(0, 1))) { + if (!Deluge.Client) { + this.toRegister.push(eventName); + } else { + Deluge.Client.web.register_event_listener(eventName); + } + } + Events.superclass.addListener.call(this, eventName, fn, scope, o); + }, + + poll: function() { + Deluge.Client.web.get_events({ + success: this.onPollSuccess, + scope: this + }); + }, + + start: function() { + Ext.each(this.toRegister, function(eventName) { + Deluge.Client.web.register_event_listener(eventName); + }); + this.poll = this.poll.bind(this); + this.running = setInterval(this.poll, 2000); + this.poll(); + }, + + stop: function() { + if (this.running) { + clearInterval(this.running); + } + }, + + onLogin: function() { + this.start(); + this.on('PluginEnabledEvent', this.onPluginEnabled, this); + this.on('PluginDisabledEvent', this.onPluginDisabled, this); + }, + + onPollSuccess: function(events) { + if (!events) return; + Ext.each(events, function(event) { + var name = event[0], args = event[1]; + args.splice(0, 0, name); + this.fireEvent.apply(this, args); + }, this); + } + }); + Events.prototype.on = Events.prototype.addListener + Events.prototype.fire = Events.prototype.fireEvent + Deluge.Events = new Events(); +})(); +/* +Script: + Deluge.OptionsManager.js + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Deluge'); + +/** + * @class Deluge.OptionsManager + * @extends Ext.util.Observable + * A class that can be used to manage options throughout the ui. + * @constructor + * Creates a new OptionsManager + * @param {Object} config Configuration options + */ +Deluge.OptionsManager = Ext.extend(Ext.util.Observable, { + + /** + * Create a new instance of the OptionsManager. + */ + constructor: function(config) { + config = config || {}; + this.binds = {}; + this.changed = {}; + this.options = (config && config['options']) || {}; + this.focused = null; + + this.addEvents({ + 'add': true, + 'changed': true, + 'reset': true + }); + this.on('changed', this.onChange, this); + + Deluge.OptionsManager.superclass.constructor.call(this); + }, + + /** + * Add a set of default options and values to the options manager + * @param {Object} options The default options. + */ + addOptions: function(options) { + this.options = Ext.applyIf(this.options, options); + }, + + /** + * Binds a form field to the specified option. + * @param {String} option + * @param {Ext.form.Field} field + */ + bind: function(option, field) { + this.binds[option] = this.binds[option] || []; + this.binds[option].push(field); + field._doption = option; + + field.on('focus', this.onFieldFocus, this); + field.on('blur', this.onFieldBlur, this); + field.on('change', this.onFieldChange, this); + field.on('check', this.onFieldChange, this); + return field; + }, + + /** + * Changes all the changed values to be the default values + */ + commit: function() { + this.options = Ext.apply(this.options, this.changed); + this.reset(); + }, + + /** + * Converts the value so it matches the originals type + * @param {Mixed} oldValue The original value + * @param {Mixed} value The new value to convert + */ + convertValueType: function(oldValue, value) { + if (Ext.type(oldValue) != Ext.type(value)) { + switch (Ext.type(oldValue)) { + case 'string': + value = String(value); + break; + case 'number': + value = Number(value); + break; + case 'boolean': + if (Ext.type(value) == 'string') { + value = value.toLowerCase(); + value = (value == 'true' || value == '1' || value == 'on') ? true : false; + } else { + value = Boolean(value); + } + break; + } + } + return value; + }, + + /** + * Get the value for an option or options. + * @param {String} [option] A single option or an array of options to return. + * @returns {Object} the options value. + */ + get: function() { + if (arguments.length == 1) { + var option = arguments[0]; + return (this.isDirty(option)) ? this.changed[option] : this.options[option]; + } else { + var options = {}; + Ext.each(arguments, function(option) { + if (!this.has(option)) return; + options[option] = (this.isDirty(option)) ? this.changed[option] : this.options[option]; + }, this); + return options; + } + }, + + /** + * Get the default value for an option or options. + * @param {String|Array} [option] A single option or an array of options to return. + * @returns {Object} the value of the option + */ + getDefault: function(option) { + return this.options[option]; + }, + + /** + * Returns the dirty (changed) values. + * @returns {Object} the changed options + */ + getDirty: function() { + return this.changed; + }, + + /** + * @param {String} [option] The option to check + * @returns {Boolean} true if the option has been changed from the default. + */ + isDirty: function(option) { + return !Ext.isEmpty(this.changed[option]); + }, + + /** + * Check to see if an option exists in the options manager + * @param {String} option + * @returns {Boolean} true if the option exists, else false. + */ + has: function(option) { + return (this.options[option]); + }, + + /** + * Reset the options back to the default values. + */ + reset: function() { + this.changed = {}; + }, + + /** + * Sets the value of specified option(s) for the passed in id. + * @param {String} option + * @param {Object} value The value for the option + */ + set: function(option, value) { + if (option === undefined) { + return; + } else if (typeof option == 'object') { + var options = option; + this.options = Ext.apply(this.options, options); + for (var option in options) { + this.onChange(option, options[option]); + } + } else { + this.options[option] = value; + this.onChange(option, value) + } + }, + + /** + * Update the value for the specified option and id. + * @param {String|Object} option or options to update + * @param {Object} [value]; + */ + update: function(option, value) { + if (option === undefined) { + return; + } else if (value === undefined) { + for (var key in option) { + this.update(key, option[key]); + } + } else { + var defaultValue = this.getDefault(option); + value = this.convertValueType(defaultValue, value); + + var oldValue = this.get(option); + if (oldValue == value) return; + + if (defaultValue == value) { + if (this.isDirty(option)) delete this.changed[option]; + this.fireEvent('changed', option, value, oldValue); + return; + } + + this.changed[option] = value; + this.fireEvent('changed', option, value, oldValue); + } + }, + + /****************** + * Event Handlers * + ******************/ + /** + * Lets the option manager know when a field is blurred so if a value + * so value changing operations can continue on that field. + */ + onFieldBlur: function(field, event) { + if (this.focused == field) { + this.focused = null; + } + }, + + /** + * Stops a form fields value from being blocked by the change functions + * @param {Ext.form.Field} field + * @private + */ + onFieldChange: function(field, event) { + this.update(field._doption, field.getValue()); + }, + + /** + * Lets the option manager know when a field is focused so if a value + * changing operation is performed it won't change the value of the + * field. + */ + onFieldFocus: function(field, event) { + this.focused = field; + }, + + onChange: function(option, newValue, oldValue) { + // If we don't have a bind there's nothing to do. + if (Ext.isEmpty(this.binds[option])) return; + Ext.each(this.binds[option], function(bind) { + // The field is currently focused so we don't want to + // change it. + if (bind == this.focused) return; + // Set the form field to the new value. + bind.setValue(newValue); + }, this); + } +}); +/* +Script: + Deluge.MultiOptionsManager.js + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +/** + * @description A class that can be used to manage options throughout the ui. + * @namespace Deluge + * @class Deluge.MultiOptionsManager + */ +Deluge.MultiOptionsManager = Ext.extend(Deluge.OptionsManager, { + + constructor: function(config) { + this.currentId = null; + this.stored = {}; + Deluge.MultiOptionsManager.superclass.constructor.call(this, config); + }, + + /** + * Changes bound fields to use the specified id. + * @param {String} id + */ + changeId: function(id, dontUpdateBinds) { + var oldId = this.currentId; + this.currentId = id; + if (!dontUpdateBinds) { + for (var option in this.options) { + if (!this.binds[option]) continue; + Ext.each(this.binds[option], function(bind) { + bind.setValue(this.get(option)); + }, this); + } + } + return oldId; + }, + + /** + * Changes all the changed values to be the default values + * @param {String} id + */ + commit: function() { + this.stored[this.currentId] = Ext.apply(this.stored[this.currentId], this.changed[this.currentId]); + this.reset(); + }, + + /** + * Get the value for an option + * @param {String|Array} [option] A single option or an array of options to return. + * @returns {Object} the options value. + */ + get: function() { + if (arguments.length == 1) { + var option = arguments[0]; + return (this.isDirty(option)) ? this.changed[this.currentId][option] : this.getDefault(option); + } else if (arguments.length == 0) { + var options = {}; + for (var option in this.options) { + options[option] = (this.isDirty(option)) ? this.changed[this.currentId][option] : this.getDefault(option); + } + return options; + } else { + var options = {}; + Ext.each(arguments, function(option) { + options[option] = (this.isDirty(option)) ? this.changed[this.currentId][option] : this.getDefault(option); + }, this); + return options; + } + }, + + /** + * Get the default value for an option. + * @param {String|Array} [option] A single option or an array of options to return. + * @returns {Object} the value of the option + */ + getDefault: function(option) { + return (this.has(option)) ? this.stored[this.currentId][option] : this.options[option]; + }, + + /** + * Returns the dirty (changed) values. + * @returns {Object} the changed options + */ + getDirty: function() { + return (this.changed[this.currentId]) ? this.changed[this.currentId] : {}; + }, + + /** + * Check to see if the option has been changed. + * @param {String} option + * @returns {Boolean} true if the option has been changed, else false. + */ + isDirty: function(option) { + return (this.changed[this.currentId] && !Ext.isEmpty(this.changed[this.currentId][option])); + }, + + /** + * Check to see if an id has had an option set to something other than the + * default value. + * @param {String} option + * @returns {Boolean} true if the id has an option, else false. + */ + has: function(option) { + return (this.stored[this.currentId] && !Ext.isEmpty(this.stored[this.currentId][option])); + }, + + /** + * Reset the options back to the default values for the specified id. + */ + reset: function() { + if (this.changed[this.currentId]) delete this.changed[this.currentId]; + if (this.stored[this.currentId]) delete this.stored[this.currentId]; + }, + + /** + * Reset the options back to their defaults for all ids. + */ + resetAll: function() { + this.changed = {}; + this.stored = {}; + this.changeId(null); + }, + + /** + * Sets the value of specified option for the passed in id. + * @param {String} id + * @param {String} option + * @param {Object} value The value for the option + */ + setDefault: function(option, value) { + if (option === undefined) { + return; + } else if (value === undefined) { + for (var key in option) { + this.setDefault(key, option[key]); + } + } else { + var oldValue = this.getDefault(option); + value = this.convertValueType(oldValue, value); + + // If the value is the same as the old value there is + // no point in setting it again. + if (oldValue == value) return; + + // Store the new default + if (!this.stored[this.currentId]) this.stored[this.currentId] = {}; + this.stored[this.currentId][option] = value; + + if (!this.isDirty(option)) { + this.fireEvent('changed', this.currentId, option, value, oldValue); + } + } + }, + + /** + * Update the value for the specified option and id. + * @param {String} id + * @param {String|Object} option or options to update + * @param {Object} [value]; + */ + update: function(option, value) { + if (option === undefined) { + return; + } else if (value === undefined) { + for (var key in option) { + this.update(key, option[key]); + } + } else { + if (!this.changed[this.currentId]) this.changed[this.currentId] = {}; + + var defaultValue = this.getDefault(option); + value = this.convertValueType(defaultValue, value); + + var oldValue = this.get(option); + if (oldValue == value) return; + + if (defaultValue == value) { + if (this.isDirty(option)) delete this.changed[this.currentId][option]; + this.fireEvent('changed', this.currentId, option, value, oldValue); + return; + } else { + this.changed[this.currentId][option] = value; + this.fireEvent('changed', this.currentId, option, value, oldValue); + } + } + }, + + /****************** + * Event Handlers * + ******************/ + /** + * Stops a form fields value from being blocked by the change functions + * @param {Ext.form.Field} field + * @private + */ + onFieldChange: function(field, event) { + this.update(field._doption, field.getValue()); + }, + + onChange: function(id, option, newValue, oldValue) { + // If we don't have a bind there's nothing to do. + if (Ext.isEmpty(this.binds[option])) return; + Ext.each(this.binds[option], function(bind) { + // The field is currently focused so we don't want to + // change it. + if (bind == this.focused) return; + // Set the form field to the new value. + bind.setValue(newValue); + }, this); + } +}); +/* +Script: Deluge.Add.js + Contains the Add Torrent window. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.add'); +Ext.deluge.add.OptionsPanel = Ext.extend(Ext.TabPanel, { + + torrents: {}, + + constructor: function(config) { + config = Ext.apply({ + region: 'south', + margins: '5 5 5 5', + activeTab: 0, + height: 220 + }, config); + Ext.deluge.add.OptionsPanel.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.add.OptionsPanel.superclass.initComponent.call(this); + this.files = this.add(new Ext.tree.ColumnTree({ + layout: 'fit', + title: _('Files'), + rootVisible: false, + autoScroll: true, + height: 170, + border: false, + animate: false, + disabled: true, + + columns: [{ + header: _('Filename'), + width: 275, + dataIndex: 'filename' + },{ + header: _('Size'), + width: 80, + dataIndex: 'size' + }], + + root: new Ext.tree.AsyncTreeNode({ + text: 'Files' + }) + })); + new Ext.tree.TreeSorter(this.files, { + folderSort: true + }); + + this.optionsManager = new Deluge.MultiOptionsManager(); + + this.form = this.add({ + xtype: 'form', + labelWidth: 1, + title: _('Options'), + bodyStyle: 'padding: 5px;', + border: false, + height: 170, + disabled: true + }); + + var fieldset = this.form.add({ + xtype: 'fieldset', + title: _('Download Location'), + border: false, + autoHeight: true, + defaultType: 'textfield', + labelWidth: 1, + fieldLabel: '' + }); + this.optionsManager.bind('download_location', fieldset.add({ + fieldLabel: '', + name: 'download_location', + width: 400, + labelSeparator: '' + })); + + var panel = this.form.add({ + border: false, + layout: 'column', + defaultType: 'fieldset' + }); + fieldset = panel.add({ + title: _('Allocation'), + border: false, + autoHeight: true, + defaultType: 'radio', + width: 100 + }); + + this.optionsManager.bind('compact_allocation', fieldset.add({ + xtype: 'radiogroup', + columns: 1, + vertical: true, + labelSeparator: '', + items: [{ + name: 'compact_allocation', + value: false, + inputValue: false, + boxLabel: _('Full'), + fieldLabel: '', + labelSeparator: '' + }, { + name: 'compact_allocation', + value: true, + inputValue: true, + boxLabel: _('Compact'), + fieldLabel: '', + labelSeparator: '', + }] + })); + + fieldset = panel.add({ + title: _('Bandwidth'), + border: false, + autoHeight: true, + labelWidth: 100, + width: 200, + defaultType: 'uxspinner' + }); + this.optionsManager.bind('max_download_speed', fieldset.add({ + fieldLabel: _('Max Down Speed'), + /*labelStyle: 'margin-left: 10px',*/ + name: 'max_download_speed', + width: 60 + })); + this.optionsManager.bind('max_upload_speed', fieldset.add({ + fieldLabel: _('Max Up Speed'), + /*labelStyle: 'margin-left: 10px',*/ + name: 'max_upload_speed', + width: 60 + })); + this.optionsManager.bind('max_connections', fieldset.add({ + fieldLabel: _('Max Connections'), + /*labelStyle: 'margin-left: 10px',*/ + name: 'max_connections', + width: 60 + })); + this.optionsManager.bind('max_upload_slots', fieldset.add({ + fieldLabel: _('Max Upload Slots'), + /*labelStyle: 'margin-left: 10px',*/ + name: 'max_upload_slots', + width: 60 + })); + + fieldset = panel.add({ + title: _('General'), + border: false, + autoHeight: true, + defaultType: 'checkbox' + }); + this.optionsManager.bind('add_paused', fieldset.add({ + name: 'add_paused', + boxLabel: _('Add In Paused State'), + fieldLabel: '', + labelSeparator: '', + })); + this.optionsManager.bind('prioritize_first_last_pieces', fieldset.add({ + name: 'prioritize_first_last_pieces', + boxLabel: _('Prioritize First/Last Pieces'), + fieldLabel: '', + labelSeparator: '', + })); + + this.form.on('render', this.onFormRender, this); + }, + + onFormRender: function(form) { + form.layout = new Ext.layout.FormLayout(); + form.layout.setContainer(form); + form.doLayout(); + }, + + addTorrent: function(torrent) { + this.torrents[torrent['info_hash']] = torrent; + var fileIndexes = {}; + this.walkFileTree(torrent['files_tree'], function(filename, type, entry, parent) { + if (type != 'file') return; + fileIndexes[entry[0]] = entry[2]; + }, this); + + var priorities = []; + Ext.each(Ext.keys(fileIndexes), function(index) { + priorities[index] = fileIndexes[index]; + }); + + var oldId = this.optionsManager.changeId(torrent['info_hash'], true); + this.optionsManager.setDefault('file_priorities', priorities); + this.optionsManager.changeId(oldId, true); + }, + + clear: function() { + this.clearFiles(); + this.optionsManager.resetAll(); + }, + + clearFiles: function() { + var root = this.files.getRootNode(); + if (!root.hasChildNodes()) return; + root.cascade(function(node) { + if (!node.parentNode || !node.getOwnerTree()) return; + node.remove(); + }); + }, + + getDefaults: function() { + var keys = ['add_paused','compact_allocation','download_location', + 'max_connections_per_torrent','max_download_speed_per_torrent', + 'max_upload_slots_per_torrent','max_upload_speed_per_torrent', + 'prioritize_first_last_pieces']; + + Deluge.Client.core.get_config_values(keys, { + success: function(config) { + var options = { + 'file_priorities': [], + 'add_paused': config.add_paused, + 'compact_allocation': config.compact_allocation, + 'download_location': config.download_location, + 'max_connections': config.max_connections_per_torrent, + 'max_download_speed': config.max_download_speed_per_torrent, + 'max_upload_slots': config.max_upload_slots_per_torrent, + 'max_upload_speed': config.max_upload_speed_per_torrent, + 'prioritize_first_last_pieces': config.prioritize_first_last_pieces + } + this.optionsManager.options = options; + this.optionsManager.resetAll(); + }, + scope: this + }); + }, + + getFilename: function(torrentId) { + return this.torrents[torrentId]['filename']; + }, + + getOptions: function(torrentId) { + var oldId = this.optionsManager.changeId(torrentId, true); + var options = this.optionsManager.get(); + this.optionsManager.changeId(oldId, true); + Ext.each(options['file_priorities'], function(priority, index) { + options['file_priorities'][index] = (priority) ? 1 : 0; + }); + return options; + }, + + setTorrent: function(torrentId) { + if (!torrentId) return; + + this.torrentId = torrentId; + this.optionsManager.changeId(torrentId); + + this.clearFiles(); + var root = this.files.getRootNode(); + var priorities = this.optionsManager.get('file_priorities'); + + this.walkFileTree(this.torrents[torrentId]['files_tree'], function(filename, type, entry, parent) { + if (type == 'dir') { + var folder = new Ext.tree.TreeNode({ + text: filename, + checked: true + }); + folder.on('checkchange', this.onFolderCheck, this); + parent.appendChild(folder); + return folder; + } else { + var node = new Ext.tree.TreeNode({ + filename: filename, + fileindex: entry[0], + text: filename, // this needs to be here for sorting reasons + size: fsize(entry[1]), + leaf: true, + checked: priorities[entry[0]], + iconCls: 'x-deluge-file', + uiProvider: Ext.tree.ColumnNodeUI + }); + node.on('checkchange', this.onNodeCheck, this); + parent.appendChild(node); + } + }, this, root); + root.firstChild.expand(); + }, + + walkFileTree: function(files, callback, scope, parent) { + for (var filename in files) { + var entry = files[filename]; + var type = (Ext.type(entry) == 'object') ? 'dir' : 'file'; + + if (scope) { + var ret = callback.apply(scope, [filename, type, entry, parent]); + } else { + var ret = callback(filename, type, entry, parent); + } + + if (type == 'dir') this.walkFileTree(entry, callback, scope, ret); + } + }, + + onFolderCheck: function(node, checked) { + var priorities = this.optionsManager.get('file_priorities'); + node.cascade(function(child) { + if (!child.ui.checkbox) { + child.attributes.checked = checked; + } else { + child.ui.checkbox.checked = checked; + } + priorities[child.attributes.fileindex] = checked; + }, this); + this.optionsManager.setDefault('file_priorities', priorities); + }, + + onNodeCheck: function(node, checked) { + var priorities = this.optionsManager.get('file_priorities'); + priorities[node.attributes.fileindex] = checked; + this.optionsManager.update('file_priorities', priorities); + } +}); + +Ext.deluge.add.Window = Ext.extend(Ext.Window, { + initComponent: function() { + Ext.deluge.add.Window.superclass.initComponent.call(this); + this.addEvents( + 'beforeadd', + 'add' + ); + }, + + createTorrentId: function() { + return new Date().getTime(); + } +}); + +Ext.deluge.add.AddWindow = Ext.extend(Ext.deluge.add.Window, { + + constructor: function(config) { + config = Ext.apply({ + title: _('Add Torrents'), + layout: 'border', + width: 470, + height: 450, + bodyStyle: 'padding: 10px 5px;', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + plain: true, + iconCls: 'x-deluge-add-window-icon' + }, config); + Ext.deluge.add.AddWindow.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.add.AddWindow.superclass.initComponent.call(this); + + this.addButton(_('Cancel'), this.onCancelClick, this); + this.addButton(_('Add'), this.onAddClick, this); + + function torrentRenderer(value, p, r) { + if (r.data['info_hash']) { + return String.format('
{0}
', value); + } else { + return String.format('
{0}
', value); + } + } + + this.grid = this.add({ + xtype: 'grid', + region: 'center', + store: new Ext.data.SimpleStore({ + fields: [ + {name: 'info_hash', mapping: 1}, + {name: 'text', mapping: 2} + ], + id: 0 + }), + columns: [{ + id: 'torrent', + width: 150, + sortable: true, + renderer: torrentRenderer, + dataIndex: 'text' + }], + stripeRows: true, + selModel: new Ext.grid.RowSelectionModel({ + singleSelect: true, + listeners: { + 'rowselect': { + fn: this.onSelect, + scope: this + } + } + }), + hideHeaders: true, + autoExpandColumn: 'torrent', + deferredRender: false, + autoScroll: true, + margins: '5 5 5 5', + bbar: new Ext.Toolbar({ + items: [{ + id: 'file', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-add-file', + text: _('File'), + handler: this.onFile, + scope: this + }, { + id: 'url', + cls: 'x-btn-text-icon', + text: _('Url'), + icon: '/icons/add_url.png', + handler: this.onUrl, + scope: this + }, { + id: 'infohash', + cls: 'x-btn-text-icon', + text: _('Infohash'), + icon: '/icons/add_magnet.png', + disabled: true + }, '->', { + id: 'remove', + cls: 'x-btn-text-icon', + text: _('Remove'), + icon: '/icons/remove.png', + handler: this.onRemove, + scope: this + }] + }) + }); + + this.optionsPanel = this.add(new Ext.deluge.add.OptionsPanel()); + this.on('hide', this.onHide, this); + this.on('show', this.onShow, this); + }, + + clear: function() { + this.grid.getStore().removeAll(); + this.optionsPanel.clear(); + }, + + onAddClick: function() { + var torrents = []; + if (!this.grid) return; + this.grid.getStore().each(function(r) { + var id = r.get('info_hash'); + torrents.push({ + path: this.optionsPanel.getFilename(id), + options: this.optionsPanel.getOptions(id) + }); + }, this); + + Deluge.Client.web.add_torrents(torrents, { + success: function(result) { + } + }) + this.clear(); + this.hide(); + }, + + onCancelClick: function() { + this.clear(); + this.hide(); + }, + + onFile: function() { + this.file.show(); + }, + + onHide: function() { + this.optionsPanel.setActiveTab(0); + this.optionsPanel.files.setDisabled(true); + this.optionsPanel.form.setDisabled(true); + }, + + onRemove: function() { + var selection = this.grid.getSelectionModel(); + if (!selection.hasSelection()) return; + var torrent = selection.getSelected(); + this.grid.getStore().remove(torrent); + this.optionsPanel.clear(); + + if (this.torrents && this.torrents[torrent.id]) delete this.torrents[torrent.id]; + }, + + onSelect: function(selModel, rowIndex, record) { + this.optionsPanel.setTorrent(record.get('info_hash')); + this.optionsPanel.files.setDisabled(false); + this.optionsPanel.form.setDisabled(false); + }, + + onShow: function() { + if (!this.url) { + this.url = new Ext.deluge.add.UrlWindow(); + this.url.on('beforeadd', this.onTorrentBeforeAdd, this); + this.url.on('add', this.onTorrentAdd, this); + } + + if (!this.file) { + this.file = new Ext.deluge.add.FileWindow(); + this.file.on('beforeadd', this.onTorrentBeforeAdd, this); + this.file.on('add', this.onTorrentAdd, this); + } + + this.optionsPanel.getDefaults(); + }, + + onTorrentBeforeAdd: function(torrentId, text) { + var store = this.grid.getStore(); + store.loadData([[torrentId, null, text]], true); + }, + + onTorrentAdd: function(torrentId, info) { + if (!info) { + Ext.MessageBox.show({ + title: _('Error'), + msg: _('Not a valid torrent'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error' + }); + return; + } + + var r = this.grid.getStore().getById(torrentId); + r.set('info_hash', info['info_hash']); + r.set('text', info['name']); + this.grid.getStore().commitChanges(); + this.optionsPanel.addTorrent(info); + }, + + onUrl: function(button, event) { + this.url.show(); + } +}); +Deluge.Add = new Ext.deluge.add.AddWindow(); +/* +Script: Deluge.Add.File.js + Contains the Add Torrent by file window. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.add'); +Ext.deluge.add.FileWindow = Ext.extend(Ext.deluge.add.Window, { + constructor: function(config) { + config = Ext.apply({ + layout: 'fit', + width: 350, + height: 115, + bodyStyle: 'padding: 10px 5px;', + buttonAlign: 'center', + closeAction: 'hide', + modal: true, + plain: true, + title: _('Add from File'), + iconCls: 'x-deluge-add-file' + }, config); + Ext.deluge.add.FileWindow.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.add.FileWindow.superclass.initComponent.call(this); + this.addButton(_('Add'), this.onAdd, this); + + this.form = this.add({ + xtype: 'form', + baseCls: 'x-plain', + labelWidth: 55, + autoHeight: true, + fileUpload: true, + items: [{ + xtype: 'fileuploadfield', + id: 'torrentFile', + emptyText: _('Select a torrent'), + fieldLabel: _('File'), + name: 'file', + buttonCfg: { + text: _('Browse') + '...' + } + }] + }); + }, + + onAdd: function(field, e) { + if (this.form.getForm().isValid()) { + this.torrentId = this.createTorrentId(); + this.form.getForm().submit({ + url: '/upload', + waitMsg: _('Uploading your torrent...'), + success: this.onUploadSuccess, + scope: this + }); + var name = this.form.getForm().findField('torrentFile').value; + this.fireEvent('beforeadd', this.torrentId, name); + } + }, + + onGotInfo: function(info, obj, response, request) { + info['filename'] = request.options.filename; + this.fireEvent('add', this.torrentId, info); + }, + + onUploadSuccess: function(fp, upload) { + this.hide(); + if (upload.result.success) { + var filename = upload.result.files[0]; + this.form.getForm().findField('torrentFile').setValue(''); + Deluge.Client.web.get_torrent_info(filename, { + success: this.onGotInfo, + scope: this, + filename: filename + }); + } + } +}); +/* +Script: Deluge.Add.Url.js + Contains the Add Torrent by url window. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.add'); +Ext.deluge.add.UrlWindow = Ext.extend(Ext.deluge.add.Window, { + constructor: function(config) { + config = Ext.apply({ + layout: 'fit', + width: 350, + height: 155, + bodyStyle: 'padding: 10px 5px;', + buttonAlign: 'center', + closeAction: 'hide', + modal: true, + plain: true, + title: _('Add from Url'), + iconCls: 'x-deluge-add-url-window-icon' + }, config); + Ext.deluge.add.UrlWindow.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.add.UrlWindow.superclass.initComponent.call(this); + this.addButton(_('Add'), this.onAdd, this); + + var form = this.add({ + xtype: 'form', + defaultType: 'textfield', + baseCls: 'x-plain', + labelWidth: 55 + }); + + this.urlField = form.add({ + fieldLabel: _('Url'), + id: 'url', + name: 'url', + anchor: '100%' + }); + this.urlField.on('specialkey', this.onAdd, this); + + this.cookieField = form.add({ + fieldLabel: _('Cookies'), + id: 'cookies', + name: 'cookies', + anchor: '100%' + }); + this.cookieField.on('specialkey', this.onAdd, this); + }, + + onAdd: function(field, e) { + if ((field.id == 'url' || field.id == 'cookies') && e.getKey() != e.ENTER) return; + + var field = this.urlField; + var url = field.getValue(); + var cookies = this.cookieField.getValue(); + var torrentId = this.createTorrentId(); + + Deluge.Client.web.download_torrent_from_url(url, cookies, { + success: this.onDownload, + scope: this, + torrentId: torrentId + }); + this.hide(); + this.fireEvent('beforeadd', torrentId, url); + }, + + onDownload: function(filename, obj, resp, req) { + this.urlField.setValue(''); + Deluge.Client.web.get_torrent_info(filename, { + success: this.onGotInfo, + scope: this, + filename: filename, + torrentId: req.options.torrentId + }); + }, + + onGotInfo: function(info, obj, response, request) { + info['filename'] = request.options.filename; + this.fireEvent('add', request.options.torrentId, info); + } +}); +/* +Script: Deluge.Client.js + A JSON-RPC proxy built on top of ext-core. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.ux.util'); +(function() { + Ext.ux.util.RpcClient = Ext.extend(Ext.util.Observable, { + + _components: [], + + _methods: [], + + _requests: {}, + + _url: null, + + _optionKeys: ['scope', 'success', 'failure'], + + constructor: function(config) { + Ext.ux.util.RpcClient.superclass.constructor.call(this, config); + this._url = config.url || null; + this._id = 0; + + this.addEvents( + // raw events + /** + * @event connected + * Fires when the client has retrieved the list of methods from the server. + * @param {Ext.ux.util.RpcClient} this + */ + 'connected', + + 'error' + ); + this.reloadMethods(); + }, + + reloadMethods: function() { + Ext.each(this._components, function(component) { + delete this[component]; + }, this); + this._execute('system.listMethods', { + success: this._setMethods, + scope: this + }); + }, + + _execute: function(method, options) { + options = options || {}; + options.params = options.params || []; + options.id = this._id; + + var request = Ext.encode({ + method: method, + params: options.params, + id: options.id + }); + this._id++; + + return Ext.Ajax.request({ + url: this._url, + method: 'POST', + success: this._onSuccess, + failure: this._onFailure, + scope: this, + jsonData: request, + options: options + }); + }, + + _onFailure: function(response, requestOptions) { + var options = requestOptions.options; + errorObj = { + id: options.id, + result: null, + error: { + msg: 'HTTP: ' + response.status + ' ' + response.statusText, + code: 255 + } + } + + this.fireEvent('error', errorObj, response, requestOptions) + + if (Ext.type(options.failure) != 'function') return; + if (options.scope) { + options.failure.call(options.scope, errorObj, response, requestOptions); + } else { + options.failure(errorObj, response, requestOptions); + } + }, + + _onSuccess: function(response, requestOptions) { + var responseObj = Ext.decode(response.responseText); + var options = requestOptions.options; + if (responseObj.error) { + this.fireEvent('error', responseObj, response, requestOptions); + + if (Ext.type(options.failure) != 'function') return; + if (options.scope) { + options.failure.call(options.scope, responseObj, response, requestOptions); + } else { + options.failure(responseObj, response, requestOptions); + } + } else { + if (Ext.type(options.success) != 'function') return; + if (options.scope) { + options.success.call(options.scope, responseObj.result, responseObj, response, requestOptions); + } else { + options.success(responseObj.result, responseObj, response, requestOptions); + } + } + }, + + _parseArgs: function(args) { + var params = []; + Ext.each(args, function(arg) { + params.push(arg); + }); + + var options = params[params.length - 1]; + if (Ext.type(options) == 'object') { + var keys = Ext.keys(options), isOption = false; + + Ext.each(this._optionKeys, function(key) { + if (keys.indexOf(key) > -1) isOption = true; + }); + + if (isOption) { + params.remove(options) + } else { + options = {} + } + } else { + options = {} + } + options.params = params; + return options; + }, + + _setMethods: function(methods) { + var components = {}, self = this; + + Ext.each(methods, function(method) { + var parts = method.split('.'); + var component = components[parts[0]] || {}; + + var fn = function() { + var options = self._parseArgs(arguments); + return self._execute(method, options); + } + component[parts[1]] = fn; + components[parts[0]] = component; + }); + + for (var name in components) { + self[name] = components[name]; + } + + this._components = Ext.keys(components); + this.fireEvent('connected', this); + } + }); +})(); +/* +Script: deluge-connections.js + Contains all objects and functions related to the connection manager. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +(function() { + var hostRenderer = function(value, p, r) { + return value + ':' + r.data['port'] + } + + Ext.deluge.AddConnectionWindow = Ext.extend(Ext.Window, { + + constructor: function(config) { + config = Ext.apply({ + layout: 'fit', + width: 300, + height: 195, + bodyStyle: 'padding: 10px 5px;', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + plain: true, + title: _('Add Connection'), + iconCls: 'x-deluge-add-window-icon' + }, config); + Ext.deluge.AddConnectionWindow.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.AddConnectionWindow.superclass.initComponent.call(this); + + this.addEvents('hostadded'); + + this.addButton(_('Close'), this.hide, this); + this.addButton(_('Add'), this.onAddClick, this); + + this.on('hide', this.onHide, this); + + this.form = this.add({ + xtype: 'form', + defaultType: 'textfield', + id: 'connectionAddForm', + baseCls: 'x-plain', + labelWidth: 55 + }); + + this.hostField = this.form.add({ + fieldLabel: _('Host'), + id: 'host', + name: 'host', + anchor: '100%', + value: '' + }); + + this.portField = this.form.add({ + fieldLabel: _('Port'), + id: 'port', + xtype: 'uxspinner', + ctCls: 'x-form-uxspinner', + name: 'port', + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 65535 + }, + value: '58846', + anchor: '50%' + }); + + this.usernameField = this.form.add({ + fieldLabel: _('Username'), + id: 'username', + name: 'username', + anchor: '100%', + value: '' + }); + + this.passwordField = this.form.add({ + fieldLabel: _('Password'), + anchor: '100%', + id: '_password', + name: '_password', + inputType: 'password', + value: '' + }); + }, + + onAddClick: function() { + var host = this.hostField.getValue(); + var port = this.portField.getValue(); + var username = this.usernameField.getValue(); + var password = this.passwordField.getValue(); + + Deluge.Client.web.add_host(host, port, username, password, { + success: function(result) { + if (!result[0]) { + Ext.MessageBox.show({ + title: _('Error'), + msg: "Unable to add host: " + result[1], + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error' + }); + } else { + this.fireEvent('hostadded'); + } + this.hide(); + }, + scope: this + }); + }, + + onHide: function() { + this.form.getForm().reset(); + } + }); + + Ext.deluge.ConnectionManager = Ext.extend(Ext.Window, { + + layout: 'fit', + width: 300, + height: 220, + bodyStyle: 'padding: 10px 5px;', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + plain: true, + title: _('Connection Manager'), + iconCls: 'x-deluge-connect-window-icon', + + initComponent: function() { + Ext.deluge.ConnectionManager.superclass.initComponent.call(this); + this.on({ + 'hide': this.onHide, + 'show': this.onShow + }); + Deluge.Events.on('login', this.onLogin, this); + Deluge.Events.on('logout', this.onLogout, this); + + this.addButton(_('Close'), this.onClose, this); + this.addButton(_('Connect'), this.onConnect, this); + + this.grid = this.add({ + xtype: 'grid', + store: new Ext.data.SimpleStore({ + fields: [ + {name: 'status', mapping: 3}, + {name: 'host', mapping: 1}, + {name: 'port', mapping: 2}, + {name: 'version', mapping: 4} + ], + id: 0 + }), + columns: [{ + header: _('Status'), + width: 65, + sortable: true, + renderer: fplain, + dataIndex: 'status' + }, { + id:'host', + header: _('Host'), + width: 150, + sortable: true, + renderer: hostRenderer, + dataIndex: 'host' + }, { + header: _('Version'), + width: 75, + sortable: true, + renderer: fplain, + dataIndex: 'version' + }], + stripeRows: true, + selModel: new Ext.grid.RowSelectionModel({ + singleSelect: true, + listeners: { + 'rowselect': {fn: this.onSelect, scope: this}, + 'selectionchange': {fn: this.onSelectionChanged, scope: this} + } + }), + autoExpandColumn: 'host', + deferredRender:false, + autoScroll:true, + margins: '0 0 0 0', + bbar: new Ext.Toolbar({ + buttons: [ + { + id: 'cm-add', + cls: 'x-btn-text-icon', + text: _('Add'), + icon: '/icons/add.png', + handler: this.onAddClick, + scope: this + }, { + id: 'cm-remove', + cls: 'x-btn-text-icon', + text: _('Remove'), + icon: '/icons/remove.png', + handler: this.onRemove, + disabled: true, + scope: this + }, '->', { + id: 'cm-stop', + cls: 'x-btn-text-icon', + text: _('Stop Daemon'), + icon: '/icons/error.png', + handler: this.onStop, + disabled: true, + scope: this + } + ] + }) + }); + }, + + disconnect: function() { + Deluge.Events.fire('disconnect'); + }, + + loadHosts: function() { + Deluge.Client.web.get_hosts({ + success: this.onGetHosts, + scope: this + }); + }, + + update: function(self) { + self.grid.getStore().each(function(r) { + Deluge.Client.web.get_host_status(r.id, { + success: self.onGetHostStatus, + scope: self + }); + }, this); + }, + + /** + * Updates the buttons in the Connection Manager UI according to the + * passed in records host state. + * @param {Ext.data.Record} record The hosts record to update the UI for + */ + updateButtons: function(record) { + var button = this.buttons[1], status = record.get('status'); + + // Update the Connect/Disconnect button + if (status == _('Connected')) { + button.enable(); + button.setText(_('Disconnect')); + } else if (status == _('Offline')) { + button.disable(); + } else { + button.enable(); + button.setText(_('Connect')); + } + + // Update the Stop/Start Daemon button + if (status == _('Offline')) { + if (record.get('host') == '127.0.0.1' || record.get('host') == 'localhost') { + this.stopHostButton.enable(); + this.stopHostButton.setText(_('Start Daemon')); + } else { + this.stopHostButton.disable(); + } + } else { + this.stopHostButton.enable(); + this.stopHostButton.setText(_('Stop Daemon')); + } + }, + + onAddClick: function(button, e) { + if (!this.addWindow) { + this.addWindow = new Ext.deluge.AddConnectionWindow(); + this.addWindow.on('hostadded', this.onHostAdded, this); + } + this.addWindow.show(); + }, + + onHostAdded: function() { + this.loadHosts(); + }, + + onClose: function(e) { + if (this.running) window.clearInterval(this.running); + this.hide(); + }, + + onConnect: function(e) { + var selected = this.grid.getSelectionModel().getSelected(); + if (!selected) return; + + if (selected.get('status') == _('Connected')) { + Deluge.Client.web.disconnect({ + success: function(result) { + this.update(this); + Deluge.Events.fire('disconnect'); + }, + scope: this + }); + } else { + var id = selected.id; + Deluge.Client.web.connect(id, { + success: function(methods) { + Deluge.Client.reloadMethods(); + Deluge.Client.on('connected', function(e) { + Deluge.Events.fire('connect'); + }, this, {single: true}); + } + }); + if (this.running) window.clearInterval(this.running); + this.hide(); + } + }, + + onGetHosts: function(hosts) { + this.grid.getStore().loadData(hosts); + Ext.each(hosts, function(host) { + Deluge.Client.web.get_host_status(host[0], { + success: this.onGetHostStatus, + scope: this + }); + }, this); + }, + + onGetHostStatus: function(host) { + var record = this.grid.getStore().getById(host[0]); + record.set('status', host[3]) + record.set('version', host[4]) + record.commit(); + if (this.grid.getSelectionModel().getSelected() == record) this.updateButtons(record); + }, + + onLogin: function() { + Deluge.Client.web.connected({ + success: function(connected) { + if (connected) { + Deluge.Events.fire('connect'); + } else { + this.show(); + } + }, + scope: this + }); + }, + + onLogout: function() { + this.disconnect(); + if (!this.hidden && this.rendered) { + this.hide(); + } + }, + + onRemove: function(button) { + var connection = this.grid.getSelectionModel().getSelected(); + if (!connection) return; + + Deluge.Client.web.remove_host(connection.id, { + success: function(result) { + if (!result) { + Ext.MessageBox.show({ + title: _('Error'), + msg: result[1], + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error' + }); + } else { + this.grid.getStore().remove(connection); + } + }, + scope: this + }); + }, + + onSelect: function(selModel, rowIndex, record) { + this.selectedRow = rowIndex; + }, + + onSelectionChanged: function(selModel) { + var record = selModel.getSelected(); + if (selModel.hasSelection()) { + this.removeHostButton.enable(); + this.stopHostButton.enable(); + this.stopHostButton.setText(_('Stop Daemon')); + } else { + this.removeHostButton.disable(); + this.stopHostButton.disable(); + } + this.updateButtons(record); + }, + + onShow: function() { + if (!this.addHostButton) { + var bbar = this.grid.getBottomToolbar(); + this.addHostButton = bbar.items.get('cm-add'); + this.removeHostButton = bbar.items.get('cm-remove'); + this.stopHostButton = bbar.items.get('cm-stop'); + } + this.loadHosts(); + this.running = window.setInterval(this.update, 2000, this); + }, + + onStop: function(button, e) { + var connection = this.grid.getSelectionModel().getSelected(); + if (!connection) return; + + if (connection.get('status') == 'Offline') { + // This means we need to start the daemon + Deluge.Client.web.start_daemon(connection.get('port')); + } else { + // This means we need to stop the daemon + Deluge.Client.web.stop_daemon(connection.id, { + success: function(result) { + if (!result[0]) { + Ext.MessageBox.show({ + title: _('Error'), + msg: result[1], + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error' + }); + } + } + }); + } + } + }); + Deluge.ConnectionManager = new Ext.deluge.ConnectionManager(); +})(); +/* +Script: Deluge.Details.js + Contains all objects and functions related to the lower details panel and + it's containing tabs. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +(function() { + Ext.namespace('Ext.deluge.details'); + Ext.deluge.details.TabPanel = Ext.extend(Ext.TabPanel, { + + constructor: function(config) { + config = Ext.apply({ + region: 'south', + id: 'torrentDetails', + split: true, + height: 220, + minSize: 100, + collapsible: true, + margins: '0 5 5 5', + activeTab: 0 + }, config); + Ext.deluge.details.TabPanel.superclass.constructor.call(this, config); + }, + + clear: function() { + this.items.each(function(panel) { + if (panel.clear) { + panel.clear.defer(100, panel); + panel.disable(); + } + }); + }, + + + update: function(tab) { + var torrent = Deluge.Torrents.getSelected(); + if (!torrent) { + this.clear(); + return; + } + + this.items.each(function(tab) { + if (tab.disabled) tab.enable(); + }); + + tab = tab || this.getActiveTab(); + if (tab.update) tab.update(torrent.id); + }, + + /* Event Handlers */ + + // We need to add the events in onRender since Deluge.Torrents hasn't + // been created yet. + onRender: function(ct, position) { + Ext.deluge.details.TabPanel.superclass.onRender.call(this, ct, position); + Deluge.Events.on('disconnect', this.clear, this); + Deluge.Torrents.on('rowclick', this.onTorrentsClick, this); + this.on('tabchange', this.onTabChange, this); + + Deluge.Torrents.getSelectionModel().on('selectionchange', function(selModel) { + if (!selModel.hasSelection()) this.clear(); + }, this); + }, + + onTabChange: function(panel, tab) { + this.update(tab); + }, + + onTorrentsClick: function(grid, rowIndex, e) { + this.update(); + } + }); + Deluge.Details = new Ext.deluge.details.TabPanel(); +})(); +/* +Script: Deluge.Details.Status.js + The status tab displayed in the details panel. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.deluge.details.StatusTab = Ext.extend(Ext.Panel, { + title: _('Status'), + autoScroll: true, + + onRender: function(ct, position) { + Ext.deluge.details.StatusTab.superclass.onRender.call(this, ct, position); + + this.progressBar = this.add({ + xtype: 'fullprogressbar', + cls: 'x-deluge-status-progressbar' + }); + + this.status = this.add({ + cls: 'x-deluge-status', + id: 'deluge-details-status', + + border: false, + width: 1000, + listeners: { + 'render': { + fn: function(panel) { + panel.load({ + url: '/render/tab_status.html', + text: _('Loading') + '...' + }); + panel.getUpdater().on('update', this.onPanelUpdate, this); + }, + scope: this + } + } + }); + }, + + clear: function() { + this.progressBar.updateProgress(0, ' '); + for (var k in this.fields) { + this.fields[k].innerHTML = ''; + } + }, + + update: function(torrentId) { + if (!this.fields) this.getFields(); + Deluge.Client.core.get_torrent_status(torrentId, Deluge.Keys.Status, { + success: this.onRequestComplete, + scope: this + }); + }, + + onPanelUpdate: function(el, response) { + this.fields = {}; + Ext.each(Ext.query('dd', this.status.body.dom), function(field) { + this.fields[field.className] = field; + }, this); + }, + + onRequestComplete: function(status) { + seeders = status.total_seeds > -1 ? status.num_seeds + ' (' + status.total_seeds + ')' : status.num_seeds + peers = status.total_peers > -1 ? status.num_peers + ' (' + status.total_peers + ')' : status.num_peers + var data = { + downloaded: fsize(status.total_done) + ' (' + fsize(status.total_payload_download) + ')', + uploaded: fsize(status.total_uploaded) + ' (' + fsize(status.total_payload_upload) + ')', + share: status.ratio.toFixed(3), + announce: ftime(status.next_announce), + tracker_status: status.tracker_status, + downspeed: fspeed(status.download_payload_rate), + upspeed: fspeed(status.upload_payload_rate), + eta: ftime(status.eta), + pieces: status.num_pieces + ' (' + fsize(status.piece_length) + ')', + seeders: seeders, + peers: peers, + avail: status.distributed_copies.toFixed(3), + active_time: ftime(status.active_time), + seeding_time: ftime(status.seeding_time), + seed_rank: status.seed_rank, + time_added: fdate(status.time_added) + } + data.auto_managed = _((status.is_auto_managed) ? 'True' : 'False'); + + for (var field in this.fields) { + this.fields[field].innerHTML = data[field]; + } + var text = status.state + ' ' + status.progress.toFixed(2) + '%'; + this.progressBar.updateProgress(status.progress, text); + } +}); +Deluge.Details.add(new Ext.deluge.details.StatusTab()); +/* +Script: Deluge.Details.Details.js + The details tab displayed in the details panel. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.deluge.details.DetailsTab = Ext.extend(Ext.Panel, { + title: _('Details'), + bodyStyle: 'padding 5px', + + onRender: function(ct, position) { + Ext.deluge.details.DetailsTab.superclass.onRender.call(this, ct, position); + this.load({ + url: '/render/tab_details.html', + text: _('Loading') + '...' + }); + this.oldData = {}; + this.body.setStyle('padding', '5px'); + this.getUpdater().on('update', this.onPanelUpdate, this); + }, + + clear: function() { + if (!this.fields) return; + for (var k in this.fields) { + this.fields[k].innerHTML = ''; + } + }, + + update: function(torrentId) { + Deluge.Client.core.get_torrent_status(torrentId, Deluge.Keys.Details, { + success: this.onRequestComplete, + scope: this, + torrentId: torrentId + }); + }, + + onPanelUpdate: function(el, response) { + this.fields = {}; + Ext.each(Ext.query('dd', this.body.dom), function(field) { + this.fields[field.className] = field; + }, this); + }, + + onRequestComplete: function(torrent, request, response, options) { + var data = { + torrent_name: torrent.name, + hash: options.options.torrentId, + path: torrent.save_path, + size: fsize(torrent.total_size), + files: torrent.num_files, + status: torrent.tracker_status, + tracker: torrent.tracker, + comment: torrent.comment + }; + + for (var field in this.fields) { + if (data[field] == this.oldData[field]) continue; + this.fields[field].innerHTML = Ext.escapeHTML(data[field]); + } + this.oldData = data; + } +}); +Deluge.Details.add(new Ext.deluge.details.DetailsTab());/* +Script: Deluge.Details.Files.js + The files tab displayed in the details panel. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ +(function() { + /* Renderers for the column tree */ + function fileProgressRenderer(value) { + var progress = value * 100; + return Deluge.progressBar(progress, this.width - 50, progress.toFixed(2) + '%', 0); + } + function priorityRenderer(value) { + return String.format('
{1}
', FILE_PRIORITY_CSS[value], _(FILE_PRIORITY[value])); + } + + Ext.deluge.details.FilesTab = Ext.extend(Ext.tree.ColumnTree, { + + constructor: function(config) { + config = Ext.apply({ + title: _('Files'), + rootVisible: false, + autoScroll: true, + selModel: new Ext.tree.MultiSelectionModel(), + + columns: [{ + header: _('Filename'), + width: 330, + dataIndex: 'filename' + }, { + header: _('Size'), + width: 150, + dataIndex: 'size', + renderer: fsize + }, { + header: _('Progress'), + width: 150, + dataIndex: 'progress', + renderer: fileProgressRenderer + }, { + header: _('Priority'), + width: 150, + dataIndex: 'priority', + renderer: priorityRenderer + }], + + root: new Ext.tree.TreeNode({ + text: 'Files' + }) + }, config); + Ext.deluge.details.FilesTab.superclass.constructor.call(this, config); + }, + + onRender: function(ct, position) { + Ext.deluge.details.FilesTab.superclass.onRender.call(this, ct, position); + Deluge.Menus.FilePriorities.on('itemclick', this.onItemClick, this); + this.on('contextmenu', this.onContextMenu, this); + this.sorter = new Ext.tree.TreeSorter(this, { + folderSort: true + }); + }, + + clear: function() { + var root = this.getRootNode(); + if (!root.hasChildNodes()) return; + root.cascade(function(node) { + var parent = node.parentNode; + if (!parent) return; + if (!parent.ownerTree) return; + parent.removeChild(node); + }); + }, + + update: function(torrentId) { + if (this.torrentId != torrentId) { + this.clear(); + this.torrentId = torrentId; + } + + Deluge.Client.web.get_torrent_files(torrentId, { + success: this.onRequestComplete, + scope: this, + torrentId: torrentId + }); + }, + + onContextMenu: function(node, e) { + e.stopEvent(); + var selModel = this.getSelectionModel(); + if (selModel.getSelectedNodes().length < 2) { + selModel.clearSelections(); + node.select(); + } + Deluge.Menus.FilePriorities.showAt(e.getPoint()); + }, + + onItemClick: function(baseItem, e) { + switch (baseItem.id) { + case 'expandAll': + this.expandAll(); + break; + default: + var indexes = {}; + function walk(node) { + if (Ext.isEmpty(node.attributes.fileIndex)) return; + indexes[node.attributes.fileIndex] = node.attributes.priority; + } + this.getRootNode().cascade(walk); + + var nodes = this.getSelectionModel().getSelectedNodes(); + Ext.each(nodes, function(node) { + if (!node.isLeaf()) { + function setPriorities(node) { + if (Ext.isEmpty(node.attributes.fileIndex)) return; + indexes[node.attributes.fileIndex] = baseItem.filePriority; + } + node.cascade(setPriorities); + } else if (!Ext.isEmpty(node.attributes.fileIndex)) { + indexes[node.attributes.fileIndex] = baseItem.filePriority; + return; + } + }); + + var priorities = new Array(Ext.keys(indexes).length); + for (var index in indexes) { + priorities[index] = indexes[index]; + } + + Deluge.Client.core.set_torrent_file_priorities(this.torrentId, priorities, { + success: function() { + Ext.each(nodes, function(node) { + node.setColumnValue(3, baseItem.filePriority); + }); + }, + scope: this + }); + break; + } + }, + + onRequestComplete: function(files, options) { + function walk(files, parent) { + for (var file in files) { + var item = files[file]; + var child = parent.findChild('id', file); + if (Ext.type(item) == 'object') { + if (!child) { + child = new Ext.tree.TreeNode({ + id: file, + text: file + }); + parent.appendChild(child); + } + walk(item, child); + } else { + if (!child) { + child = new Ext.tree.ColumnTreeNode({ + id: file, + filename: file, + text: file, // this needs to be here for sorting + fileIndex: item[0], + size: item[1], + progress: item[2], + priority: item[3], + leaf: true, + iconCls: 'x-deluge-file', + uiProvider: Ext.tree.ColumnNodeUI + }); + parent.appendChild(child); + } + child.setColumnValue(1, item[1]); + child.setColumnValue(2, item[2]); + child.setColumnValue(3, item[3]); + } + } + } + var root = this.getRootNode(); + walk(files, root); + root.firstChild.expand(); + } + }); + Deluge.Details.add(new Ext.deluge.details.FilesTab()); +})(); +/* +Script: Deluge.Details.Peers.js + The peers tab displayed in the details panel. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +(function() { + function flagRenderer(value) { + return String.format('', value); + } + function peerAddressRenderer(value, p, record) { + var seed = (record.data['seed'] == 1024) ? 'x-deluge-seed' : 'x-deluge-peer' + return String.format('
{1}
', seed, value); + } + function peerProgressRenderer(value) { + var progress = (value * 100).toFixed(0); + var width = new Number(this.style.match(/\w+:\s*(\d+)\w+/)[1]).toFixed(0) - 8; + return Deluge.progressBar(progress, width, progress + '%'); + } + function sort_address(value) { + var m = value.match(/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\:(\d+)/); + var address = 0; + var parts = [m[1], m[2], m[3], m[4]]; + Ext.each(parts, function(part, index) { + part = parseInt(part); + address = address | part << ((3 - index) * 8); + //alert("Total: " + address + "\nPart: " + part + "\nIndex: " + index + "\nCalc: " + (part << ((3 - index) * 8))); + }); + return address; + } + + Ext.deluge.details.PeersTab = Ext.extend(Ext.grid.GridPanel, { + + constructor: function(config) { + config = Ext.apply({ + title: _('Peers'), + cls: 'x-deluge-peers', + store: new Ext.data.SimpleStore({ + fields: [ + {name: 'country'}, + {name: 'address', sortType: sort_address}, + {name: 'client'}, + {name: 'progress', type: 'float'}, + {name: 'downspeed', type: 'int'}, + {name: 'upspeed', type: 'int'}, + {name: 'seed', type: 'int'} + ], + id: 0 + }), + columns: [{ + header: ' ', + width: 30, + sortable: true, + renderer: flagRenderer, + dataIndex: 'country' + }, { + header: 'Address', + width: 125, + sortable: true, + renderer: peerAddressRenderer, + dataIndex: 'address' + }, { + header: 'Client', + width: 125, + sortable: true, + renderer: fplain, + dataIndex: 'client' + }, { + header: 'Progress', + width: 150, + sortable: true, + renderer: peerProgressRenderer, + dataIndex: 'progress' + }, { + header: 'Down Speed', + width: 100, + sortable: true, + renderer: fspeed, + dataIndex: 'downspeed' + }, { + header: 'Up Speed', + width: 100, + sortable: true, + renderer: fspeed, + dataIndex: 'upspeed' + }], + stripeRows: true, + deferredRender:false, + autoScroll:true + }, config); + Ext.deluge.details.PeersTab.superclass.constructor.call(this, config); + }, + + onRender: function(ct, position) { + Ext.deluge.details.PeersTab.superclass.onRender.call(this, ct, position); + }, + + clear: function() { + this.getStore().loadData([]); + }, + + update: function(torrentId) { + Deluge.Client.core.get_torrent_status(torrentId, Deluge.Keys.Peers, { + success: this.onRequestComplete, + scope: this + }); + }, + + onRequestComplete: function(torrent, options) { + if (!torrent) return; + var peers = new Array(); + Ext.each(torrent.peers, function(peer) { + peers.push([peer.country, peer.ip, peer.client, peer.progress, peer.down_speed, peer.up_speed, peer.seed]); + }, this); + this.getStore().loadData(peers); + } + }); + Deluge.Details.add(new Ext.deluge.details.PeersTab()); +})();/* +Script: Deluge.Details.Options.js + The options tab displayed in the details panel. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.deluge.details.OptionsTab = Ext.extend(Ext.form.FormPanel, { + + constructor: function(config) { + config = Ext.apply({ + autoScroll: true, + bodyStyle: 'padding: 5px;', + border: false, + cls: 'x-deluge-options', + defaults: { + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }, + deferredRender: false, + layout: 'column', + title: _('Options') + }, config); + Ext.deluge.details.OptionsTab.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.details.OptionsTab.superclass.initComponent.call(this); + + this.fieldsets = {}, this.fields = {}; + this.optionsManager = new Deluge.MultiOptionsManager({ + options: { + 'max_download_speed': -1, + 'max_upload_speed': -1, + 'max_connections': -1, + 'max_upload_slots': -1, + 'auto_managed': false, + 'stop_at_ratio': false, + 'stop_ratio': 2.0, + 'remove_at_ratio': false, + 'move_completed': null, + 'private': false, + 'prioritize_first_last': false + } + }); + + /* + * Bandwidth Options + */ + this.fieldsets.bandwidth = this.add({ + xtype: 'fieldset', + defaultType: 'uxspinner', + bodyStyle: 'padding: 5px', + + layout: 'table', + layoutConfig: {columns: 3}, + labelWidth: 150, + + style: 'margin-left: 10px; margin-right: 5px; padding: 5px', + title: _('Bandwidth'), + width: 250 + }); + + /* + * Max Download Speed + */ + this.fieldsets.bandwidth.add({ + xtype: 'label', + text: _('Max Download Speed'), + forId: 'max_download_speed', + cls: 'x-deluge-options-label' + }); + this.fields.max_download_speed = this.fieldsets.bandwidth.add({ + id: 'max_download_speed', + name: 'max_download_speed', + width: 70, + strategy: { + xtype: 'number', + decimalPrecision: 1, + minValue: -1, + maxValue: 99999 + } + }); + this.fieldsets.bandwidth.add({ + xtype: 'label', + text: _('KiB/s'), + style: 'margin-left: 10px' + }); + + /* + * Max Upload Speed + */ + this.fieldsets.bandwidth.add({ + xtype: 'label', + text: _('Max Upload Speed'), + forId: 'max_upload_speed', + cls: 'x-deluge-options-label' + }); + this.fields.max_upload_speed = this.fieldsets.bandwidth.add({ + id: 'max_upload_speed', + name: 'max_upload_speed', + width: 70, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 1, + minValue: -1, + maxValue: 99999 + } + }); + this.fieldsets.bandwidth.add({ + xtype: 'label', + text: _('KiB/s'), + style: 'margin-left: 10px' + }); + + /* + * Max Connections + */ + this.fieldsets.bandwidth.add({ + xtype: 'label', + text: _('Max Connections'), + forId: 'max_connections', + cls: 'x-deluge-options-label' + }); + this.fields.max_connections = this.fieldsets.bandwidth.add({ + id: 'max_connections', + name: 'max_connections', + width: 70, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + colspan: 2 + }); + + /* + * Max Upload Slots + */ + this.fieldsets.bandwidth.add({ + xtype: 'label', + text: _('Max Upload Slots'), + forId: 'max_upload_slots', + cls: 'x-deluge-options-label' + }); + this.fields.max_upload_slots = this.fieldsets.bandwidth.add({ + id: 'max_upload_slots', + name: 'max_upload_slots', + width: 70, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + colspan: 2 + }); + + /* + * Queue Options + */ + this.fieldsets.queue = this.add({ + xtype: 'fieldset', + title: _('Queue'), + style: 'margin-left: 5px; margin-right: 5px; padding: 5px', + width: 210, + + layout: 'table', + layoutConfig: {columns: 2}, + labelWidth: 0, + + defaults: { + fieldLabel: '', + labelSeparator: '' + } + }); + + this.fields.auto_managed = this.fieldsets.queue.add({ + xtype: 'checkbox', + fieldLabel: '', + labelSeparator: '', + name: 'is_auto_managed', + boxLabel: _('Auto Managed'), + width: 200, + colspan: 2 + }); + + this.fields.stop_at_ratio = this.fieldsets.queue.add({ + fieldLabel: '', + labelSeparator: '', + id: 'stop_at_ratio', + width: 120, + boxLabel: _('Stop seed at ratio'), + handler: this.onStopRatioChecked, + scope: this + }); + + this.fields.stop_ratio = this.fieldsets.queue.add({ + xtype: 'uxspinner', + id: 'stop_ratio', + name: 'stop_ratio', + disabled: true, + width: 50, + value: 2.0, + strategy: { + xtype: 'number', + minValue: -1, + maxValue: 99999, + incrementValue: 0.1, + alternateIncrementValue: 1, + decimalPrecision: 1 + } + }); + + this.fields.remove_at_ratio = this.fieldsets.queue.add({ + fieldLabel: '', + labelSeparator: '', + id: 'remove_at_ratio', + ctCls: 'x-deluge-indent-checkbox', + bodyStyle: 'padding-left: 10px', + boxLabel: _('Remove at ratio'), + disabled: true, + colspan: 2 + }); + + this.fields.move_completed = this.fieldsets.queue.add({ + fieldLabel: '', + labelSeparator: '', + id: 'move_completed', + boxLabel: _('Move Completed'), + colspan: 2 + }); + + + /* + * General Options + */ + this.rightColumn = this.add({ + border: false, + autoHeight: true, + style: 'margin-left: 5px', + width: 200 + }); + + this.fieldsets.general = this.rightColumn.add({ + xtype: 'fieldset', + autoHeight: true, + defaultType: 'checkbox', + title: _('General'), + layout: 'form' + }); + + this.fields['private'] = this.fieldsets.general.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Private'), + id: 'private', + disabled: true + }); + + this.fields.prioritize_first_last = this.fieldsets.general.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Prioritize First/Last'), + id: 'prioritize_first_last' + }); + + // Bind the fields so the options manager can manage them. + for (var id in this.fields) { + this.optionsManager.bind(id, this.fields[id]); + } + + /* + * Buttons + */ + this.buttonPanel = this.rightColumn.add({ + layout: 'column', + xtype: 'panel', + border: false + }); + + // The buttons below are required to be added to a panel + // first as simply adding them to the column layout throws an + // error c.getSize() does not exist. This could be intentional + // or it may possible be a bug in ext-js. Take care when upgrading + // to ext-js 3.0. + + /* + * Edit Trackers button + */ + this.buttonPanel.add({ + xtype: 'panel', + border: false + }).add({ + id: 'edit_trackers', + xtype: 'button', + text: _('Edit Trackers'), + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-edit-trackers', + border: false, + width: 100, + handler: this.onEditTrackers, + scope: this + }); + + /* + * Apply button + */ + this.buttonPanel.add({ + xtype: 'panel', + border: false + }).add({ + id: 'apply', + xtype: 'button', + text: _('Apply'), + style: 'margin-left: 10px;', + border: false, + width: 100, + handler: this.onApply, + scope: this + }); + }, + + onRender: function(ct, position) { + Ext.deluge.details.OptionsTab.superclass.onRender.call(this, ct, position); + + // This is another hack I think, so keep an eye out here when upgrading. + this.layout = new Ext.layout.ColumnLayout(); + this.layout.setContainer(this); + this.doLayout(); + }, + + clear: function() { + if (this.torrentId == null) return; + this.torrentId = null; + this.optionsManager.changeId(null); + }, + + reset: function() { + if (this.torrentId) this.optionsManager.reset(); + }, + + update: function(torrentId) { + if (this.torrentId && !torrentId) this.clear(); // we want to clear the pane if we get a null torrent torrentIds + + if (!torrentId) return; // we don't care about null torrentIds + + if (this.torrentId != torrentId) { + this.torrentId = torrentId; + this.optionsManager.changeId(torrentId); + } + Deluge.Client.core.get_torrent_status(torrentId, Deluge.Keys.Options, { + success: this.onRequestComplete, + scope: this + }); + }, + + onApply: function() { + var changed = this.optionsManager.getDirty(); + if (!Ext.isEmpty(changed['prioritize_first_last'])) { + var value = changed['prioritize_first_last']; + Deluge.Client.core.set_torrent_prioritize_first_last(this.torrentId, value, { + success: function() { + this.optionsManager.set('prioritize_first_last', value); + }, + scope: this + }); + } + Deluge.Client.core.set_torrent_options([this.torrentId], changed, { + success: function() { + this.optionsManager.commit(); + }, + scope: this + }); + }, + + onEditTrackers: function() { + Deluge.EditTrackers.show(); + }, + + onStopRatioChecked: function(checkbox, checked) { + this.fields.remove_at_ratio.setDisabled(!checked); + this.fields.stop_ratio.setDisabled(!checked); + }, + + onRequestComplete: function(torrent, options) { + this.fields['private'].setValue(torrent['private']); + this.fields['private'].setDisabled(true); + delete torrent['private']; + torrent['auto_managed'] = torrent['is_auto_managed']; + this.optionsManager.setDefault(torrent); + var stop_at_ratio = this.optionsManager.get('stop_at_ratio'); + this.fields.remove_at_ratio.setDisabled(!stop_at_ratio); + this.fields.stop_ratio.setDisabled(!stop_at_ratio); + } +}); +Deluge.Details.add(new Ext.deluge.details.OptionsTab()); +/* +Script: Deluge.EditTrackers.js + Contains the edit trackers window. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +(function() { + Ext.deluge.AddTracker = Ext.extend(Ext.Window, { + constructor: function(config) { + config = Ext.apply({ + title: _('Add Tracker'), + width: 375, + height: 150, + bodyStyle: 'padding: 5px', + layout: 'fit', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + iconCls: 'x-deluge-edit-trackers', + plain: true, + resizable: false + }, config); + Ext.deluge.AddTracker.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.AddTracker.superclass.initComponent.call(this); + + this.addButton(_('Cancel'), this.onCancelClick, this); + this.addButton(_('Add'), this.onAddClick, this); + this.addEvents('add'); + + this.form = this.add({ + xtype: 'form', + defaultType: 'textarea', + baseCls: 'x-plain', + labelWidth: 55, + items: [{ + fieldLabel: _('Trackers'), + name: 'trackers', + anchor: '100%' + }] + }) + }, + + onAddClick: function() { + var trackers = this.form.getForm().findField('trackers').getValue(); + trackers = trackers.split('\n'); + + var cleaned = []; + Ext.each(trackers, function(tracker) { + if (Ext.form.VTypes.url(tracker)) { + cleaned.push(tracker); + } + }, this); + this.fireEvent('add', cleaned); + this.hide(); + this.form.getForm().findField('trackers').setValue(''); + }, + + onCancelClick: function() { + this.form.getForm().findField('trackers').setValue(''); + this.hide(); + } + }); + + Ext.deluge.EditTracker = Ext.extend(Ext.Window, { + constructor: function(config) { + config = Ext.apply({ + title: _('Edit Tracker'), + width: 375, + height: 110, + bodyStyle: 'padding: 5px', + layout: 'fit', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + iconCls: 'x-deluge-edit-trackers', + plain: true, + resizable: false + }, config); + Ext.deluge.EditTracker.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.EditTracker.superclass.initComponent.call(this); + + this.addButton(_('Cancel'), this.onCancel, this); + this.addButton(_('Save'), this.onSave, this); + this.on('hide', this.onHide, this); + + this.form = this.add({ + xtype: 'form', + defaultType: 'textfield', + baseCls: 'x-plain', + labelWidth: 55, + items: [{ + fieldLabel: _('Tracker'), + name: 'tracker', + anchor: '100%' + }] + }); + }, + + show: function(record) { + Ext.deluge.EditTracker.superclass.show.call(this); + + this.record = record; + this.form.getForm().findField('tracker').setValue(record.data['url']); + }, + + onCancel: function() { + this.hide(); + }, + + onHide: function() { + this.form.getForm().findField('tracker').setValue(''); + }, + + onSave: function() { + var url = this.form.getForm().findField('tracker').getValue(); + this.record.set('url', url); + this.record.commit(); + this.hide(); + } + }); + + Ext.deluge.EditTrackers = Ext.extend(Ext.Window, { + + constructor: function(config) { + config = Ext.apply({ + title: _('Edit Trackers'), + width: 350, + height: 220, + bodyStyle: 'padding: 5px', + layout: 'fit', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + iconCls: 'x-deluge-edit-trackers', + plain: true, + resizable: true + }, config); + Ext.deluge.EditTrackers.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.EditTrackers.superclass.initComponent.call(this); + + this.addButton(_('Cancel'), this.onCancel, this); + this.addButton(_('Ok'), this.onOk, this); + this.addEvents('save'); + + this.on('show', this.onShow, this); + this.on('save', this.onSave, this); + + this.addWindow = new Ext.deluge.AddTracker(); + this.addWindow.on('add', this.onAddTrackers, this); + this.editWindow = new Ext.deluge.EditTracker(); + + this.grid = this.add({ + xtype: 'grid', + store: new Ext.data.SimpleStore({ + fields: [ + {name: 'tier', mapping: 0}, + {name: 'url', mapping: 1} + ] + }), + columns: [{ + header: _('Tier'), + width: 50, + sortable: true, + renderer: fplain, + dataIndex: 'tier' + }, { + id:'tracker', + header: _('Tracker'), + sortable: true, + renderer: fplain, + dataIndex: 'url' + }], + stripeRows: true, + selModel: new Ext.grid.RowSelectionModel({ + singleSelect: true, + listeners: { + 'selectionchange': {fn: this.onSelect, scope: this} + } + }), + autoExpandColumn: 'tracker', + deferredRender:false, + autoScroll:true, + margins: '0 0 0 0', + bbar: new Ext.Toolbar({ + items: [ + { + cls: 'x-btn-text-icon', + text: _('Up'), + icon: '/icons/up.png', + handler: this.onUpClick, + scope: this + }, { + cls: 'x-btn-text-icon', + text: _('Down'), + icon: '/icons/down.png', + handler: this.onDownClick, + scope: this + }, '->', { + cls: 'x-btn-text-icon', + text: _('Add'), + icon: '/icons/add.png', + handler: this.onAddClick, + scope: this + }, { + cls: 'x-btn-text-icon', + text: _('Edit'), + icon: '/icons/edit_trackers.png', + handler: this.onEditClick, + scope: this + }, { + cls: 'x-btn-text-icon', + text: _('Remove'), + icon: '/icons/remove.png', + handler: this.onRemoveClick, + scope: this + } + ] + }) + }); + }, + + onAddClick: function() { + this.addWindow.show(); + }, + + onAddTrackers: function(trackers) { + var store = this.grid.getStore(); + Ext.each(trackers, function(tracker) { + var duplicate = false, heightestTier = -1; + store.each(function(record) { + if (record.get('tier') > heightestTier) { + heightestTier = record.get('tier'); + } + if (tracker == record.get('tracker')) { + duplicate = true; + return false; + } + }, this); + if (!duplicate) { + store.loadData([[heightestTier + 1, tracker]], true); + } + }, this); + }, + + onCancelClick: function() { + this.hide(); + }, + + onEditClick: function() { + var r = this.grid.getSelectionModel().getSelected(); + this.editWindow.show(r); + }, + + onHide: function() { + this.grid.getStore().removeAll(); + }, + + onOk: function() { + var trackers = []; + this.grid.getStore().each(function(record) { + trackers.push({ + 'tier': record.get('tier'), + 'url': record.get('url') + }) + }, this); + + Deluge.Client.core.set_torrent_trackers(this.torrentId, trackers, { + failure: this.onSaveFail, + scope: this + }); + + this.hide(); + }, + + onRemove: function() { + // Remove from the grid + var r = this.grid.getSelectionModel().getSelected(); + this.grid.getStore().remove(r); + }, + + onRequestComplete: function(status) { + var trackers = []; + Ext.each(status['trackers'], function(tracker) { + trackers.push([tracker['tier'], tracker['url']]); + }); + this.grid.getStore().loadData(trackers); + }, + + onSaveFail: function() { + + }, + + onSelect: function(sm) { + if (sm.hasSelection()) { + this.grid.getBottomToolbar().items.get(4).enable(); + } + }, + + onShow: function() { + this.grid.getBottomToolbar().items.get(4).disable(); + var r = Deluge.Torrents.getSelected(); + this.torrentId = r.id; + Deluge.Client.core.get_torrent_status(r.id, ['trackers'], { + success: this.onRequestComplete, + scope: this + }); + } + }); + Deluge.EditTrackers = new Ext.deluge.EditTrackers(); +})(); +/* +Script: Deluge.Login.js + Contains all objects and functions related to the login system. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.deluge.LoginWindow = Ext.extend(Ext.Window, { + + firstShow: true, + bodyStyle: 'padding: 10px 5px;', + buttonAlign: 'center', + closable: false, + closeAction: 'hide', + iconCls: 'x-deluge-login-window-icon', + layout: 'fit', + modal: true, + plain: true, + resizable: false, + title: _('Login'), + width: 300, + height: 120, + + initComponent: function() { + Ext.deluge.LoginWindow.superclass.initComponent.call(this); + this.on('show', this.onShow, this); + + this.addButton({ + text: _('Login'), + handler: this.onLogin, + scope: this + }); + + this.form = this.add({ + xtype: 'form', + baseCls: 'x-plain', + labelWidth: 55, + width: 300, + defaults: {width: 200}, + defaultType: 'textfield', + }); + + this.passwordField = this.form.add({ + xtype: 'textfield', + fieldLabel: _('Password'), + id: '_password', + name: 'password', + inputType: 'password' + }); + this.passwordField.on('specialkey', this.onSpecialKey, this); + }, + + logout: function() { + Deluge.Events.fire('logout'); + Deluge.Client.auth.delete_session({ + success: function(result) { + this.show(true); + }, + scope: this + }); + }, + + show: function(skipCheck) { + if (this.firstShow) { + Deluge.Client.on('error', this.onClientError, this); + this.firstShow = false; + } + + if (skipCheck) { + return Ext.deluge.LoginWindow.superclass.show.call(this); + } + + Deluge.Client.auth.check_session({ + success: function(result) { + if (result) { + Deluge.Events.fire('login'); + } else { + this.show(true); + } + }, + failure: function(result) { + this.show(true); + }, + scope: this + }); + }, + + onSpecialKey: function(field, e) { + if (e.getKey() == 13) this.onLogin(); + }, + + onLogin: function() { + var passwordField = this.passwordField; + Deluge.Client.auth.login(passwordField.getValue(), { + success: function(result) { + if (result) { + Deluge.Events.fire('login'); + this.hide(); + passwordField.setRawValue(''); + } else { + Ext.MessageBox.show({ + title: _('Login Failed'), + msg: _('You entered an incorrect password'), + buttons: Ext.MessageBox.OK, + modal: false, + fn: function() { + passwordField.focus(); + }, + icon: Ext.MessageBox.WARNING, + iconCls: 'x-deluge-icon-warning' + }); + } + }, + scope: this + }); + }, + + onClientError: function(errorObj, response, requestOptions) { + if (errorObj.error.code == 1) { + Deluge.Events.fire('logout'); + this.show(true); + } + }, + + onShow: function() { + this.passwordField.focus(false, 150); + this.passwordField.setRawValue(''); + } +}); + +Deluge.Login = new Ext.deluge.LoginWindow(); +/* +Script: Deluge.MoveStorage.js + Contains the move storage window. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge'); +Ext.deluge.MoveStorage = Ext.extend(Ext.Window, { + + constructor: function(config) { + config = Ext.apply({ + title: _('Move Storage'), + width: 375, + height: 110, + layout: 'fit', + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + iconCls: 'x-deluge-move-storage', + plain: true, + resizable: false + }, config); + Ext.deluge.MoveStorage.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.MoveStorage.superclass.initComponent.call(this); + + this.addButton(_('Cancel'), this.onCancel, this); + this.addButton(_('Move'), this.onMove, this); + + this.form = this.add({ + xtype: 'form', + border: false, + defaultType: 'textfield', + width: 300, + bodyStyle: 'padding: 5px' + }); + + this.moveLocation = this.form.add({ + fieldLabel: _('Location'), + name: 'location', + width: 240 + }); + }, + + hide: function() { + Ext.deluge.MoveStorage.superclass.hide.call(this); + this.torrentIds = null; + }, + + show: function(torrentIds) { + Ext.deluge.MoveStorage.superclass.show.call(this); + this.torrentIds = torrentIds; + }, + + onCancel: function() { + this.hide(); + }, + + onMove: function() { + var dest = this.moveLocation.getValue(); + Deluge.Client.core.move_storage(this.torrentIds, dest); + this.hide(); + } +}); +Deluge.MoveStorage = new Ext.deluge.MoveStorage(); +/* +Script: Deluge.Plugin.js + Contains a base class for plugins to extend. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +Deluge.Plugin = Ext.extend(Ext.util.Observable, { + constructor: function(config) { + this.name = config.name; + this.addEvents({ + "enabled": true, + "disabled": true + }); + this.isDelugePlugin = true; + Deluge.Plugins[this.name] = this; + Deluge.Plugin.superclass.constructor.call(this, config); + }, + + disable: function() { + this.fireEvent("disabled", this); + if (this.onDisable) this.onDisable(); + }, + + enable: function() { + this.fireEvent("enable", this); + if (this.onEnable) this.onEnable(); + } +});/* +Script: Deluge.Preferences.js + Contains the preferences window. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.deluge.PreferencesWindow = Ext.extend(Ext.Window, { + + currentPage: null, + + constructor: function(config) { + config = Ext.apply({ + layout: 'border', + width: 485, + height: 500, + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + iconCls: 'x-deluge-preferences', + plain: true, + resizable: false, + title: _('Preferences'), + + items: [{ + xtype: 'grid', + region: 'west', + title: _('Categories'), + store: new Ext.data.SimpleStore({ + fields: [{name: 'name', mapping: 0}] + }), + columns: [{id: 'name', renderer: fplain, dataIndex: 'name'}], + sm: new Ext.grid.RowSelectionModel({ + singleSelect: true, + listeners: {'rowselect': {fn: this.onPageSelect, scope: this}} + }), + hideHeaders: true, + autoExpandColumn: 'name', + deferredRender: false, + autoScroll: true, + margins: '5 0 5 5', + cmargins: '5 0 5 5', + width: 120, + collapsible: true + }, ] + }, config); + Ext.deluge.PreferencesWindow.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.PreferencesWindow.superclass.initComponent.call(this); + this.categoriesGrid = this.items.get(0); + this.configPanel = this.add({ + region: 'center', + header: false, + layout: 'fit', + height: 400, + autoScroll: true, + margins: '5 5 5 5', + cmargins: '5 5 5 5' + }); + + this.addButton(_('Close'), this.onClose, this); + this.addButton(_('Apply'), this.onApply, this); + this.addButton(_('Ok'), this.onOk, this); + + this.pages = {}; + this.optionsManager = new Deluge.OptionsManager(); + this.on('show', this.onShow, this); + }, + + onApply: function(e) { + var changed = this.optionsManager.getDirty(); + if (!Ext.isObjectEmpty(changed)) { + Deluge.Client.core.set_config(changed, { + success: this.onSetConfig, + scope: this + }); + } + + for (var page in this.pages) { + if (this.pages[page].onApply) this.pages[page].onApply(); + } + }, + + onClose: function() { + this.hide(); + }, + + onOk: function() { + Deluge.Client.core.set_config(this.optionsManager.getDirty()); + this.hide(); + }, + + /** + * Adds a page to the preferences window. + * @param {mixed} page + */ + addPage: function(page) { + var store = this.categoriesGrid.getStore(); + var name = page.title; + store.loadData([[name]], true); + page['bodyStyle'] = 'margin: 5px'; + this.pages[name] = this.configPanel.add(page); + return this.pages[name]; + }, + + /** + * Removes a preferences page from the window. + * @param {mixed} name + */ + removePage: function(page) { + var name = page.title; + var store = this.categoriesGrid.getStore(); + store.removeAt(store.find('name', name)); + this.configPanel.remove(page); + delete this.pages[page.title]; + }, + + /** + * Return the options manager for the preferences window. + * @returns {Deluge.OptionsManager} the options manager + */ + getOptionsManager: function() { + return this.optionsManager; + }, + + onGotConfig: function(config) { + this.getOptionsManager().set(config); + }, + + onPageSelect: function(selModel, rowIndex, r) { + if (this.currentPage == null) { + for (var page in this.pages) { + this.pages[page].hide(); + } + } else { + this.currentPage.hide(); + } + + var name = r.get('name'); + + this.pages[name].show(); + this.currentPage = this.pages[name]; + this.configPanel.doLayout(); + }, + + onSetConfig: function() { + this.getOptionsManager().commit(); + }, + + onShow: function() { + if (!this.categoriesGrid.getSelectionModel().hasSelection()) { + this.categoriesGrid.getSelectionModel().selectFirstRow(); + } + + Deluge.Client.core.get_config({ + success: this.onGotConfig, + scope: this + }) + } +}); + +Deluge.Preferences = new Ext.deluge.PreferencesWindow(); +/* +Script: Deluge.Preferences.Downloads.js + The downloads preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Downloads = Ext.extend(Ext.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Downloads'), + layout: 'form', + autoHeight: true, + width: 320 + }, config); + Ext.deluge.preferences.Downloads.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Downloads.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Folders'), + labelWidth: 150, + defaultType: 'togglefield', + autoHeight: true, + labelAlign: 'top', + width: 300, + style: 'margin-bottom: 5px; padding-bottom: 5px;' + }); + + optMan.bind('download_location', fieldset.add({ + xtype: 'textfield', + name: 'download_location', + fieldLabel: _('Download to'), + width: 280 + })); + + var field = fieldset.add({ + name: 'move_completed_path', + fieldLabel: _('Move completed to'), + width: 280 + }); + optMan.bind('move_completed', field.toggle); + optMan.bind('move_completed_path', field.input); + + field = fieldset.add({ + name: 'torrentfiles_location', + fieldLabel: _('Copy of .torrent files to'), + width: 280 + }); + optMan.bind('copy_torrent_file', field.toggle); + optMan.bind('torrentfiles_location', field.input); + + field = fieldset.add({ + name: 'autoadd_location', + fieldLabel: _('Autoadd .torrent files from'), + width: 280 + }); + optMan.bind('autoadd_enable', field.toggle); + optMan.bind('autoadd_location', field.input); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Allocation'), + autoHeight: true, + labelWidth: 1, + defaultType: 'radiogroup', + style: 'margin-bottom: 5px; margin-top: 0; padding-bottom: 5px; padding-top: 0;', + width: 240 + }); + optMan.bind('compact_allocation', fieldset.add({ + name: 'compact_allocation', + width: 200, + labelSeparator: '', + defaults: { + width: 80, + height: 22, + name: 'compact_allocation' + }, + items: [{ + boxLabel: _('Use Full'), + inputValue: false + }, { + boxLabel: _('Use Compact'), + inputValue: true + }] + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Options'), + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox', + style: 'margin-bottom: 0; padding-bottom: 0;', + width: 280 + }); + optMan.bind('prioritize_first_last_pieces', fieldset.add({ + name: 'prioritize_first_last_pieces', + labelSeparator: '', + height: 22, + boxLabel: _('Prioritize first and last pieces of torrent') + })); + optMan.bind('add_paused', fieldset.add({ + name: 'add_paused', + labelSeparator: '', + height: 22, + boxLabel: _('Add torrents in Paused state') + })); + + this.on('show', this.onShow, this); + }, + + onShow: function() { + Ext.deluge.preferences.Downloads.superclass.onShow.call(this); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Downloads()); +/* +Script: Deluge.Preferences.Network.js + The network preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); + +Ext.deluge.preferences.Network = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Network'), + layout: 'form' + }, config); + Ext.deluge.preferences.Network.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Network.superclass.initComponent.call(this); + var optMan = Deluge.Preferences.getOptionsManager(); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Incoming Ports'), + style: 'margin-bottom: 5px; padding-bottom: 0px;', + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('random_port', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Use Random Ports'), + name: 'random_port', + height: 22, + listeners: { + 'check': { + fn: function(e, checked) { + this.listenPorts.setDisabled(checked); + }, + scope: this + } + } + })); + this.listenPorts = fieldset.add({ + xtype: 'uxspinnergroup', + name: 'listen_ports', + fieldLabel: '', + labelSeparator: '', + colCfg: { + labelWidth: 40, + style: 'margin-right: 10px;' + }, + items: [{ + fieldLabel: 'From', + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + }, { + fieldLabel: 'To', + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + }] + }); + optMan.bind('listen_ports', this.listenPorts); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Outgoing Ports'), + style: 'margin-bottom: 5px; padding-bottom: 0px;', + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('random_outgoing_ports', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Use Random Ports'), + name: 'random_outgoing_ports', + height: 22, + listeners: { + 'check': { + fn: function(e, checked) { + this.outgoingPorts.setDisabled(checked); + }, + scope: this + } + } + })); + this.outgoingPorts = fieldset.add({ + xtype: 'uxspinnergroup', + name: 'outgoing_ports', + fieldLabel: '', + labelSeparator: '', + colCfg: { + labelWidth: 40, + style: 'margin-right: 10px;' + }, + items: [{ + fieldLabel: 'From', + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + }, { + fieldLabel: 'To', + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + }] + }); + optMan.bind('outgoing_ports', this.outgoingPorts); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Network Interface'), + style: 'margin-bottom: 5px; padding-bottom: 0px;', + autoHeight: true, + labelWidth: 1, + defaultType: 'textfield' + }); + optMan.bind('listen_interface', fieldset.add({ + name: 'listen_interface', + fieldLabel: '', + labelSeparator: '', + width: 200 + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('TOS'), + style: 'margin-bottom: 5px; padding-bottom: 0px;', + bodyStyle: 'margin: 0px; padding: 0px', + autoHeight: true, + defaultType: 'textfield' + }); + optMan.bind('peer_tos', fieldset.add({ + name: 'peer_tos', + fieldLabel: _('Peer TOS Byte'), + width: 80 + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Network Extras'), + autoHeight: true, + layout: 'table', + layoutConfig: { + columns: 3 + }, + defaultType: 'checkbox' + }); + optMan.bind('upnp', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('UPnP'), + name: 'upnp' + })); + optMan.bind('natpmp', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('NAT-PMP'), + ctCls: 'x-deluge-indent-checkbox', + name: 'natpmp' + })); + optMan.bind('utpex', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Peer Exchange'), + ctCls: 'x-deluge-indent-checkbox', + name: 'utpex' + })); + optMan.bind('lsd', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('LSD'), + name: 'lsd' + })); + optMan.bind('dht', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + boxLabel: _('DHT'), + ctCls: 'x-deluge-indent-checkbox', + name: 'dht' + })); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Network()); +/* +Script: Deluge.Preferences.Encryption.js + The encryption preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Encryption = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Encryption'), + layout: 'form' + }, config); + Ext.deluge.preferences.Encryption.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Encryption.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Settings'), + autoHeight: true, + defaultType: 'combo' + }); + optMan.bind('enc_in_policy', fieldset.add({ + fieldLabel: _('Inbound'), + mode: 'local', + width: 150, + store: new Ext.data.SimpleStore({ + fields: ['id', 'text'], + data: [ + [0, _('Forced')], + [1, _('Enabled')], + [2, _('Disabled')] + ] + }), + triggerAction: 'all', + valueField: 'id', + displayField: 'text' + })); + optMan.bind('enc_out_policy', fieldset.add({ + fieldLabel: _('Outbound'), + mode: 'local', + width: 150, + store: new Ext.data.SimpleStore({ + fields: ['id', 'text'], + data: [ + [0, _('Forced')], + [1, _('Enabled')], + [2, _('Disabled')] + ] + }), + triggerAction: 'all', + valueField: 'id', + displayField: 'text' + })); + optMan.bind('enc_level', fieldset.add({ + fieldLabel: _('Level'), + mode: 'local', + width: 150, + store: new Ext.data.SimpleStore({ + fields: ['id', 'text'], + data: [ + [0, _('Handshake')], + [1, _('Full Stream')], + [2, _('Either')] + ] + }), + triggerAction: 'all', + valueField: 'id', + displayField: 'text' + })); + optMan.bind('enc_prefer_rc4', fieldset.add({ + xtype: 'checkbox', + name: 'enc_prefer_rc4', + height: 40, + hideLabel: true, + boxLabel: _('Encrypt entire stream') + })); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Encryption());/* +Script: Deluge.Preferences.Bandwidth.js + The bandwidth preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Bandwidth = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Bandwidth'), + layout: 'form', + labelWidth: 10 + }, config); + Ext.deluge.preferences.Bandwidth.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Bandwidth.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Global Bandwidth Usage'), + labelWidth: 200, + defaultType: 'uxspinner', + style: 'margin-bottom: 0px; padding-bottom: 0px;', + autoHeight: true + }); + optMan.bind('max_connections_global', fieldset.add({ + name: 'max_connections_global', + fieldLabel: _('Maximum Connections'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_upload_slots_global', fieldset.add({ + name: 'max_upload_slots_global', + fieldLabel: _('Maximum Upload Slots'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_download_speed', fieldset.add({ + name: 'max_download_speed', + fieldLabel: _('Maximum Download Speed (KiB/s)'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 1, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_upload_speed', fieldset.add({ + name: 'max_upload_speed', + fieldLabel: _('Maximum Upload Speed (KiB/s)'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 1, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_half_open_connections', fieldset.add({ + name: 'max_half_open_connections', + fieldLabel: _('Maximum Half-Open Connections'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_connections_per_second', fieldset.add({ + name: 'max_connections_per_second', + fieldLabel: _('Maximum Connection Attempts per Second'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: '', + defaultType: 'checkbox', + style: 'padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;', + autoHeight: true + }); + optMan.bind('ignore_limits_on_local_network', fieldset.add({ + name: 'ignore_limits_on_local_network', + height: 22, + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Ignore limits on local network'), + })); + optMan.bind('rate_limit_ip_overhead', fieldset.add({ + name: 'rate_limit_ip_overhead', + height: 22, + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Rate limit IP overhead'), + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Per Torrent Bandwidth Usage'), + style: 'margin-bottom: 0px; padding-bottom: 0px;', + defaultType: 'uxspinner', + labelWidth: 200, + autoHeight: true + }); + optMan.bind('max_connections_per_torrent', fieldset.add({ + name: 'max_connections_per_torrent', + fieldLabel: _('Maximum Connections'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_upload_slots_per_torrent', fieldset.add({ + name: 'max_upload_slots_per_torrent', + fieldLabel: _('Maximum Upload Slots'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_download_speed_per_torrent', fieldset.add({ + name: 'max_download_speed_per_torrent', + fieldLabel: _('Maximum Download Speed (KiB/s)'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 1, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('max_upload_speed_per_torrent', fieldset.add({ + name: 'max_upload_speed_per_torrent', + fieldLabel: _('Maximum Upload Speed (KiB/s)'), + width: 80, + value: -1, + strategy: { + xtype: 'number', + decimalPrecision: 1, + minValue: -1, + maxValue: 99999 + } + })); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Bandwidth()); +/* +Script: Deluge.Preferences.Interface.js + The interface preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Interface = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Interface'), + layout: 'form' + }, config); + Ext.deluge.preferences.Interface.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Interface.superclass.initComponent.call(this); + + var optMan = this.optionsManager = new Deluge.OptionsManager(); + this.on('show', this.onShow, this); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Interface'), + style: 'margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px', + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('show_session_speed', fieldset.add({ + name: 'show_session_speed', + height: 22, + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Show session speed in titlebar') + })); + optMan.bind('sidebar_show_zero', fieldset.add({ + name: 'sidebar_show_zero', + height: 22, + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Show filters with zero torrents') + })); + optMan.bind('sidebar_show_trackers', fieldset.add({ + name: 'sidebar_show_trackers', + height: 22, + fieldLabel: '', + labelSeparator: '', + boxLabel: _('Show trackers with zero torrents') + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Password'), + style: 'margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px', + autoHeight: true, + labelWidth: 110, + defaultType: 'textfield', + defaults: { + width: 180, + inputType: 'password' + } + }); + + this.oldPassword = fieldset.add({ + name: 'old_password', + fieldLabel: _('Old Password') + }); + this.newPassword = fieldset.add({ + name: 'new_password', + fieldLabel: _('New Password') + }); + this.confirmPassword = fieldset.add({ + name: 'confirm_password', + fieldLabel: _('Confirm Password') + }); + + var panel = fieldset.add({ + xtype: 'panel', + autoHeight: true, + border: false, + width: 320, + bodyStyle: 'padding-left: 230px' + }) + panel.add({ + xtype: 'button', + text: _('Change'), + listeners: { + 'click': { + fn: this.onPasswordChange, + scope: this + } + } + }); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Server'), + style: 'margin-top: 0px; padding-top: 0px; margin-bottom: 0px; padding-bottom: 0px', + autoHeight: true, + labelWidth: 110, + defaultType: 'uxspinner', + defaults: { + width: 80, + } + }); + optMan.bind('session_timeout', fieldset.add({ + name: 'session_timeout', + fieldLabel: _('Session Timeout'), + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + optMan.bind('port', fieldset.add({ + name: 'port', + fieldLabel: _('Port'), + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + })); + this.httpsField = optMan.bind('https', fieldset.add({ + xtype: 'checkbox', + name: 'https', + hideLabel: true, + width: 280, + height: 22, + boxLabel: _('Use SSL (paths relative to Deluge config folder)') + })); + this.httpsField.on('check', this.onSSLCheck, this); + this.pkeyField = optMan.bind('pkey', fieldset.add({ + xtype: 'textfield', + disabled: true, + name: 'pkey', + width: 180, + fieldLabel: _('Private Key') + })); + this.certField = optMan.bind('cert', fieldset.add({ + xtype: 'textfield', + disabled: true, + name: 'cert', + width: 180, + fieldLabel: _('Certificate') + })); + }, + + onApply: function() { + var changed = this.optionsManager.getDirty(); + if (!Ext.isObjectEmpty(changed)) { + Deluge.Client.web.set_config(changed, { + success: this.onSetConfig, + scope: this + }); + } + }, + + onGotConfig: function(config) { + this.optionsManager.set(config); + }, + + onPasswordChange: function() { + var newPassword = this.newPassword.getValue(); + if (newPassword != this.confirmPassword.getValue()) { + Ext.MessageBox.show({ + title: _('Invalid Password'), + msg: _('Your passwords don\'t match!'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error' + }); + return; + } + + var oldPassword = this.oldPassword.getValue(); + Deluge.Client.auth.change_password(oldPassword, newPassword, { + success: function(result) { + if (!result) { + Ext.MessageBox.show({ + title: _('Password'), + msg: _('Your old password was incorrect!'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.ERROR, + iconCls: 'x-deluge-icon-error' + }); + this.oldPassword.setValue(''); + } else { + Ext.MessageBox.show({ + title: _('Change Successful'), + msg: _('Your password was successfully changed!'), + buttons: Ext.MessageBox.OK, + modal: false, + icon: Ext.MessageBox.INFO, + iconCls: 'x-deluge-icon-info' + }); + this.oldPassword.setValue(''); + this.newPassword.setValue(''); + this.confirmPassword.setValue(''); + } + }, + scope: this + }); + }, + + onSetConfig: function() { + this.optionsManager.commit(); + }, + + onShow: function() { + Ext.deluge.preferences.Interface.superclass.onShow.call(this); + Deluge.Client.web.get_config({ + success: this.onGotConfig, + scope: this + }) + }, + + onSSLCheck: function(e, checked) { + this.pkeyField.setDisabled(!checked); + this.certField.setDisabled(!checked); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Interface()); +/* +Script: Deluge.Preferences.Other.js + The other preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Other = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Other'), + layout: 'form' + }, config); + Ext.deluge.preferences.Other.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Other.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Updates'), + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('new_release_check', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + height: 22, + name: 'new_release_check', + boxLabel: _('Be alerted about new releases') + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('System Information'), + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + fieldset.add({ + xtype: 'panel', + border: false, + bodyCfg: { + html: _('Help us improve Deluge by sending us your ' + + 'Python version, PyGTK version, OS and processor ' + + 'types. Absolutely no other information is sent.') + } + }); + optMan.bind('send_info', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + height: 22, + boxLabel: _('Yes, please send anonymous statistics'), + name: 'send_info' + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('GeoIP Database'), + autoHeight: true, + labelWidth: 80, + defaultType: 'textfield' + }); + optMan.bind('geoip_db_location', fieldset.add({ + name: 'geoip_db_location', + fieldLabel: _('Location'), + width: 200 + })); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Other()); +/* +Script: Deluge.Preferences.Daemon.js + The daemon preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Daemon = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Daemon'), + layout: 'form' + }, config); + Ext.deluge.preferences.Daemon.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Daemon.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Port'), + autoHeight: true, + defaultType: 'uxspinner' + }); + optMan.bind('daemon_port', fieldset.add({ + fieldLabel: _('Daemon port'), + name: 'daemon_port', + value: 58846, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Connections'), + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('allow_remote', fieldset.add({ + fieldLabel: '', + height: 22, + labelSeparator: '', + boxLabel: _('Allow Remote Connections'), + name: 'allow_remote' + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Other'), + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('new_release_check', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + height: 40, + boxLabel: _('Periodically check the website for new releases'), + id: 'new_release_check' + })); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Daemon()); +/* +Script: Deluge.Preferences.Queue.js + The queue preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Queue = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Queue'), + layout: 'form' + }, config); + Ext.deluge.preferences.Queue.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Queue.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('General'), + style: 'padding-top: 5px;', + autoHeight: true, + labelWidth: 1, + defaultType: 'checkbox' + }); + optMan.bind('queue_new_to_top', fieldset.add({ + fieldLabel: '', + labelSeparator: '', + height: 22, + boxLabel: _('Queue new torrents to top'), + name: 'queue_new_to_top' + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Active Torrents'), + autoHeight: true, + labelWidth: 150, + defaultType: 'uxspinner', + style: 'margin-bottom: 0px; padding-bottom: 0px;', + }); + optMan.bind('max_active_limit', fieldset.add({ + fieldLabel: _('Total Active'), + name: 'max_active_limit', + value: 8, + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + optMan.bind('max_active_downloading', fieldset.add({ + fieldLabel: _('Total Active Downloading'), + name: 'max_active_downloading', + value: 3, + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + optMan.bind('max_active_seeding', fieldset.add({ + fieldLabel: _('Total Active Seeding'), + name: 'max_active_seeding', + value: 5, + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + optMan.bind('dont_count_slow_torrents', fieldset.add({ + xtype: 'checkbox', + name: 'dont_count_slow_torrents', + height: 40, + hideLabel: true, + boxLabel: _('Do not count slow torrents') + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Seeding'), + autoHeight: true, + labelWidth: 150, + defaultType: 'uxspinner', + style: 'margin-bottom: 0px; padding-bottom: 0px; margin-top: 0; padding-top: 0;', + }); + optMan.bind('share_ratio_limit', fieldset.add({ + fieldLabel: _('Share Ratio Limit'), + name: 'share_ratio_limit', + value: 8, + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + optMan.bind('seed_time_ratio_limit', fieldset.add({ + fieldLabel: _('Share Time Ratio'), + name: 'seed_time_ratio_limit', + value: 3, + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + optMan.bind('seed_time_limit', fieldset.add({ + fieldLabel: _('Seed Time (m)'), + name: 'seed_time_limit', + value: 5, + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + + fieldset = this.add({ + xtype: 'fieldset', + border: false, + autoHeight: true, + + layout: 'table', + layoutConfig: {columns: 2}, + labelWidth: 0, + defaultType: 'checkbox', + + defaults: { + fieldLabel: '', + labelSeparator: '' + } + }); + this.stopAtRatio = fieldset.add({ + name: 'stop_seed_at_ratio', + boxLabel: _('Stop seeding when share ratio reaches:') + }); + this.stopAtRatio.on('check', this.onStopRatioCheck, this); + optMan.bind('stop_seed_at_ratio', this.stopAtRatio); + + this.stopRatio = fieldset.add({ + xtype: 'uxspinner', + name: 'stop_seed_ratio', + ctCls: 'x-deluge-indent-checkbox', + disabled: true, + value: 2.0, + width: 60, + strategy: { + xtype: 'number', + minValue: -1, + maxValue: 99999, + incrementValue: 0.1, + alternateIncrementValue: 1, + decimalPrecision: 1 + } + }); + optMan.bind('stop_seed_ratio', this.stopRatio); + + this.removeAtRatio = fieldset.add({ + name: 'remove_seed_at_ratio', + ctCls: 'x-deluge-indent-checkbox', + boxLabel: _('Remove torrent when share ratio is reached'), + disabled: true, + colspan: 2 + }); + optMan.bind('remove_seed_at_ratio', this.removeAtRatio); + }, + + onStopRatioCheck: function(e, checked) { + this.stopRatio.setDisabled(!checked); + this.removeAtRatio.setDisabled(!checked); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Queue()); +/* +Script: Deluge.Preferences.Proxy.js + The proxy preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.ProxyField = Ext.extend(Ext.form.FieldSet, { + + constructor: function(config) { + config = Ext.apply({ + border: false, + autoHeight: true, + labelWidth: 70 + }, config); + Ext.deluge.preferences.ProxyField.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.ProxyField.superclass.initComponent.call(this); + this.type = this.add({ + xtype: 'combo', + fieldLabel: _('Type'), + name: 'type', + mode: 'local', + width: 150, + store: new Ext.data.SimpleStore({ + fields: ['id', 'text'], + data: [ + [0, _('None')], + [1, _('Socksv4')], + [2, _('Socksv5')], + [3, _('Socksv5 with Auth')], + [4, _('HTTP')], + [5, _('HTTP with Auth')], + ] + }), + value: 0, + triggerAction: 'all', + valueField: 'id', + displayField: 'text' + }) + this.hostname = this.add({ + xtype: 'textfield', + name: 'hostname', + fieldLabel: _('Host'), + width: 220 + }); + + this.port = this.add({ + xtype: 'uxspinner', + name: 'port', + fieldLabel: _('Port'), + width: 80, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + } + }); + + this.username = this.add({ + xtype: 'textfield', + name: 'username', + fieldLabel: _('Username'), + width: 220 + }); + + this.password = this.add({ + xtype: 'textfield', + name: 'password', + fieldLabel: _('Password'), + inputType: 'password', + width: 220 + }); + + this.type.on('change', this.onFieldChange, this); + this.type.on('select', this.onTypeSelect, this); + this.setting = false; + }, + + getName: function() { + return this.initialConfig.name; + }, + + getValue: function() { + return { + 'type': this.type.getValue(), + 'hostname': this.hostname.getValue(), + 'port': Number(this.port.getValue()), + 'username': this.username.getValue(), + 'password': this.password.getValue() + } + }, + + /** + * Set the values of the proxies + */ + setValue: function(value) { + this.setting = true; + this.type.setValue(value['type']); + var index = this.type.getStore().find('id', value['type']); + var record = this.type.getStore().getAt(index); + + this.hostname.setValue(value['hostname']); + this.port.setValue(value['port']); + this.username.setValue(value['username']); + this.password.setValue(value['password']); + this.onTypeSelect(this.type, record, index); + this.setting = false; + }, + + onFieldChange: function(field, newValue, oldValue) { + if (this.setting) return; + var newValues = this.getValue(); + var oldValues = Ext.apply({}, newValues); + oldValues[field.getName()] = oldValue; + + this.fireEvent('change', this, newValues, oldValues); + }, + + onTypeSelect: function(combo, record, index) { + var typeId = record.get('id'); + if (typeId > 0) { + this.hostname.show(); + this.port.show(); + } else { + this.hostname.hide(); + this.port.hide(); + } + + if (typeId == 3 || typeId == 5) { + this.username.show(); + this.password.show(); + } else { + this.username.hide(); + this.password.hide(); + } + } +}); + + +Ext.deluge.preferences.Proxy = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Proxy'), + layout: 'form' + }, config); + Ext.deluge.preferences.Proxy.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Proxy.superclass.initComponent.call(this); + this.peer = this.add(new Ext.deluge.preferences.ProxyField({ + title: _('Peer'), + name: 'peer' + })); + this.peer.on('change', this.onProxyChange, this); + + this.web_seed = this.add(new Ext.deluge.preferences.ProxyField({ + title: _('Web Seed'), + name: 'web_seed' + })); + this.web_seed.on('change', this.onProxyChange, this); + + this.tracker = this.add(new Ext.deluge.preferences.ProxyField({ + title: _('Tracker'), + name: 'tracker' + })); + this.tracker.on('change', this.onProxyChange, this); + + this.dht = this.add(new Ext.deluge.preferences.ProxyField({ + title: _('DHT'), + name: 'dht' + })); + this.dht.on('change', this.onProxyChange, this); + + Deluge.Preferences.getOptionsManager().bind('proxies', this); + }, + + getValue: function() { + return { + 'dht': this.dht.getValue(), + 'peer': this.peer.getValue(), + 'tracker': this.tracker.getValue(), + 'web_seed': this.web_seed.getValue() + } + }, + + setValue: function(value) { + for (var proxy in value) { + this[proxy].setValue(value[proxy]); + } + }, + + onProxyChange: function(field, newValue, oldValue) { + var newValues = this.getValue(); + var oldValues = Ext.apply({}, newValues); + oldValues[field.getName()] = oldValue; + + this.fireEvent('change', this, newValues, oldValues); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Proxy());/*Deluge.Preferences.addPage(_('Notification'), { + border: false, + xtype: 'form', + layout: 'form', + items: [] +}); +*//* +Script: Deluge.Preferences.Cache.js + The cache preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); +Ext.deluge.preferences.Cache = Ext.extend(Ext.form.FormPanel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Cache'), + layout: 'form' + }, config); + Ext.deluge.preferences.Cache.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.preferences.Cache.superclass.initComponent.call(this); + + var optMan = Deluge.Preferences.getOptionsManager(); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + title: _('Settings'), + autoHeight: true, + labelWidth: 180, + defaultType: 'uxspinner' + }); + optMan.bind('cache_size', fieldset.add({ + fieldLabel: _('Cache Size (16 KiB Blocks)'), + name: 'cache_size', + width: 60, + value: 512, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + optMan.bind('cache_expiry', fieldset.add({ + fieldLabel: _('Cache Expiry (seconds)'), + name: 'cache_expiry', + width: 60, + value: 60, + strategy: { + xtype: 'number', + decimalPrecision: 0, + minValue: -1, + maxValue: 99999 + }, + })); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Cache());/* +Script: Deluge.Preferences.Plugins.js + The plugins preferences page. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.namespace('Ext.deluge.preferences'); + +Ext.deluge.preferences.InstallPlugin = Ext.extend(Ext.Window, { + + height: 115, + width: 350, + + bodyStyle: 'padding: 10px 5px;', + + buttonAlign: 'center', + + closeAction: 'hide', + + iconCls: 'x-deluge-install-plugin', + + layout: 'fit', + + modal: true, + + plain: true, + + title: _('Install Plugin'), + + initComponent: function() { + Ext.deluge.add.FileWindow.superclass.initComponent.call(this); + this.addButton(_('Install'), this.onInstall, this); + + this.form = this.add({ + xtype: 'form', + baseCls: 'x-plain', + labelWidth: 70, + autoHeight: true, + fileUpload: true, + items: [{ + xtype: 'fileuploadfield', + id: 'pluginEgg', + emptyText: _('Select an egg'), + fieldLabel: _('Plugin Egg'), + name: 'file', + buttonCfg: { + text: _('Browse') + '...' + } + }] + }); + }, + + onInstall: function(field, e) { + this.form.getForm().submit({ + url: '/upload', + waitMsg: _('Uploading your plugin...'), + success: this.onUploadSuccess, + scope: this + }); + }, + + onUploadPlugin: function(info, obj, response, request) { + this.fireEvent('pluginadded'); + }, + + onUploadSuccess: function(fp, upload) { + this.hide(); + if (upload.result.success) { + var filename = this.form.getForm().findField('pluginEgg').value; + var path = upload.result.files[0] + this.form.getForm().findField('pluginEgg').setValue(''); + Deluge.Client.web.upload_plugin(filename, path, { + success: this.onUploadPlugin, + scope: this, + filename: filename + }); + } + } +}); + + +Ext.deluge.preferences.Plugins = Ext.extend(Ext.Panel, { + constructor: function(config) { + config = Ext.apply({ + border: false, + title: _('Plugins'), + layout: 'border', + height: 400, + cls: 'x-deluge-plugins' + }, config); + Ext.deluge.preferences.Plugins.superclass.constructor.call(this, config); + }, + + pluginTemplate: new Ext.Template( + '
' + + '
Author:
{author}
' + + '
Version:
{version}
' + + '
Author Email:
{email}
' + + '
Homepage:
{homepage}
' + + '
Details:
{details}
' + + '
' + ), + + initComponent: function() { + Ext.deluge.preferences.Plugins.superclass.initComponent.call(this); + this.defaultValues = { + 'version': '', + 'email': '', + 'homepage': '', + 'details': '' + }; + this.pluginTemplate.compile(); + + var checkboxRenderer = function(v, p, record){ + p.css += ' x-grid3-check-col-td'; + return '
'; + } + + this.grid = this.add({ + xtype: 'grid', + region: 'center', + store: new Ext.data.SimpleStore({ + fields: [ + {name: 'enabled', mapping: 0}, + {name: 'plugin', mapping: 1} + ] + }), + columns: [{ + id: 'enabled', + header: _('Enabled'), + width: 50, + sortable: true, + renderer: checkboxRenderer, + dataIndex: 'enabled' + }, { + id: 'plugin', + header: _('Plugin'), + sortable: true, + dataIndex: 'plugin' + }], + stripeRows: true, + selModel: new Ext.grid.RowSelectionModel({ + singleSelect: true, + listeners: { + 'rowselect': { + fn: this.onPluginSelect, + scope: this + } + } + }), + autoExpandColumn: 'plugin', + deferredRender: false, + autoScroll: true, + margins: '5 5 5 5', + bbar: new Ext.Toolbar({ + items: [{ + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-install-plugin', + text: _('Install'), + handler: this.onInstallPlugin, + scope: this + }, '->', { + cls: 'x-btn-text-icon', + text: _('Find More'), + iconCls: 'x-deluge-find-more', + handler: this.onFindMorePlugins, + scope: this + }] + }) + }); + + var fieldset = this.add({ + xtype: 'fieldset', + border: false, + region: 'south', + title: _('Info'), + autoHeight: true, + labelWidth: 1 + }); + this.pluginInfo = fieldset.add({ + xtype: 'panel', + border: false, + bodyCfg: { + style: 'margin-left: 10px' + } + }); + + this.on('show', this.onShow, this); + this.pluginInfo.on('render', this.onPluginInfoRender, this); + this.grid.on('cellclick', this.onCellClick, this); + Deluge.Preferences.on('show', this.onPreferencesShow, this); + Deluge.Events.on('PluginDisabledEvent', this.onPluginDisabled, this); + Deluge.Events.on('PluginEnabledEvent', this.onPluginEnabled, this); + }, + + disablePlugin: function(plugin) { + Deluge.Client.core.disable_plugin(plugin); + }, + + enablePlugin: function(plugin) { + Deluge.Client.core.enable_plugin(plugin); + }, + + setInfo: function(plugin) { + if (!this.pluginInfo.rendered) return; + var values = plugin || this.defaultValues; + this.pluginInfo.body.dom.innerHTML = this.pluginTemplate.apply(values); + }, + + updatePlugins: function() { + Deluge.Client.web.get_plugins({ + success: this.onGotPlugins, + scope: this + }); + }, + + updatePluginsGrid: function() { + var plugins = []; + Ext.each(this.availablePlugins, function(plugin) { + if (this.enabledPlugins.indexOf(plugin) > -1) { + plugins.push([true, plugin]); + } else { + plugins.push([false, plugin]); + } + }, this); + this.grid.getStore().loadData(plugins); + }, + + onCellClick: function(grid, rowIndex, colIndex, e) { + if (colIndex != 0) return; + var r = grid.getStore().getAt(rowIndex); + r.set('enabled', !r.get('enabled')); + r.commit(); + if (r.get('enabled')) { + this.enablePlugin(r.get('plugin')); + } else { + this.disablePlugin(r.get('plugin')); + } + }, + + onFindMorePlugins: function() { + window.open('http://dev.deluge-torrent.org/wiki/Plugins'); + }, + + onGotPlugins: function(plugins) { + this.enabledPlugins = plugins.enabled_plugins; + this.availablePlugins = plugins.available_plugins; + this.setInfo(); + this.updatePluginsGrid(); + }, + + onGotPluginInfo: function(info) { + var values = { + author: info['Author'], + version: info['Version'], + email: info['Author-email'], + homepage: info['Home-page'], + details: info['Description'] + } + this.setInfo(values); + delete info; + }, + + onInstallPlugin: function() { + if (!this.installWindow) { + this.installWindow = new Ext.deluge.preferences.InstallPlugin(); + this.installWindow.on('pluginadded', this.onPluginInstall, this); + } + this.installWindow.show(); + }, + + onPluginEnabled: function(pluginName) { + var index = this.grid.getStore().find('plugin', pluginName); + var plugin = this.grid.getStore().getAt(index); + plugin.set('enabled', true); + plugin.commit(); + }, + + onPluginDisabled: function(pluginName) { + var index = this.grid.getStore().find('plugin', pluginName); + var plugin = this.grid.getStore().getAt(index); + plugin.set('enabled', false); + plugin.commit(); + }, + + onPluginInstall: function() { + this.updatePlugins(); + }, + + onPluginSelect: function(selmodel, rowIndex, r) { + Deluge.Client.web.get_plugin_info(r.get('plugin'), { + success: this.onGotPluginInfo, + scope: this + }); + }, + + onPreferencesShow: function() { + this.updatePlugins(); + }, + + onPluginInfoRender: function(ct, position) { + this.setInfo(); + } +}); +Deluge.Preferences.addPage(new Ext.deluge.preferences.Plugins()); +/* +Script: + Deluge.Remove.js + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + 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.deluge.RemoveWindow = Ext.extend(Ext.Window, { + + constructor: function(config) { + config = Ext.apply({ + title: _('Remove Torrent'), + layout: 'fit', + width: 350, + height: 100, + buttonAlign: 'right', + closeAction: 'hide', + closable: true, + plain: true, + iconCls: 'x-deluge-remove-window-icon' + }, config); + Ext.deluge.RemoveWindow.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.RemoveWindow.superclass.initComponent.call(this); + this.addButton(_('Cancel'), this.onCancel, this); + this.addButton(_('Remove With Data'), this.onRemoveData, this); + this.addButton(_('Remove Torrent'), this.onRemove, this); + + this.add({ + border: false, + bodyStyle: 'padding: 5px; padding-left: 10px;', + html: 'Are you sure you wish to remove the torrent(s)?' + }); + }, + + remove: function(removeData) { + Ext.each(this.torrentIds, function(torrentId) { + Deluge.Client.core.remove_torrent(torrentId, removeData, { + success: function() { + this.onRemoved(torrentId); + }, + scope: this, + torrentId: torrentId + }); + }, this); + + }, + + show: function(ids) { + Ext.deluge.RemoveWindow.superclass.show.call(this); + this.torrentIds = ids; + }, + + onCancel: function() { + this.hide(); + this.torrentIds = null; + }, + + onRemove: function() { + this.remove(false); + }, + + onRemoveData: function() { + this.remove(true); + }, + + onRemoved: function(torrentId) { + Deluge.Events.fire('torrentRemoved', torrentId); + this.hide(); + Deluge.UI.update(); + } +}); + +Deluge.RemoveWindow = new Ext.deluge.RemoveWindow(); +/* +Script: deluge-bars.js + Contains all objects and functions related to the statusbar, toolbar and + sidebar. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +// These are just so gen_gettext.py will pick up the strings +// _('State') +// _('Tracker Host') + +(function() { + // Renderer for the items in the filter grids. + function filterRenderer(value, p, r) { + var lname = value.toLowerCase().replace('.', '_'); + + var image = ''; + if (r.store.id == 'tracker_host') { + if (value != 'Error') { + image = String.format('url(/tracker/{0})', value); + } else { + lname = null; + } + } + if (image) { + return String.format('
{0} ({1})
', value, r.data['count'], image); + } else if (lname) { + return String.format('
{0} ({1})
', value, r.data['count'], lname); + } else { + return String.format('
{0} ({1})
', value, r.data['count']); + } + } + + Ext.deluge.Sidebar = Ext.extend(Ext.Panel, { + + // private + panels: {}, + + // private + selected: null, + + constructor: function(config) { + config = Ext.apply({ + id: 'sidebar', + region: 'west', + cls: 'deluge-sidebar', + title: _('Filters'), + layout: 'accordion', + split: true, + width: 200, + minSize: 175, + collapsible: true, + margins: '5 0 0 5', + cmargins: '5 0 0 5' + }, config); + Ext.deluge.Sidebar.superclass.constructor.call(this, config); + }, + + // private + initComponent: function() { + Ext.deluge.Sidebar.superclass.initComponent.call(this); + Deluge.Events.on("disconnect", this.onDisconnect, this); + }, + + createFilter: function(filter, states) { + var store = new Ext.data.SimpleStore({ + id: filter, + fields: [ + {name: 'filter'}, + {name: 'count'} + ] + }); + + var title = filter.replace('_', ' '); + var parts = title.split(' '); + title = ''; + Ext.each(parts, function(part) { + firstLetter = part.substring(0, 1); + firstLetter = firstLetter.toUpperCase(); + part = firstLetter + part.substring(1); + title += part + ' '; + }); + + var panel = new Ext.grid.GridPanel({ + id: filter + '-panel', + border: false, + store: store, + title: _(title), + columns: [ + {id: 'filter', sortable: false, renderer: filterRenderer, dataIndex: 'filter'} + ], + stripeRows: false, + selModel: new Ext.grid.RowSelectionModel({ + singleSelect: true, + listeners: { + 'rowselect': {fn: this.onFilterSelect, scope: this} + } + }), + hideHeaders: true, + autoExpandColumn: 'filter', + deferredRender: false, + autoScroll: true + }); + + if (Deluge.config['sidebar_show_zero'] == false) { + states = this.removeZero(states); + } + + store.loadData(states); + this.add(panel); + + this.doLayout(); + this.panels[filter] = panel; + + if (!this.selected) { + panel.getSelectionModel().selectFirstRow(); + this.selected = { + row: 0, + filter: states[0][0], + panel: panel + } + } + }, + + getFilters: function() { + var filters = {} + if (!this.selected) { + return filters; + } + if (!this.selected.filter || !this.selected.panel) { + return filters; + } + var filterType = this.selected.panel.store.id; + if (filterType == "state" && this.selected.filter == "All") { + return filters; + } + + filters[filterType] = this.selected.filter; + return filters; + }, + + // private + onDisconnect: function() { + Ext.each(Ext.getKeys(this.panels), function(filter) { + this.remove(filter + '-panel'); + }, this); + this.panels = {}; + this.selected = null; + }, + + onFilterSelect: function(selModel, rowIndex, record) { + if (!this.selected) needsUpdate = true; + else if (this.selected.row != rowIndex) needsUpdate = true; + else needsUpdate = false; + this.selected = { + row: rowIndex, + filter: record.get('filter'), + panel: this.panels[record.store.id] + } + + if (needsUpdate) Deluge.UI.update(); + }, + + /** + * Remove the states with zero torrents in them. + */ + removeZero: function(states) { + var newStates = []; + Ext.each(states, function(state) { + if (state[1] > 0 || state[0] == _('All')) { + newStates.push(state); + } + }); + return newStates; + }, + + update: function(filters) { + for (var filter in filters) { + var states = filters[filter]; + if (Ext.getKeys(this.panels).indexOf(filter) > -1) { + this.updateFilter(filter, states); + } else { + this.createFilter(filter, states); + } + } + + // Perform a cleanup of fitlers that aren't enabled any more + Ext.each(Ext.keys(this.panels), function(filter) { + if (Ext.keys(filters).indexOf(filter) == -1) { + // We need to remove the panel + this.panels[filter] + } + }, this); + }, + + updateFilter: function(filter, states) { + if (Deluge.config['sidebar_show_zero'] == false) { + states = this.removeZero(states); + } + + this.panels[filter].store.loadData(states); + if (this.selected && this.selected.panel == this.panels[filter]) { + this.panels[filter].getSelectionModel().selectRow(this.selected.row); + } + } + }); + Deluge.Sidebar = new Ext.deluge.Sidebar(); +})(); +Ext.deluge.Statusbar = Ext.extend(Ext.Toolbar, { + constructor: function(config) { + config = Ext.apply({ + id: 'deluge-statusbar', + defaultIconCls: 'x-not-connected', + defaultText: _('Not Connected') + }, config); + Ext.deluge.Statusbar.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.Statusbar.superclass.initComponent.call(this); + + Deluge.Events.on('connect', this.onConnect, this); + Deluge.Events.on('disconnect', this.onDisconnect, this); + }, + + createButtons: function() { + this.add({ + id: 'statusbar-connections', + text: ' ', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-connections', + tooltip: _('Connections'), + menu: Deluge.Menus.Connections + }, '-', { + id: 'statusbar-downspeed', + text: ' ', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-downloading', + tooltip: _('Download Speed'), + menu: Deluge.Menus.Download + }, '-', { + id: 'statusbar-upspeed', + text: ' ', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-seeding', + tooltip: _('Upload Speed'), + menu: Deluge.Menus.Upload + }, '-', { + id: 'statusbar-traffic', + text: ' ', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-traffic', + tooltip: _('Protocol Traffic Download/Upload') + }, '-', { + id: 'statusbar-dht', + text: ' ', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-dht', + tooltip: _('DHT Nodes') + }, '-', { + id: 'statusbar-freespace', + text: ' ', + cls: 'x-btn-text-icon', + iconCls: 'x-deluge-freespace', + tooltip: _('Freespace in download location') + }); + this.created = true; + }, + + onConnect: function() { + //this.setStatus({ + // iconCls: 'x-connected', + // text: '' + //}); + if (!this.created) this.createButtons(); + else { + this.items.each(function(item) { + item.show(); + item.enable(); + }); + } + }, + + onDisconnect: function() { + //this.clearStatus({useDefaults:true}); + this.items.each(function(item) { + item.hide(); + item.disable(); + }); + }, + + update: function(stats) { + if (!stats) return; + + function addSpeed(val) {return val + ' KiB/s'} + + var updateStat = function(name, config) { + var item = this.items.get('statusbar-' + name); + if (config.limit.value > 0) { + var value = (config.value.formatter) ? config.value.formatter(config.value.value) : config.value.value; + var limit = (config.limit.formatter) ? config.limit.formatter(config.limit.value) : config.limit.value; + var str = String.format(config.format, value, limit); + } else { + var str = (config.value.formatter) ? config.value.formatter(config.value.value) : config.value.value; + } + item.setText(str); + }.bind(this); + + updateStat('connections', { + value: {value: stats.num_connections}, + limit: {value: stats.max_num_connections}, + format: '{0} ({1})' + }); + + updateStat('downspeed', { + value: { + value: stats.download_rate, + formatter: Deluge.Formatters.speed + }, + limit: { + value: stats.max_download, + formatter: addSpeed + }, + format: '{0} ({1})' + }); + + updateStat('upspeed', { + value: { + value: stats.upload_rate, + formatter: Deluge.Formatters.speed + }, + limit: { + value: stats.max_upload, + formatter: addSpeed + }, + format: '{0} ({1})' + }); + + updateStat('traffic', { + value: { + value: stats.download_protocol_rate, + formatter: Deluge.Formatters.speed + }, + limit: { + value: stats.upload_protocol_rate, + formatter: Deluge.Formatters.speed + }, + format: '{0}/{1}' + }); + + this.items.get('statusbar-dht').setText(stats.dht_nodes); + this.items.get('statusbar-freespace').setText(fsize(stats.free_space)); + + Deluge.Menus.Connections.setValue(stats.max_num_connections); + Deluge.Menus.Download.setValue(stats.max_download); + Deluge.Menus.Upload.setValue(stats.max_upload); + } +}); +Deluge.Statusbar = new Ext.deluge.Statusbar(); +/* +Script: Deluge.Toolbar.js + Contains the Deluge toolbar. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +(function() { + Ext.deluge.Toolbar = Ext.extend(Ext.Toolbar, { + constructor: function(config) { + config = Ext.apply({ + items: [ + { + id: 'create', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Create'), + icon: '/icons/create.png', + handler: this.onTorrentAction + },{ + id: 'add', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Add'), + icon: '/icons/add.png', + handler: this.onTorrentAdd + },{ + id: 'remove', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Remove'), + icon: '/icons/remove.png', + handler: this.onTorrentAction + },'|',{ + id: 'pause', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Pause'), + icon: '/icons/pause.png', + handler: this.onTorrentAction + },{ + id: 'resume', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Resume'), + icon: '/icons/start.png', + handler: this.onTorrentAction + },'|',{ + id: 'up', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Up'), + icon: '/icons/up.png', + handler: this.onTorrentAction + },{ + id: 'down', + cls: 'x-btn-text-icon', + disabled: true, + text: _('Down'), + icon: '/icons/down.png', + handler: this.onTorrentAction + },'|',{ + id: 'preferences', + cls: 'x-btn-text-icon', + text: _('Preferences'), + iconCls: 'x-deluge-preferences', + handler: this.onPreferencesClick, + scope: this + },{ + id: 'connectionman', + cls: 'x-btn-text-icon', + text: _('Connection Manager'), + iconCls: 'x-deluge-connection-manager', + handler: this.onConnectionManagerClick, + scope: this + },'->',{ + id: 'help', + cls: 'x-btn-text-icon', + icon: '/icons/help.png', + text: _('Help'), + handler: this.onHelpClick, + scope: this + },{ + id: 'logout', + cls: 'x-btn-text-icon', + icon: '/icons/logout.png', + disabled: true, + text: _('Logout'), + handler: this.onLogout, + scope: this + } + ] + }, config); + Ext.deluge.Toolbar.superclass.constructor.call(this, config); + }, + + connectedButtons: [ + 'add', 'remove', 'pause', 'resume', 'up', 'down' + ], + + initComponent: function() { + Ext.deluge.Toolbar.superclass.initComponent.call(this); + Deluge.Events.on('connect', this.onConnect, this); + Deluge.Events.on('login', this.onLogin, this); + }, + + onConnect: function() { + Ext.each(this.connectedButtons, function(buttonId) { + this.items.get(buttonId).enable(); + }, this); + }, + + onDisconnect: function() { + Ext.each(this.connectedButtons, function(buttonId) { + this.items.get(buttonId).disable(); + }, this); + }, + + onLogin: function() { + this.items.get('logout').enable(); + }, + + onLogout: function() { + this.items.get('logout').disable(); + Deluge.Login.logout(); + }, + + onConnectionManagerClick: function() { + Deluge.ConnectionManager.show(); + }, + + onHelpClick: function() { + window.open('http://dev.deluge-torrent.org/wiki/UserGuide'); + }, + + onPreferencesClick: function() { + Deluge.Preferences.show(); + }, + + onTorrentAction: function(item) { + var selection = Deluge.Torrents.getSelections(); + var ids = []; + Ext.each(selection, function(record) { + ids.push(record.id); + }); + + switch (item.id) { + case 'remove': + Deluge.RemoveWindow.show(ids); + break; + case 'pause': + case 'resume': + Deluge.Client.core[item.id + '_torrent'](ids, { + success: function() { + Deluge.UI.update(); + } + }); + break; + case 'up': + case 'down': + Deluge.Client.core['queue_' + item.id](ids, { + success: function() { + Deluge.UI.update(); + } + }); + break; + } + }, + + onTorrentAdd: function() { + Deluge.Add.show(); + } + }); + + Deluge.Toolbar = new Ext.deluge.Toolbar(); +})(); +/* +Script: Deluge.Torrents.js + Contains all objects and functions related to the torrent grid. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. + +*/ + +(function() { + /* Renderers for the Torrent Grid */ + function queueRenderer(value) { + return (value == 99999) ? '' : value + 1; + } + function torrentNameRenderer(value, p, r) { + return String.format('
{1}
', r.data['state'].toLowerCase(), value); + } + function torrentSpeedRenderer(value) { + if (!value) return; + return fspeed(value); + } + function torrentProgressRenderer(value, p, r) { + value = new Number(value); + var progress = value; + var text = r.data['state'] + ' ' + value.toFixed(2) + '%' + //var width = new Number(this.style.match(/\w+:\s*(\d+)\w+/)[1]) - 8; + return Deluge.progressBar(value, this.width - 8, text); + } + function seedsRenderer(value, p, r) { + if (r.data['total_seeds'] > -1) { + return String.format('{0} ({1})', value, r.data['total_seeds']); + } else { + return value; + } + } + function peersRenderer(value, p, r) { + if (r.data['total_peers'] > -1) { + return String.format('{0} ({1})', value, r.data['total_peers']); + } else { + return value; + } + } + function availRenderer(value, p, r) { + return (value < 0) ? '∞' : new Number(value).toFixed(3); + } + function trackerRenderer(value, p, r) { + return String.format('
{0}
', value); + } + + function etaSorter(eta) { + return eta * -1; + } + + /** + * Ext.deluge.TorrentGrid Class + * + * @author Damien Churchill + * @version 1.2 + * + * @class Ext.deluge.TorrentGrid + * @extends Ext.grid.GridPanel + * @constructor + * @param {Object} config Configuration options + */ + Ext.deluge.TorrentGrid = Ext.extend(Ext.grid.GridPanel, { + constructor: function(config) { + config = Ext.apply({ + id: 'torrentGrid', + store: new Ext.data.JsonStore({ + root: 'torrents', + idProperty: 'id', + fields: [ + {name: 'queue'}, + {name: 'name'}, + {name: 'total_size', type: 'int'}, + {name: 'state'}, + {name: 'progress', type: 'float'}, + {name: 'num_seeds', type: 'int'}, + {name: 'total_seeds', type: 'int'}, + {name: 'num_peers', type: 'int'}, + {name: 'total_peers', type: 'int'}, + {name: 'download_payload_rate', type: 'int'}, + {name: 'upload_payload_speed', type: 'int'}, + {name: 'eta', type: 'int', sortType: etaSorter}, + {name: 'ratio', type: 'float'}, + {name: 'distributed_copies', type: 'float'}, + {name: 'time_added', type: 'int'}, + {name: 'tracker_host'} + ] + }), + columns: [{ + id:'queue', + header: _('#'), + width: 30, + sortable: true, + renderer: queueRenderer, + dataIndex: 'queue' + }, { + id:'name', + header: _('Name'), + width: 150, + sortable: true, + renderer: torrentNameRenderer, + dataIndex: 'name' + }, { + header: _('Size'), + width: 75, + sortable: true, + renderer: fsize, + dataIndex: 'total_size' + }, { + header: _('Progress'), + width: 150, + sortable: true, + renderer: torrentProgressRenderer, + dataIndex: 'progress' + }, { + header: _('Seeders'), + width: 60, + sortable: true, + renderer: seedsRenderer, + dataIndex: 'num_seeds' + }, { + header: _('Peers'), + width: 60, + sortable: true, + renderer: peersRenderer, + dataIndex: 'num_peers' + }, { + header: _('Down Speed'), + width: 80, + sortable: true, + renderer: torrentSpeedRenderer, + dataIndex: 'download_payload_rate' + }, { + header: _('Up Speed'), + width: 80, + sortable: true, + renderer: torrentSpeedRenderer, + dataIndex: 'upload_payload_rate' + }, { + header: _('ETA'), + width: 60, + sortable: true, + renderer: ftime, + dataIndex: 'eta' + }, { + header: _('Ratio'), + width: 60, + sortable: true, + renderer: availRenderer, + dataIndex: 'ratio' + }, { + header: _('Avail'), + width: 60, + sortable: true, + renderer: availRenderer, + dataIndex: 'distributed_copies' + }, { + header: _('Added'), + width: 80, + sortable: true, + renderer: fdate, + dataIndex: 'time_added' + }, { + header: _('Tracker'), + width: 120, + sortable: true, + renderer: trackerRenderer, + dataIndex: 'tracker_host' + }], + region: 'center', + cls: 'deluge-torrents', + stripeRows: true, + autoExpandColumn: 'name', + deferredRender:false, + autoScroll:true, + margins: '5 5 0 0', + stateful: true + }, config); + Ext.deluge.TorrentGrid.superclass.constructor.call(this, config); + }, + + initComponent: function() { + Ext.deluge.TorrentGrid.superclass.initComponent.call(this); + Deluge.Events.on('torrentRemoved', this.onTorrentRemoved, this); + Deluge.Events.on('logout', this.onDisconnect, this); + + this.on('rowcontextmenu', function(grid, rowIndex, e) { + e.stopEvent(); + var selection = grid.getSelectionModel(); + if (!selection.hasSelection()) { + selection.selectRow(rowIndex); + } + Deluge.Menus.Torrent.showAt(e.getPoint()); + }); + }, + + /** + * Returns the record representing the torrent at the specified index. + * + * @param index {int} The row index of the torrent you wish to retrieve. + * @return {Ext.data.Record} The record representing the torrent. + */ + getTorrent: function(index) { + return this.getStore().getAt(index); + }, + + getSelected: function() { + return this.getSelectionModel().getSelected(); + }, + + getSelections: function() { + return this.getSelectionModel().getSelections(); + }, + + update: function(torrents, bulk) { + if (bulk) { + this.getStore().loadData({"torrents": Ext.values(torrents)}); + } else { + this.getStore().loadData({"torrents": Ext.values(torrents)}); + } + }, + + onDisconnect: function() { + this.getStore().removeAll(); + }, + + // private + onTorrentRemoved: function(torrentIds) { + var selModel = this.getSelectionModel(); + Ext.each(torrentIds, function(torrentId) { + var record = this.getStore().getById(torrentId); + if (selModel.isSelected(record)) { + selModel.deselectRow(this.getStore().indexOf(record)); + } + this.getStore().remove(record); + }, this); + } +}); +Deluge.Torrents = new Ext.deluge.TorrentGrid(); +})(); +/* +Script: Deluge.UI.js + The core ui module that builds up the ui layout and controls the polling + of the server. + +Copyright: + (C) Damien Churchill 2009 + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, write to: + The Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of portions of this program with the OpenSSL + library. + You must obey the GNU General Public License in all respects for all of + the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete + this exception statement from your version. If you delete this exception + statement from all source files in the program, then also delete it here. +*/ + +/** + * @static + * @class Deluge.UI + * The controller for the whole interface, that ties all the components + * together and handles the 2 second poll. + */ +Deluge.UI = { + + errorCount: 0, + + /** + * @description Create all the interface components, the json-rpc client + * and set up various events that the UI will utilise. + */ + initialize: function() { + this.MainPanel = new Ext.Panel({ + id: 'mainPanel', + iconCls: 'x-deluge-main-panel', + title: 'Deluge', + layout: 'border', + tbar: Deluge.Toolbar, + items: [ + Deluge.Sidebar, + Deluge.Details, + Deluge.Torrents + ], + bbar: Deluge.Statusbar + }); + + this.Viewport = new Ext.Viewport({ + layout: 'fit', + items: [this.MainPanel] + }); + + Deluge.Events.on("connect", this.onConnect, this); + Deluge.Events.on("disconnect", this.onDisconnect, this); + Deluge.Client = new Ext.ux.util.RpcClient({ + url: '/json' + }); + + for (var plugin in Deluge.Plugins) { + plugin = Deluge.Plugins[plugin]; + plugin.enable(); + } + + // Initialize quicktips so all the tooltip configs start working. + Ext.QuickTips.init(); + + Deluge.Client.on('connected', function(e) { + Deluge.Login.show(); + }, this, {single: true}); + + this.update = this.update.bind(this); + }, + + update: function() { + var filters = Deluge.Sidebar.getFilters(); + Deluge.Client.web.update_ui(Deluge.Keys.Grid, filters, { + success: this.onUpdate, + failure: this.onUpdateError, + scope: this + }); + Deluge.Details.update(); + }, + + onUpdateError: function(error) { + if (this.errorCount == 2) { + Ext.MessageBox.show({ + title: 'Lost Connection', + msg: 'The connection to the webserver has been lost!', + buttons: Ext.MessageBox.OK, + icon: Ext.MessageBox.ERROR + }); + } + this.errorCount++; + }, + + /** + * @static + * @private + * Updates the various components in the interface. + */ + onUpdate: function(data) { + if (!data['connected']) Deluge.Events.fire('disconnect'); + Deluge.Torrents.update(data['torrents']); + Deluge.Statusbar.update(data['stats']); + Deluge.Sidebar.update(data['filters']); + this.errorCount = 0; + }, + + /** + * @static + * @private + * Start the Deluge UI polling the server and update the interface. + */ + onConnect: function() { + if (!this.running) { + this.running = setInterval(this.update, 2000); + this.update(); + } + }, + + /** + * @static + * @private + */ + onDisconnect: function() { + this.stop(); + }, + + onPluginEnabled: function(pluginName) { + Deluge.Client.web.get_plugin_resources(pluginName, { + success: this.onGotPluginResources, + scope: this + }) + }, + + onGotPluginResources: function(resources) { + var scripts = (Deluge.debug) ? resources.debug_scripts : resources.scripts; + Ext.each(scripts, function(script) { + Ext.ux.JSLoader({ + url: script, + onLoad: this.onPluginLoaded, + pluginName: resources.name + }); + }, this); + }, + + onPluginDisabled: function(pluginName) { + Deluge.Plugins[pluginName].disable(); + }, + + onPluginLoaded: function(options) { + // This could happen if the plugin has multiple scripts + if (!Deluge.Plugins[options.pluginName]) return; + + // Enable the plugin + Deluge.Plugins[options.pluginName].enable(); + }, + + /** + * @static + * Stop the Deluge UI polling the server and clear the interface. + */ + stop: function() { + if (this.running) { + clearInterval(this.running); + this.running = false; + Deluge.Torrents.getStore().removeAll(); + } + } +} + +Ext.onReady(function(e) { + Deluge.UI.initialize(); +}); diff --git a/deluge/ui/web/js/deluge-all.js b/deluge/ui/web/js/deluge-all.js index 54edd2052..0042c4612 100644 --- a/deluge/ui/web/js/deluge-all.js +++ b/deluge/ui/web/js/deluge-all.js @@ -1 +1 @@ -Ext.namespace("Ext.deluge");Ext.state.Manager.setProvider(new Ext.state.CookieProvider());(function(){Ext.apply(Function.prototype,{bind:function(b){var a=this;return function(){return a.apply(b,arguments)}}});Ext.apply(Ext,{escapeHTML:function(a){a=String(a).replace("<","<").replace(">",">");return a.replace("&","&")},isObjectEmpty:function(b){for(var a in b){return false}return true},keys:function(b){var a=[];for(i in b){if(b.hasOwnProperty(i)){a.push(i)}}return a},splat:function(b){var a=Ext.type(b);return(a)?((a!="array")?[b]:b):[]}});Ext.getKeys=Ext.keys;Ext.BLANK_IMAGE_URL="/images/s.gif"})();(function(){var a='
{0}
{0}
';Deluge.progressBar=function(d,f,h,b){b=Ext.value(b,10);var c=((f/100)*d).toFixed(0);var e=c-1;var g=((c-b)>0?c-b:0);return String.format(a,h,f,e,g)};Deluge.Plugins={}})();FILE_PRIORITY={0:"Do Not Download",1:"Normal Priority",2:"High Priority",5:"Highest Priority","Do Not Download":0,"Normal Priority":1,"High Priority":2,"Highest Priority":5};FILE_PRIORITY_CSS={0:"x-no-download",1:"x-normal-download",2:"x-high-download",5:"x-highest-download"};Deluge.Formatters={date:function(c){function b(d,e){var f=d+"";while(f.length0){return b+"m "+d+"s"}else{return b+"m"}}else{c=c/60}if(c<24){var a=Math.floor(c);var b=Math.round(60*(c-a));if(b>0){return a+"h "+b+"m"}else{return a+"h"}}else{c=c/24}var e=Math.floor(c);var a=Math.round(24*(c-e));if(a>0){return e+"d "+a+"h"}else{return e+"d"}},plain:function(a){return a}};var fsize=Deluge.Formatters.size;var fspeed=Deluge.Formatters.speed;var ftime=Deluge.Formatters.timeRemaining;var fdate=Deluge.Formatters.date;var fplain=Deluge.Formatters.plain;Deluge.Menus={onTorrentAction:function(c,d){var b=Deluge.Torrents.getSelections();var a=[];Ext.each(b,function(e){a.push(e.id)});switch(c.id){case"pause":case"resume":Deluge.Client.core[c.id+"_torrent"](a,{success:function(){Deluge.UI.update()}});break;case"top":case"up":case"down":case"bottom":Deluge.Client.core["queue_"+c.id](a,{success:function(){Deluge.UI.update()}});break;case"edit_trackers":Deluge.EditTrackers.show();break;case"update":Deluge.Client.core.force_reannounce(a,{success:function(){Deluge.UI.update()}});break;case"remove":Deluge.RemoveWindow.show(a);break;case"recheck":Deluge.Client.core.force_recheck(a,{success:function(){Deluge.UI.update()}});break;case"move":Deluge.MoveStorage.show(a);break}}};Deluge.Menus.Torrent=new Ext.menu.Menu({id:"torrentMenu",items:[{id:"pause",text:_("Pause"),icon:"/icons/pause.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"resume",text:_("Resume"),icon:"/icons/start.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},"-",{id:"options",text:_("Options"),icon:"/icons/preferences.png",menu:new Ext.menu.Menu({items:[{text:_("D/L Speed Limit"),iconCls:"x-deluge-downloading",menu:new Ext.menu.Menu({items:[{text:_("5 KiB/s")},{text:_("10 KiB/s")},{text:_("30 KiB/s")},{text:_("80 KiB/s")},{text:_("300 KiB/s")},{text:_("Unlimited")}]})},{text:_("U/L Speed Limit"),iconCls:"x-deluge-seeding",menu:new Ext.menu.Menu({items:[{text:_("5 KiB/s")},{text:_("10 KiB/s")},{text:_("30 KiB/s")},{text:_("80 KiB/s")},{text:_("300 KiB/s")},{text:_("Unlimited")}]})},{text:_("Connection Limit"),iconCls:"x-deluge-connections",menu:new Ext.menu.Menu({items:[{text:_("50")},{text:_("100")},{text:_("200")},{text:_("300")},{text:_("500")},{text:_("Unlimited")}]})},{text:_("Upload Slot Limit"),icon:"/icons/upload_slots.png",menu:new Ext.menu.Menu({items:[{text:_("0")},{text:_("1")},{text:_("2")},{text:_("3")},{text:_("5")},{text:_("Unlimited")}]})},{id:"auto_managed",text:_("Auto Managed"),checked:false}]})},"-",{text:_("Queue"),icon:"/icons/queue.png",menu:new Ext.menu.Menu({items:[{id:"top",text:_("Top"),icon:"/icons/top.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"up",text:_("Up"),icon:"/icons/up.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"down",text:_("Down"),icon:"/icons/down.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"bottom",text:_("Bottom"),icon:"/icons/bottom.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus}]})},"-",{id:"update",text:_("Update Tracker"),icon:"/icons/update.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"edit_trackers",text:_("Edit Trackers"),icon:"/icons/edit_trackers.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},"-",{id:"remove",text:_("Remove Torrent"),icon:"/icons/remove.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},"-",{id:"recheck",text:_("Force Recheck"),icon:"/icons/recheck.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"move",text:_("Move Storage"),icon:"/icons/move.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus}]});Ext.deluge.StatusbarMenu=Ext.extend(Ext.menu.Menu,{setValue:function(b){b=(b==0)?-1:b;var a=this.items.get(b);if(!a){a=this.items.get("other")}a.suspendEvents();a.setChecked(true);a.resumeEvents()}});Deluge.Menus.Connections=new Ext.deluge.StatusbarMenu({id:"connectionsMenu",items:[{id:"50",text:"50",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"100",text:"100",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"200",text:"200",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"300",text:"300",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"500",text:"500",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"-1",text:_("Unlimited"),group:"max_connections_global",checked:false,checkHandler:onLimitChanged},"-",{id:"other",text:_("Other"),group:"max_connections_global",checked:false,checkHandler:onLimitChanged}]});Deluge.Menus.Download=new Ext.deluge.StatusbarMenu({id:"downspeedMenu",items:[{id:"5",text:"5 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"10",text:"10 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"30",text:"30 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"80",text:"80 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"300",text:"300 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"-1",text:_("Unlimited"),group:"max_download_speed",checked:false,checkHandler:onLimitChanged},"-",{id:"other",text:_("Other"),group:"max_download_speed",checked:false,checkHandler:onLimitChanged}]});Deluge.Menus.Upload=new Ext.deluge.StatusbarMenu({id:"upspeedMenu",items:[{id:"5",text:"5 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"10",text:"10 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"30",text:"30 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"80",text:"80 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"300",text:"300 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"-1",text:_("Unlimited"),group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},"-",{id:"other",text:_("Other"),group:"max_upload_speed",checked:false,checkHandler:onLimitChanged}]});Deluge.Menus.FilePriorities=new Ext.menu.Menu({id:"filePrioritiesMenu",items:[{id:"expandAll",text:_("Expand All"),icon:"/icons/expand_all.png"},"-",{id:"no_download",text:_("Do Not Download"),icon:"/icons/no_download.png",filePriority:0},{id:"normal",text:_("Normal Priority"),icon:"/icons/normal.png",filePriority:1},{id:"high",text:_("High Priority"),icon:"/icons/high.png",filePriority:2},{id:"highest",text:_("Highest Priority"),icon:"/icons/highest.png",filePriority:5}]});function onLimitChanged(b,a){if(b.id=="other"){}else{config={};config[b.group]=b.id;Deluge.Client.core.set_config(config,{success:function(){Deluge.UI.update()}})}}(function(){Events=Ext.extend(Ext.util.Observable,{constructor:function(){this.toRegister=[];Events.superclass.constructor.call(this)},addListener:function(a,c,b,d){this.addEvents(a);if(/[A-Z]/.test(a.substring(0,1))){if(!Deluge.Client){this.toRegister.push(a)}else{Deluge.Client.web.register_event_listener(a)}}Events.superclass.addListener.call(this,a,c,b,d)},poll:function(){Deluge.Client.web.get_events({success:this.onPollSuccess,scope:this})},start:function(){Ext.each(this.toRegister,function(a){Deluge.Client.web.register_event_listener(a)});this.poll=this.poll.bind(this);this.running=setInterval(this.poll,2000);this.poll()},stop:function(){if(this.running){clearInterval(this.running)}},onPollSuccess:function(a){if(!a){return}Ext.each(a,function(d){var c=d[0],b=d[1];b.splice(0,0,c);this.fireEvent.apply(this,b)},this)}});Events.prototype.on=Events.prototype.addListener;Events.prototype.fire=Events.prototype.fireEvent;Deluge.Events=new Events()})();Ext.namespace("Deluge");Deluge.OptionsManager=Ext.extend(Ext.util.Observable,{constructor:function(a){a=a||{};this.binds={};this.changed={};this.options=(a&&a.options)||{};this.focused=null;this.addEvents({add:true,changed:true,reset:true});this.on("changed",this.onChange,this);Deluge.OptionsManager.superclass.constructor.call(this)},addOptions:function(a){this.options=Ext.applyIf(this.options,a)},bind:function(a,b){this.binds[a]=this.binds[a]||[];this.binds[a].push(b);b._doption=a;b.on("focus",this.onFieldFocus,this);b.on("blur",this.onFieldBlur,this);b.on("change",this.onFieldChange,this);b.on("check",this.onFieldChange,this);return b},commit:function(){this.options=Ext.apply(this.options,this.changed);this.reset()},convertValueType:function(a,b){if(Ext.type(a)!=Ext.type(b)){switch(Ext.type(a)){case"string":b=String(b);break;case"number":b=Number(b);break;case"boolean":if(Ext.type(b)=="string"){b=b.toLowerCase();b=(b=="true"||b=="1"||b=="on")?true:false}else{b=Boolean(b)}break}}return b},get:function(){if(arguments.length==1){var b=arguments[0];return(this.isDirty(b))?this.changed[b]:this.options[b]}else{var a={};Ext.each(arguments,function(c){if(!this.has(c)){return}a[c]=(this.isDirty(c))?this.changed[c]:this.options[c]},this);return a}},getDefault:function(a){return this.options[a]},getDirty:function(){return this.changed},isDirty:function(a){return !Ext.isEmpty(this.changed[a])},has:function(a){return(this.options[a])},reset:function(){this.changed={}},set:function(b,c){if(b===undefined){return}else{if(typeof b=="object"){var a=b;this.options=Ext.apply(this.options,a);for(var b in a){this.onChange(b,a[b])}}else{this.options[b]=c;this.onChange(b,c)}}},update:function(d,e){if(d===undefined){return}else{if(e===undefined){for(var c in d){this.update(c,d[c])}}else{var a=this.getDefault(d);e=this.convertValueType(a,e);var b=this.get(d);if(b==e){return}if(a==e){if(this.isDirty(d)){delete this.changed[d]}this.fireEvent("changed",d,e,b);return}this.changed[d]=e;this.fireEvent("changed",d,e,b)}}},onFieldBlur:function(b,a){if(this.focused==b){this.focused=null}},onFieldChange:function(b,a){this.update(b._doption,b.getValue())},onFieldFocus:function(b,a){this.focused=b},onChange:function(b,c,a){if(Ext.isEmpty(this.binds[b])){return}Ext.each(this.binds[b],function(d){if(d==this.focused){return}d.setValue(c)},this)}});Deluge.MultiOptionsManager=Ext.extend(Deluge.OptionsManager,{constructor:function(a){this.currentId=null;this.stored={};Deluge.MultiOptionsManager.superclass.constructor.call(this,a)},changeId:function(d,b){var c=this.currentId;this.currentId=d;if(!b){for(var a in this.options){if(!this.binds[a]){continue}Ext.each(this.binds[a],function(e){e.setValue(this.get(a))},this)}}return c},commit:function(){this.stored[this.currentId]=Ext.apply(this.stored[this.currentId],this.changed[this.currentId]);this.reset()},get:function(){if(arguments.length==1){var b=arguments[0];return(this.isDirty(b))?this.changed[this.currentId][b]:this.getDefault(b)}else{if(arguments.length==0){var a={};for(var b in this.options){a[b]=(this.isDirty(b))?this.changed[this.currentId][b]:this.getDefault(b)}return a}else{var a={};Ext.each(arguments,function(c){a[c]=(this.isDirty(c))?this.changed[this.currentId][c]:this.getDefault(c)},this);return a}}},getDefault:function(a){return(this.has(a))?this.stored[this.currentId][a]:this.options[a]},getDirty:function(){return(this.changed[this.currentId])?this.changed[this.currentId]:{}},isDirty:function(a){return(this.changed[this.currentId]&&!Ext.isEmpty(this.changed[this.currentId][a]))},has:function(a){return(this.stored[this.currentId]&&!Ext.isEmpty(this.stored[this.currentId][a]))},reset:function(){if(this.changed[this.currentId]){delete this.changed[this.currentId]}if(this.stored[this.currentId]){delete this.stored[this.currentId]}},resetAll:function(){this.changed={};this.stored={};this.changeId(null)},setDefault:function(c,d){if(c===undefined){return}else{if(d===undefined){for(var b in c){this.setDefault(b,c[b])}}else{var a=this.getDefault(c);d=this.convertValueType(a,d);if(a==d){return}if(!this.stored[this.currentId]){this.stored[this.currentId]={}}this.stored[this.currentId][c]=d;if(!this.isDirty(c)){this.fireEvent("changed",this.currentId,c,d,a)}}}},update:function(d,e){if(d===undefined){return}else{if(e===undefined){for(var c in d){this.update(c,d[c])}}else{if(!this.changed[this.currentId]){this.changed[this.currentId]={}}var a=this.getDefault(d);e=this.convertValueType(a,e);var b=this.get(d);if(b==e){return}if(a==e){if(this.isDirty(d)){delete this.changed[this.currentId][d]}this.fireEvent("changed",this.currentId,d,e,b);return}else{this.changed[this.currentId][d]=e;this.fireEvent("changed",this.currentId,d,e,b)}}}},onFieldChange:function(b,a){this.update(b._doption,b.getValue())},onChange:function(d,b,c,a){if(Ext.isEmpty(this.binds[b])){return}Ext.each(this.binds[b],function(e){if(e==this.focused){return}e.setValue(c)},this)}});Ext.namespace("Ext.deluge.add");Ext.deluge.add.OptionsPanel=Ext.extend(Ext.TabPanel,{torrents:{},constructor:function(a){a=Ext.apply({region:"south",margins:"5 5 5 5",activeTab:0,height:220},a);Ext.deluge.add.OptionsPanel.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.OptionsPanel.superclass.initComponent.call(this);this.files=this.add(new Ext.tree.ColumnTree({layout:"fit",title:_("Files"),rootVisible:false,autoScroll:true,height:170,border:false,animate:false,disabled:true,columns:[{header:_("Filename"),width:275,dataIndex:"filename"},{header:_("Size"),width:80,dataIndex:"size"}],root:new Ext.tree.AsyncTreeNode({text:"Files"})}));new Ext.tree.TreeSorter(this.files,{folderSort:true});this.optionsManager=new Deluge.MultiOptionsManager();this.form=this.add({xtype:"form",labelWidth:1,title:_("Options"),bodyStyle:"padding: 5px;",border:false,height:170,disabled:true});var a=this.form.add({xtype:"fieldset",title:_("Download Location"),border:false,autoHeight:true,defaultType:"textfield",labelWidth:1,fieldLabel:""});this.optionsManager.bind("download_location",a.add({fieldLabel:"",name:"download_location",width:400,labelSeparator:""}));var b=this.form.add({border:false,layout:"column",defaultType:"fieldset"});a=b.add({title:_("Allocation"),border:false,autoHeight:true,defaultType:"radio",width:100});this.optionsManager.bind("compact_allocation",a.add({xtype:"radiogroup",columns:1,vertical:true,labelSeparator:"",items:[{name:"compact_allocation",value:false,inputValue:false,boxLabel:_("Full"),fieldLabel:"",labelSeparator:""},{name:"compact_allocation",value:true,inputValue:true,boxLabel:_("Compact"),fieldLabel:"",labelSeparator:"",}]}));a=b.add({title:_("Bandwidth"),border:false,autoHeight:true,labelWidth:100,width:200,defaultType:"uxspinner"});this.optionsManager.bind("max_download_speed",a.add({fieldLabel:_("Max Down Speed"),name:"max_download_speed",width:60}));this.optionsManager.bind("max_upload_speed",a.add({fieldLabel:_("Max Up Speed"),name:"max_upload_speed",width:60}));this.optionsManager.bind("max_connections",a.add({fieldLabel:_("Max Connections"),name:"max_connections",width:60}));this.optionsManager.bind("max_upload_slots",a.add({fieldLabel:_("Max Upload Slots"),name:"max_upload_slots",width:60}));a=b.add({title:_("General"),border:false,autoHeight:true,defaultType:"checkbox"});this.optionsManager.bind("add_paused",a.add({name:"add_paused",boxLabel:_("Add In Paused State"),fieldLabel:"",labelSeparator:"",}));this.optionsManager.bind("prioritize_first_last_pieces",a.add({name:"prioritize_first_last_pieces",boxLabel:_("Prioritize First/Last Pieces"),fieldLabel:"",labelSeparator:"",}));this.form.on("render",this.onFormRender,this)},onFormRender:function(a){a.layout=new Ext.layout.FormLayout();a.layout.setContainer(a);a.doLayout()},addTorrent:function(c){this.torrents[c.info_hash]=c;var b={};this.walkFileTree(c.files_tree,function(e,g,h,f){if(g!="file"){return}b[h[0]]=h[2]},this);var a=[];Ext.each(Ext.keys(b),function(e){a[e]=b[e]});var d=this.optionsManager.changeId(c.info_hash,true);this.optionsManager.setDefault("file_priorities",a);this.optionsManager.changeId(d,true)},clear:function(){this.clearFiles();this.optionsManager.resetAll()},clearFiles:function(){var a=this.files.getRootNode();if(!a.hasChildNodes()){return}a.cascade(function(b){if(!b.parentNode||!b.getOwnerTree()){return}b.remove()})},getDefaults:function(){var a=["add_paused","compact_allocation","download_location","max_connections_per_torrent","max_download_speed_per_torrent","max_upload_slots_per_torrent","max_upload_speed_per_torrent","prioritize_first_last_pieces"];Deluge.Client.core.get_config_values(a,{success:function(c){var b={file_priorities:[],add_paused:c.add_paused,compact_allocation:c.compact_allocation,download_location:c.download_location,max_connections:c.max_connections_per_torrent,max_download_speed:c.max_download_speed_per_torrent,max_upload_slots:c.max_upload_slots_per_torrent,max_upload_speed:c.max_upload_speed_per_torrent,prioritize_first_last_pieces:c.prioritize_first_last_pieces};this.optionsManager.options=b;this.optionsManager.resetAll()},scope:this})},getFilename:function(a){return this.torrents[a]["filename"]},getOptions:function(a){var c=this.optionsManager.changeId(a,true);var b=this.optionsManager.get();this.optionsManager.changeId(c,true);Ext.each(b.file_priorities,function(e,d){b.file_priorities[d]=(e)?1:0});return b},setTorrent:function(b){if(!b){return}this.torrentId=b;this.optionsManager.changeId(b);this.clearFiles();var a=this.files.getRootNode();var c=this.optionsManager.get("file_priorities");this.walkFileTree(this.torrents[b]["files_tree"],function(d,f,j,e){if(f=="dir"){var h=new Ext.tree.TreeNode({text:d,checked:true});h.on("checkchange",this.onFolderCheck,this);e.appendChild(h);return h}else{var g=new Ext.tree.TreeNode({filename:d,fileindex:j[0],text:d,size:fsize(j[1]),leaf:true,checked:c[j[0]],iconCls:"x-deluge-file",uiProvider:Ext.tree.ColumnNodeUI});g.on("checkchange",this.onNodeCheck,this);e.appendChild(g)}},this,a);a.firstChild.expand()},walkFileTree:function(g,h,e,d){for(var a in g){var f=g[a];var c=(Ext.type(f)=="object")?"dir":"file";if(e){var b=h.apply(e,[a,c,f,d])}else{var b=h(a,c,f,d)}if(c=="dir"){this.walkFileTree(f,h,e,b)}}},onFolderCheck:function(c,b){var a=this.optionsManager.get("file_priorities");c.cascade(function(d){if(!d.ui.checkbox){d.attributes.checked=b}else{d.ui.checkbox.checked=b}a[d.attributes.fileindex]=b},this);this.optionsManager.setDefault("file_priorities",a)},onNodeCheck:function(c,b){var a=this.optionsManager.get("file_priorities");a[c.attributes.fileindex]=b;this.optionsManager.update("file_priorities",a)}});Ext.deluge.add.Window=Ext.extend(Ext.Window,{initComponent:function(){Ext.deluge.add.Window.superclass.initComponent.call(this);this.addEvents("beforeadd","add")},createTorrentId:function(){return new Date().getTime()}});Ext.deluge.add.AddWindow=Ext.extend(Ext.deluge.add.Window,{constructor:function(a){a=Ext.apply({title:_("Add Torrents"),layout:"border",width:470,height:450,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:true,plain:true,iconCls:"x-deluge-add-window-icon"},a);Ext.deluge.add.AddWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.AddWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Add"),this.onAdd,this);function a(c,d,b){if(b.data.info_hash){return String.format('
{0}
',c)}else{return String.format('
{0}
',c)}}this.grid=this.add({xtype:"grid",region:"center",store:new Ext.data.SimpleStore({fields:[{name:"info_hash",mapping:1},{name:"text",mapping:2}],id:0}),columns:[{id:"torrent",width:150,sortable:true,renderer:a,dataIndex:"text"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onSelect,scope:this}}}),hideHeaders:true,autoExpandColumn:"torrent",deferredRender:false,autoScroll:true,margins:"5 5 5 5",bbar:new Ext.Toolbar({items:[{id:"file",cls:"x-btn-text-icon",iconCls:"x-deluge-add-file",text:_("File"),handler:this.onFile,scope:this},{id:"url",cls:"x-btn-text-icon",text:_("Url"),icon:"/icons/add_url.png",handler:this.onUrl,scope:this},{id:"infohash",cls:"x-btn-text-icon",text:_("Infohash"),icon:"/icons/add_magnet.png",disabled:true},"->",{id:"remove",cls:"x-btn-text-icon",text:_("Remove"),icon:"/icons/remove.png",handler:this.onRemove,scope:this}]})});this.optionsPanel=this.add(new Ext.deluge.add.OptionsPanel());this.on("hide",this.onHide,this);this.on("show",this.onShow,this)},clear:function(){this.grid.getStore().removeAll();this.optionsPanel.clear()},onAdd:function(){var a=[];this.grid.getStore().each(function(b){var c=b.get("info_hash");a.push({path:this.optionsPanel.getFilename(c),options:this.optionsPanel.getOptions(c)})},this);Deluge.Client.web.add_torrents(a,{success:function(b){}});this.clear();this.hide()},onCancel:function(){this.clear();this.hide()},onFile:function(){this.file.show()},onHide:function(){this.optionsPanel.setActiveTab(0);this.optionsPanel.files.setDisabled(true);this.optionsPanel.form.setDisabled(true)},onRemove:function(){var a=this.grid.getSelectionModel();if(!a.hasSelection()){return}var b=a.getSelected();this.grid.getStore().remove(b);this.optionsPanel.clear();if(this.torrents&&this.torrents[b.id]){delete this.torrents[b.id]}},onSelect:function(b,c,a){this.optionsPanel.setTorrent(a.get("info_hash"));this.optionsPanel.files.setDisabled(false);this.optionsPanel.form.setDisabled(false)},onShow:function(){if(!this.url){this.url=new Ext.deluge.add.UrlWindow();this.url.on("beforeadd",this.onTorrentBeforeAdd,this);this.url.on("add",this.onTorrentAdd,this)}if(!this.file){this.file=new Ext.deluge.add.FileWindow();this.file.on("beforeadd",this.onTorrentBeforeAdd,this);this.file.on("add",this.onTorrentAdd,this)}this.optionsPanel.getDefaults()},onTorrentBeforeAdd:function(b,c){var a=this.grid.getStore();a.loadData([[b,null,c]],true)},onTorrentAdd:function(a,c){if(!c){Ext.MessageBox.show({title:_("Error"),msg:_("Not a valid torrent"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});return}var b=this.grid.getStore().getById(a);b.set("info_hash",c.info_hash);b.set("text",c.name);this.grid.getStore().commitChanges();this.optionsPanel.addTorrent(c)},onUrl:function(a,b){this.url.show()}});Deluge.Add=new Ext.deluge.add.AddWindow();Ext.namespace("Ext.deluge.add");Ext.deluge.add.FileWindow=Ext.extend(Ext.deluge.add.Window,{constructor:function(a){a=Ext.apply({layout:"fit",width:350,height:115,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",modal:true,plain:true,title:_("Add from File"),iconCls:"x-deluge-add-file"},a);Ext.deluge.add.FileWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.FileWindow.superclass.initComponent.call(this);this.addButton(_("Add"),this.onAdd,this);this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:55,autoHeight:true,fileUpload:true,items:[{xtype:"fileuploadfield",id:"torrentFile",emptyText:_("Select a torrent"),fieldLabel:_("File"),name:"file",buttonCfg:{text:_("Browse")+"..."}}]})},onAdd:function(c,b){if(this.form.getForm().isValid()){this.torrentId=this.createTorrentId();this.form.getForm().submit({url:"/upload",waitMsg:_("Uploading your torrent..."),success:this.onUploadSuccess,scope:this});var a=this.form.getForm().findField("torrentFile").value;this.fireEvent("beforeadd",this.torrentId,a)}},onGotInfo:function(d,c,a,b){d.filename=b.options.filename;this.fireEvent("add",this.torrentId,d)},onUploadSuccess:function(c,b){this.hide();if(b.result.success){var a=b.result.files[0];this.form.getForm().findField("torrentFile").setValue("");Deluge.Client.web.get_torrent_info(a,{success:this.onGotInfo,scope:this,filename:a})}}});Ext.namespace("Ext.deluge.add");Ext.deluge.add.UrlWindow=Ext.extend(Ext.deluge.add.Window,{constructor:function(a){a=Ext.apply({layout:"fit",width:350,height:155,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",modal:true,plain:true,title:_("Add from Url"),iconCls:"x-deluge-add-url-window-icon"},a);Ext.deluge.add.UrlWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.UrlWindow.superclass.initComponent.call(this);this.addButton(_("Add"),this.onAdd,this);var a=this.add({xtype:"form",defaultType:"textfield",baseCls:"x-plain",labelWidth:55});this.urlField=a.add({fieldLabel:_("Url"),id:"url",name:"url",anchor:"100%"});this.urlField.on("specialkey",this.onAdd,this);this.cookieField=a.add({fieldLabel:_("Cookies"),id:"cookies",name:"cookies",anchor:"100%"});this.cookieField.on("specialkey",this.onAdd,this)},onAdd:function(f,d){if((f.id=="url"||f.id=="cookies")&&d.getKey()!=d.ENTER){return}var f=this.urlField;var b=f.getValue();var c=this.cookieField.getValue();var a=this.createTorrentId();Deluge.Client.web.download_torrent_from_url(b,c,{success:this.onDownload,scope:this,torrentId:a});this.hide();this.fireEvent("beforeadd",a,b)},onDownload:function(a,c,d,b){this.urlField.setValue("");Deluge.Client.web.get_torrent_info(a,{success:this.onGotInfo,scope:this,filename:a,torrentId:b.options.torrentId})},onGotInfo:function(d,c,a,b){d.filename=b.options.filename;this.fireEvent("add",b.options.torrentId,d)}});Ext.namespace("Ext.ux.util");(function(){Ext.ux.util.RpcClient=Ext.extend(Ext.util.Observable,{_components:[],_methods:[],_requests:{},_url:null,_optionKeys:["scope","success","failure"],constructor:function(a){Ext.ux.util.RpcClient.superclass.constructor.call(this,a);this._url=a.url||null;this._id=0;this.addEvents("connected","error");this.reloadMethods()},reloadMethods:function(){Ext.each(this._components,function(a){delete this[a]},this);this._execute("system.listMethods",{success:this._setMethods,scope:this})},_execute:function(c,a){a=a||{};a.params=a.params||[];a.id=this._id;var b=Ext.encode({method:c,params:a.params,id:a.id});this._id++;return Ext.Ajax.request({url:this._url,method:"POST",success:this._onSuccess,failure:this._onFailure,scope:this,jsonData:b,options:a})},_onFailure:function(b,a){var c=a.options;errorObj={id:c.id,result:null,error:{msg:"HTTP: "+b.status+" "+b.statusText,code:255}};this.fireEvent("error",errorObj,b,a);if(Ext.type(c.failure)!="function"){return}if(c.scope){c.failure.call(c.scope,errorObj,b,a)}else{c.failure(errorObj,b,a)}},_onSuccess:function(c,a){var b=Ext.decode(c.responseText);var d=a.options;if(b.error){this.fireEvent("error",b,c,a);if(Ext.type(d.failure)!="function"){return}if(d.scope){d.failure.call(d.scope,b,c,a)}else{d.failure(b,c,a)}}else{if(Ext.type(d.success)!="function"){return}if(d.scope){d.success.call(d.scope,b.result,b,c,a)}else{d.success(b.result,b,c,a)}}},_parseArgs:function(c){var e=[];Ext.each(c,function(f){e.push(f)});var b=e[e.length-1];if(Ext.type(b)=="object"){var d=Ext.keys(b),a=false;Ext.each(this._optionKeys,function(f){if(d.indexOf(f)>-1){a=true}});if(a){e.remove(b)}else{b={}}}else{b={}}b.params=e;return b},_setMethods:function(b){var d={},a=this;Ext.each(b,function(h){var g=h.split(".");var e=d[g[0]]||{};var f=function(){var j=a._parseArgs(arguments);return a._execute(h,j)};e[g[1]]=f;d[g[0]]=e});for(var c in d){a[c]=d[c]}this._components=Ext.keys(d);this.fireEvent("connected",this)}})})();(function(){var a=function(c,d,b){return c+":"+b.data.port};Ext.deluge.AddConnectionWindow=Ext.extend(Ext.Window,{constructor:function(b){b=Ext.apply({layout:"fit",width:300,height:195,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:true,plain:true,title:_("Add Connection"),iconCls:"x-deluge-add-window-icon"},b);Ext.deluge.AddConnectionWindow.superclass.constructor.call(this,b)},initComponent:function(){Ext.deluge.AddConnectionWindow.superclass.initComponent.call(this);this.addEvents("hostadded");this.addButton(_("Close"),this.hide,this);this.addButton(_("Add"),this.onAdd,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form",defaultType:"textfield",id:"connectionAddForm",baseCls:"x-plain",labelWidth:55});this.hostField=this.form.add({fieldLabel:_("Host"),id:"host",name:"host",anchor:"100%",value:""});this.portField=this.form.add({fieldLabel:_("Port"),id:"port",xtype:"uxspinner",ctCls:"x-form-uxspinner",name:"port",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:65535},value:"58846",anchor:"50%"});this.usernameField=this.form.add({fieldLabel:_("Username"),id:"username",name:"username",anchor:"100%",value:""});this.passwordField=this.form.add({fieldLabel:_("Password"),anchor:"100%",id:"_password",name:"_password",inputType:"password",value:""})},onAdd:function(){var d=this.hostField.getValue();var b=this.portField.getValue();var e=this.usernameField.getValue();var c=this.passwordField.getValue();Deluge.Client.web.add_host(d,b,e,c,{success:function(f){if(!f[0]){Ext.MessageBox.show({title:_("Error"),msg:"Unable to add host: "+f[1],buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}else{this.fireEvent("hostadded")}this.hide()},scope:this})},onHide:function(){this.form.getForm().reset()}});Ext.deluge.ConnectionManager=Ext.extend(Ext.Window,{layout:"fit",width:300,height:220,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:true,plain:true,title:_("Connection Manager"),iconCls:"x-deluge-connect-window-icon",initComponent:function(){Ext.deluge.ConnectionManager.superclass.initComponent.call(this);this.on({hide:this.onHide,show:this.onShow});Deluge.Events.on("login",this.onLogin,this);Deluge.Events.on("logout",this.onLogout,this);this.addButton(_("Close"),this.onClose,this);this.addButton(_("Connect"),this.onConnect,this);this.grid=this.add({xtype:"grid",store:new Ext.data.SimpleStore({fields:[{name:"status",mapping:3},{name:"host",mapping:1},{name:"port",mapping:2},{name:"version",mapping:4}],id:0}),columns:[{header:_("Status"),width:65,sortable:true,renderer:fplain,dataIndex:"status"},{id:"host",header:_("Host"),width:150,sortable:true,renderer:a,dataIndex:"host"},{header:_("Version"),width:75,sortable:true,renderer:fplain,dataIndex:"version"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onSelect,scope:this},selectionchange:{fn:this.onSelectionChanged,scope:this}}}),autoExpandColumn:"host",deferredRender:false,autoScroll:true,margins:"0 0 0 0",bbar:new Ext.Toolbar({buttons:[{id:"cm-add",cls:"x-btn-text-icon",text:_("Add"),icon:"/icons/add.png",handler:this.onAdd,scope:this},{id:"cm-remove",cls:"x-btn-text-icon",text:_("Remove"),icon:"/icons/remove.png",handler:this.onRemove,disabled:true,scope:this},"->",{id:"cm-stop",cls:"x-btn-text-icon",text:_("Stop Daemon"),icon:"/icons/error.png",handler:this.onStop,disabled:true,scope:this}]})})},disconnect:function(){Deluge.Events.fire("disconnect")},loadHosts:function(){Deluge.Client.web.get_hosts({success:this.onGetHosts,scope:this})},update:function(b){b.grid.getStore().each(function(c){Deluge.Client.web.get_host_status(c.id,{success:b.onGetHostStatus,scope:b})},this)},updateButtons:function(c){var d=this.buttons[1],b=c.get("status");if(b==_("Connected")){d.enable();d.setText(_("Disconnect"))}else{if(b==_("Offline")){d.disable()}else{d.enable();d.setText(_("Connect"))}}if(b==_("Offline")){if(c.get("host")=="127.0.0.1"||c.get("host")=="localhost"){this.stopHostButton.enable();this.stopHostButton.setText(_("Start Daemon"))}else{this.stopHostButton.disable()}}else{this.stopHostButton.enable();this.stopHostButton.setText(_("Stop Daemon"))}},onAdd:function(b,c){if(!this.addWindow){this.addWindow=new Ext.deluge.AddConnectionWindow();this.addWindow.on("hostadded",this.onHostAdded,this)}this.addWindow.show()},onHostAdded:function(){this.loadHosts()},onClose:function(b){if(this.running){window.clearInterval(this.running)}this.hide()},onConnect:function(c){var b=this.grid.getSelectionModel().getSelected();if(!b){return}if(b.get("status")==_("Connected")){Deluge.Client.web.disconnect({success:function(e){this.update(this);Deluge.Events.fire("disconnect")},scope:this})}else{var d=b.id;Deluge.Client.web.connect(d,{success:function(e){Deluge.Client.reloadMethods();Deluge.Client.on("connected",function(f){Deluge.Events.fire("connect")},this,{single:true})}});if(this.running){window.clearInterval(this.running)}this.hide()}},onGetHosts:function(b){this.grid.getStore().loadData(b);Ext.each(b,function(c){Deluge.Client.web.get_host_status(c[0],{success:this.onGetHostStatus,scope:this})},this)},onGetHostStatus:function(c){var b=this.grid.getStore().getById(c[0]);b.set("status",c[3]);b.set("version",c[4]);b.commit();if(this.grid.getSelectionModel().getSelected()==b){this.updateButtons(b)}},onLogin:function(){Deluge.Client.web.connected({success:function(b){if(b){Deluge.Events.fire("connect")}else{this.show()}},scope:this})},onLogout:function(){this.disconnect();if(!this.hidden&&this.rendered){this.hide()}},onRemove:function(c){var b=this.grid.getSelectionModel().getSelected();if(!b){return}Deluge.Client.web.remove_host(b.id,{success:function(d){if(!d){Ext.MessageBox.show({title:_("Error"),msg:d[1],buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}else{this.grid.getStore().remove(b)}},scope:this})},onSelect:function(c,d,b){this.selectedRow=d},onSelectionChanged:function(c){var b=c.getSelected();if(c.hasSelection()){this.removeHostButton.enable();this.stopHostButton.enable();this.stopHostButton.setText(_("Stop Daemon"))}else{this.removeHostButton.disable();this.stopHostButton.disable()}this.updateButtons(b)},onShow:function(){if(!this.addHostButton){var b=this.grid.getBottomToolbar();this.addHostButton=b.items.get("cm-add");this.removeHostButton=b.items.get("cm-remove");this.stopHostButton=b.items.get("cm-stop")}this.loadHosts();this.running=window.setInterval(this.update,2000,this)},onStop:function(c,d){var b=this.grid.getSelectionModel().getSelected();if(!b){return}if(b.get("status")=="Offline"){Deluge.Client.web.start_daemon(b.get("port"))}else{Deluge.Client.web.stop_daemon(b.id,{success:function(e){if(!e[0]){Ext.MessageBox.show({title:_("Error"),msg:e[1],buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}}})}}});Deluge.ConnectionManager=new Ext.deluge.ConnectionManager()})();(function(){Ext.namespace("Ext.deluge.details");Ext.deluge.details.TabPanel=Ext.extend(Ext.TabPanel,{constructor:function(a){a=Ext.apply({region:"south",id:"torrentDetails",split:true,height:220,minSize:100,collapsible:true,margins:"0 5 5 5",activeTab:0},a);Ext.deluge.details.TabPanel.superclass.constructor.call(this,a)},clear:function(){this.items.each(function(a){if(a.clear){a.clear.defer(100,a);a.disable()}})},update:function(a){var b=Deluge.Torrents.getSelected();if(!b){this.clear();return}this.items.each(function(c){if(c.disabled){c.enable()}});a=a||this.getActiveTab();if(a.update){a.update(b.id)}},onRender:function(b,a){Ext.deluge.details.TabPanel.superclass.onRender.call(this,b,a);Deluge.Events.on("disconnect",this.clear,this);Deluge.Torrents.on("rowclick",this.onTorrentsClick,this);this.on("tabchange",this.onTabChange,this);Deluge.Torrents.getSelectionModel().on("selectionchange",function(c){if(!c.hasSelection()){this.clear()}},this)},onTabChange:function(a,b){this.update(b)},onTorrentsClick:function(a,c,b){this.update()}});Deluge.Details=new Ext.deluge.details.TabPanel()})();Ext.deluge.details.StatusTab=Ext.extend(Ext.Panel,{title:_("Status"),autoScroll:true,onRender:function(b,a){Ext.deluge.details.StatusTab.superclass.onRender.call(this,b,a);this.progressBar=this.add({xtype:"fullprogressbar",cls:"x-deluge-status-progressbar"});this.status=this.add({cls:"x-deluge-status",id:"deluge-details-status",border:false,width:1000,listeners:{render:{fn:function(c){c.load({url:"/render/tab_status.html",text:_("Loading")+"..."});c.getUpdater().on("update",this.onPanelUpdate,this)},scope:this}}})},clear:function(){this.progressBar.updateProgress(0," ");for(var a in this.fields){this.fields[a].innerHTML=""}},update:function(a){if(!this.fields){this.getFields()}Deluge.Client.core.get_torrent_status(a,Deluge.Keys.Status,{success:this.onRequestComplete,scope:this})},onPanelUpdate:function(b,a){this.fields={};Ext.each(Ext.query("dd",this.status.body.dom),function(c){this.fields[c.className]=c},this)},onRequestComplete:function(a){seeders=a.total_seeds>-1?a.num_seeds+" ("+a.total_seeds+")":a.num_seeds;peers=a.total_peers>-1?a.num_peers+" ("+a.total_peers+")":a.num_peers;var b={downloaded:fsize(a.total_done)+" ("+fsize(a.total_payload_download)+")",uploaded:fsize(a.total_uploaded)+" ("+fsize(a.total_payload_upload)+")",share:a.ratio.toFixed(3),announce:ftime(a.next_announce),tracker_status:a.tracker_status,downspeed:fspeed(a.download_payload_rate),upspeed:fspeed(a.upload_payload_rate),eta:ftime(a.eta),pieces:a.num_pieces+" ("+fsize(a.piece_length)+")",seeders:seeders,peers:peers,avail:a.distributed_copies.toFixed(3),active_time:ftime(a.active_time),seeding_time:ftime(a.seeding_time),seed_rank:a.seed_rank,time_added:fdate(a.time_added)};b.auto_managed=_((a.is_auto_managed)?"True":"False");for(var c in this.fields){this.fields[c].innerHTML=b[c]}var d=a.state+" "+a.progress.toFixed(2)+"%";this.progressBar.updateProgress(a.progress,d)}});Deluge.Details.add(new Ext.deluge.details.StatusTab());Ext.deluge.details.DetailsTab=Ext.extend(Ext.Panel,{title:_("Details"),bodyStyle:"padding 5px",onRender:function(b,a){Ext.deluge.details.DetailsTab.superclass.onRender.call(this,b,a);this.load({url:"/render/tab_details.html",text:_("Loading")+"..."});this.oldData={};this.body.setStyle("padding","5px");this.getUpdater().on("update",this.onPanelUpdate,this)},clear:function(){if(!this.fields){return}for(var a in this.fields){this.fields[a].innerHTML=""}},update:function(a){Deluge.Client.core.get_torrent_status(a,Deluge.Keys.Details,{success:this.onRequestComplete,scope:this,torrentId:a})},onPanelUpdate:function(b,a){this.fields={};Ext.each(Ext.query("dd",this.body.dom),function(c){this.fields[c.className]=c},this)},onRequestComplete:function(e,c,a,b){var d={torrent_name:e.name,hash:b.options.torrentId,path:e.save_path,size:fsize(e.total_size),files:e.num_files,status:e.tracker_status,tracker:e.tracker,comment:e.comment};for(var f in this.fields){if(d[f]==this.oldData[f]){continue}this.fields[f].innerHTML=Ext.escapeHTML(d[f])}this.oldData=d}});Deluge.Details.add(new Ext.deluge.details.DetailsTab());(function(){function b(d){var c=d*100;return Deluge.progressBar(c,this.width-50,c.toFixed(2)+"%",0)}function a(c){return String.format('
{1}
',FILE_PRIORITY_CSS[c],_(FILE_PRIORITY[c]))}Ext.deluge.details.FilesTab=Ext.extend(Ext.tree.ColumnTree,{constructor:function(c){c=Ext.apply({title:_("Files"),rootVisible:false,autoScroll:true,selModel:new Ext.tree.MultiSelectionModel(),columns:[{header:_("Filename"),width:330,dataIndex:"filename"},{header:_("Size"),width:150,dataIndex:"size",renderer:fsize},{header:_("Progress"),width:150,dataIndex:"progress",renderer:b},{header:_("Priority"),width:150,dataIndex:"priority",renderer:a}],root:new Ext.tree.TreeNode({text:"Files"})},c);Ext.deluge.details.FilesTab.superclass.constructor.call(this,c)},onRender:function(d,c){Ext.deluge.details.FilesTab.superclass.onRender.call(this,d,c);Deluge.Menus.FilePriorities.on("itemclick",this.onItemClick,this);this.on("contextmenu",this.onContextMenu,this);this.sorter=new Ext.tree.TreeSorter(this,{folderSort:true})},clear:function(){var c=this.getRootNode();if(!c.hasChildNodes()){return}c.cascade(function(e){var d=e.parentNode;if(!d){return}if(!d.ownerTree){return}d.removeChild(e)})},update:function(c){if(this.torrentId!=c){this.clear();this.torrentId=c}Deluge.Client.web.get_torrent_files(c,{success:this.onRequestComplete,scope:this,torrentId:c})},onContextMenu:function(d,f){f.stopEvent();var c=this.getSelectionModel();if(c.getSelectedNodes().length<2){c.clearSelections();d.select()}Deluge.Menus.FilePriorities.showAt(f.getPoint())},onItemClick:function(k,j){switch(k.id){case"expandAll":this.expandAll();break;default:var h={};function c(e){if(Ext.isEmpty(e.attributes.fileIndex)){return}h[e.attributes.fileIndex]=e.attributes.priority}this.getRootNode().cascade(c);var d=this.getSelectionModel().getSelectedNodes();Ext.each(d,function(l){if(!l.isLeaf()){function e(m){if(Ext.isEmpty(m.attributes.fileIndex)){return}h[m.attributes.fileIndex]=k.filePriority}l.cascade(e)}else{if(!Ext.isEmpty(l.attributes.fileIndex)){h[l.attributes.fileIndex]=k.filePriority;return}}});var g=new Array(Ext.keys(h).length);for(var f in h){g[f]=h[f]}Deluge.Client.core.set_torrent_file_priorities(this.torrentId,g,{success:function(){Ext.each(d,function(e){e.setColumnValue(3,k.filePriority)})},scope:this});break}},onRequestComplete:function(f,e){function d(k,h){for(var g in k){var j=k[g];var l=h.findChild("id",g);if(Ext.type(j)=="object"){if(!l){l=new Ext.tree.TreeNode({id:g,text:g});h.appendChild(l)}d(j,l)}else{if(!l){l=new Ext.tree.ColumnTreeNode({id:g,filename:g,text:g,fileIndex:j[0],size:j[1],progress:j[2],priority:j[3],leaf:true,iconCls:"x-deluge-file",uiProvider:Ext.tree.ColumnNodeUI});h.appendChild(l)}l.setColumnValue(1,j[1]);l.setColumnValue(2,j[2]);l.setColumnValue(3,j[3])}}}var c=this.getRootNode();d(f,c);c.firstChild.expand()}});Deluge.Details.add(new Ext.deluge.details.FilesTab())})();(function(){function a(e){return String.format('',e)}function c(g,h,f){var e=(f.data.seed==1024)?"x-deluge-seed":"x-deluge-peer";return String.format('
{1}
',e,g)}function d(g){var e=(g*100).toFixed(0);var f=new Number(this.style.match(/\w+:\s*(\d+)\w+/)[1]).toFixed(0)-8;return Deluge.progressBar(e,f,e+"%")}function b(g){var e=g.match(/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\:(\d+)/);var f=0;var h=[e[1],e[2],e[3],e[4]];Ext.each(h,function(k,j){k=parseInt(k);f=f|k<<((3-j)*8)});return f}Ext.deluge.details.PeersTab=Ext.extend(Ext.grid.GridPanel,{constructor:function(e){e=Ext.apply({title:_("Peers"),cls:"x-deluge-peers",store:new Ext.data.SimpleStore({fields:[{name:"country"},{name:"address",sortType:b},{name:"client"},{name:"progress",type:"float"},{name:"downspeed",type:"int"},{name:"upspeed",type:"int"},{name:"seed",type:"int"}],id:0}),columns:[{header:" ",width:30,sortable:true,renderer:a,dataIndex:"country"},{header:"Address",width:125,sortable:true,renderer:c,dataIndex:"address"},{header:"Client",width:125,sortable:true,renderer:fplain,dataIndex:"client"},{header:"Progress",width:150,sortable:true,renderer:d,dataIndex:"progress"},{header:"Down Speed",width:100,sortable:true,renderer:fspeed,dataIndex:"downspeed"},{header:"Up Speed",width:100,sortable:true,renderer:fspeed,dataIndex:"upspeed"}],stripeRows:true,deferredRender:false,autoScroll:true},e);Ext.deluge.details.PeersTab.superclass.constructor.call(this,e)},onRender:function(f,e){Ext.deluge.details.PeersTab.superclass.onRender.call(this,f,e)},clear:function(){this.getStore().loadData([])},update:function(e){Deluge.Client.core.get_torrent_status(e,Deluge.Keys.Peers,{success:this.onRequestComplete,scope:this})},onRequestComplete:function(g,f){if(!g){return}var e=new Array();Ext.each(g.peers,function(h){e.push([h.country,h.ip,h.client,h.progress,h.down_speed,h.up_speed,h.seed])},this);this.getStore().loadData(e)}});Deluge.Details.add(new Ext.deluge.details.PeersTab())})();Ext.deluge.details.OptionsTab=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({autoScroll:true,bodyStyle:"padding: 5px;",border:false,cls:"x-deluge-options",defaults:{autoHeight:true,labelWidth:1,defaultType:"checkbox"},deferredRender:false,layout:"column",title:_("Options")},a);Ext.deluge.details.OptionsTab.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.details.OptionsTab.superclass.initComponent.call(this);this.fieldsets={},this.fields={};this.optionsManager=new Deluge.MultiOptionsManager({options:{max_download_speed:-1,max_upload_speed:-1,max_connections:-1,max_upload_slots:-1,auto_managed:false,stop_at_ratio:false,stop_ratio:2,remove_at_ratio:false,move_completed:null,"private":false,prioritize_first_last:false}});this.fieldsets.bandwidth=this.add({xtype:"fieldset",defaultType:"uxspinner",bodyStyle:"padding: 5px",layout:"table",layoutConfig:{columns:3},labelWidth:150,style:"margin-left: 10px; margin-right: 5px; padding: 5px",title:_("Bandwidth"),width:250});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Download Speed"),forId:"max_download_speed",cls:"x-deluge-options-label"});this.fields.max_download_speed=this.fieldsets.bandwidth.add({id:"max_download_speed",name:"max_download_speed",width:70,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}});this.fieldsets.bandwidth.add({xtype:"label",text:_("KiB/s"),style:"margin-left: 10px"});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Upload Speed"),forId:"max_upload_speed",cls:"x-deluge-options-label"});this.fields.max_upload_speed=this.fieldsets.bandwidth.add({id:"max_upload_speed",name:"max_upload_speed",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}});this.fieldsets.bandwidth.add({xtype:"label",text:_("KiB/s"),style:"margin-left: 10px"});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Connections"),forId:"max_connections",cls:"x-deluge-options-label"});this.fields.max_connections=this.fieldsets.bandwidth.add({id:"max_connections",name:"max_connections",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},colspan:2});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Upload Slots"),forId:"max_upload_slots",cls:"x-deluge-options-label"});this.fields.max_upload_slots=this.fieldsets.bandwidth.add({id:"max_upload_slots",name:"max_upload_slots",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},colspan:2});this.fieldsets.queue=this.add({xtype:"fieldset",title:_("Queue"),style:"margin-left: 5px; margin-right: 5px; padding: 5px",width:210,layout:"table",layoutConfig:{columns:2},labelWidth:0,defaults:{fieldLabel:"",labelSeparator:""}});this.fields.auto_managed=this.fieldsets.queue.add({xtype:"checkbox",fieldLabel:"",labelSeparator:"",name:"is_auto_managed",boxLabel:_("Auto Managed"),width:200,colspan:2});this.fields.stop_at_ratio=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"stop_at_ratio",width:120,boxLabel:_("Stop seed at ratio"),handler:this.onStopRatioChecked,scope:this});this.fields.stop_ratio=this.fieldsets.queue.add({xtype:"uxspinner",id:"stop_ratio",name:"stop_ratio",disabled:true,width:50,value:2,strategy:{xtype:"number",minValue:-1,maxValue:99999,incrementValue:0.1,alternateIncrementValue:1,decimalPrecision:1}});this.fields.remove_at_ratio=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"remove_at_ratio",ctCls:"x-deluge-indent-checkbox",bodyStyle:"padding-left: 10px",boxLabel:_("Remove at ratio"),disabled:true,colspan:2});this.fields.move_completed=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"move_completed",boxLabel:_("Move Completed"),colspan:2});this.rightColumn=this.add({border:false,autoHeight:true,style:"margin-left: 5px",width:200});this.fieldsets.general=this.rightColumn.add({xtype:"fieldset",autoHeight:true,defaultType:"checkbox",title:_("General"),layout:"form"});this.fields["private"]=this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Private"),id:"private",disabled:true});this.fields.prioritize_first_last=this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Prioritize First/Last"),id:"prioritize_first_last"});for(var a in this.fields){this.optionsManager.bind(a,this.fields[a])}this.buttonPanel=this.rightColumn.add({layout:"column",xtype:"panel",border:false});this.buttonPanel.add({xtype:"panel",border:false}).add({id:"edit_trackers",xtype:"button",text:_("Edit Trackers"),cls:"x-btn-text-icon",iconCls:"x-deluge-edit-trackers",border:false,width:100,handler:this.onEditTrackers,scope:this});this.buttonPanel.add({xtype:"panel",border:false}).add({id:"apply",xtype:"button",text:_("Apply"),style:"margin-left: 10px;",border:false,width:100,handler:this.onApply,scope:this})},onRender:function(b,a){Ext.deluge.details.OptionsTab.superclass.onRender.call(this,b,a);this.layout=new Ext.layout.ColumnLayout();this.layout.setContainer(this);this.doLayout()},clear:function(){if(this.torrentId==null){return}this.torrentId=null;this.optionsManager.changeId(null)},reset:function(){if(this.torrentId){this.optionsManager.reset()}},update:function(a){if(this.torrentId&&!a){this.clear()}if(!a){return}if(this.torrentId!=a){this.torrentId=a;this.optionsManager.changeId(a)}Deluge.Client.core.get_torrent_status(a,Deluge.Keys.Options,{success:this.onRequestComplete,scope:this})},onApply:function(){var b=this.optionsManager.getDirty();if(!Ext.isEmpty(b.prioritize_first_last)){var a=b.prioritize_first_last;Deluge.Client.core.set_torrent_prioritize_first_last(this.torrentId,a,{success:function(){this.optionsManager.set("prioritize_first_last",a)},scope:this})}Deluge.Client.core.set_torrent_options([this.torrentId],b,{success:function(){this.optionsManager.commit()},scope:this})},onEditTrackers:function(){Deluge.EditTrackers.show()},onStopRatioChecked:function(b,a){this.fields.remove_at_ratio.setDisabled(!a);this.fields.stop_ratio.setDisabled(!a)},onRequestComplete:function(c,b){this.fields["private"].setValue(c["private"]);this.fields["private"].setDisabled(true);delete c["private"];c.auto_managed=c.is_auto_managed;this.optionsManager.setDefault(c);var a=this.optionsManager.get("stop_at_ratio");this.fields.remove_at_ratio.setDisabled(!a);this.fields.stop_ratio.setDisabled(!a)}});Deluge.Details.add(new Ext.deluge.details.OptionsTab());(function(){Ext.deluge.AddTracker=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Add Tracker"),width:375,height:150,bodyStyle:"padding: 5px",layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-edit-trackers",plain:true,resizable:false},a);Ext.deluge.AddTracker.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.AddTracker.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Add"),this.onAdd,this);this.addEvents("add");this.form=this.add({xtype:"form",defaultType:"textarea",baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Trackers"),name:"trackers",anchor:"100%"}]})},onCancel:function(){this.form.getForm().findField("trackers").setValue("");this.hide()},onAdd:function(){var b=this.form.getForm().findField("trackers").getValue();b=b.split("\n");var a=[];Ext.each(b,function(c){if(Ext.form.VTypes.url(c)){a.push(c)}},this);this.fireEvent("add",a);this.hide();this.form.getForm().findField("trackers").setValue("")}});Ext.deluge.EditTracker=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Edit Tracker"),width:375,height:110,bodyStyle:"padding: 5px",layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-edit-trackers",plain:true,resizable:false},a);Ext.deluge.EditTracker.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.EditTracker.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Save"),this.onSave,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form",defaultType:"textfield",baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Tracker"),name:"tracker",anchor:"100%"}]})},show:function(a){Ext.deluge.EditTracker.superclass.show.call(this);this.record=a;this.form.getForm().findField("tracker").setValue(a.data.url)},onCancel:function(){this.hide()},onHide:function(){this.form.getForm().findField("tracker").setValue("")},onSave:function(){var a=this.form.getForm().findField("tracker").getValue();this.record.set("url",a);this.record.commit();this.hide()}});Ext.deluge.EditTrackers=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Edit Trackers"),width:350,height:220,bodyStyle:"padding: 5px",layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-edit-trackers",plain:true,resizable:true},a);Ext.deluge.EditTrackers.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.EditTrackers.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Ok"),this.onOk,this);this.addEvents("save");this.on("show",this.onShow,this);this.on("save",this.onSave,this);this.addWindow=new Ext.deluge.AddTracker();this.addWindow.on("add",this.onAddTrackers,this);this.editWindow=new Ext.deluge.EditTracker();this.grid=this.add({xtype:"grid",store:new Ext.data.SimpleStore({fields:[{name:"tier",mapping:0},{name:"url",mapping:1}]}),columns:[{header:_("Tier"),width:50,sortable:true,renderer:fplain,dataIndex:"tier"},{id:"tracker",header:_("Tracker"),sortable:true,renderer:fplain,dataIndex:"url"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{selectionchange:{fn:this.onSelect,scope:this}}}),autoExpandColumn:"tracker",deferredRender:false,autoScroll:true,margins:"0 0 0 0",bbar:new Ext.Toolbar({items:[{cls:"x-btn-text-icon",text:_("Up"),icon:"/icons/up.png",handler:this.onUp,scope:this},{cls:"x-btn-text-icon",text:_("Down"),icon:"/icons/down.png",handler:this.onDown,scope:this},"->",{cls:"x-btn-text-icon",text:_("Add"),icon:"/icons/add.png",handler:this.onAdd,scope:this},{cls:"x-btn-text-icon",text:_("Edit"),icon:"/icons/edit_trackers.png",handler:this.onEdit,scope:this},{cls:"x-btn-text-icon",text:_("Remove"),icon:"/icons/remove.png",handler:this.onRemove,scope:this}]})})},onAdd:function(){this.addWindow.show()},onAddTrackers:function(b){var a=this.grid.getStore();Ext.each(b,function(d){var e=false,c=-1;a.each(function(f){if(f.get("tier")>c){c=f.get("tier")}if(d==f.get("tracker")){e=true;return false}},this);if(!e){a.loadData([[c+1,d]],true)}},this)},onCancel:function(){this.hide()},onEdit:function(){var a=this.grid.getSelectionModel().getSelected();this.editWindow.show(a)},onHide:function(){this.grid.getStore().removeAll()},onOk:function(){var a=[];this.grid.getStore().each(function(b){a.push({tier:b.get("tier"),url:b.get("url")})},this);Deluge.Client.core.set_torrent_trackers(this.torrentId,a,{failure:this.onSaveFail,scope:this});this.hide()},onRemove:function(){var a=this.grid.getSelectionModel().getSelected();this.grid.getStore().remove(a)},onRequestComplete:function(a){var b=[];Ext.each(a.trackers,function(c){b.push([c.tier,c.url])});this.grid.getStore().loadData(b)},onSaveFail:function(){},onSelect:function(a){if(a.hasSelection()){this.grid.getBottomToolbar().items.get(4).enable()}},onShow:function(){this.grid.getBottomToolbar().items.get(4).disable();var a=Deluge.Torrents.getSelected();this.torrentId=a.id;Deluge.Client.core.get_torrent_status(a.id,["trackers"],{success:this.onRequestComplete,scope:this})}});Deluge.EditTrackers=new Ext.deluge.EditTrackers()})();Deluge.Keys={Grid:["queue","name","total_size","state","progress","num_seeds","total_seeds","num_peers","total_peers","download_payload_rate","upload_payload_rate","eta","ratio","distributed_copies","is_auto_managed","time_added","tracker_host"],Status:["total_done","total_payload_download","total_uploaded","total_payload_upload","next_announce","tracker_status","num_pieces","piece_length","is_auto_managed","active_time","seeding_time","seed_rank"],Files:["files","file_progress","file_priorities"],Peers:["peers"],Details:["name","save_path","total_size","num_files","tracker_status","tracker","comment"],Options:["max_download_speed","max_upload_speed","max_connections","max_upload_slots","is_auto_managed","stop_at_ratio","stop_ratio","remove_at_ratio","private","prioritize_first_last"]};Ext.each(Deluge.Keys.Grid,function(a){Deluge.Keys.Status.push(a)});(function(){Ext.deluge.LoginWindow=Ext.extend(Ext.Window,{firstShow:true,constructor:function(a){a=Ext.apply({layout:"fit",width:300,height:120,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",closable:false,modal:true,plain:true,resizable:false,title:_("Login"),iconCls:"x-deluge-login-window-icon"},a);Ext.deluge.LoginWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.LoginWindow.superclass.initComponent.call(this);this.on("show",this.onShow,this);this.addButton({text:_("Login"),handler:this.onLogin,scope:this});this.loginForm=this.add({xtype:"form",defaultType:"textfield",id:"loginForm",baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Password"),id:"password",name:"password",inputType:"password",anchor:"100%",listeners:{specialkey:{fn:this.onKey,scope:this}}}]})},logout:function(){Deluge.Events.fire("logout");Deluge.Client.auth.delete_session({success:function(a){this.show(true)},scope:this})},show:function(a){if(this.firstShow){Deluge.Client.on("error",this.onClientError,this);this.firstShow=false}if(a){return Ext.deluge.LoginWindow.superclass.show.call(this)}Deluge.Client.auth.check_session({success:function(b){if(b){Deluge.Events.fire("login")}else{this.show(true)}},failure:function(b){this.show(true)},scope:this})},onKey:function(b,a){if(a.getKey()==13){this.onLogin()}},onLogin:function(){var a=this.loginForm.items.get("password");Deluge.Client.auth.login(a.getValue(),{success:function(b){if(b){Deluge.Events.fire("login");this.hide();a.setRawValue("")}else{Ext.MessageBox.show({title:_("Login Failed"),msg:_("You entered an incorrect password"),buttons:Ext.MessageBox.OK,modal:false,fn:function(){a.focus()},icon:Ext.MessageBox.WARNING,iconCls:"x-deluge-icon-warning"})}},scope:this})},onClientError:function(c,b,a){if(c.error.code==1){Deluge.Events.fire("logout");this.show(true)}},onShow:function(){var a=this.loginForm.items.get("password");a.focus(false,150);a.setRawValue("")}});Deluge.Login=new Ext.deluge.LoginWindow()})();Ext.namespace("Ext.deluge");Ext.deluge.MoveStorage=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Move Storage"),width:375,height:110,layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-move-storage",plain:true,resizable:false},a);Ext.deluge.MoveStorage.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.MoveStorage.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Move"),this.onMove,this);this.form=this.add({xtype:"form",border:false,defaultType:"textfield",width:300,bodyStyle:"padding: 5px"});this.moveLocation=this.form.add({fieldLabel:_("Location"),name:"location",width:240})},hide:function(){Ext.deluge.MoveStorage.superclass.hide.call(this);this.torrentIds=null},show:function(a){Ext.deluge.MoveStorage.superclass.show.call(this);this.torrentIds=a},onCancel:function(){this.hide()},onMove:function(){var a=this.moveLocation.getValue();Deluge.Client.core.move_storage(this.torrentIds,a);this.hide()}});Deluge.MoveStorage=new Ext.deluge.MoveStorage();Deluge.Plugin=Ext.extend(Ext.util.Observable,{constructor:function(a){this.name=a.name;this.addEvents({enabled:true,disabled:true});this.isDelugePlugin=true;Deluge.Plugins[this.name]=this;Deluge.Plugin.superclass.constructor.call(this,a)},disable:function(){this.fireEvent("disabled",this);if(this.onDisable){this.onDisable()}},enable:function(){this.fireEvent("enable",this);if(this.onEnable){this.onEnable()}}});Ext.deluge.PreferencesWindow=Ext.extend(Ext.Window,{currentPage:null,constructor:function(a){a=Ext.apply({layout:"border",width:485,height:500,buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-preferences",plain:true,resizable:false,title:_("Preferences"),items:[{xtype:"grid",region:"west",title:_("Categories"),store:new Ext.data.SimpleStore({fields:[{name:"name",mapping:0}]}),columns:[{id:"name",renderer:fplain,dataIndex:"name"}],sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onPageSelect,scope:this}}}),hideHeaders:true,autoExpandColumn:"name",deferredRender:false,autoScroll:true,margins:"5 0 5 5",cmargins:"5 0 5 5",width:120,collapsible:true},]},a);Ext.deluge.PreferencesWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.PreferencesWindow.superclass.initComponent.call(this);this.categoriesGrid=this.items.get(0);this.configPanel=this.add({region:"center",header:false,layout:"fit",height:400,autoScroll:true,margins:"5 5 5 5",cmargins:"5 5 5 5"});this.addButton(_("Close"),this.onClose,this);this.addButton(_("Apply"),this.onApply,this);this.addButton(_("Ok"),this.onOk,this);this.pages={};this.optionsManager=new Deluge.OptionsManager();this.on("show",this.onShow,this)},onApply:function(b){var c=this.optionsManager.getDirty();if(!Ext.isObjectEmpty(c)){Deluge.Client.core.set_config(c,{success:this.onSetConfig,scope:this})}for(var a in this.pages){if(this.pages[a].onApply){this.pages[a].onApply()}}},onClose:function(){this.hide()},onOk:function(){Deluge.Client.core.set_config(this.optionsManager.getDirty());this.hide()},addPage:function(c){var a=this.categoriesGrid.getStore();var b=c.title;a.loadData([[b]],true);c.bodyStyle="margin: 5px";this.pages[b]=this.configPanel.add(c);return this.pages[b]},removePage:function(c){var b=c.title;var a=this.categoriesGrid.getStore();a.removeAt(a.find("name",b));this.configPanel.remove(c);delete this.pages[c.title]},getOptionsManager:function(){return this.optionsManager},onGotConfig:function(a){this.getOptionsManager().set(a)},onPageSelect:function(a,e,c){if(this.currentPage==null){for(var d in this.pages){this.pages[d].hide()}}else{this.currentPage.hide()}var b=c.get("name");this.pages[b].show();this.currentPage=this.pages[b];this.configPanel.doLayout()},onSetConfig:function(){this.getOptionsManager().commit()},onShow:function(){if(!this.categoriesGrid.getSelectionModel().hasSelection()){this.categoriesGrid.getSelectionModel().selectFirstRow()}Deluge.Client.core.get_config({success:this.onGotConfig,scope:this})}});Deluge.Preferences=new Ext.deluge.PreferencesWindow();Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Downloads=Ext.extend(Ext.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Downloads"),layout:"form",autoHeight:true,width:320},a);Ext.deluge.preferences.Downloads.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Downloads.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Folders"),labelWidth:150,defaultType:"togglefield",autoHeight:true,labelAlign:"top",width:300,style:"margin-bottom: 5px; padding-bottom: 5px;"});b.bind("download_location",a.add({xtype:"textfield",name:"download_location",fieldLabel:_("Download to"),width:280}));var c=a.add({name:"move_completed_path",fieldLabel:_("Move completed to"),width:280});b.bind("move_completed",c.toggle);b.bind("move_completed_path",c.input);c=a.add({name:"torrentfiles_location",fieldLabel:_("Copy of .torrent files to"),width:280});b.bind("copy_torrent_file",c.toggle);b.bind("torrentfiles_location",c.input);c=a.add({name:"autoadd_location",fieldLabel:_("Autoadd .torrent files from"),width:280});b.bind("autoadd_enable",c.toggle);b.bind("autoadd_location",c.input);a=this.add({xtype:"fieldset",border:false,title:_("Allocation"),autoHeight:true,labelWidth:1,defaultType:"radiogroup",style:"margin-bottom: 5px; margin-top: 0; padding-bottom: 5px; padding-top: 0;",width:240});b.bind("compact_allocation",a.add({name:"compact_allocation",width:200,labelSeparator:"",defaults:{width:80,height:22,name:"compact_allocation"},items:[{boxLabel:_("Use Full"),inputValue:false},{boxLabel:_("Use Compact"),inputValue:true}]}));a=this.add({xtype:"fieldset",border:false,title:_("Options"),autoHeight:true,labelWidth:1,defaultType:"checkbox",style:"margin-bottom: 0; padding-bottom: 0;",width:280});b.bind("prioritize_first_last_pieces",a.add({name:"prioritize_first_last_pieces",labelSeparator:"",height:22,boxLabel:_("Prioritize first and last pieces of torrent")}));b.bind("add_paused",a.add({name:"add_paused",labelSeparator:"",height:22,boxLabel:_("Add torrents in Paused state")}));this.on("show",this.onShow,this)},onShow:function(){Ext.deluge.preferences.Downloads.superclass.onShow.call(this)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Downloads());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Network=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Network"),layout:"form"},a);Ext.deluge.preferences.Network.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Network.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Incoming Ports"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("random_port",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Use Random Ports"),name:"random_port",height:22,listeners:{check:{fn:function(d,c){this.listenPorts.setDisabled(c)},scope:this}}}));this.listenPorts=a.add({xtype:"uxspinnergroup",name:"listen_ports",fieldLabel:"",labelSeparator:"",colCfg:{labelWidth:40,style:"margin-right: 10px;"},items:[{fieldLabel:"From",width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},},{fieldLabel:"To",width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}]});b.bind("listen_ports",this.listenPorts);a=this.add({xtype:"fieldset",border:false,title:_("Outgoing Ports"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("random_outgoing_ports",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Use Random Ports"),name:"random_outgoing_ports",height:22,listeners:{check:{fn:function(d,c){this.outgoingPorts.setDisabled(c)},scope:this}}}));this.outgoingPorts=a.add({xtype:"uxspinnergroup",name:"outgoing_ports",fieldLabel:"",labelSeparator:"",colCfg:{labelWidth:40,style:"margin-right: 10px;"},items:[{fieldLabel:"From",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},},{fieldLabel:"To",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}]});b.bind("outgoing_ports",this.outgoingPorts);a=this.add({xtype:"fieldset",border:false,title:_("Network Interface"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:true,labelWidth:1,defaultType:"textfield"});b.bind("listen_interface",a.add({name:"listen_interface",fieldLabel:"",labelSeparator:"",width:200}));a=this.add({xtype:"fieldset",border:false,title:_("TOS"),style:"margin-bottom: 5px; padding-bottom: 0px;",bodyStyle:"margin: 0px; padding: 0px",autoHeight:true,defaultType:"textfield"});b.bind("peer_tos",a.add({name:"peer_tos",fieldLabel:_("Peer TOS Byte"),width:80}));a=this.add({xtype:"fieldset",border:false,title:_("Network Extras"),autoHeight:true,layout:"table",layoutConfig:{columns:3},defaultType:"checkbox"});b.bind("upnp",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("UPnP"),name:"upnp"}));b.bind("natpmp",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("NAT-PMP"),ctCls:"x-deluge-indent-checkbox",name:"natpmp"}));b.bind("utpex",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Peer Exchange"),ctCls:"x-deluge-indent-checkbox",name:"utpex"}));b.bind("lsd",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("LSD"),name:"lsd"}));b.bind("dht",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("DHT"),ctCls:"x-deluge-indent-checkbox",name:"dht"}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Network());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Encryption=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Encryption"),layout:"form"},a);Ext.deluge.preferences.Encryption.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Encryption.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Settings"),autoHeight:true,defaultType:"combo"});b.bind("enc_in_policy",a.add({fieldLabel:_("Inbound"),mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Forced")],[1,_("Enabled")],[2,_("Disabled")]]}),triggerAction:"all",valueField:"id",displayField:"text"}));b.bind("enc_out_policy",a.add({fieldLabel:_("Outbound"),mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Forced")],[1,_("Enabled")],[2,_("Disabled")]]}),triggerAction:"all",valueField:"id",displayField:"text"}));b.bind("enc_level",a.add({fieldLabel:_("Level"),mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Handshake")],[1,_("Full Stream")],[2,_("Either")]]}),triggerAction:"all",valueField:"id",displayField:"text"}));b.bind("enc_prefer_rc4",a.add({xtype:"checkbox",name:"enc_prefer_rc4",height:40,hideLabel:true,boxLabel:_("Encrypt entire stream")}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Encryption());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Bandwidth=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Bandwidth"),layout:"form",labelWidth:10},a);Ext.deluge.preferences.Bandwidth.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Bandwidth.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Global Bandwidth Usage"),labelWidth:200,defaultType:"uxspinner",style:"margin-bottom: 0px; padding-bottom: 0px;",autoHeight:true});b.bind("max_connections_global",a.add({name:"max_connections_global",fieldLabel:_("Maximum Connections"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_upload_slots_global",a.add({name:"max_upload_slots_global",fieldLabel:_("Maximum Upload Slots"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_download_speed",a.add({name:"max_download_speed",fieldLabel:_("Maximum Download Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}));b.bind("max_upload_speed",a.add({name:"max_upload_speed",fieldLabel:_("Maximum Upload Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}));b.bind("max_half_open_connections",a.add({name:"max_half_open_connections",fieldLabel:_("Maximum Half-Open Connections"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_connections_per_second",a.add({name:"max_connections_per_second",fieldLabel:_("Maximum Connection Attempts per Second"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));a=this.add({xtype:"fieldset",border:false,title:"",defaultType:"checkbox",style:"padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;",autoHeight:true});b.bind("ignore_limits_on_local_network",a.add({name:"ignore_limits_on_local_network",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Ignore limits on local network"),}));b.bind("rate_limit_ip_overhead",a.add({name:"rate_limit_ip_overhead",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Rate limit IP overhead"),}));a=this.add({xtype:"fieldset",border:false,title:_("Per Torrent Bandwidth Usage"),style:"margin-bottom: 0px; padding-bottom: 0px;",defaultType:"uxspinner",labelWidth:200,autoHeight:true});b.bind("max_connections_per_torrent",a.add({name:"max_connections_per_torrent",fieldLabel:_("Maximum Connections"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_upload_slots_per_torrent",a.add({name:"max_upload_slots_per_torrent",fieldLabel:_("Maximum Upload Slots"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_download_speed_per_torrent",a.add({name:"max_download_speed_per_torrent",fieldLabel:_("Maximum Download Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}));b.bind("max_upload_speed_per_torrent",a.add({name:"max_upload_speed_per_torrent",fieldLabel:_("Maximum Upload Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Bandwidth());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Interface=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Interface"),layout:"form"},a);Ext.deluge.preferences.Interface.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Interface.superclass.initComponent.call(this);var c=this.optionsManager=new Deluge.OptionsManager();this.on("show",this.onShow,this);var a=this.add({xtype:"fieldset",border:false,title:_("Interface"),style:"margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px",autoHeight:true,labelWidth:1,defaultType:"checkbox"});c.bind("show_session_speed",a.add({name:"show_session_speed",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Show session speed in titlebar")}));c.bind("sidebar_show_zero",a.add({name:"sidebar_show_zero",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Show filters with zero torrents")}));c.bind("sidebar_show_trackers",a.add({name:"sidebar_show_trackers",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Show trackers with zero torrents")}));a=this.add({xtype:"fieldset",border:false,title:_("Password"),style:"margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px",autoHeight:true,labelWidth:110,defaultType:"textfield",defaults:{width:180,inputType:"password"}});this.oldPassword=a.add({name:"old_password",fieldLabel:_("Old Password")});this.newPassword=a.add({name:"new_password",fieldLabel:_("New Password")});this.confirmPassword=a.add({name:"confirm_password",fieldLabel:_("Confirm Password")});var b=a.add({xtype:"panel",autoHeight:true,border:false,width:320,bodyStyle:"padding-left: 230px"});b.add({xtype:"button",text:_("Change"),listeners:{click:{fn:this.onPasswordChange,scope:this}}});a=this.add({xtype:"fieldset",border:false,title:_("Server"),style:"margin-top: 0px; padding-top: 0px; margin-bottom: 0px; padding-bottom: 0px",autoHeight:true,labelWidth:110,defaultType:"uxspinner",defaults:{width:80,}});c.bind("session_timeout",a.add({name:"session_timeout",fieldLabel:_("Session Timeout"),strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));c.bind("port",a.add({name:"port",fieldLabel:_("Port"),strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));this.httpsField=c.bind("https",a.add({xtype:"checkbox",name:"https",hideLabel:true,width:280,height:22,boxLabel:_("Use SSL (paths relative to Deluge config folder)")}));this.httpsField.on("check",this.onSSLCheck,this);this.pkeyField=c.bind("pkey",a.add({xtype:"textfield",disabled:true,name:"pkey",width:180,fieldLabel:_("Private Key")}));this.certField=c.bind("cert",a.add({xtype:"textfield",disabled:true,name:"cert",width:180,fieldLabel:_("Certificate")}))},onApply:function(){var a=this.optionsManager.getDirty();if(!Ext.isObjectEmpty(a)){Deluge.Client.web.set_config(a,{success:this.onSetConfig,scope:this})}},onGotConfig:function(a){this.optionsManager.set(a)},onPasswordChange:function(){var b=this.newPassword.getValue();if(b!=this.confirmPassword.getValue()){Ext.MessageBox.show({title:_("Invalid Password"),msg:_("Your passwords don't match!"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});return}var a=this.oldPassword.getValue();Deluge.Client.auth.change_password(a,b,{success:function(c){if(!c){Ext.MessageBox.show({title:_("Password"),msg:_("Your old password was incorrect!"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});this.oldPassword.setValue("")}else{Ext.MessageBox.show({title:_("Change Successful"),msg:_("Your password was successfully changed!"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.INFO,iconCls:"x-deluge-icon-info"});this.oldPassword.setValue("");this.newPassword.setValue("");this.confirmPassword.setValue("")}},scope:this})},onSetConfig:function(){this.optionsManager.commit()},onShow:function(){Ext.deluge.preferences.Interface.superclass.onShow.call(this);Deluge.Client.web.get_config({success:this.onGotConfig,scope:this})},onSSLCheck:function(b,a){this.pkeyField.setDisabled(!a);this.certField.setDisabled(!a)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Interface());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Other=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Other"),layout:"form"},a);Ext.deluge.preferences.Other.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Other.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Updates"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("new_release_check",a.add({fieldLabel:"",labelSeparator:"",height:22,name:"new_release_check",boxLabel:_("Be alerted about new releases")}));a=this.add({xtype:"fieldset",border:false,title:_("System Information"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});a.add({xtype:"panel",border:false,bodyCfg:{html:_("Help us improve Deluge by sending us your Python version, PyGTK version, OS and processor types. Absolutely no other information is sent.")}});b.bind("send_info",a.add({fieldLabel:"",labelSeparator:"",height:22,boxLabel:_("Yes, please send anonymous statistics"),name:"send_info"}));a=this.add({xtype:"fieldset",border:false,title:_("GeoIP Database"),autoHeight:true,labelWidth:80,defaultType:"textfield"});b.bind("geoip_db_location",a.add({name:"geoip_db_location",fieldLabel:_("Location"),width:200}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Other());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Daemon=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Daemon"),layout:"form"},a);Ext.deluge.preferences.Daemon.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Daemon.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Port"),autoHeight:true,defaultType:"uxspinner"});b.bind("daemon_port",a.add({fieldLabel:_("Daemon port"),name:"daemon_port",value:58846,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));a=this.add({xtype:"fieldset",border:false,title:_("Connections"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("allow_remote",a.add({fieldLabel:"",height:22,labelSeparator:"",boxLabel:_("Allow Remote Connections"),name:"allow_remote"}));a=this.add({xtype:"fieldset",border:false,title:_("Other"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("new_release_check",a.add({fieldLabel:"",labelSeparator:"",height:40,boxLabel:_("Periodically check the website for new releases"),id:"new_release_check"}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Daemon());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Queue=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Queue"),layout:"form"},a);Ext.deluge.preferences.Queue.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Queue.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("General"),style:"padding-top: 5px;",autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("queue_new_to_top",a.add({fieldLabel:"",labelSeparator:"",height:22,boxLabel:_("Queue new torrents to top"),name:"queue_new_to_top"}));a=this.add({xtype:"fieldset",border:false,title:_("Active Torrents"),autoHeight:true,labelWidth:150,defaultType:"uxspinner",style:"margin-bottom: 0px; padding-bottom: 0px;",});b.bind("max_active_limit",a.add({fieldLabel:_("Total Active"),name:"max_active_limit",value:8,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("max_active_downloading",a.add({fieldLabel:_("Total Active Downloading"),name:"max_active_downloading",value:3,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("max_active_seeding",a.add({fieldLabel:_("Total Active Seeding"),name:"max_active_seeding",value:5,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("dont_count_slow_torrents",a.add({xtype:"checkbox",name:"dont_count_slow_torrents",height:40,hideLabel:true,boxLabel:_("Do not count slow torrents")}));a=this.add({xtype:"fieldset",border:false,title:_("Seeding"),autoHeight:true,labelWidth:150,defaultType:"uxspinner",style:"margin-bottom: 0px; padding-bottom: 0px; margin-top: 0; padding-top: 0;",});b.bind("share_ratio_limit",a.add({fieldLabel:_("Share Ratio Limit"),name:"share_ratio_limit",value:8,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("seed_time_ratio_limit",a.add({fieldLabel:_("Share Time Ratio"),name:"seed_time_ratio_limit",value:3,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("seed_time_limit",a.add({fieldLabel:_("Seed Time (m)"),name:"seed_time_limit",value:5,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));a=this.add({xtype:"fieldset",border:false,autoHeight:true,layout:"table",layoutConfig:{columns:2},labelWidth:0,defaultType:"checkbox",defaults:{fieldLabel:"",labelSeparator:""}});this.stopAtRatio=a.add({name:"stop_seed_at_ratio",boxLabel:_("Stop seeding when share ratio reaches:")});this.stopAtRatio.on("check",this.onStopRatioCheck,this);b.bind("stop_seed_at_ratio",this.stopAtRatio);this.stopRatio=a.add({xtype:"uxspinner",name:"stop_seed_ratio",ctCls:"x-deluge-indent-checkbox",disabled:true,value:2,width:60,strategy:{xtype:"number",minValue:-1,maxValue:99999,incrementValue:0.1,alternateIncrementValue:1,decimalPrecision:1}});b.bind("stop_seed_ratio",this.stopRatio);this.removeAtRatio=a.add({name:"remove_seed_at_ratio",ctCls:"x-deluge-indent-checkbox",boxLabel:_("Remove torrent when share ratio is reached"),disabled:true,colspan:2});b.bind("remove_seed_at_ratio",this.removeAtRatio)},onStopRatioCheck:function(b,a){this.stopRatio.setDisabled(!a);this.removeAtRatio.setDisabled(!a)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Queue());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.ProxyField=Ext.extend(Ext.form.FieldSet,{constructor:function(a){a=Ext.apply({border:false,autoHeight:true,labelWidth:70},a);Ext.deluge.preferences.ProxyField.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.ProxyField.superclass.initComponent.call(this);this.type=this.add({xtype:"combo",fieldLabel:_("Type"),name:"type",mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("None")],[1,_("Socksv4")],[2,_("Socksv5")],[3,_("Socksv5 with Auth")],[4,_("HTTP")],[5,_("HTTP with Auth")],]}),value:0,triggerAction:"all",valueField:"id",displayField:"text"});this.hostname=this.add({xtype:"textfield",name:"hostname",fieldLabel:_("Host"),width:220});this.port=this.add({xtype:"uxspinner",name:"port",fieldLabel:_("Port"),width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}});this.username=this.add({xtype:"textfield",name:"username",fieldLabel:_("Username"),width:220});this.password=this.add({xtype:"textfield",name:"password",fieldLabel:_("Password"),inputType:"password",width:220});this.type.on("change",this.onFieldChange,this);this.type.on("select",this.onTypeSelect,this);this.setting=false},getName:function(){return this.initialConfig.name},getValue:function(){return{type:this.type.getValue(),hostname:this.hostname.getValue(),port:Number(this.port.getValue()),username:this.username.getValue(),password:this.password.getValue()}},setValue:function(c){this.setting=true;this.type.setValue(c.type);var b=this.type.getStore().find("id",c.type);var a=this.type.getStore().getAt(b);this.hostname.setValue(c.hostname);this.port.setValue(c.port);this.username.setValue(c.username);this.password.setValue(c.password);this.onTypeSelect(this.type,a,b);this.setting=false},onFieldChange:function(e,d,c){if(this.setting){return}var b=this.getValue();var a=Ext.apply({},b);a[e.getName()]=c;this.fireEvent("change",this,b,a)},onTypeSelect:function(d,a,b){var c=a.get("id");if(c>0){this.hostname.show();this.port.show()}else{this.hostname.hide();this.port.hide()}if(c==3||c==5){this.username.show();this.password.show()}else{this.username.hide();this.password.hide()}}});Ext.deluge.preferences.Proxy=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Proxy"),layout:"form"},a);Ext.deluge.preferences.Proxy.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Proxy.superclass.initComponent.call(this);this.peer=this.add(new Ext.deluge.preferences.ProxyField({title:_("Peer"),name:"peer"}));this.peer.on("change",this.onProxyChange,this);this.web_seed=this.add(new Ext.deluge.preferences.ProxyField({title:_("Web Seed"),name:"web_seed"}));this.web_seed.on("change",this.onProxyChange,this);this.tracker=this.add(new Ext.deluge.preferences.ProxyField({title:_("Tracker"),name:"tracker"}));this.tracker.on("change",this.onProxyChange,this);this.dht=this.add(new Ext.deluge.preferences.ProxyField({title:_("DHT"),name:"dht"}));this.dht.on("change",this.onProxyChange,this);Deluge.Preferences.getOptionsManager().bind("proxies",this)},getValue:function(){return{dht:this.dht.getValue(),peer:this.peer.getValue(),tracker:this.tracker.getValue(),web_seed:this.web_seed.getValue()}},setValue:function(b){for(var a in b){this[a].setValue(b[a])}},onProxyChange:function(e,d,c){var b=this.getValue();var a=Ext.apply({},b);a[e.getName()]=c;this.fireEvent("change",this,b,a)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Proxy());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Cache=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Cache"),layout:"form"},a);Ext.deluge.preferences.Cache.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Cache.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Settings"),autoHeight:true,labelWidth:180,defaultType:"uxspinner"});b.bind("cache_size",a.add({fieldLabel:_("Cache Size (16 KiB Blocks)"),name:"cache_size",width:60,value:512,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("cache_expiry",a.add({fieldLabel:_("Cache Expiry (seconds)"),name:"cache_expiry",width:60,value:60,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Cache());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.InstallPlugin=Ext.extend(Ext.Window,{height:115,width:350,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",iconCls:"x-deluge-install-plugin",layout:"fit",modal:true,plain:true,title:_("Install Plugin"),initComponent:function(){Ext.deluge.add.FileWindow.superclass.initComponent.call(this);this.addButton(_("Install"),this.onInstall,this);this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:70,autoHeight:true,fileUpload:true,items:[{xtype:"fileuploadfield",id:"pluginEgg",emptyText:_("Select an egg"),fieldLabel:_("Plugin Egg"),name:"file",buttonCfg:{text:_("Browse")+"..."}}]})},onInstall:function(b,a){this.form.getForm().submit({url:"/upload",waitMsg:_("Uploading your plugin..."),success:this.onUploadSuccess,scope:this})},onUploadPlugin:function(d,c,a,b){this.fireEvent("pluginadded")},onUploadSuccess:function(c,b){this.hide();if(b.result.success){var a=this.form.getForm().findField("pluginEgg").value;var d=b.result.files[0];this.form.getForm().findField("pluginEgg").setValue("");Deluge.Client.web.upload_plugin(a,d,{success:this.onUploadPlugin,scope:this,filename:a})}}});Ext.deluge.preferences.Plugins=Ext.extend(Ext.Panel,{constructor:function(a){a=Ext.apply({border:false,title:_("Plugins"),layout:"border",height:400,cls:"x-deluge-plugins"},a);Ext.deluge.preferences.Plugins.superclass.constructor.call(this,a)},pluginTemplate:new Ext.Template('
Author:
{author}
Version:
{version}
Author Email:
{email}
Homepage:
{homepage}
Details:
{details}
'),initComponent:function(){Ext.deluge.preferences.Plugins.superclass.initComponent.call(this);this.defaultValues={version:"",email:"",homepage:"",details:""};this.pluginTemplate.compile();var b=function(d,e,c){e.css+=" x-grid3-check-col-td";return'
'};this.grid=this.add({xtype:"grid",region:"center",store:new Ext.data.SimpleStore({fields:[{name:"enabled",mapping:0},{name:"plugin",mapping:1}]}),columns:[{id:"enabled",header:_("Enabled"),width:50,sortable:true,renderer:b,dataIndex:"enabled"},{id:"plugin",header:_("Plugin"),sortable:true,dataIndex:"plugin"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onPluginSelect,scope:this}}}),autoExpandColumn:"plugin",deferredRender:false,autoScroll:true,margins:"5 5 5 5",bbar:new Ext.Toolbar({items:[{cls:"x-btn-text-icon",iconCls:"x-deluge-install-plugin",text:_("Install"),handler:this.onInstallPlugin,scope:this},"->",{cls:"x-btn-text-icon",text:_("Find More"),iconCls:"x-deluge-find-more",handler:this.onFindMorePlugins,scope:this}]})});var a=this.add({xtype:"fieldset",border:false,region:"south",title:_("Info"),autoHeight:true,labelWidth:1});this.pluginInfo=a.add({xtype:"panel",border:false,bodyCfg:{style:"margin-left: 10px"}});this.on("show",this.onShow,this);this.pluginInfo.on("render",this.onPluginInfoRender,this);this.grid.on("cellclick",this.onCellClick,this);Deluge.Preferences.on("show",this.onPreferencesShow,this);Deluge.Events.on("PluginDisabledEvent",this.onPluginDisabled,this);Deluge.Events.on("PluginEnabledEvent",this.onPluginEnabled,this)},disablePlugin:function(a){Deluge.Client.core.disable_plugin(a)},enablePlugin:function(a){Deluge.Client.core.enable_plugin(a)},setInfo:function(b){if(!this.pluginInfo.rendered){return}var a=b||this.defaultValues;this.pluginInfo.body.dom.innerHTML=this.pluginTemplate.apply(a)},updatePlugins:function(){Deluge.Client.web.get_plugins({success:this.onGotPlugins,scope:this})},updatePluginsGrid:function(){var a=[];Ext.each(this.availablePlugins,function(b){if(this.enabledPlugins.indexOf(b)>-1){a.push([true,b])}else{a.push([false,b])}},this);this.grid.getStore().loadData(a)},onCellClick:function(b,f,a,d){if(a!=0){return}var c=b.getStore().getAt(f);c.set("enabled",!c.get("enabled"));c.commit();if(c.get("enabled")){this.enablePlugin(c.get("plugin"))}else{this.disablePlugin(c.get("plugin"))}},onFindMorePlugins:function(){window.open("http://dev.deluge-torrent.org/wiki/Plugins")},onGotPlugins:function(a){this.enabledPlugins=a.enabled_plugins;this.availablePlugins=a.available_plugins;this.setInfo();this.updatePluginsGrid()},onGotPluginInfo:function(b){var a={author:b.Author,version:b.Version,email:b["Author-email"],homepage:b["Home-page"],details:b.Description};this.setInfo(a);delete b},onInstallPlugin:function(){if(!this.installWindow){this.installWindow=new Ext.deluge.preferences.InstallPlugin();this.installWindow.on("pluginadded",this.onPluginInstall,this)}this.installWindow.show()},onPluginEnabled:function(c){var a=this.grid.getStore().find("plugin",c);var b=this.grid.getStore().getAt(a);b.set("enabled",true);b.commit()},onPluginDisabled:function(c){var a=this.grid.getStore().find("plugin",c);var b=this.grid.getStore().getAt(a);b.set("enabled",false);b.commit()},onPluginInstall:function(){this.updatePlugins()},onPluginSelect:function(b,c,a){Deluge.Client.web.get_plugin_info(a.get("plugin"),{success:this.onGotPluginInfo,scope:this})},onPreferencesShow:function(){this.updatePlugins()},onPluginInfoRender:function(b,a){this.setInfo()}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Plugins());Ext.deluge.RemoveWindow=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Remove Torrent"),layout:"fit",width:350,height:100,buttonAlign:"right",closeAction:"hide",closable:true,plain:true,iconCls:"x-deluge-remove-window-icon"},a);Ext.deluge.RemoveWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.RemoveWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Remove With Data"),this.onRemoveData,this);this.addButton(_("Remove Torrent"),this.onRemove,this);this.add({border:false,bodyStyle:"padding: 5px; padding-left: 10px;",html:"Are you sure you wish to remove the torrent(s)?"})},remove:function(a){Ext.each(this.torrentIds,function(b){Deluge.Client.core.remove_torrent(b,a,{success:function(){this.onRemoved(b)},scope:this,torrentId:b})},this)},show:function(a){Ext.deluge.RemoveWindow.superclass.show.call(this);this.torrentIds=a},onCancel:function(){this.hide();this.torrentIds=null},onRemove:function(){this.remove(false)},onRemoveData:function(){this.remove(true)},onRemoved:function(a){Deluge.Events.fire("torrentRemoved",a);this.hide();Deluge.UI.update()}});Deluge.RemoveWindow=new Ext.deluge.RemoveWindow();(function(){function a(d,f,c){var b=d.toLowerCase().replace(".","_");var e="";if(c.store.id=="tracker_host"){if(d!="Error"){e=String.format("url(/tracker/{0})",d)}else{b=null}}if(e){return String.format('
{0} ({1})
',d,c.data.count,e)}else{if(b){return String.format('
{0} ({1})
',d,c.data.count,b)}else{return String.format('
{0} ({1})
',d,c.data.count)}}}Ext.deluge.Sidebar=Ext.extend(Ext.Panel,{panels:{},selected:null,constructor:function(b){b=Ext.apply({id:"sidebar",region:"west",cls:"deluge-sidebar",title:_("Filters"),layout:"accordion",split:true,width:200,minSize:175,collapsible:true,margins:"5 0 0 5",cmargins:"5 0 0 5"},b);Ext.deluge.Sidebar.superclass.constructor.call(this,b)},initComponent:function(){Ext.deluge.Sidebar.superclass.initComponent.call(this);Deluge.Events.on("disconnect",this.onDisconnect,this)},createFilter:function(e,d){var c=new Ext.data.SimpleStore({id:e,fields:[{name:"filter"},{name:"count"}]});var g=e.replace("_"," ");var f=g.split(" ");g="";Ext.each(f,function(h){firstLetter=h.substring(0,1);firstLetter=firstLetter.toUpperCase();h=firstLetter+h.substring(1);g+=h+" "});var b=new Ext.grid.GridPanel({id:e+"-panel",border:false,store:c,title:_(g),columns:[{id:"filter",sortable:false,renderer:a,dataIndex:"filter"}],stripeRows:false,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onFilterSelect,scope:this}}}),hideHeaders:true,autoExpandColumn:"filter",deferredRender:false,autoScroll:true});if(Deluge.config.sidebar_show_zero==false){d=this.removeZero(d)}c.loadData(d);this.add(b);this.doLayout();this.panels[e]=b;if(!this.selected){b.getSelectionModel().selectFirstRow();this.selected={row:0,filter:d[0][0],panel:b}}},getFilters:function(){var c={};if(!this.selected){return c}if(!this.selected.filter||!this.selected.panel){return c}var b=this.selected.panel.store.id;if(b=="state"&&this.selected.filter=="All"){return c}c[b]=this.selected.filter;return c},onDisconnect:function(){Ext.each(Ext.getKeys(this.panels),function(b){this.remove(b+"-panel")},this);this.panels={};this.selected=null},onFilterSelect:function(c,d,b){if(!this.selected){needsUpdate=true}else{if(this.selected.row!=d){needsUpdate=true}else{needsUpdate=false}}this.selected={row:d,filter:b.get("filter"),panel:this.panels[b.store.id]};if(needsUpdate){Deluge.UI.update()}},removeZero:function(b){var c=[];Ext.each(b,function(d){if(d[1]>0||d[0]==_("All")){c.push(d)}});return c},update:function(d){for(var c in d){var b=d[c];if(Ext.getKeys(this.panels).indexOf(c)>-1){this.updateFilter(c,b)}else{this.createFilter(c,b)}}Ext.each(Ext.keys(this.panels),function(e){if(Ext.keys(d).indexOf(e)==-1){this.panels[e]}},this)},updateFilter:function(c,b){if(Deluge.config.sidebar_show_zero==false){b=this.removeZero(b)}this.panels[c].store.loadData(b);if(this.selected&&this.selected.panel==this.panels[c]){this.panels[c].getSelectionModel().selectRow(this.selected.row)}}});Deluge.Sidebar=new Ext.deluge.Sidebar()})();(function(){Ext.deluge.Statusbar=Ext.extend(Ext.StatusBar,{constructor:function(a){a=Ext.apply({id:"deluge-statusbar",defaultIconCls:"x-not-connected",defaultText:_("Not Connected")},a);Ext.deluge.Statusbar.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.Statusbar.superclass.initComponent.call(this);Deluge.Events.on("connect",this.onConnect,this);Deluge.Events.on("disconnect",this.onDisconnect,this)},createButtons:function(){this.add({id:"statusbar-connections",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-connections",tooltip:_("Connections"),menu:Deluge.Menus.Connections},"-",{id:"statusbar-downspeed",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-downloading",tooltip:_("Download Speed"),menu:Deluge.Menus.Download},"-",{id:"statusbar-upspeed",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-seeding",tooltip:_("Upload Speed"),menu:Deluge.Menus.Upload},"-",{id:"statusbar-traffic",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-traffic",tooltip:_("Protocol Traffic Download/Upload")},"-",{id:"statusbar-dht",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-dht",tooltip:_("DHT Nodes")},"-",{id:"statusbar-freespace",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-freespace",tooltip:_("Freespace in download location")});this.created=true},onConnect:function(){this.setStatus({iconCls:"x-connected",text:""});if(!this.created){this.createButtons()}else{this.items.each(function(a){a.show();a.enable()})}},onDisconnect:function(){this.clearStatus({useDefaults:true});this.items.each(function(a){a.hide();a.disable()})},update:function(b){if(!b){return}function c(d){return d+" KiB/s"}var a=function(f,e){var g=this.items.get("statusbar-"+f);if(e.limit.value>0){var h=(e.value.formatter)?e.value.formatter(e.value.value):e.value.value;var d=(e.limit.formatter)?e.limit.formatter(e.limit.value):e.limit.value;var j=String.format(e.format,h,d)}else{var j=(e.value.formatter)?e.value.formatter(e.value.value):e.value.value}g.setText(j)}.bind(this);a("connections",{value:{value:b.num_connections},limit:{value:b.max_num_connections},format:"{0} ({1})"});a("downspeed",{value:{value:b.download_rate,formatter:Deluge.Formatters.speed},limit:{value:b.max_download,formatter:c},format:"{0} ({1})"});a("upspeed",{value:{value:b.upload_rate,formatter:Deluge.Formatters.speed},limit:{value:b.max_upload,formatter:c},format:"{0} ({1})"});a("traffic",{value:{value:b.download_protocol_rate,formatter:Deluge.Formatters.speed},limit:{value:b.upload_protocol_rate,formatter:Deluge.Formatters.speed},format:"{0}/{1}"});this.items.get("statusbar-dht").setText(b.dht_nodes);this.items.get("statusbar-freespace").setText(fsize(b.free_space));Deluge.Menus.Connections.setValue(b.max_num_connections);Deluge.Menus.Download.setValue(b.max_download);Deluge.Menus.Upload.setValue(b.max_upload)}});Deluge.Statusbar=new Ext.deluge.Statusbar()})();(function(){Ext.deluge.Toolbar=Ext.extend(Ext.Toolbar,{constructor:function(a){a=Ext.apply({items:[{id:"create",cls:"x-btn-text-icon",disabled:true,text:_("Create"),icon:"/icons/create.png",handler:this.onTorrentAction},{id:"add",cls:"x-btn-text-icon",disabled:true,text:_("Add"),icon:"/icons/add.png",handler:this.onTorrentAdd},{id:"remove",cls:"x-btn-text-icon",disabled:true,text:_("Remove"),icon:"/icons/remove.png",handler:this.onTorrentAction},"|",{id:"pause",cls:"x-btn-text-icon",disabled:true,text:_("Pause"),icon:"/icons/pause.png",handler:this.onTorrentAction},{id:"resume",cls:"x-btn-text-icon",disabled:true,text:_("Resume"),icon:"/icons/start.png",handler:this.onTorrentAction},"|",{id:"up",cls:"x-btn-text-icon",disabled:true,text:_("Up"),icon:"/icons/up.png",handler:this.onTorrentAction},{id:"down",cls:"x-btn-text-icon",disabled:true,text:_("Down"),icon:"/icons/down.png",handler:this.onTorrentAction},"|",{id:"preferences",cls:"x-btn-text-icon",text:_("Preferences"),iconCls:"x-deluge-preferences",handler:this.onPreferencesClick,scope:this},{id:"connectionman",cls:"x-btn-text-icon",text:_("Connection Manager"),iconCls:"x-deluge-connection-manager",handler:this.onConnectionManagerClick,scope:this},"->",{id:"help",cls:"x-btn-text-icon",icon:"/icons/help.png",text:_("Help"),handler:this.onHelpClick,scope:this},{id:"logout",cls:"x-btn-text-icon",icon:"/icons/logout.png",disabled:true,text:_("Logout"),handler:this.onLogout,scope:this}]},a);Ext.deluge.Toolbar.superclass.constructor.call(this,a)},connectedButtons:["add","remove","pause","resume","up","down"],initComponent:function(){Ext.deluge.Toolbar.superclass.initComponent.call(this);Deluge.Events.on("connect",this.onConnect,this);Deluge.Events.on("login",this.onLogin,this)},onConnect:function(){Ext.each(this.connectedButtons,function(a){this.items.get(a).enable()},this)},onDisconnect:function(){Ext.each(this.connectedButtons,function(a){this.items.get(a).disable()},this)},onLogin:function(){this.items.get("logout").enable()},onLogout:function(){this.items.get("logout").disable();Deluge.Login.logout()},onConnectionManagerClick:function(){Deluge.ConnectionManager.show()},onHelpClick:function(){window.open("http://dev.deluge-torrent.org/wiki/UserGuide")},onPreferencesClick:function(){Deluge.Preferences.show()},onTorrentAction:function(c){var b=Deluge.Torrents.getSelections();var a=[];Ext.each(b,function(d){a.push(d.id)});switch(c.id){case"remove":Deluge.RemoveWindow.show(a);break;case"pause":case"resume":Deluge.Client.core[c.id+"_torrent"](a,{success:function(){Deluge.UI.update()}});break;case"up":case"down":Deluge.Client.core["queue_"+c.id](a,{success:function(){Deluge.UI.update()}});break}},onTorrentAdd:function(){Deluge.Add.show()}});Deluge.Toolbar=new Ext.deluge.Toolbar()})();(function(){function c(k){return(k==99999)?"":k+1}function e(l,m,k){return String.format('
{1}
',k.data.state.toLowerCase(),l)}function g(k){if(!k){return}return fspeed(k)}function j(n,o,m){n=new Number(n);var k=n;var q=m.data.state+" "+n.toFixed(2)+"%";var l=new Number(this.style.match(/\w+:\s*(\d+)\w+/)[1])-8;return Deluge.progressBar(n,l,q)}function a(l,m,k){if(k.data.total_seeds>-1){return String.format("{0} ({1})",l,k.data.total_seeds)}else{return l}}function d(l,m,k){if(k.data.total_peers>-1){return String.format("{0} ({1})",l,k.data.total_peers)}else{return l}}function b(l,m,k){return(l<0)?"∞":new Number(l).toFixed(3)}function f(l,m,k){return String.format('
{0}
',l)}function h(k){return k*-1}Ext.deluge.TorrentGrid=Ext.extend(Ext.grid.GridPanel,{constructor:function(k){k=Ext.apply({id:"torrentGrid",store:new Ext.data.SimpleStore({fields:[{name:"queue"},{name:"name"},{name:"size",type:"int"},{name:"state"},{name:"progress",type:"float"},{name:"seeds",type:"int"},{name:"total_seeds",type:"int"},{name:"peers",type:"int"},{name:"total_peers",type:"int"},{name:"downspeed",type:"int"},{name:"upspeed",type:"int"},{name:"eta",type:"int",sortType:h},{name:"ratio",type:"float"},{name:"avail",type:"float"},{name:"added",type:"int"},{name:"tracker"}],id:16}),columns:[{id:"queue",header:_("#"),width:30,sortable:true,renderer:c,dataIndex:"queue"},{id:"name",header:_("Name"),width:150,sortable:true,renderer:e,dataIndex:"name"},{header:_("Size"),width:75,sortable:true,renderer:fsize,dataIndex:"size"},{header:_("Progress"),width:150,sortable:true,renderer:j,dataIndex:"progress"},{header:_("Seeders"),width:60,sortable:true,renderer:a,dataIndex:"seeds"},{header:_("Peers"),width:60,sortable:true,renderer:d,dataIndex:"peers"},{header:_("Down Speed"),width:80,sortable:true,renderer:g,dataIndex:"downspeed"},{header:_("Up Speed"),width:80,sortable:true,renderer:g,dataIndex:"upspeed"},{header:_("ETA"),width:60,sortable:true,renderer:ftime,dataIndex:"eta"},{header:_("Ratio"),width:60,sortable:true,renderer:b,dataIndex:"ratio"},{header:_("Avail"),width:60,sortable:true,renderer:b,dataIndex:"avail"},{header:_("Added"),width:80,sortable:true,renderer:fdate,dataIndex:"added"},{header:_("Tracker"),width:120,sortable:true,renderer:f,dataIndex:"tracker"}],region:"center",cls:"deluge-torrents",stripeRows:true,autoExpandColumn:"name",deferredRender:false,autoScroll:true,margins:"5 5 0 0",stateful:true},k);Ext.deluge.TorrentGrid.superclass.constructor.call(this,k)},initComponent:function(){Ext.deluge.TorrentGrid.superclass.initComponent.call(this);Deluge.Events.on("torrentRemoved",this.onTorrentRemoved,this);this.on("rowcontextmenu",function(k,n,m){m.stopEvent();var l=k.getSelectionModel();if(!l.hasSelection()){l.selectRow(n)}Deluge.Menus.Torrent.showAt(m.getPoint())})},getTorrent:function(k){return this.getStore().getAt(k)},getSelected:function(){return this.getSelectionModel().getSelected()},getSelections:function(){return this.getSelectionModel().getSelections()},update:function(o){var m=this.getStore();for(var l in o){var k=m.getById(l);var q=o[l];if(!k){var p=[((q.queue==-1)?99999:q.queue),q.name,q.total_size,q.state,q.progress,q.num_seeds,q.total_seeds,q.num_peers,q.total_peers,q.download_payload_rate,q.upload_payload_rate,q.eta,q.ratio,q.distributed_copies,q.time_added,q.tracker_host,l];m.loadData([p],true)}else{k.set("queue",((q.queue==-1)?99999:q.queue));k.set("name",q.name);k.set("size",q.total_size);k.set("state",q.state);k.set("progress",q.progress);k.set("seeds",q.num_seeds);k.set("total_seeds",q.total_seeds);k.set("peers",q.num_peers);k.set("total_peers",q.total_peers);k.set("downspeed",q.download_payload_rate);k.set("upspeed",q.upload_payload_rate);k.set("eta",q.eta);k.set("ratio",q.ratio);k.set("avail",q.distributed_copies);k.set("added",q.time_added);k.set("tracker",q.tracker_host);k.commit()}}var n=Ext.keys(o);m.each(function(r){if(n.indexOf(r.id)==-1){m.remove(r)}},this)},onTorrentRemoved:function(l){var k=this.getSelectionModel();Ext.each(l,function(n){var m=this.getStore().getById(n);if(k.isSelected(m)){k.deselectRow(this.getStore().indexOf(m))}this.getStore().remove(m)},this)}});Deluge.Torrents=new Ext.deluge.TorrentGrid()})();Deluge.UI={errorCount:0,initialize:function(){this.MainPanel=new Ext.Panel({id:"mainPanel",iconCls:"x-deluge-main-panel",title:"Deluge",layout:"border",tbar:Deluge.Toolbar,items:[Deluge.Sidebar,Deluge.Details,Deluge.Torrents],bbar:Deluge.Statusbar});this.Viewport=new Ext.Viewport({layout:"fit",items:[this.MainPanel]});Deluge.Events.on("connect",this.onConnect,this);Deluge.Events.on("disconnect",this.onDisconnect,this);Deluge.Client=new Ext.ux.util.RpcClient({url:"/json"});for(var a in Deluge.Plugins){a=Deluge.Plugins[a];a.enable()}Ext.QuickTips.init();Deluge.Client.on("connected",function(b){Deluge.Login.show();Deluge.Events.start();Deluge.Events.on("PluginEnabledEvent",this.onPluginEnabled,this);Deluge.Events.on("PluginDisabledEvent",this.onPluginDisabled,this)},this,{single:true});this.update=this.update.bind(this)},update:function(){var a=Deluge.Sidebar.getFilters();Deluge.Client.web.update_ui(Deluge.Keys.Grid,a,{success:this.onUpdate,failure:this.onUpdateError,scope:this});Deluge.Details.update()},onUpdateError:function(a){if(this.errorCount==2){Ext.MessageBox.show({title:"Lost Connection",msg:"The connection to the webserver has been lost!",buttons:Ext.MessageBox.OK,icon:Ext.MessageBox.ERROR})}this.errorCount++},onUpdate:function(a){if(!a.connected){Deluge.Events.fire("disconnect")}Deluge.Torrents.update(a.torrents);Deluge.Statusbar.update(a.stats);Deluge.Sidebar.update(a.filters);this.errorCount=0},onConnect:function(){if(!this.running){this.running=setInterval(this.update,2000);this.update()}},onDisconnect:function(){this.stop()},onPluginEnabled:function(a){Deluge.Client.web.get_plugin_resources(a,{success:this.onGotPluginResources,scope:this})},onGotPluginResources:function(b){var a=(Deluge.debug)?b.debug_scripts:b.scripts;Ext.each(a,function(c){Ext.ux.JSLoader({url:c,onLoad:this.onPluginLoaded,pluginName:b.name})},this)},onPluginDisabled:function(a){Deluge.Plugins[a].disable()},onPluginLoaded:function(a){if(!Deluge.Plugins[a.pluginName]){return}Deluge.Plugins[a.pluginName].enable()},stop:function(){if(this.running){clearInterval(this.running);this.running=false;Deluge.Torrents.getStore().removeAll()}}};Ext.onReady(function(a){Deluge.UI.initialize()}); \ No newline at end of file +Ext.namespace("Ext.deluge");Ext.state.Manager.setProvider(new Ext.state.CookieProvider());(function(){Ext.apply(Function.prototype,{bind:function(b){var a=this;return function(){return a.apply(b,arguments)}}});Ext.apply(Ext,{escapeHTML:function(a){a=String(a).replace("<","<").replace(">",">");return a.replace("&","&")},isObjectEmpty:function(b){for(var a in b){return false}return true},keys:function(c){var b=[];for(var a in c){if(c.hasOwnProperty(a)){b.push(a)}}return b},values:function(c){var a=[];for(var b in c){if(c.hasOwnProperty(b)){a.push(c[b])}}return a},splat:function(b){var a=Ext.type(b);return(a)?((a!="array")?[b]:b):[]}});Ext.getKeys=Ext.keys;Ext.BLANK_IMAGE_URL="/images/s.gif";Ext.USE_NATIVE_JSON=true})();(function(){var a='
{0}
{0}
';Deluge.progressBar=function(d,f,h,b){b=Ext.value(b,10);var c=((f/100)*d).toFixed(0);var e=c-1;var g=((c-b)>0?c-b:0);return String.format(a,h,f,e,g)};Deluge.Plugins={}})();FILE_PRIORITY={0:"Do Not Download",1:"Normal Priority",2:"High Priority",5:"Highest Priority","Do Not Download":0,"Normal Priority":1,"High Priority":2,"Highest Priority":5};FILE_PRIORITY_CSS={0:"x-no-download",1:"x-normal-download",2:"x-high-download",5:"x-highest-download"};Deluge.Formatters={date:function(c){function b(d,e){var f=d+"";while(f.length0){return b+"m "+d+"s"}else{return b+"m"}}else{c=c/60}if(c<24){var a=Math.floor(c);var b=Math.round(60*(c-a));if(b>0){return a+"h "+b+"m"}else{return a+"h"}}else{c=c/24}var e=Math.floor(c);var a=Math.round(24*(c-e));if(a>0){return e+"d "+a+"h"}else{return e+"d"}},plain:function(a){return a}};var fsize=Deluge.Formatters.size;var fspeed=Deluge.Formatters.speed;var ftime=Deluge.Formatters.timeRemaining;var fdate=Deluge.Formatters.date;var fplain=Deluge.Formatters.plain;Deluge.Keys={Grid:["queue","name","total_size","state","progress","num_seeds","total_seeds","num_peers","total_peers","download_payload_rate","upload_payload_rate","eta","ratio","distributed_copies","is_auto_managed","time_added","tracker_host"],Status:["total_done","total_payload_download","total_uploaded","total_payload_upload","next_announce","tracker_status","num_pieces","piece_length","is_auto_managed","active_time","seeding_time","seed_rank"],Files:["files","file_progress","file_priorities"],Peers:["peers"],Details:["name","save_path","total_size","num_files","tracker_status","tracker","comment"],Options:["max_download_speed","max_upload_speed","max_connections","max_upload_slots","is_auto_managed","stop_at_ratio","stop_ratio","remove_at_ratio","private","prioritize_first_last"]};Ext.each(Deluge.Keys.Grid,function(a){Deluge.Keys.Status.push(a)});Deluge.Menus={onTorrentAction:function(c,d){var b=Deluge.Torrents.getSelections();var a=[];Ext.each(b,function(e){a.push(e.id)});switch(c.id){case"pause":case"resume":Deluge.Client.core[c.id+"_torrent"](a,{success:function(){Deluge.UI.update()}});break;case"top":case"up":case"down":case"bottom":Deluge.Client.core["queue_"+c.id](a,{success:function(){Deluge.UI.update()}});break;case"edit_trackers":Deluge.EditTrackers.show();break;case"update":Deluge.Client.core.force_reannounce(a,{success:function(){Deluge.UI.update()}});break;case"remove":Deluge.RemoveWindow.show(a);break;case"recheck":Deluge.Client.core.force_recheck(a,{success:function(){Deluge.UI.update()}});break;case"move":Deluge.MoveStorage.show(a);break}}};Deluge.Menus.Torrent=new Ext.menu.Menu({id:"torrentMenu",items:[{id:"pause",text:_("Pause"),icon:"/icons/pause.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"resume",text:_("Resume"),icon:"/icons/start.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},"-",{id:"options",text:_("Options"),icon:"/icons/preferences.png",menu:new Ext.menu.Menu({items:[{text:_("D/L Speed Limit"),iconCls:"x-deluge-downloading",menu:new Ext.menu.Menu({items:[{text:_("5 KiB/s")},{text:_("10 KiB/s")},{text:_("30 KiB/s")},{text:_("80 KiB/s")},{text:_("300 KiB/s")},{text:_("Unlimited")}]})},{text:_("U/L Speed Limit"),iconCls:"x-deluge-seeding",menu:new Ext.menu.Menu({items:[{text:_("5 KiB/s")},{text:_("10 KiB/s")},{text:_("30 KiB/s")},{text:_("80 KiB/s")},{text:_("300 KiB/s")},{text:_("Unlimited")}]})},{text:_("Connection Limit"),iconCls:"x-deluge-connections",menu:new Ext.menu.Menu({items:[{text:_("50")},{text:_("100")},{text:_("200")},{text:_("300")},{text:_("500")},{text:_("Unlimited")}]})},{text:_("Upload Slot Limit"),icon:"/icons/upload_slots.png",menu:new Ext.menu.Menu({items:[{text:_("0")},{text:_("1")},{text:_("2")},{text:_("3")},{text:_("5")},{text:_("Unlimited")}]})},{id:"auto_managed",text:_("Auto Managed"),checked:false}]})},"-",{text:_("Queue"),icon:"/icons/queue.png",menu:new Ext.menu.Menu({items:[{id:"top",text:_("Top"),icon:"/icons/top.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"up",text:_("Up"),icon:"/icons/up.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"down",text:_("Down"),icon:"/icons/down.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"bottom",text:_("Bottom"),icon:"/icons/bottom.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus}]})},"-",{id:"update",text:_("Update Tracker"),icon:"/icons/update.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"edit_trackers",text:_("Edit Trackers"),icon:"/icons/edit_trackers.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},"-",{id:"remove",text:_("Remove Torrent"),icon:"/icons/remove.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},"-",{id:"recheck",text:_("Force Recheck"),icon:"/icons/recheck.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus},{id:"move",text:_("Move Storage"),icon:"/icons/move.png",handler:Deluge.Menus.onTorrentAction,scope:Deluge.Menus}]});Ext.deluge.StatusbarMenu=Ext.extend(Ext.menu.Menu,{setValue:function(b){b=(b==0)?-1:b;var a=this.items.get(b);if(!a){a=this.items.get("other")}a.suspendEvents();a.setChecked(true);a.resumeEvents()}});Deluge.Menus.Connections=new Ext.deluge.StatusbarMenu({id:"connectionsMenu",items:[{id:"50",text:"50",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"100",text:"100",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"200",text:"200",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"300",text:"300",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"500",text:"500",group:"max_connections_global",checked:false,checkHandler:onLimitChanged},{id:"-1",text:_("Unlimited"),group:"max_connections_global",checked:false,checkHandler:onLimitChanged},"-",{id:"other",text:_("Other"),group:"max_connections_global",checked:false,checkHandler:onLimitChanged}]});Deluge.Menus.Download=new Ext.deluge.StatusbarMenu({id:"downspeedMenu",items:[{id:"5",text:"5 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"10",text:"10 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"30",text:"30 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"80",text:"80 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"300",text:"300 KiB/s",group:"max_download_speed",checked:false,checkHandler:onLimitChanged},{id:"-1",text:_("Unlimited"),group:"max_download_speed",checked:false,checkHandler:onLimitChanged},"-",{id:"other",text:_("Other"),group:"max_download_speed",checked:false,checkHandler:onLimitChanged}]});Deluge.Menus.Upload=new Ext.deluge.StatusbarMenu({id:"upspeedMenu",items:[{id:"5",text:"5 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"10",text:"10 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"30",text:"30 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"80",text:"80 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"300",text:"300 KiB/s",group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},{id:"-1",text:_("Unlimited"),group:"max_upload_speed",checked:false,checkHandler:onLimitChanged},"-",{id:"other",text:_("Other"),group:"max_upload_speed",checked:false,checkHandler:onLimitChanged}]});Deluge.Menus.FilePriorities=new Ext.menu.Menu({id:"filePrioritiesMenu",items:[{id:"expandAll",text:_("Expand All"),icon:"/icons/expand_all.png"},"-",{id:"no_download",text:_("Do Not Download"),icon:"/icons/no_download.png",filePriority:0},{id:"normal",text:_("Normal Priority"),icon:"/icons/normal.png",filePriority:1},{id:"high",text:_("High Priority"),icon:"/icons/high.png",filePriority:2},{id:"highest",text:_("Highest Priority"),icon:"/icons/highest.png",filePriority:5}]});function onLimitChanged(b,a){if(b.id=="other"){}else{config={};config[b.group]=b.id;Deluge.Client.core.set_config(config,{success:function(){Deluge.UI.update()}})}}(function(){Events=Ext.extend(Ext.util.Observable,{constructor:function(){this.toRegister=[];this.on("login",this.onLogin,this);Events.superclass.constructor.call(this)},addListener:function(a,c,b,d){this.addEvents(a);if(/[A-Z]/.test(a.substring(0,1))){if(!Deluge.Client){this.toRegister.push(a)}else{Deluge.Client.web.register_event_listener(a)}}Events.superclass.addListener.call(this,a,c,b,d)},poll:function(){Deluge.Client.web.get_events({success:this.onPollSuccess,scope:this})},start:function(){Ext.each(this.toRegister,function(a){Deluge.Client.web.register_event_listener(a)});this.poll=this.poll.bind(this);this.running=setInterval(this.poll,2000);this.poll()},stop:function(){if(this.running){clearInterval(this.running)}},onLogin:function(){this.start();this.on("PluginEnabledEvent",this.onPluginEnabled,this);this.on("PluginDisabledEvent",this.onPluginDisabled,this)},onPollSuccess:function(a){if(!a){return}Ext.each(a,function(d){var c=d[0],b=d[1];b.splice(0,0,c);this.fireEvent.apply(this,b)},this)}});Events.prototype.on=Events.prototype.addListener;Events.prototype.fire=Events.prototype.fireEvent;Deluge.Events=new Events()})();Ext.namespace("Deluge");Deluge.OptionsManager=Ext.extend(Ext.util.Observable,{constructor:function(a){a=a||{};this.binds={};this.changed={};this.options=(a&&a.options)||{};this.focused=null;this.addEvents({add:true,changed:true,reset:true});this.on("changed",this.onChange,this);Deluge.OptionsManager.superclass.constructor.call(this)},addOptions:function(a){this.options=Ext.applyIf(this.options,a)},bind:function(a,b){this.binds[a]=this.binds[a]||[];this.binds[a].push(b);b._doption=a;b.on("focus",this.onFieldFocus,this);b.on("blur",this.onFieldBlur,this);b.on("change",this.onFieldChange,this);b.on("check",this.onFieldChange,this);return b},commit:function(){this.options=Ext.apply(this.options,this.changed);this.reset()},convertValueType:function(a,b){if(Ext.type(a)!=Ext.type(b)){switch(Ext.type(a)){case"string":b=String(b);break;case"number":b=Number(b);break;case"boolean":if(Ext.type(b)=="string"){b=b.toLowerCase();b=(b=="true"||b=="1"||b=="on")?true:false}else{b=Boolean(b)}break}}return b},get:function(){if(arguments.length==1){var b=arguments[0];return(this.isDirty(b))?this.changed[b]:this.options[b]}else{var a={};Ext.each(arguments,function(c){if(!this.has(c)){return}a[c]=(this.isDirty(c))?this.changed[c]:this.options[c]},this);return a}},getDefault:function(a){return this.options[a]},getDirty:function(){return this.changed},isDirty:function(a){return !Ext.isEmpty(this.changed[a])},has:function(a){return(this.options[a])},reset:function(){this.changed={}},set:function(b,c){if(b===undefined){return}else{if(typeof b=="object"){var a=b;this.options=Ext.apply(this.options,a);for(var b in a){this.onChange(b,a[b])}}else{this.options[b]=c;this.onChange(b,c)}}},update:function(d,e){if(d===undefined){return}else{if(e===undefined){for(var c in d){this.update(c,d[c])}}else{var a=this.getDefault(d);e=this.convertValueType(a,e);var b=this.get(d);if(b==e){return}if(a==e){if(this.isDirty(d)){delete this.changed[d]}this.fireEvent("changed",d,e,b);return}this.changed[d]=e;this.fireEvent("changed",d,e,b)}}},onFieldBlur:function(b,a){if(this.focused==b){this.focused=null}},onFieldChange:function(b,a){this.update(b._doption,b.getValue())},onFieldFocus:function(b,a){this.focused=b},onChange:function(b,c,a){if(Ext.isEmpty(this.binds[b])){return}Ext.each(this.binds[b],function(d){if(d==this.focused){return}d.setValue(c)},this)}});Deluge.MultiOptionsManager=Ext.extend(Deluge.OptionsManager,{constructor:function(a){this.currentId=null;this.stored={};Deluge.MultiOptionsManager.superclass.constructor.call(this,a)},changeId:function(d,b){var c=this.currentId;this.currentId=d;if(!b){for(var a in this.options){if(!this.binds[a]){continue}Ext.each(this.binds[a],function(e){e.setValue(this.get(a))},this)}}return c},commit:function(){this.stored[this.currentId]=Ext.apply(this.stored[this.currentId],this.changed[this.currentId]);this.reset()},get:function(){if(arguments.length==1){var b=arguments[0];return(this.isDirty(b))?this.changed[this.currentId][b]:this.getDefault(b)}else{if(arguments.length==0){var a={};for(var b in this.options){a[b]=(this.isDirty(b))?this.changed[this.currentId][b]:this.getDefault(b)}return a}else{var a={};Ext.each(arguments,function(c){a[c]=(this.isDirty(c))?this.changed[this.currentId][c]:this.getDefault(c)},this);return a}}},getDefault:function(a){return(this.has(a))?this.stored[this.currentId][a]:this.options[a]},getDirty:function(){return(this.changed[this.currentId])?this.changed[this.currentId]:{}},isDirty:function(a){return(this.changed[this.currentId]&&!Ext.isEmpty(this.changed[this.currentId][a]))},has:function(a){return(this.stored[this.currentId]&&!Ext.isEmpty(this.stored[this.currentId][a]))},reset:function(){if(this.changed[this.currentId]){delete this.changed[this.currentId]}if(this.stored[this.currentId]){delete this.stored[this.currentId]}},resetAll:function(){this.changed={};this.stored={};this.changeId(null)},setDefault:function(c,d){if(c===undefined){return}else{if(d===undefined){for(var b in c){this.setDefault(b,c[b])}}else{var a=this.getDefault(c);d=this.convertValueType(a,d);if(a==d){return}if(!this.stored[this.currentId]){this.stored[this.currentId]={}}this.stored[this.currentId][c]=d;if(!this.isDirty(c)){this.fireEvent("changed",this.currentId,c,d,a)}}}},update:function(d,e){if(d===undefined){return}else{if(e===undefined){for(var c in d){this.update(c,d[c])}}else{if(!this.changed[this.currentId]){this.changed[this.currentId]={}}var a=this.getDefault(d);e=this.convertValueType(a,e);var b=this.get(d);if(b==e){return}if(a==e){if(this.isDirty(d)){delete this.changed[this.currentId][d]}this.fireEvent("changed",this.currentId,d,e,b);return}else{this.changed[this.currentId][d]=e;this.fireEvent("changed",this.currentId,d,e,b)}}}},onFieldChange:function(b,a){this.update(b._doption,b.getValue())},onChange:function(d,b,c,a){if(Ext.isEmpty(this.binds[b])){return}Ext.each(this.binds[b],function(e){if(e==this.focused){return}e.setValue(c)},this)}});Ext.namespace("Ext.deluge.add");Ext.deluge.add.OptionsPanel=Ext.extend(Ext.TabPanel,{torrents:{},constructor:function(a){a=Ext.apply({region:"south",margins:"5 5 5 5",activeTab:0,height:220},a);Ext.deluge.add.OptionsPanel.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.OptionsPanel.superclass.initComponent.call(this);this.files=this.add(new Ext.tree.ColumnTree({layout:"fit",title:_("Files"),rootVisible:false,autoScroll:true,height:170,border:false,animate:false,disabled:true,columns:[{header:_("Filename"),width:275,dataIndex:"filename"},{header:_("Size"),width:80,dataIndex:"size"}],root:new Ext.tree.AsyncTreeNode({text:"Files"})}));new Ext.tree.TreeSorter(this.files,{folderSort:true});this.optionsManager=new Deluge.MultiOptionsManager();this.form=this.add({xtype:"form",labelWidth:1,title:_("Options"),bodyStyle:"padding: 5px;",border:false,height:170,disabled:true});var a=this.form.add({xtype:"fieldset",title:_("Download Location"),border:false,autoHeight:true,defaultType:"textfield",labelWidth:1,fieldLabel:""});this.optionsManager.bind("download_location",a.add({fieldLabel:"",name:"download_location",width:400,labelSeparator:""}));var b=this.form.add({border:false,layout:"column",defaultType:"fieldset"});a=b.add({title:_("Allocation"),border:false,autoHeight:true,defaultType:"radio",width:100});this.optionsManager.bind("compact_allocation",a.add({xtype:"radiogroup",columns:1,vertical:true,labelSeparator:"",items:[{name:"compact_allocation",value:false,inputValue:false,boxLabel:_("Full"),fieldLabel:"",labelSeparator:""},{name:"compact_allocation",value:true,inputValue:true,boxLabel:_("Compact"),fieldLabel:"",labelSeparator:"",}]}));a=b.add({title:_("Bandwidth"),border:false,autoHeight:true,labelWidth:100,width:200,defaultType:"uxspinner"});this.optionsManager.bind("max_download_speed",a.add({fieldLabel:_("Max Down Speed"),name:"max_download_speed",width:60}));this.optionsManager.bind("max_upload_speed",a.add({fieldLabel:_("Max Up Speed"),name:"max_upload_speed",width:60}));this.optionsManager.bind("max_connections",a.add({fieldLabel:_("Max Connections"),name:"max_connections",width:60}));this.optionsManager.bind("max_upload_slots",a.add({fieldLabel:_("Max Upload Slots"),name:"max_upload_slots",width:60}));a=b.add({title:_("General"),border:false,autoHeight:true,defaultType:"checkbox"});this.optionsManager.bind("add_paused",a.add({name:"add_paused",boxLabel:_("Add In Paused State"),fieldLabel:"",labelSeparator:"",}));this.optionsManager.bind("prioritize_first_last_pieces",a.add({name:"prioritize_first_last_pieces",boxLabel:_("Prioritize First/Last Pieces"),fieldLabel:"",labelSeparator:"",}));this.form.on("render",this.onFormRender,this)},onFormRender:function(a){a.layout=new Ext.layout.FormLayout();a.layout.setContainer(a);a.doLayout()},addTorrent:function(c){this.torrents[c.info_hash]=c;var b={};this.walkFileTree(c.files_tree,function(e,g,h,f){if(g!="file"){return}b[h[0]]=h[2]},this);var a=[];Ext.each(Ext.keys(b),function(e){a[e]=b[e]});var d=this.optionsManager.changeId(c.info_hash,true);this.optionsManager.setDefault("file_priorities",a);this.optionsManager.changeId(d,true)},clear:function(){this.clearFiles();this.optionsManager.resetAll()},clearFiles:function(){var a=this.files.getRootNode();if(!a.hasChildNodes()){return}a.cascade(function(b){if(!b.parentNode||!b.getOwnerTree()){return}b.remove()})},getDefaults:function(){var a=["add_paused","compact_allocation","download_location","max_connections_per_torrent","max_download_speed_per_torrent","max_upload_slots_per_torrent","max_upload_speed_per_torrent","prioritize_first_last_pieces"];Deluge.Client.core.get_config_values(a,{success:function(c){var b={file_priorities:[],add_paused:c.add_paused,compact_allocation:c.compact_allocation,download_location:c.download_location,max_connections:c.max_connections_per_torrent,max_download_speed:c.max_download_speed_per_torrent,max_upload_slots:c.max_upload_slots_per_torrent,max_upload_speed:c.max_upload_speed_per_torrent,prioritize_first_last_pieces:c.prioritize_first_last_pieces};this.optionsManager.options=b;this.optionsManager.resetAll()},scope:this})},getFilename:function(a){return this.torrents[a]["filename"]},getOptions:function(a){var c=this.optionsManager.changeId(a,true);var b=this.optionsManager.get();this.optionsManager.changeId(c,true);Ext.each(b.file_priorities,function(e,d){b.file_priorities[d]=(e)?1:0});return b},setTorrent:function(b){if(!b){return}this.torrentId=b;this.optionsManager.changeId(b);this.clearFiles();var a=this.files.getRootNode();var c=this.optionsManager.get("file_priorities");this.walkFileTree(this.torrents[b]["files_tree"],function(d,f,i,e){if(f=="dir"){var h=new Ext.tree.TreeNode({text:d,checked:true});h.on("checkchange",this.onFolderCheck,this);e.appendChild(h);return h}else{var g=new Ext.tree.TreeNode({filename:d,fileindex:i[0],text:d,size:fsize(i[1]),leaf:true,checked:c[i[0]],iconCls:"x-deluge-file",uiProvider:Ext.tree.ColumnNodeUI});g.on("checkchange",this.onNodeCheck,this);e.appendChild(g)}},this,a);a.firstChild.expand()},walkFileTree:function(g,h,e,d){for(var a in g){var f=g[a];var c=(Ext.type(f)=="object")?"dir":"file";if(e){var b=h.apply(e,[a,c,f,d])}else{var b=h(a,c,f,d)}if(c=="dir"){this.walkFileTree(f,h,e,b)}}},onFolderCheck:function(c,b){var a=this.optionsManager.get("file_priorities");c.cascade(function(d){if(!d.ui.checkbox){d.attributes.checked=b}else{d.ui.checkbox.checked=b}a[d.attributes.fileindex]=b},this);this.optionsManager.setDefault("file_priorities",a)},onNodeCheck:function(c,b){var a=this.optionsManager.get("file_priorities");a[c.attributes.fileindex]=b;this.optionsManager.update("file_priorities",a)}});Ext.deluge.add.Window=Ext.extend(Ext.Window,{initComponent:function(){Ext.deluge.add.Window.superclass.initComponent.call(this);this.addEvents("beforeadd","add")},createTorrentId:function(){return new Date().getTime()}});Ext.deluge.add.AddWindow=Ext.extend(Ext.deluge.add.Window,{constructor:function(a){a=Ext.apply({title:_("Add Torrents"),layout:"border",width:470,height:450,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:true,plain:true,iconCls:"x-deluge-add-window-icon"},a);Ext.deluge.add.AddWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.AddWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("Add"),this.onAddClick,this);function a(c,d,b){if(b.data.info_hash){return String.format('
{0}
',c)}else{return String.format('
{0}
',c)}}this.grid=this.add({xtype:"grid",region:"center",store:new Ext.data.SimpleStore({fields:[{name:"info_hash",mapping:1},{name:"text",mapping:2}],id:0}),columns:[{id:"torrent",width:150,sortable:true,renderer:a,dataIndex:"text"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onSelect,scope:this}}}),hideHeaders:true,autoExpandColumn:"torrent",deferredRender:false,autoScroll:true,margins:"5 5 5 5",bbar:new Ext.Toolbar({items:[{id:"file",cls:"x-btn-text-icon",iconCls:"x-deluge-add-file",text:_("File"),handler:this.onFile,scope:this},{id:"url",cls:"x-btn-text-icon",text:_("Url"),icon:"/icons/add_url.png",handler:this.onUrl,scope:this},{id:"infohash",cls:"x-btn-text-icon",text:_("Infohash"),icon:"/icons/add_magnet.png",disabled:true},"->",{id:"remove",cls:"x-btn-text-icon",text:_("Remove"),icon:"/icons/remove.png",handler:this.onRemove,scope:this}]})});this.optionsPanel=this.add(new Ext.deluge.add.OptionsPanel());this.on("hide",this.onHide,this);this.on("show",this.onShow,this)},clear:function(){this.grid.getStore().removeAll();this.optionsPanel.clear()},onAddClick:function(){var a=[];if(!this.grid){return}this.grid.getStore().each(function(b){var c=b.get("info_hash");a.push({path:this.optionsPanel.getFilename(c),options:this.optionsPanel.getOptions(c)})},this);Deluge.Client.web.add_torrents(a,{success:function(b){}});this.clear();this.hide()},onCancelClick:function(){this.clear();this.hide()},onFile:function(){this.file.show()},onHide:function(){this.optionsPanel.setActiveTab(0);this.optionsPanel.files.setDisabled(true);this.optionsPanel.form.setDisabled(true)},onRemove:function(){var a=this.grid.getSelectionModel();if(!a.hasSelection()){return}var b=a.getSelected();this.grid.getStore().remove(b);this.optionsPanel.clear();if(this.torrents&&this.torrents[b.id]){delete this.torrents[b.id]}},onSelect:function(b,c,a){this.optionsPanel.setTorrent(a.get("info_hash"));this.optionsPanel.files.setDisabled(false);this.optionsPanel.form.setDisabled(false)},onShow:function(){if(!this.url){this.url=new Ext.deluge.add.UrlWindow();this.url.on("beforeadd",this.onTorrentBeforeAdd,this);this.url.on("add",this.onTorrentAdd,this)}if(!this.file){this.file=new Ext.deluge.add.FileWindow();this.file.on("beforeadd",this.onTorrentBeforeAdd,this);this.file.on("add",this.onTorrentAdd,this)}this.optionsPanel.getDefaults()},onTorrentBeforeAdd:function(b,c){var a=this.grid.getStore();a.loadData([[b,null,c]],true)},onTorrentAdd:function(a,c){if(!c){Ext.MessageBox.show({title:_("Error"),msg:_("Not a valid torrent"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});return}var b=this.grid.getStore().getById(a);b.set("info_hash",c.info_hash);b.set("text",c.name);this.grid.getStore().commitChanges();this.optionsPanel.addTorrent(c)},onUrl:function(a,b){this.url.show()}});Deluge.Add=new Ext.deluge.add.AddWindow();Ext.namespace("Ext.deluge.add");Ext.deluge.add.FileWindow=Ext.extend(Ext.deluge.add.Window,{constructor:function(a){a=Ext.apply({layout:"fit",width:350,height:115,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",modal:true,plain:true,title:_("Add from File"),iconCls:"x-deluge-add-file"},a);Ext.deluge.add.FileWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.FileWindow.superclass.initComponent.call(this);this.addButton(_("Add"),this.onAdd,this);this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:55,autoHeight:true,fileUpload:true,items:[{xtype:"fileuploadfield",id:"torrentFile",emptyText:_("Select a torrent"),fieldLabel:_("File"),name:"file",buttonCfg:{text:_("Browse")+"..."}}]})},onAdd:function(c,b){if(this.form.getForm().isValid()){this.torrentId=this.createTorrentId();this.form.getForm().submit({url:"/upload",waitMsg:_("Uploading your torrent..."),success:this.onUploadSuccess,scope:this});var a=this.form.getForm().findField("torrentFile").value;this.fireEvent("beforeadd",this.torrentId,a)}},onGotInfo:function(d,c,a,b){d.filename=b.options.filename;this.fireEvent("add",this.torrentId,d)},onUploadSuccess:function(c,b){this.hide();if(b.result.success){var a=b.result.files[0];this.form.getForm().findField("torrentFile").setValue("");Deluge.Client.web.get_torrent_info(a,{success:this.onGotInfo,scope:this,filename:a})}}});Ext.namespace("Ext.deluge.add");Ext.deluge.add.UrlWindow=Ext.extend(Ext.deluge.add.Window,{constructor:function(a){a=Ext.apply({layout:"fit",width:350,height:155,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",modal:true,plain:true,title:_("Add from Url"),iconCls:"x-deluge-add-url-window-icon"},a);Ext.deluge.add.UrlWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.add.UrlWindow.superclass.initComponent.call(this);this.addButton(_("Add"),this.onAdd,this);var a=this.add({xtype:"form",defaultType:"textfield",baseCls:"x-plain",labelWidth:55});this.urlField=a.add({fieldLabel:_("Url"),id:"url",name:"url",anchor:"100%"});this.urlField.on("specialkey",this.onAdd,this);this.cookieField=a.add({fieldLabel:_("Cookies"),id:"cookies",name:"cookies",anchor:"100%"});this.cookieField.on("specialkey",this.onAdd,this)},onAdd:function(f,d){if((f.id=="url"||f.id=="cookies")&&d.getKey()!=d.ENTER){return}var f=this.urlField;var b=f.getValue();var c=this.cookieField.getValue();var a=this.createTorrentId();Deluge.Client.web.download_torrent_from_url(b,c,{success:this.onDownload,scope:this,torrentId:a});this.hide();this.fireEvent("beforeadd",a,b)},onDownload:function(a,c,d,b){this.urlField.setValue("");Deluge.Client.web.get_torrent_info(a,{success:this.onGotInfo,scope:this,filename:a,torrentId:b.options.torrentId})},onGotInfo:function(d,c,a,b){d.filename=b.options.filename;this.fireEvent("add",b.options.torrentId,d)}});Ext.namespace("Ext.ux.util");(function(){Ext.ux.util.RpcClient=Ext.extend(Ext.util.Observable,{_components:[],_methods:[],_requests:{},_url:null,_optionKeys:["scope","success","failure"],constructor:function(a){Ext.ux.util.RpcClient.superclass.constructor.call(this,a);this._url=a.url||null;this._id=0;this.addEvents("connected","error");this.reloadMethods()},reloadMethods:function(){Ext.each(this._components,function(a){delete this[a]},this);this._execute("system.listMethods",{success:this._setMethods,scope:this})},_execute:function(c,a){a=a||{};a.params=a.params||[];a.id=this._id;var b=Ext.encode({method:c,params:a.params,id:a.id});this._id++;return Ext.Ajax.request({url:this._url,method:"POST",success:this._onSuccess,failure:this._onFailure,scope:this,jsonData:b,options:a})},_onFailure:function(b,a){var c=a.options;errorObj={id:c.id,result:null,error:{msg:"HTTP: "+b.status+" "+b.statusText,code:255}};this.fireEvent("error",errorObj,b,a);if(Ext.type(c.failure)!="function"){return}if(c.scope){c.failure.call(c.scope,errorObj,b,a)}else{c.failure(errorObj,b,a)}},_onSuccess:function(c,a){var b=Ext.decode(c.responseText);var d=a.options;if(b.error){this.fireEvent("error",b,c,a);if(Ext.type(d.failure)!="function"){return}if(d.scope){d.failure.call(d.scope,b,c,a)}else{d.failure(b,c,a)}}else{if(Ext.type(d.success)!="function"){return}if(d.scope){d.success.call(d.scope,b.result,b,c,a)}else{d.success(b.result,b,c,a)}}},_parseArgs:function(c){var e=[];Ext.each(c,function(f){e.push(f)});var b=e[e.length-1];if(Ext.type(b)=="object"){var d=Ext.keys(b),a=false;Ext.each(this._optionKeys,function(f){if(d.indexOf(f)>-1){a=true}});if(a){e.remove(b)}else{b={}}}else{b={}}b.params=e;return b},_setMethods:function(b){var d={},a=this;Ext.each(b,function(h){var g=h.split(".");var e=d[g[0]]||{};var f=function(){var i=a._parseArgs(arguments);return a._execute(h,i)};e[g[1]]=f;d[g[0]]=e});for(var c in d){a[c]=d[c]}this._components=Ext.keys(d);this.fireEvent("connected",this)}})})();(function(){var a=function(c,d,b){return c+":"+b.data.port};Ext.deluge.AddConnectionWindow=Ext.extend(Ext.Window,{constructor:function(b){b=Ext.apply({layout:"fit",width:300,height:195,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:true,plain:true,title:_("Add Connection"),iconCls:"x-deluge-add-window-icon"},b);Ext.deluge.AddConnectionWindow.superclass.constructor.call(this,b)},initComponent:function(){Ext.deluge.AddConnectionWindow.superclass.initComponent.call(this);this.addEvents("hostadded");this.addButton(_("Close"),this.hide,this);this.addButton(_("Add"),this.onAddClick,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form",defaultType:"textfield",id:"connectionAddForm",baseCls:"x-plain",labelWidth:55});this.hostField=this.form.add({fieldLabel:_("Host"),id:"host",name:"host",anchor:"100%",value:""});this.portField=this.form.add({fieldLabel:_("Port"),id:"port",xtype:"uxspinner",ctCls:"x-form-uxspinner",name:"port",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:65535},value:"58846",anchor:"50%"});this.usernameField=this.form.add({fieldLabel:_("Username"),id:"username",name:"username",anchor:"100%",value:""});this.passwordField=this.form.add({fieldLabel:_("Password"),anchor:"100%",id:"_password",name:"_password",inputType:"password",value:""})},onAddClick:function(){var d=this.hostField.getValue();var b=this.portField.getValue();var e=this.usernameField.getValue();var c=this.passwordField.getValue();Deluge.Client.web.add_host(d,b,e,c,{success:function(f){if(!f[0]){Ext.MessageBox.show({title:_("Error"),msg:"Unable to add host: "+f[1],buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}else{this.fireEvent("hostadded")}this.hide()},scope:this})},onHide:function(){this.form.getForm().reset()}});Ext.deluge.ConnectionManager=Ext.extend(Ext.Window,{layout:"fit",width:300,height:220,bodyStyle:"padding: 10px 5px;",buttonAlign:"right",closeAction:"hide",closable:true,plain:true,title:_("Connection Manager"),iconCls:"x-deluge-connect-window-icon",initComponent:function(){Ext.deluge.ConnectionManager.superclass.initComponent.call(this);this.on({hide:this.onHide,show:this.onShow});Deluge.Events.on("login",this.onLogin,this);Deluge.Events.on("logout",this.onLogout,this);this.addButton(_("Close"),this.onClose,this);this.addButton(_("Connect"),this.onConnect,this);this.grid=this.add({xtype:"grid",store:new Ext.data.SimpleStore({fields:[{name:"status",mapping:3},{name:"host",mapping:1},{name:"port",mapping:2},{name:"version",mapping:4}],id:0}),columns:[{header:_("Status"),width:65,sortable:true,renderer:fplain,dataIndex:"status"},{id:"host",header:_("Host"),width:150,sortable:true,renderer:a,dataIndex:"host"},{header:_("Version"),width:75,sortable:true,renderer:fplain,dataIndex:"version"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onSelect,scope:this},selectionchange:{fn:this.onSelectionChanged,scope:this}}}),autoExpandColumn:"host",deferredRender:false,autoScroll:true,margins:"0 0 0 0",bbar:new Ext.Toolbar({buttons:[{id:"cm-add",cls:"x-btn-text-icon",text:_("Add"),icon:"/icons/add.png",handler:this.onAddClick,scope:this},{id:"cm-remove",cls:"x-btn-text-icon",text:_("Remove"),icon:"/icons/remove.png",handler:this.onRemove,disabled:true,scope:this},"->",{id:"cm-stop",cls:"x-btn-text-icon",text:_("Stop Daemon"),icon:"/icons/error.png",handler:this.onStop,disabled:true,scope:this}]})})},disconnect:function(){Deluge.Events.fire("disconnect")},loadHosts:function(){Deluge.Client.web.get_hosts({success:this.onGetHosts,scope:this})},update:function(b){b.grid.getStore().each(function(c){Deluge.Client.web.get_host_status(c.id,{success:b.onGetHostStatus,scope:b})},this)},updateButtons:function(c){var d=this.buttons[1],b=c.get("status");if(b==_("Connected")){d.enable();d.setText(_("Disconnect"))}else{if(b==_("Offline")){d.disable()}else{d.enable();d.setText(_("Connect"))}}if(b==_("Offline")){if(c.get("host")=="127.0.0.1"||c.get("host")=="localhost"){this.stopHostButton.enable();this.stopHostButton.setText(_("Start Daemon"))}else{this.stopHostButton.disable()}}else{this.stopHostButton.enable();this.stopHostButton.setText(_("Stop Daemon"))}},onAddClick:function(b,c){if(!this.addWindow){this.addWindow=new Ext.deluge.AddConnectionWindow();this.addWindow.on("hostadded",this.onHostAdded,this)}this.addWindow.show()},onHostAdded:function(){this.loadHosts()},onClose:function(b){if(this.running){window.clearInterval(this.running)}this.hide()},onConnect:function(c){var b=this.grid.getSelectionModel().getSelected();if(!b){return}if(b.get("status")==_("Connected")){Deluge.Client.web.disconnect({success:function(e){this.update(this);Deluge.Events.fire("disconnect")},scope:this})}else{var d=b.id;Deluge.Client.web.connect(d,{success:function(e){Deluge.Client.reloadMethods();Deluge.Client.on("connected",function(f){Deluge.Events.fire("connect")},this,{single:true})}});if(this.running){window.clearInterval(this.running)}this.hide()}},onGetHosts:function(b){this.grid.getStore().loadData(b);Ext.each(b,function(c){Deluge.Client.web.get_host_status(c[0],{success:this.onGetHostStatus,scope:this})},this)},onGetHostStatus:function(c){var b=this.grid.getStore().getById(c[0]);b.set("status",c[3]);b.set("version",c[4]);b.commit();if(this.grid.getSelectionModel().getSelected()==b){this.updateButtons(b)}},onLogin:function(){Deluge.Client.web.connected({success:function(b){if(b){Deluge.Events.fire("connect")}else{this.show()}},scope:this})},onLogout:function(){this.disconnect();if(!this.hidden&&this.rendered){this.hide()}},onRemove:function(c){var b=this.grid.getSelectionModel().getSelected();if(!b){return}Deluge.Client.web.remove_host(b.id,{success:function(d){if(!d){Ext.MessageBox.show({title:_("Error"),msg:d[1],buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}else{this.grid.getStore().remove(b)}},scope:this})},onSelect:function(c,d,b){this.selectedRow=d},onSelectionChanged:function(c){var b=c.getSelected();if(c.hasSelection()){this.removeHostButton.enable();this.stopHostButton.enable();this.stopHostButton.setText(_("Stop Daemon"))}else{this.removeHostButton.disable();this.stopHostButton.disable()}this.updateButtons(b)},onShow:function(){if(!this.addHostButton){var b=this.grid.getBottomToolbar();this.addHostButton=b.items.get("cm-add");this.removeHostButton=b.items.get("cm-remove");this.stopHostButton=b.items.get("cm-stop")}this.loadHosts();this.running=window.setInterval(this.update,2000,this)},onStop:function(c,d){var b=this.grid.getSelectionModel().getSelected();if(!b){return}if(b.get("status")=="Offline"){Deluge.Client.web.start_daemon(b.get("port"))}else{Deluge.Client.web.stop_daemon(b.id,{success:function(e){if(!e[0]){Ext.MessageBox.show({title:_("Error"),msg:e[1],buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"})}}})}}});Deluge.ConnectionManager=new Ext.deluge.ConnectionManager()})();(function(){Ext.namespace("Ext.deluge.details");Ext.deluge.details.TabPanel=Ext.extend(Ext.TabPanel,{constructor:function(a){a=Ext.apply({region:"south",id:"torrentDetails",split:true,height:220,minSize:100,collapsible:true,margins:"0 5 5 5",activeTab:0},a);Ext.deluge.details.TabPanel.superclass.constructor.call(this,a)},clear:function(){this.items.each(function(a){if(a.clear){a.clear.defer(100,a);a.disable()}})},update:function(a){var b=Deluge.Torrents.getSelected();if(!b){this.clear();return}this.items.each(function(c){if(c.disabled){c.enable()}});a=a||this.getActiveTab();if(a.update){a.update(b.id)}},onRender:function(b,a){Ext.deluge.details.TabPanel.superclass.onRender.call(this,b,a);Deluge.Events.on("disconnect",this.clear,this);Deluge.Torrents.on("rowclick",this.onTorrentsClick,this);this.on("tabchange",this.onTabChange,this);Deluge.Torrents.getSelectionModel().on("selectionchange",function(c){if(!c.hasSelection()){this.clear()}},this)},onTabChange:function(a,b){this.update(b)},onTorrentsClick:function(a,c,b){this.update()}});Deluge.Details=new Ext.deluge.details.TabPanel()})();Ext.deluge.details.StatusTab=Ext.extend(Ext.Panel,{title:_("Status"),autoScroll:true,onRender:function(b,a){Ext.deluge.details.StatusTab.superclass.onRender.call(this,b,a);this.progressBar=this.add({xtype:"fullprogressbar",cls:"x-deluge-status-progressbar"});this.status=this.add({cls:"x-deluge-status",id:"deluge-details-status",border:false,width:1000,listeners:{render:{fn:function(c){c.load({url:"/render/tab_status.html",text:_("Loading")+"..."});c.getUpdater().on("update",this.onPanelUpdate,this)},scope:this}}})},clear:function(){this.progressBar.updateProgress(0," ");for(var a in this.fields){this.fields[a].innerHTML=""}},update:function(a){if(!this.fields){this.getFields()}Deluge.Client.core.get_torrent_status(a,Deluge.Keys.Status,{success:this.onRequestComplete,scope:this})},onPanelUpdate:function(b,a){this.fields={};Ext.each(Ext.query("dd",this.status.body.dom),function(c){this.fields[c.className]=c},this)},onRequestComplete:function(a){seeders=a.total_seeds>-1?a.num_seeds+" ("+a.total_seeds+")":a.num_seeds;peers=a.total_peers>-1?a.num_peers+" ("+a.total_peers+")":a.num_peers;var b={downloaded:fsize(a.total_done)+" ("+fsize(a.total_payload_download)+")",uploaded:fsize(a.total_uploaded)+" ("+fsize(a.total_payload_upload)+")",share:a.ratio.toFixed(3),announce:ftime(a.next_announce),tracker_status:a.tracker_status,downspeed:fspeed(a.download_payload_rate),upspeed:fspeed(a.upload_payload_rate),eta:ftime(a.eta),pieces:a.num_pieces+" ("+fsize(a.piece_length)+")",seeders:seeders,peers:peers,avail:a.distributed_copies.toFixed(3),active_time:ftime(a.active_time),seeding_time:ftime(a.seeding_time),seed_rank:a.seed_rank,time_added:fdate(a.time_added)};b.auto_managed=_((a.is_auto_managed)?"True":"False");for(var c in this.fields){this.fields[c].innerHTML=b[c]}var d=a.state+" "+a.progress.toFixed(2)+"%";this.progressBar.updateProgress(a.progress,d)}});Deluge.Details.add(new Ext.deluge.details.StatusTab());Ext.deluge.details.DetailsTab=Ext.extend(Ext.Panel,{title:_("Details"),bodyStyle:"padding 5px",onRender:function(b,a){Ext.deluge.details.DetailsTab.superclass.onRender.call(this,b,a);this.load({url:"/render/tab_details.html",text:_("Loading")+"..."});this.oldData={};this.body.setStyle("padding","5px");this.getUpdater().on("update",this.onPanelUpdate,this)},clear:function(){if(!this.fields){return}for(var a in this.fields){this.fields[a].innerHTML=""}},update:function(a){Deluge.Client.core.get_torrent_status(a,Deluge.Keys.Details,{success:this.onRequestComplete,scope:this,torrentId:a})},onPanelUpdate:function(b,a){this.fields={};Ext.each(Ext.query("dd",this.body.dom),function(c){this.fields[c.className]=c},this)},onRequestComplete:function(e,c,a,b){var d={torrent_name:e.name,hash:b.options.torrentId,path:e.save_path,size:fsize(e.total_size),files:e.num_files,status:e.tracker_status,tracker:e.tracker,comment:e.comment};for(var f in this.fields){if(d[f]==this.oldData[f]){continue}this.fields[f].innerHTML=Ext.escapeHTML(d[f])}this.oldData=d}});Deluge.Details.add(new Ext.deluge.details.DetailsTab());(function(){function b(d){var c=d*100;return Deluge.progressBar(c,this.width-50,c.toFixed(2)+"%",0)}function a(c){return String.format('
{1}
',FILE_PRIORITY_CSS[c],_(FILE_PRIORITY[c]))}Ext.deluge.details.FilesTab=Ext.extend(Ext.tree.ColumnTree,{constructor:function(c){c=Ext.apply({title:_("Files"),rootVisible:false,autoScroll:true,selModel:new Ext.tree.MultiSelectionModel(),columns:[{header:_("Filename"),width:330,dataIndex:"filename"},{header:_("Size"),width:150,dataIndex:"size",renderer:fsize},{header:_("Progress"),width:150,dataIndex:"progress",renderer:b},{header:_("Priority"),width:150,dataIndex:"priority",renderer:a}],root:new Ext.tree.TreeNode({text:"Files"})},c);Ext.deluge.details.FilesTab.superclass.constructor.call(this,c)},onRender:function(d,c){Ext.deluge.details.FilesTab.superclass.onRender.call(this,d,c);Deluge.Menus.FilePriorities.on("itemclick",this.onItemClick,this);this.on("contextmenu",this.onContextMenu,this);this.sorter=new Ext.tree.TreeSorter(this,{folderSort:true})},clear:function(){var c=this.getRootNode();if(!c.hasChildNodes()){return}c.cascade(function(e){var d=e.parentNode;if(!d){return}if(!d.ownerTree){return}d.removeChild(e)})},update:function(c){if(this.torrentId!=c){this.clear();this.torrentId=c}Deluge.Client.web.get_torrent_files(c,{success:this.onRequestComplete,scope:this,torrentId:c})},onContextMenu:function(d,f){f.stopEvent();var c=this.getSelectionModel();if(c.getSelectedNodes().length<2){c.clearSelections();d.select()}Deluge.Menus.FilePriorities.showAt(f.getPoint())},onItemClick:function(j,i){switch(j.id){case"expandAll":this.expandAll();break;default:var h={};function c(e){if(Ext.isEmpty(e.attributes.fileIndex)){return}h[e.attributes.fileIndex]=e.attributes.priority}this.getRootNode().cascade(c);var d=this.getSelectionModel().getSelectedNodes();Ext.each(d,function(k){if(!k.isLeaf()){function e(l){if(Ext.isEmpty(l.attributes.fileIndex)){return}h[l.attributes.fileIndex]=j.filePriority}k.cascade(e)}else{if(!Ext.isEmpty(k.attributes.fileIndex)){h[k.attributes.fileIndex]=j.filePriority;return}}});var g=new Array(Ext.keys(h).length);for(var f in h){g[f]=h[f]}Deluge.Client.core.set_torrent_file_priorities(this.torrentId,g,{success:function(){Ext.each(d,function(e){e.setColumnValue(3,j.filePriority)})},scope:this});break}},onRequestComplete:function(f,e){function d(j,h){for(var g in j){var i=j[g];var k=h.findChild("id",g);if(Ext.type(i)=="object"){if(!k){k=new Ext.tree.TreeNode({id:g,text:g});h.appendChild(k)}d(i,k)}else{if(!k){k=new Ext.tree.ColumnTreeNode({id:g,filename:g,text:g,fileIndex:i[0],size:i[1],progress:i[2],priority:i[3],leaf:true,iconCls:"x-deluge-file",uiProvider:Ext.tree.ColumnNodeUI});h.appendChild(k)}k.setColumnValue(1,i[1]);k.setColumnValue(2,i[2]);k.setColumnValue(3,i[3])}}}var c=this.getRootNode();d(f,c);c.firstChild.expand()}});Deluge.Details.add(new Ext.deluge.details.FilesTab())})();(function(){function a(e){return String.format('',e)}function c(g,h,f){var e=(f.data.seed==1024)?"x-deluge-seed":"x-deluge-peer";return String.format('
{1}
',e,g)}function d(g){var e=(g*100).toFixed(0);var f=new Number(this.style.match(/\w+:\s*(\d+)\w+/)[1]).toFixed(0)-8;return Deluge.progressBar(e,f,e+"%")}function b(g){var e=g.match(/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\:(\d+)/);var f=0;var h=[e[1],e[2],e[3],e[4]];Ext.each(h,function(j,i){j=parseInt(j);f=f|j<<((3-i)*8)});return f}Ext.deluge.details.PeersTab=Ext.extend(Ext.grid.GridPanel,{constructor:function(e){e=Ext.apply({title:_("Peers"),cls:"x-deluge-peers",store:new Ext.data.SimpleStore({fields:[{name:"country"},{name:"address",sortType:b},{name:"client"},{name:"progress",type:"float"},{name:"downspeed",type:"int"},{name:"upspeed",type:"int"},{name:"seed",type:"int"}],id:0}),columns:[{header:" ",width:30,sortable:true,renderer:a,dataIndex:"country"},{header:"Address",width:125,sortable:true,renderer:c,dataIndex:"address"},{header:"Client",width:125,sortable:true,renderer:fplain,dataIndex:"client"},{header:"Progress",width:150,sortable:true,renderer:d,dataIndex:"progress"},{header:"Down Speed",width:100,sortable:true,renderer:fspeed,dataIndex:"downspeed"},{header:"Up Speed",width:100,sortable:true,renderer:fspeed,dataIndex:"upspeed"}],stripeRows:true,deferredRender:false,autoScroll:true},e);Ext.deluge.details.PeersTab.superclass.constructor.call(this,e)},onRender:function(f,e){Ext.deluge.details.PeersTab.superclass.onRender.call(this,f,e)},clear:function(){this.getStore().loadData([])},update:function(e){Deluge.Client.core.get_torrent_status(e,Deluge.Keys.Peers,{success:this.onRequestComplete,scope:this})},onRequestComplete:function(g,f){if(!g){return}var e=new Array();Ext.each(g.peers,function(h){e.push([h.country,h.ip,h.client,h.progress,h.down_speed,h.up_speed,h.seed])},this);this.getStore().loadData(e)}});Deluge.Details.add(new Ext.deluge.details.PeersTab())})();Ext.deluge.details.OptionsTab=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({autoScroll:true,bodyStyle:"padding: 5px;",border:false,cls:"x-deluge-options",defaults:{autoHeight:true,labelWidth:1,defaultType:"checkbox"},deferredRender:false,layout:"column",title:_("Options")},a);Ext.deluge.details.OptionsTab.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.details.OptionsTab.superclass.initComponent.call(this);this.fieldsets={},this.fields={};this.optionsManager=new Deluge.MultiOptionsManager({options:{max_download_speed:-1,max_upload_speed:-1,max_connections:-1,max_upload_slots:-1,auto_managed:false,stop_at_ratio:false,stop_ratio:2,remove_at_ratio:false,move_completed:null,"private":false,prioritize_first_last:false}});this.fieldsets.bandwidth=this.add({xtype:"fieldset",defaultType:"uxspinner",bodyStyle:"padding: 5px",layout:"table",layoutConfig:{columns:3},labelWidth:150,style:"margin-left: 10px; margin-right: 5px; padding: 5px",title:_("Bandwidth"),width:250});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Download Speed"),forId:"max_download_speed",cls:"x-deluge-options-label"});this.fields.max_download_speed=this.fieldsets.bandwidth.add({id:"max_download_speed",name:"max_download_speed",width:70,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}});this.fieldsets.bandwidth.add({xtype:"label",text:_("KiB/s"),style:"margin-left: 10px"});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Upload Speed"),forId:"max_upload_speed",cls:"x-deluge-options-label"});this.fields.max_upload_speed=this.fieldsets.bandwidth.add({id:"max_upload_speed",name:"max_upload_speed",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}});this.fieldsets.bandwidth.add({xtype:"label",text:_("KiB/s"),style:"margin-left: 10px"});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Connections"),forId:"max_connections",cls:"x-deluge-options-label"});this.fields.max_connections=this.fieldsets.bandwidth.add({id:"max_connections",name:"max_connections",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},colspan:2});this.fieldsets.bandwidth.add({xtype:"label",text:_("Max Upload Slots"),forId:"max_upload_slots",cls:"x-deluge-options-label"});this.fields.max_upload_slots=this.fieldsets.bandwidth.add({id:"max_upload_slots",name:"max_upload_slots",width:70,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},colspan:2});this.fieldsets.queue=this.add({xtype:"fieldset",title:_("Queue"),style:"margin-left: 5px; margin-right: 5px; padding: 5px",width:210,layout:"table",layoutConfig:{columns:2},labelWidth:0,defaults:{fieldLabel:"",labelSeparator:""}});this.fields.auto_managed=this.fieldsets.queue.add({xtype:"checkbox",fieldLabel:"",labelSeparator:"",name:"is_auto_managed",boxLabel:_("Auto Managed"),width:200,colspan:2});this.fields.stop_at_ratio=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"stop_at_ratio",width:120,boxLabel:_("Stop seed at ratio"),handler:this.onStopRatioChecked,scope:this});this.fields.stop_ratio=this.fieldsets.queue.add({xtype:"uxspinner",id:"stop_ratio",name:"stop_ratio",disabled:true,width:50,value:2,strategy:{xtype:"number",minValue:-1,maxValue:99999,incrementValue:0.1,alternateIncrementValue:1,decimalPrecision:1}});this.fields.remove_at_ratio=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"remove_at_ratio",ctCls:"x-deluge-indent-checkbox",bodyStyle:"padding-left: 10px",boxLabel:_("Remove at ratio"),disabled:true,colspan:2});this.fields.move_completed=this.fieldsets.queue.add({fieldLabel:"",labelSeparator:"",id:"move_completed",boxLabel:_("Move Completed"),colspan:2});this.rightColumn=this.add({border:false,autoHeight:true,style:"margin-left: 5px",width:200});this.fieldsets.general=this.rightColumn.add({xtype:"fieldset",autoHeight:true,defaultType:"checkbox",title:_("General"),layout:"form"});this.fields["private"]=this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Private"),id:"private",disabled:true});this.fields.prioritize_first_last=this.fieldsets.general.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Prioritize First/Last"),id:"prioritize_first_last"});for(var a in this.fields){this.optionsManager.bind(a,this.fields[a])}this.buttonPanel=this.rightColumn.add({layout:"column",xtype:"panel",border:false});this.buttonPanel.add({xtype:"panel",border:false}).add({id:"edit_trackers",xtype:"button",text:_("Edit Trackers"),cls:"x-btn-text-icon",iconCls:"x-deluge-edit-trackers",border:false,width:100,handler:this.onEditTrackers,scope:this});this.buttonPanel.add({xtype:"panel",border:false}).add({id:"apply",xtype:"button",text:_("Apply"),style:"margin-left: 10px;",border:false,width:100,handler:this.onApply,scope:this})},onRender:function(b,a){Ext.deluge.details.OptionsTab.superclass.onRender.call(this,b,a);this.layout=new Ext.layout.ColumnLayout();this.layout.setContainer(this);this.doLayout()},clear:function(){if(this.torrentId==null){return}this.torrentId=null;this.optionsManager.changeId(null)},reset:function(){if(this.torrentId){this.optionsManager.reset()}},update:function(a){if(this.torrentId&&!a){this.clear()}if(!a){return}if(this.torrentId!=a){this.torrentId=a;this.optionsManager.changeId(a)}Deluge.Client.core.get_torrent_status(a,Deluge.Keys.Options,{success:this.onRequestComplete,scope:this})},onApply:function(){var b=this.optionsManager.getDirty();if(!Ext.isEmpty(b.prioritize_first_last)){var a=b.prioritize_first_last;Deluge.Client.core.set_torrent_prioritize_first_last(this.torrentId,a,{success:function(){this.optionsManager.set("prioritize_first_last",a)},scope:this})}Deluge.Client.core.set_torrent_options([this.torrentId],b,{success:function(){this.optionsManager.commit()},scope:this})},onEditTrackers:function(){Deluge.EditTrackers.show()},onStopRatioChecked:function(b,a){this.fields.remove_at_ratio.setDisabled(!a);this.fields.stop_ratio.setDisabled(!a)},onRequestComplete:function(c,b){this.fields["private"].setValue(c["private"]);this.fields["private"].setDisabled(true);delete c["private"];c.auto_managed=c.is_auto_managed;this.optionsManager.setDefault(c);var a=this.optionsManager.get("stop_at_ratio");this.fields.remove_at_ratio.setDisabled(!a);this.fields.stop_ratio.setDisabled(!a)}});Deluge.Details.add(new Ext.deluge.details.OptionsTab());(function(){Ext.deluge.AddTracker=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Add Tracker"),width:375,height:150,bodyStyle:"padding: 5px",layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-edit-trackers",plain:true,resizable:false},a);Ext.deluge.AddTracker.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.AddTracker.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancelClick,this);this.addButton(_("Add"),this.onAddClick,this);this.addEvents("add");this.form=this.add({xtype:"form",defaultType:"textarea",baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Trackers"),name:"trackers",anchor:"100%"}]})},onAddClick:function(){var b=this.form.getForm().findField("trackers").getValue();b=b.split("\n");var a=[];Ext.each(b,function(c){if(Ext.form.VTypes.url(c)){a.push(c)}},this);this.fireEvent("add",a);this.hide();this.form.getForm().findField("trackers").setValue("")},onCancelClick:function(){this.form.getForm().findField("trackers").setValue("");this.hide()}});Ext.deluge.EditTracker=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Edit Tracker"),width:375,height:110,bodyStyle:"padding: 5px",layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-edit-trackers",plain:true,resizable:false},a);Ext.deluge.EditTracker.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.EditTracker.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Save"),this.onSave,this);this.on("hide",this.onHide,this);this.form=this.add({xtype:"form",defaultType:"textfield",baseCls:"x-plain",labelWidth:55,items:[{fieldLabel:_("Tracker"),name:"tracker",anchor:"100%"}]})},show:function(a){Ext.deluge.EditTracker.superclass.show.call(this);this.record=a;this.form.getForm().findField("tracker").setValue(a.data.url)},onCancel:function(){this.hide()},onHide:function(){this.form.getForm().findField("tracker").setValue("")},onSave:function(){var a=this.form.getForm().findField("tracker").getValue();this.record.set("url",a);this.record.commit();this.hide()}});Ext.deluge.EditTrackers=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Edit Trackers"),width:350,height:220,bodyStyle:"padding: 5px",layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-edit-trackers",plain:true,resizable:true},a);Ext.deluge.EditTrackers.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.EditTrackers.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Ok"),this.onOk,this);this.addEvents("save");this.on("show",this.onShow,this);this.on("save",this.onSave,this);this.addWindow=new Ext.deluge.AddTracker();this.addWindow.on("add",this.onAddTrackers,this);this.editWindow=new Ext.deluge.EditTracker();this.grid=this.add({xtype:"grid",store:new Ext.data.SimpleStore({fields:[{name:"tier",mapping:0},{name:"url",mapping:1}]}),columns:[{header:_("Tier"),width:50,sortable:true,renderer:fplain,dataIndex:"tier"},{id:"tracker",header:_("Tracker"),sortable:true,renderer:fplain,dataIndex:"url"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{selectionchange:{fn:this.onSelect,scope:this}}}),autoExpandColumn:"tracker",deferredRender:false,autoScroll:true,margins:"0 0 0 0",bbar:new Ext.Toolbar({items:[{cls:"x-btn-text-icon",text:_("Up"),icon:"/icons/up.png",handler:this.onUpClick,scope:this},{cls:"x-btn-text-icon",text:_("Down"),icon:"/icons/down.png",handler:this.onDownClick,scope:this},"->",{cls:"x-btn-text-icon",text:_("Add"),icon:"/icons/add.png",handler:this.onAddClick,scope:this},{cls:"x-btn-text-icon",text:_("Edit"),icon:"/icons/edit_trackers.png",handler:this.onEditClick,scope:this},{cls:"x-btn-text-icon",text:_("Remove"),icon:"/icons/remove.png",handler:this.onRemoveClick,scope:this}]})})},onAddClick:function(){this.addWindow.show()},onAddTrackers:function(b){var a=this.grid.getStore();Ext.each(b,function(d){var e=false,c=-1;a.each(function(f){if(f.get("tier")>c){c=f.get("tier")}if(d==f.get("tracker")){e=true;return false}},this);if(!e){a.loadData([[c+1,d]],true)}},this)},onCancelClick:function(){this.hide()},onEditClick:function(){var a=this.grid.getSelectionModel().getSelected();this.editWindow.show(a)},onHide:function(){this.grid.getStore().removeAll()},onOk:function(){var a=[];this.grid.getStore().each(function(b){a.push({tier:b.get("tier"),url:b.get("url")})},this);Deluge.Client.core.set_torrent_trackers(this.torrentId,a,{failure:this.onSaveFail,scope:this});this.hide()},onRemove:function(){var a=this.grid.getSelectionModel().getSelected();this.grid.getStore().remove(a)},onRequestComplete:function(a){var b=[];Ext.each(a.trackers,function(c){b.push([c.tier,c.url])});this.grid.getStore().loadData(b)},onSaveFail:function(){},onSelect:function(a){if(a.hasSelection()){this.grid.getBottomToolbar().items.get(4).enable()}},onShow:function(){this.grid.getBottomToolbar().items.get(4).disable();var a=Deluge.Torrents.getSelected();this.torrentId=a.id;Deluge.Client.core.get_torrent_status(a.id,["trackers"],{success:this.onRequestComplete,scope:this})}});Deluge.EditTrackers=new Ext.deluge.EditTrackers()})();Ext.deluge.LoginWindow=Ext.extend(Ext.Window,{firstShow:true,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closable:false,closeAction:"hide",iconCls:"x-deluge-login-window-icon",layout:"fit",modal:true,plain:true,resizable:false,title:_("Login"),width:300,height:120,initComponent:function(){Ext.deluge.LoginWindow.superclass.initComponent.call(this);this.on("show",this.onShow,this);this.addButton({text:_("Login"),handler:this.onLogin,scope:this});this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:55,width:300,defaults:{width:200},defaultType:"textfield",});this.passwordField=this.form.add({xtype:"textfield",fieldLabel:_("Password"),id:"_password",name:"password",inputType:"password"});this.passwordField.on("specialkey",this.onSpecialKey,this)},logout:function(){Deluge.Events.fire("logout");Deluge.Client.auth.delete_session({success:function(a){this.show(true)},scope:this})},show:function(a){if(this.firstShow){Deluge.Client.on("error",this.onClientError,this);this.firstShow=false}if(a){return Ext.deluge.LoginWindow.superclass.show.call(this)}Deluge.Client.auth.check_session({success:function(b){if(b){Deluge.Events.fire("login")}else{this.show(true)}},failure:function(b){this.show(true)},scope:this})},onSpecialKey:function(b,a){if(a.getKey()==13){this.onLogin()}},onLogin:function(){var a=this.passwordField;Deluge.Client.auth.login(a.getValue(),{success:function(b){if(b){Deluge.Events.fire("login");this.hide();a.setRawValue("")}else{Ext.MessageBox.show({title:_("Login Failed"),msg:_("You entered an incorrect password"),buttons:Ext.MessageBox.OK,modal:false,fn:function(){a.focus()},icon:Ext.MessageBox.WARNING,iconCls:"x-deluge-icon-warning"})}},scope:this})},onClientError:function(c,b,a){if(c.error.code==1){Deluge.Events.fire("logout");this.show(true)}},onShow:function(){this.passwordField.focus(false,150);this.passwordField.setRawValue("")}});Deluge.Login=new Ext.deluge.LoginWindow();Ext.namespace("Ext.deluge");Ext.deluge.MoveStorage=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Move Storage"),width:375,height:110,layout:"fit",buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-move-storage",plain:true,resizable:false},a);Ext.deluge.MoveStorage.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.MoveStorage.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Move"),this.onMove,this);this.form=this.add({xtype:"form",border:false,defaultType:"textfield",width:300,bodyStyle:"padding: 5px"});this.moveLocation=this.form.add({fieldLabel:_("Location"),name:"location",width:240})},hide:function(){Ext.deluge.MoveStorage.superclass.hide.call(this);this.torrentIds=null},show:function(a){Ext.deluge.MoveStorage.superclass.show.call(this);this.torrentIds=a},onCancel:function(){this.hide()},onMove:function(){var a=this.moveLocation.getValue();Deluge.Client.core.move_storage(this.torrentIds,a);this.hide()}});Deluge.MoveStorage=new Ext.deluge.MoveStorage();Deluge.Plugin=Ext.extend(Ext.util.Observable,{constructor:function(a){this.name=a.name;this.addEvents({enabled:true,disabled:true});this.isDelugePlugin=true;Deluge.Plugins[this.name]=this;Deluge.Plugin.superclass.constructor.call(this,a)},disable:function(){this.fireEvent("disabled",this);if(this.onDisable){this.onDisable()}},enable:function(){this.fireEvent("enable",this);if(this.onEnable){this.onEnable()}}});Ext.deluge.PreferencesWindow=Ext.extend(Ext.Window,{currentPage:null,constructor:function(a){a=Ext.apply({layout:"border",width:485,height:500,buttonAlign:"right",closeAction:"hide",closable:true,iconCls:"x-deluge-preferences",plain:true,resizable:false,title:_("Preferences"),items:[{xtype:"grid",region:"west",title:_("Categories"),store:new Ext.data.SimpleStore({fields:[{name:"name",mapping:0}]}),columns:[{id:"name",renderer:fplain,dataIndex:"name"}],sm:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onPageSelect,scope:this}}}),hideHeaders:true,autoExpandColumn:"name",deferredRender:false,autoScroll:true,margins:"5 0 5 5",cmargins:"5 0 5 5",width:120,collapsible:true},]},a);Ext.deluge.PreferencesWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.PreferencesWindow.superclass.initComponent.call(this);this.categoriesGrid=this.items.get(0);this.configPanel=this.add({region:"center",header:false,layout:"fit",height:400,autoScroll:true,margins:"5 5 5 5",cmargins:"5 5 5 5"});this.addButton(_("Close"),this.onClose,this);this.addButton(_("Apply"),this.onApply,this);this.addButton(_("Ok"),this.onOk,this);this.pages={};this.optionsManager=new Deluge.OptionsManager();this.on("show",this.onShow,this)},onApply:function(b){var c=this.optionsManager.getDirty();if(!Ext.isObjectEmpty(c)){Deluge.Client.core.set_config(c,{success:this.onSetConfig,scope:this})}for(var a in this.pages){if(this.pages[a].onApply){this.pages[a].onApply()}}},onClose:function(){this.hide()},onOk:function(){Deluge.Client.core.set_config(this.optionsManager.getDirty());this.hide()},addPage:function(c){var a=this.categoriesGrid.getStore();var b=c.title;a.loadData([[b]],true);c.bodyStyle="margin: 5px";this.pages[b]=this.configPanel.add(c);return this.pages[b]},removePage:function(c){var b=c.title;var a=this.categoriesGrid.getStore();a.removeAt(a.find("name",b));this.configPanel.remove(c);delete this.pages[c.title]},getOptionsManager:function(){return this.optionsManager},onGotConfig:function(a){this.getOptionsManager().set(a)},onPageSelect:function(a,e,c){if(this.currentPage==null){for(var d in this.pages){this.pages[d].hide()}}else{this.currentPage.hide()}var b=c.get("name");this.pages[b].show();this.currentPage=this.pages[b];this.configPanel.doLayout()},onSetConfig:function(){this.getOptionsManager().commit()},onShow:function(){if(!this.categoriesGrid.getSelectionModel().hasSelection()){this.categoriesGrid.getSelectionModel().selectFirstRow()}Deluge.Client.core.get_config({success:this.onGotConfig,scope:this})}});Deluge.Preferences=new Ext.deluge.PreferencesWindow();Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Downloads=Ext.extend(Ext.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Downloads"),layout:"form",autoHeight:true,width:320},a);Ext.deluge.preferences.Downloads.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Downloads.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Folders"),labelWidth:150,defaultType:"togglefield",autoHeight:true,labelAlign:"top",width:300,style:"margin-bottom: 5px; padding-bottom: 5px;"});b.bind("download_location",a.add({xtype:"textfield",name:"download_location",fieldLabel:_("Download to"),width:280}));var c=a.add({name:"move_completed_path",fieldLabel:_("Move completed to"),width:280});b.bind("move_completed",c.toggle);b.bind("move_completed_path",c.input);c=a.add({name:"torrentfiles_location",fieldLabel:_("Copy of .torrent files to"),width:280});b.bind("copy_torrent_file",c.toggle);b.bind("torrentfiles_location",c.input);c=a.add({name:"autoadd_location",fieldLabel:_("Autoadd .torrent files from"),width:280});b.bind("autoadd_enable",c.toggle);b.bind("autoadd_location",c.input);a=this.add({xtype:"fieldset",border:false,title:_("Allocation"),autoHeight:true,labelWidth:1,defaultType:"radiogroup",style:"margin-bottom: 5px; margin-top: 0; padding-bottom: 5px; padding-top: 0;",width:240});b.bind("compact_allocation",a.add({name:"compact_allocation",width:200,labelSeparator:"",defaults:{width:80,height:22,name:"compact_allocation"},items:[{boxLabel:_("Use Full"),inputValue:false},{boxLabel:_("Use Compact"),inputValue:true}]}));a=this.add({xtype:"fieldset",border:false,title:_("Options"),autoHeight:true,labelWidth:1,defaultType:"checkbox",style:"margin-bottom: 0; padding-bottom: 0;",width:280});b.bind("prioritize_first_last_pieces",a.add({name:"prioritize_first_last_pieces",labelSeparator:"",height:22,boxLabel:_("Prioritize first and last pieces of torrent")}));b.bind("add_paused",a.add({name:"add_paused",labelSeparator:"",height:22,boxLabel:_("Add torrents in Paused state")}));this.on("show",this.onShow,this)},onShow:function(){Ext.deluge.preferences.Downloads.superclass.onShow.call(this)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Downloads());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Network=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Network"),layout:"form"},a);Ext.deluge.preferences.Network.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Network.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Incoming Ports"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("random_port",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Use Random Ports"),name:"random_port",height:22,listeners:{check:{fn:function(d,c){this.listenPorts.setDisabled(c)},scope:this}}}));this.listenPorts=a.add({xtype:"uxspinnergroup",name:"listen_ports",fieldLabel:"",labelSeparator:"",colCfg:{labelWidth:40,style:"margin-right: 10px;"},items:[{fieldLabel:"From",width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},},{fieldLabel:"To",width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}]});b.bind("listen_ports",this.listenPorts);a=this.add({xtype:"fieldset",border:false,title:_("Outgoing Ports"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("random_outgoing_ports",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Use Random Ports"),name:"random_outgoing_ports",height:22,listeners:{check:{fn:function(d,c){this.outgoingPorts.setDisabled(c)},scope:this}}}));this.outgoingPorts=a.add({xtype:"uxspinnergroup",name:"outgoing_ports",fieldLabel:"",labelSeparator:"",colCfg:{labelWidth:40,style:"margin-right: 10px;"},items:[{fieldLabel:"From",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},},{fieldLabel:"To",strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}]});b.bind("outgoing_ports",this.outgoingPorts);a=this.add({xtype:"fieldset",border:false,title:_("Network Interface"),style:"margin-bottom: 5px; padding-bottom: 0px;",autoHeight:true,labelWidth:1,defaultType:"textfield"});b.bind("listen_interface",a.add({name:"listen_interface",fieldLabel:"",labelSeparator:"",width:200}));a=this.add({xtype:"fieldset",border:false,title:_("TOS"),style:"margin-bottom: 5px; padding-bottom: 0px;",bodyStyle:"margin: 0px; padding: 0px",autoHeight:true,defaultType:"textfield"});b.bind("peer_tos",a.add({name:"peer_tos",fieldLabel:_("Peer TOS Byte"),width:80}));a=this.add({xtype:"fieldset",border:false,title:_("Network Extras"),autoHeight:true,layout:"table",layoutConfig:{columns:3},defaultType:"checkbox"});b.bind("upnp",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("UPnP"),name:"upnp"}));b.bind("natpmp",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("NAT-PMP"),ctCls:"x-deluge-indent-checkbox",name:"natpmp"}));b.bind("utpex",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("Peer Exchange"),ctCls:"x-deluge-indent-checkbox",name:"utpex"}));b.bind("lsd",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("LSD"),name:"lsd"}));b.bind("dht",a.add({fieldLabel:"",labelSeparator:"",boxLabel:_("DHT"),ctCls:"x-deluge-indent-checkbox",name:"dht"}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Network());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Encryption=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Encryption"),layout:"form"},a);Ext.deluge.preferences.Encryption.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Encryption.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Settings"),autoHeight:true,defaultType:"combo"});b.bind("enc_in_policy",a.add({fieldLabel:_("Inbound"),mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Forced")],[1,_("Enabled")],[2,_("Disabled")]]}),triggerAction:"all",valueField:"id",displayField:"text"}));b.bind("enc_out_policy",a.add({fieldLabel:_("Outbound"),mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Forced")],[1,_("Enabled")],[2,_("Disabled")]]}),triggerAction:"all",valueField:"id",displayField:"text"}));b.bind("enc_level",a.add({fieldLabel:_("Level"),mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("Handshake")],[1,_("Full Stream")],[2,_("Either")]]}),triggerAction:"all",valueField:"id",displayField:"text"}));b.bind("enc_prefer_rc4",a.add({xtype:"checkbox",name:"enc_prefer_rc4",height:40,hideLabel:true,boxLabel:_("Encrypt entire stream")}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Encryption());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Bandwidth=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Bandwidth"),layout:"form",labelWidth:10},a);Ext.deluge.preferences.Bandwidth.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Bandwidth.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Global Bandwidth Usage"),labelWidth:200,defaultType:"uxspinner",style:"margin-bottom: 0px; padding-bottom: 0px;",autoHeight:true});b.bind("max_connections_global",a.add({name:"max_connections_global",fieldLabel:_("Maximum Connections"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_upload_slots_global",a.add({name:"max_upload_slots_global",fieldLabel:_("Maximum Upload Slots"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_download_speed",a.add({name:"max_download_speed",fieldLabel:_("Maximum Download Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}));b.bind("max_upload_speed",a.add({name:"max_upload_speed",fieldLabel:_("Maximum Upload Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}));b.bind("max_half_open_connections",a.add({name:"max_half_open_connections",fieldLabel:_("Maximum Half-Open Connections"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_connections_per_second",a.add({name:"max_connections_per_second",fieldLabel:_("Maximum Connection Attempts per Second"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));a=this.add({xtype:"fieldset",border:false,title:"",defaultType:"checkbox",style:"padding-top: 0px; padding-bottom: 5px; margin-top: 0px; margin-bottom: 0px;",autoHeight:true});b.bind("ignore_limits_on_local_network",a.add({name:"ignore_limits_on_local_network",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Ignore limits on local network"),}));b.bind("rate_limit_ip_overhead",a.add({name:"rate_limit_ip_overhead",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Rate limit IP overhead"),}));a=this.add({xtype:"fieldset",border:false,title:_("Per Torrent Bandwidth Usage"),style:"margin-bottom: 0px; padding-bottom: 0px;",defaultType:"uxspinner",labelWidth:200,autoHeight:true});b.bind("max_connections_per_torrent",a.add({name:"max_connections_per_torrent",fieldLabel:_("Maximum Connections"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_upload_slots_per_torrent",a.add({name:"max_upload_slots_per_torrent",fieldLabel:_("Maximum Upload Slots"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));b.bind("max_download_speed_per_torrent",a.add({name:"max_download_speed_per_torrent",fieldLabel:_("Maximum Download Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}));b.bind("max_upload_speed_per_torrent",a.add({name:"max_upload_speed_per_torrent",fieldLabel:_("Maximum Upload Speed (KiB/s)"),width:80,value:-1,strategy:{xtype:"number",decimalPrecision:1,minValue:-1,maxValue:99999}}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Bandwidth());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Interface=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Interface"),layout:"form"},a);Ext.deluge.preferences.Interface.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Interface.superclass.initComponent.call(this);var c=this.optionsManager=new Deluge.OptionsManager();this.on("show",this.onShow,this);var a=this.add({xtype:"fieldset",border:false,title:_("Interface"),style:"margin-bottom: 0px; padding-bottom: 5px; padding-top: 5px",autoHeight:true,labelWidth:1,defaultType:"checkbox"});c.bind("show_session_speed",a.add({name:"show_session_speed",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Show session speed in titlebar")}));c.bind("sidebar_show_zero",a.add({name:"sidebar_show_zero",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Show filters with zero torrents")}));c.bind("sidebar_show_trackers",a.add({name:"sidebar_show_trackers",height:22,fieldLabel:"",labelSeparator:"",boxLabel:_("Show trackers with zero torrents")}));a=this.add({xtype:"fieldset",border:false,title:_("Password"),style:"margin-bottom: 0px; padding-bottom: 0px; padding-top: 5px",autoHeight:true,labelWidth:110,defaultType:"textfield",defaults:{width:180,inputType:"password"}});this.oldPassword=a.add({name:"old_password",fieldLabel:_("Old Password")});this.newPassword=a.add({name:"new_password",fieldLabel:_("New Password")});this.confirmPassword=a.add({name:"confirm_password",fieldLabel:_("Confirm Password")});var b=a.add({xtype:"panel",autoHeight:true,border:false,width:320,bodyStyle:"padding-left: 230px"});b.add({xtype:"button",text:_("Change"),listeners:{click:{fn:this.onPasswordChange,scope:this}}});a=this.add({xtype:"fieldset",border:false,title:_("Server"),style:"margin-top: 0px; padding-top: 0px; margin-bottom: 0px; padding-bottom: 0px",autoHeight:true,labelWidth:110,defaultType:"uxspinner",defaults:{width:80,}});c.bind("session_timeout",a.add({name:"session_timeout",fieldLabel:_("Session Timeout"),strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));c.bind("port",a.add({name:"port",fieldLabel:_("Port"),strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}}));this.httpsField=c.bind("https",a.add({xtype:"checkbox",name:"https",hideLabel:true,width:280,height:22,boxLabel:_("Use SSL (paths relative to Deluge config folder)")}));this.httpsField.on("check",this.onSSLCheck,this);this.pkeyField=c.bind("pkey",a.add({xtype:"textfield",disabled:true,name:"pkey",width:180,fieldLabel:_("Private Key")}));this.certField=c.bind("cert",a.add({xtype:"textfield",disabled:true,name:"cert",width:180,fieldLabel:_("Certificate")}))},onApply:function(){var a=this.optionsManager.getDirty();if(!Ext.isObjectEmpty(a)){Deluge.Client.web.set_config(a,{success:this.onSetConfig,scope:this})}},onGotConfig:function(a){this.optionsManager.set(a)},onPasswordChange:function(){var b=this.newPassword.getValue();if(b!=this.confirmPassword.getValue()){Ext.MessageBox.show({title:_("Invalid Password"),msg:_("Your passwords don't match!"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});return}var a=this.oldPassword.getValue();Deluge.Client.auth.change_password(a,b,{success:function(c){if(!c){Ext.MessageBox.show({title:_("Password"),msg:_("Your old password was incorrect!"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.ERROR,iconCls:"x-deluge-icon-error"});this.oldPassword.setValue("")}else{Ext.MessageBox.show({title:_("Change Successful"),msg:_("Your password was successfully changed!"),buttons:Ext.MessageBox.OK,modal:false,icon:Ext.MessageBox.INFO,iconCls:"x-deluge-icon-info"});this.oldPassword.setValue("");this.newPassword.setValue("");this.confirmPassword.setValue("")}},scope:this})},onSetConfig:function(){this.optionsManager.commit()},onShow:function(){Ext.deluge.preferences.Interface.superclass.onShow.call(this);Deluge.Client.web.get_config({success:this.onGotConfig,scope:this})},onSSLCheck:function(b,a){this.pkeyField.setDisabled(!a);this.certField.setDisabled(!a)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Interface());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Other=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Other"),layout:"form"},a);Ext.deluge.preferences.Other.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Other.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Updates"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("new_release_check",a.add({fieldLabel:"",labelSeparator:"",height:22,name:"new_release_check",boxLabel:_("Be alerted about new releases")}));a=this.add({xtype:"fieldset",border:false,title:_("System Information"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});a.add({xtype:"panel",border:false,bodyCfg:{html:_("Help us improve Deluge by sending us your Python version, PyGTK version, OS and processor types. Absolutely no other information is sent.")}});b.bind("send_info",a.add({fieldLabel:"",labelSeparator:"",height:22,boxLabel:_("Yes, please send anonymous statistics"),name:"send_info"}));a=this.add({xtype:"fieldset",border:false,title:_("GeoIP Database"),autoHeight:true,labelWidth:80,defaultType:"textfield"});b.bind("geoip_db_location",a.add({name:"geoip_db_location",fieldLabel:_("Location"),width:200}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Other());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Daemon=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Daemon"),layout:"form"},a);Ext.deluge.preferences.Daemon.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Daemon.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Port"),autoHeight:true,defaultType:"uxspinner"});b.bind("daemon_port",a.add({fieldLabel:_("Daemon port"),name:"daemon_port",value:58846,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));a=this.add({xtype:"fieldset",border:false,title:_("Connections"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("allow_remote",a.add({fieldLabel:"",height:22,labelSeparator:"",boxLabel:_("Allow Remote Connections"),name:"allow_remote"}));a=this.add({xtype:"fieldset",border:false,title:_("Other"),autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("new_release_check",a.add({fieldLabel:"",labelSeparator:"",height:40,boxLabel:_("Periodically check the website for new releases"),id:"new_release_check"}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Daemon());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Queue=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Queue"),layout:"form"},a);Ext.deluge.preferences.Queue.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Queue.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("General"),style:"padding-top: 5px;",autoHeight:true,labelWidth:1,defaultType:"checkbox"});b.bind("queue_new_to_top",a.add({fieldLabel:"",labelSeparator:"",height:22,boxLabel:_("Queue new torrents to top"),name:"queue_new_to_top"}));a=this.add({xtype:"fieldset",border:false,title:_("Active Torrents"),autoHeight:true,labelWidth:150,defaultType:"uxspinner",style:"margin-bottom: 0px; padding-bottom: 0px;",});b.bind("max_active_limit",a.add({fieldLabel:_("Total Active"),name:"max_active_limit",value:8,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("max_active_downloading",a.add({fieldLabel:_("Total Active Downloading"),name:"max_active_downloading",value:3,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("max_active_seeding",a.add({fieldLabel:_("Total Active Seeding"),name:"max_active_seeding",value:5,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("dont_count_slow_torrents",a.add({xtype:"checkbox",name:"dont_count_slow_torrents",height:40,hideLabel:true,boxLabel:_("Do not count slow torrents")}));a=this.add({xtype:"fieldset",border:false,title:_("Seeding"),autoHeight:true,labelWidth:150,defaultType:"uxspinner",style:"margin-bottom: 0px; padding-bottom: 0px; margin-top: 0; padding-top: 0;",});b.bind("share_ratio_limit",a.add({fieldLabel:_("Share Ratio Limit"),name:"share_ratio_limit",value:8,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("seed_time_ratio_limit",a.add({fieldLabel:_("Share Time Ratio"),name:"seed_time_ratio_limit",value:3,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("seed_time_limit",a.add({fieldLabel:_("Seed Time (m)"),name:"seed_time_limit",value:5,width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));a=this.add({xtype:"fieldset",border:false,autoHeight:true,layout:"table",layoutConfig:{columns:2},labelWidth:0,defaultType:"checkbox",defaults:{fieldLabel:"",labelSeparator:""}});this.stopAtRatio=a.add({name:"stop_seed_at_ratio",boxLabel:_("Stop seeding when share ratio reaches:")});this.stopAtRatio.on("check",this.onStopRatioCheck,this);b.bind("stop_seed_at_ratio",this.stopAtRatio);this.stopRatio=a.add({xtype:"uxspinner",name:"stop_seed_ratio",ctCls:"x-deluge-indent-checkbox",disabled:true,value:2,width:60,strategy:{xtype:"number",minValue:-1,maxValue:99999,incrementValue:0.1,alternateIncrementValue:1,decimalPrecision:1}});b.bind("stop_seed_ratio",this.stopRatio);this.removeAtRatio=a.add({name:"remove_seed_at_ratio",ctCls:"x-deluge-indent-checkbox",boxLabel:_("Remove torrent when share ratio is reached"),disabled:true,colspan:2});b.bind("remove_seed_at_ratio",this.removeAtRatio)},onStopRatioCheck:function(b,a){this.stopRatio.setDisabled(!a);this.removeAtRatio.setDisabled(!a)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Queue());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.ProxyField=Ext.extend(Ext.form.FieldSet,{constructor:function(a){a=Ext.apply({border:false,autoHeight:true,labelWidth:70},a);Ext.deluge.preferences.ProxyField.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.ProxyField.superclass.initComponent.call(this);this.type=this.add({xtype:"combo",fieldLabel:_("Type"),name:"type",mode:"local",width:150,store:new Ext.data.SimpleStore({fields:["id","text"],data:[[0,_("None")],[1,_("Socksv4")],[2,_("Socksv5")],[3,_("Socksv5 with Auth")],[4,_("HTTP")],[5,_("HTTP with Auth")],]}),value:0,triggerAction:"all",valueField:"id",displayField:"text"});this.hostname=this.add({xtype:"textfield",name:"hostname",fieldLabel:_("Host"),width:220});this.port=this.add({xtype:"uxspinner",name:"port",fieldLabel:_("Port"),width:80,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999}});this.username=this.add({xtype:"textfield",name:"username",fieldLabel:_("Username"),width:220});this.password=this.add({xtype:"textfield",name:"password",fieldLabel:_("Password"),inputType:"password",width:220});this.type.on("change",this.onFieldChange,this);this.type.on("select",this.onTypeSelect,this);this.setting=false},getName:function(){return this.initialConfig.name},getValue:function(){return{type:this.type.getValue(),hostname:this.hostname.getValue(),port:Number(this.port.getValue()),username:this.username.getValue(),password:this.password.getValue()}},setValue:function(c){this.setting=true;this.type.setValue(c.type);var b=this.type.getStore().find("id",c.type);var a=this.type.getStore().getAt(b);this.hostname.setValue(c.hostname);this.port.setValue(c.port);this.username.setValue(c.username);this.password.setValue(c.password);this.onTypeSelect(this.type,a,b);this.setting=false},onFieldChange:function(e,d,c){if(this.setting){return}var b=this.getValue();var a=Ext.apply({},b);a[e.getName()]=c;this.fireEvent("change",this,b,a)},onTypeSelect:function(d,a,b){var c=a.get("id");if(c>0){this.hostname.show();this.port.show()}else{this.hostname.hide();this.port.hide()}if(c==3||c==5){this.username.show();this.password.show()}else{this.username.hide();this.password.hide()}}});Ext.deluge.preferences.Proxy=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Proxy"),layout:"form"},a);Ext.deluge.preferences.Proxy.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Proxy.superclass.initComponent.call(this);this.peer=this.add(new Ext.deluge.preferences.ProxyField({title:_("Peer"),name:"peer"}));this.peer.on("change",this.onProxyChange,this);this.web_seed=this.add(new Ext.deluge.preferences.ProxyField({title:_("Web Seed"),name:"web_seed"}));this.web_seed.on("change",this.onProxyChange,this);this.tracker=this.add(new Ext.deluge.preferences.ProxyField({title:_("Tracker"),name:"tracker"}));this.tracker.on("change",this.onProxyChange,this);this.dht=this.add(new Ext.deluge.preferences.ProxyField({title:_("DHT"),name:"dht"}));this.dht.on("change",this.onProxyChange,this);Deluge.Preferences.getOptionsManager().bind("proxies",this)},getValue:function(){return{dht:this.dht.getValue(),peer:this.peer.getValue(),tracker:this.tracker.getValue(),web_seed:this.web_seed.getValue()}},setValue:function(b){for(var a in b){this[a].setValue(b[a])}},onProxyChange:function(e,d,c){var b=this.getValue();var a=Ext.apply({},b);a[e.getName()]=c;this.fireEvent("change",this,b,a)}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Proxy());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.Cache=Ext.extend(Ext.form.FormPanel,{constructor:function(a){a=Ext.apply({border:false,title:_("Cache"),layout:"form"},a);Ext.deluge.preferences.Cache.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.preferences.Cache.superclass.initComponent.call(this);var b=Deluge.Preferences.getOptionsManager();var a=this.add({xtype:"fieldset",border:false,title:_("Settings"),autoHeight:true,labelWidth:180,defaultType:"uxspinner"});b.bind("cache_size",a.add({fieldLabel:_("Cache Size (16 KiB Blocks)"),name:"cache_size",width:60,value:512,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}));b.bind("cache_expiry",a.add({fieldLabel:_("Cache Expiry (seconds)"),name:"cache_expiry",width:60,value:60,strategy:{xtype:"number",decimalPrecision:0,minValue:-1,maxValue:99999},}))}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Cache());Ext.namespace("Ext.deluge.preferences");Ext.deluge.preferences.InstallPlugin=Ext.extend(Ext.Window,{height:115,width:350,bodyStyle:"padding: 10px 5px;",buttonAlign:"center",closeAction:"hide",iconCls:"x-deluge-install-plugin",layout:"fit",modal:true,plain:true,title:_("Install Plugin"),initComponent:function(){Ext.deluge.add.FileWindow.superclass.initComponent.call(this);this.addButton(_("Install"),this.onInstall,this);this.form=this.add({xtype:"form",baseCls:"x-plain",labelWidth:70,autoHeight:true,fileUpload:true,items:[{xtype:"fileuploadfield",id:"pluginEgg",emptyText:_("Select an egg"),fieldLabel:_("Plugin Egg"),name:"file",buttonCfg:{text:_("Browse")+"..."}}]})},onInstall:function(b,a){this.form.getForm().submit({url:"/upload",waitMsg:_("Uploading your plugin..."),success:this.onUploadSuccess,scope:this})},onUploadPlugin:function(d,c,a,b){this.fireEvent("pluginadded")},onUploadSuccess:function(c,b){this.hide();if(b.result.success){var a=this.form.getForm().findField("pluginEgg").value;var d=b.result.files[0];this.form.getForm().findField("pluginEgg").setValue("");Deluge.Client.web.upload_plugin(a,d,{success:this.onUploadPlugin,scope:this,filename:a})}}});Ext.deluge.preferences.Plugins=Ext.extend(Ext.Panel,{constructor:function(a){a=Ext.apply({border:false,title:_("Plugins"),layout:"border",height:400,cls:"x-deluge-plugins"},a);Ext.deluge.preferences.Plugins.superclass.constructor.call(this,a)},pluginTemplate:new Ext.Template('
Author:
{author}
Version:
{version}
Author Email:
{email}
Homepage:
{homepage}
Details:
{details}
'),initComponent:function(){Ext.deluge.preferences.Plugins.superclass.initComponent.call(this);this.defaultValues={version:"",email:"",homepage:"",details:""};this.pluginTemplate.compile();var b=function(d,e,c){e.css+=" x-grid3-check-col-td";return'
'};this.grid=this.add({xtype:"grid",region:"center",store:new Ext.data.SimpleStore({fields:[{name:"enabled",mapping:0},{name:"plugin",mapping:1}]}),columns:[{id:"enabled",header:_("Enabled"),width:50,sortable:true,renderer:b,dataIndex:"enabled"},{id:"plugin",header:_("Plugin"),sortable:true,dataIndex:"plugin"}],stripeRows:true,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onPluginSelect,scope:this}}}),autoExpandColumn:"plugin",deferredRender:false,autoScroll:true,margins:"5 5 5 5",bbar:new Ext.Toolbar({items:[{cls:"x-btn-text-icon",iconCls:"x-deluge-install-plugin",text:_("Install"),handler:this.onInstallPlugin,scope:this},"->",{cls:"x-btn-text-icon",text:_("Find More"),iconCls:"x-deluge-find-more",handler:this.onFindMorePlugins,scope:this}]})});var a=this.add({xtype:"fieldset",border:false,region:"south",title:_("Info"),autoHeight:true,labelWidth:1});this.pluginInfo=a.add({xtype:"panel",border:false,bodyCfg:{style:"margin-left: 10px"}});this.on("show",this.onShow,this);this.pluginInfo.on("render",this.onPluginInfoRender,this);this.grid.on("cellclick",this.onCellClick,this);Deluge.Preferences.on("show",this.onPreferencesShow,this);Deluge.Events.on("PluginDisabledEvent",this.onPluginDisabled,this);Deluge.Events.on("PluginEnabledEvent",this.onPluginEnabled,this)},disablePlugin:function(a){Deluge.Client.core.disable_plugin(a)},enablePlugin:function(a){Deluge.Client.core.enable_plugin(a)},setInfo:function(b){if(!this.pluginInfo.rendered){return}var a=b||this.defaultValues;this.pluginInfo.body.dom.innerHTML=this.pluginTemplate.apply(a)},updatePlugins:function(){Deluge.Client.web.get_plugins({success:this.onGotPlugins,scope:this})},updatePluginsGrid:function(){var a=[];Ext.each(this.availablePlugins,function(b){if(this.enabledPlugins.indexOf(b)>-1){a.push([true,b])}else{a.push([false,b])}},this);this.grid.getStore().loadData(a)},onCellClick:function(b,f,a,d){if(a!=0){return}var c=b.getStore().getAt(f);c.set("enabled",!c.get("enabled"));c.commit();if(c.get("enabled")){this.enablePlugin(c.get("plugin"))}else{this.disablePlugin(c.get("plugin"))}},onFindMorePlugins:function(){window.open("http://dev.deluge-torrent.org/wiki/Plugins")},onGotPlugins:function(a){this.enabledPlugins=a.enabled_plugins;this.availablePlugins=a.available_plugins;this.setInfo();this.updatePluginsGrid()},onGotPluginInfo:function(b){var a={author:b.Author,version:b.Version,email:b["Author-email"],homepage:b["Home-page"],details:b.Description};this.setInfo(a);delete b},onInstallPlugin:function(){if(!this.installWindow){this.installWindow=new Ext.deluge.preferences.InstallPlugin();this.installWindow.on("pluginadded",this.onPluginInstall,this)}this.installWindow.show()},onPluginEnabled:function(c){var a=this.grid.getStore().find("plugin",c);var b=this.grid.getStore().getAt(a);b.set("enabled",true);b.commit()},onPluginDisabled:function(c){var a=this.grid.getStore().find("plugin",c);var b=this.grid.getStore().getAt(a);b.set("enabled",false);b.commit()},onPluginInstall:function(){this.updatePlugins()},onPluginSelect:function(b,c,a){Deluge.Client.web.get_plugin_info(a.get("plugin"),{success:this.onGotPluginInfo,scope:this})},onPreferencesShow:function(){this.updatePlugins()},onPluginInfoRender:function(b,a){this.setInfo()}});Deluge.Preferences.addPage(new Ext.deluge.preferences.Plugins());Ext.deluge.RemoveWindow=Ext.extend(Ext.Window,{constructor:function(a){a=Ext.apply({title:_("Remove Torrent"),layout:"fit",width:350,height:100,buttonAlign:"right",closeAction:"hide",closable:true,plain:true,iconCls:"x-deluge-remove-window-icon"},a);Ext.deluge.RemoveWindow.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.RemoveWindow.superclass.initComponent.call(this);this.addButton(_("Cancel"),this.onCancel,this);this.addButton(_("Remove With Data"),this.onRemoveData,this);this.addButton(_("Remove Torrent"),this.onRemove,this);this.add({border:false,bodyStyle:"padding: 5px; padding-left: 10px;",html:"Are you sure you wish to remove the torrent(s)?"})},remove:function(a){Ext.each(this.torrentIds,function(b){Deluge.Client.core.remove_torrent(b,a,{success:function(){this.onRemoved(b)},scope:this,torrentId:b})},this)},show:function(a){Ext.deluge.RemoveWindow.superclass.show.call(this);this.torrentIds=a},onCancel:function(){this.hide();this.torrentIds=null},onRemove:function(){this.remove(false)},onRemoveData:function(){this.remove(true)},onRemoved:function(a){Deluge.Events.fire("torrentRemoved",a);this.hide();Deluge.UI.update()}});Deluge.RemoveWindow=new Ext.deluge.RemoveWindow();(function(){function a(d,f,c){var b=d.toLowerCase().replace(".","_");var e="";if(c.store.id=="tracker_host"){if(d!="Error"){e=String.format("url(/tracker/{0})",d)}else{b=null}}if(e){return String.format('
{0} ({1})
',d,c.data.count,e)}else{if(b){return String.format('
{0} ({1})
',d,c.data.count,b)}else{return String.format('
{0} ({1})
',d,c.data.count)}}}Ext.deluge.Sidebar=Ext.extend(Ext.Panel,{panels:{},selected:null,constructor:function(b){b=Ext.apply({id:"sidebar",region:"west",cls:"deluge-sidebar",title:_("Filters"),layout:"accordion",split:true,width:200,minSize:175,collapsible:true,margins:"5 0 0 5",cmargins:"5 0 0 5"},b);Ext.deluge.Sidebar.superclass.constructor.call(this,b)},initComponent:function(){Ext.deluge.Sidebar.superclass.initComponent.call(this);Deluge.Events.on("disconnect",this.onDisconnect,this)},createFilter:function(e,d){var c=new Ext.data.SimpleStore({id:e,fields:[{name:"filter"},{name:"count"}]});var g=e.replace("_"," ");var f=g.split(" ");g="";Ext.each(f,function(h){firstLetter=h.substring(0,1);firstLetter=firstLetter.toUpperCase();h=firstLetter+h.substring(1);g+=h+" "});var b=new Ext.grid.GridPanel({id:e+"-panel",border:false,store:c,title:_(g),columns:[{id:"filter",sortable:false,renderer:a,dataIndex:"filter"}],stripeRows:false,selModel:new Ext.grid.RowSelectionModel({singleSelect:true,listeners:{rowselect:{fn:this.onFilterSelect,scope:this}}}),hideHeaders:true,autoExpandColumn:"filter",deferredRender:false,autoScroll:true});if(Deluge.config.sidebar_show_zero==false){d=this.removeZero(d)}c.loadData(d);this.add(b);this.doLayout();this.panels[e]=b;if(!this.selected){b.getSelectionModel().selectFirstRow();this.selected={row:0,filter:d[0][0],panel:b}}},getFilters:function(){var c={};if(!this.selected){return c}if(!this.selected.filter||!this.selected.panel){return c}var b=this.selected.panel.store.id;if(b=="state"&&this.selected.filter=="All"){return c}c[b]=this.selected.filter;return c},onDisconnect:function(){Ext.each(Ext.getKeys(this.panels),function(b){this.remove(b+"-panel")},this);this.panels={};this.selected=null},onFilterSelect:function(c,d,b){if(!this.selected){needsUpdate=true}else{if(this.selected.row!=d){needsUpdate=true}else{needsUpdate=false}}this.selected={row:d,filter:b.get("filter"),panel:this.panels[b.store.id]};if(needsUpdate){Deluge.UI.update()}},removeZero:function(b){var c=[];Ext.each(b,function(d){if(d[1]>0||d[0]==_("All")){c.push(d)}});return c},update:function(d){for(var c in d){var b=d[c];if(Ext.getKeys(this.panels).indexOf(c)>-1){this.updateFilter(c,b)}else{this.createFilter(c,b)}}Ext.each(Ext.keys(this.panels),function(e){if(Ext.keys(d).indexOf(e)==-1){this.panels[e]}},this)},updateFilter:function(c,b){if(Deluge.config.sidebar_show_zero==false){b=this.removeZero(b)}this.panels[c].store.loadData(b);if(this.selected&&this.selected.panel==this.panels[c]){this.panels[c].getSelectionModel().selectRow(this.selected.row)}}});Deluge.Sidebar=new Ext.deluge.Sidebar()})();Ext.deluge.Statusbar=Ext.extend(Ext.Toolbar,{constructor:function(a){a=Ext.apply({id:"deluge-statusbar",defaultIconCls:"x-not-connected",defaultText:_("Not Connected")},a);Ext.deluge.Statusbar.superclass.constructor.call(this,a)},initComponent:function(){Ext.deluge.Statusbar.superclass.initComponent.call(this);Deluge.Events.on("connect",this.onConnect,this);Deluge.Events.on("disconnect",this.onDisconnect,this)},createButtons:function(){this.add({id:"statusbar-connections",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-connections",tooltip:_("Connections"),menu:Deluge.Menus.Connections},"-",{id:"statusbar-downspeed",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-downloading",tooltip:_("Download Speed"),menu:Deluge.Menus.Download},"-",{id:"statusbar-upspeed",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-seeding",tooltip:_("Upload Speed"),menu:Deluge.Menus.Upload},"-",{id:"statusbar-traffic",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-traffic",tooltip:_("Protocol Traffic Download/Upload")},"-",{id:"statusbar-dht",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-dht",tooltip:_("DHT Nodes")},"-",{id:"statusbar-freespace",text:" ",cls:"x-btn-text-icon",iconCls:"x-deluge-freespace",tooltip:_("Freespace in download location")});this.created=true},onConnect:function(){if(!this.created){this.createButtons()}else{this.items.each(function(a){a.show();a.enable()})}},onDisconnect:function(){this.items.each(function(a){a.hide();a.disable()})},update:function(b){if(!b){return}function c(d){return d+" KiB/s"}var a=function(f,e){var g=this.items.get("statusbar-"+f);if(e.limit.value>0){var h=(e.value.formatter)?e.value.formatter(e.value.value):e.value.value;var d=(e.limit.formatter)?e.limit.formatter(e.limit.value):e.limit.value;var i=String.format(e.format,h,d)}else{var i=(e.value.formatter)?e.value.formatter(e.value.value):e.value.value}g.setText(i)}.bind(this);a("connections",{value:{value:b.num_connections},limit:{value:b.max_num_connections},format:"{0} ({1})"});a("downspeed",{value:{value:b.download_rate,formatter:Deluge.Formatters.speed},limit:{value:b.max_download,formatter:c},format:"{0} ({1})"});a("upspeed",{value:{value:b.upload_rate,formatter:Deluge.Formatters.speed},limit:{value:b.max_upload,formatter:c},format:"{0} ({1})"});a("traffic",{value:{value:b.download_protocol_rate,formatter:Deluge.Formatters.speed},limit:{value:b.upload_protocol_rate,formatter:Deluge.Formatters.speed},format:"{0}/{1}"});this.items.get("statusbar-dht").setText(b.dht_nodes);this.items.get("statusbar-freespace").setText(fsize(b.free_space));Deluge.Menus.Connections.setValue(b.max_num_connections);Deluge.Menus.Download.setValue(b.max_download);Deluge.Menus.Upload.setValue(b.max_upload)}});Deluge.Statusbar=new Ext.deluge.Statusbar();(function(){Ext.deluge.Toolbar=Ext.extend(Ext.Toolbar,{constructor:function(a){a=Ext.apply({items:[{id:"create",cls:"x-btn-text-icon",disabled:true,text:_("Create"),icon:"/icons/create.png",handler:this.onTorrentAction},{id:"add",cls:"x-btn-text-icon",disabled:true,text:_("Add"),icon:"/icons/add.png",handler:this.onTorrentAdd},{id:"remove",cls:"x-btn-text-icon",disabled:true,text:_("Remove"),icon:"/icons/remove.png",handler:this.onTorrentAction},"|",{id:"pause",cls:"x-btn-text-icon",disabled:true,text:_("Pause"),icon:"/icons/pause.png",handler:this.onTorrentAction},{id:"resume",cls:"x-btn-text-icon",disabled:true,text:_("Resume"),icon:"/icons/start.png",handler:this.onTorrentAction},"|",{id:"up",cls:"x-btn-text-icon",disabled:true,text:_("Up"),icon:"/icons/up.png",handler:this.onTorrentAction},{id:"down",cls:"x-btn-text-icon",disabled:true,text:_("Down"),icon:"/icons/down.png",handler:this.onTorrentAction},"|",{id:"preferences",cls:"x-btn-text-icon",text:_("Preferences"),iconCls:"x-deluge-preferences",handler:this.onPreferencesClick,scope:this},{id:"connectionman",cls:"x-btn-text-icon",text:_("Connection Manager"),iconCls:"x-deluge-connection-manager",handler:this.onConnectionManagerClick,scope:this},"->",{id:"help",cls:"x-btn-text-icon",icon:"/icons/help.png",text:_("Help"),handler:this.onHelpClick,scope:this},{id:"logout",cls:"x-btn-text-icon",icon:"/icons/logout.png",disabled:true,text:_("Logout"),handler:this.onLogout,scope:this}]},a);Ext.deluge.Toolbar.superclass.constructor.call(this,a)},connectedButtons:["add","remove","pause","resume","up","down"],initComponent:function(){Ext.deluge.Toolbar.superclass.initComponent.call(this);Deluge.Events.on("connect",this.onConnect,this);Deluge.Events.on("login",this.onLogin,this)},onConnect:function(){Ext.each(this.connectedButtons,function(a){this.items.get(a).enable()},this)},onDisconnect:function(){Ext.each(this.connectedButtons,function(a){this.items.get(a).disable()},this)},onLogin:function(){this.items.get("logout").enable()},onLogout:function(){this.items.get("logout").disable();Deluge.Login.logout()},onConnectionManagerClick:function(){Deluge.ConnectionManager.show()},onHelpClick:function(){window.open("http://dev.deluge-torrent.org/wiki/UserGuide")},onPreferencesClick:function(){Deluge.Preferences.show()},onTorrentAction:function(c){var b=Deluge.Torrents.getSelections();var a=[];Ext.each(b,function(d){a.push(d.id)});switch(c.id){case"remove":Deluge.RemoveWindow.show(a);break;case"pause":case"resume":Deluge.Client.core[c.id+"_torrent"](a,{success:function(){Deluge.UI.update()}});break;case"up":case"down":Deluge.Client.core["queue_"+c.id](a,{success:function(){Deluge.UI.update()}});break}},onTorrentAdd:function(){Deluge.Add.show()}});Deluge.Toolbar=new Ext.deluge.Toolbar()})();(function(){function c(j){return(j==99999)?"":j+1}function e(k,l,j){return String.format('
{1}
',j.data.state.toLowerCase(),k)}function g(j){if(!j){return}return fspeed(j)}function i(l,m,k){l=new Number(l);var j=l;var n=k.data.state+" "+l.toFixed(2)+"%";return Deluge.progressBar(l,this.width-8,n)}function a(k,l,j){if(j.data.total_seeds>-1){return String.format("{0} ({1})",k,j.data.total_seeds)}else{return k}}function d(k,l,j){if(j.data.total_peers>-1){return String.format("{0} ({1})",k,j.data.total_peers)}else{return k}}function b(k,l,j){return(k<0)?"∞":new Number(k).toFixed(3)}function f(k,l,j){return String.format('
{0}
',k)}function h(j){return j*-1}Ext.deluge.TorrentGrid=Ext.extend(Ext.grid.GridPanel,{constructor:function(j){j=Ext.apply({id:"torrentGrid",store:new Ext.data.JsonStore({root:"torrents",idProperty:"id",fields:[{name:"queue"},{name:"name"},{name:"total_size",type:"int"},{name:"state"},{name:"progress",type:"float"},{name:"num_seeds",type:"int"},{name:"total_seeds",type:"int"},{name:"num_peers",type:"int"},{name:"total_peers",type:"int"},{name:"download_payload_rate",type:"int"},{name:"upload_payload_speed",type:"int"},{name:"eta",type:"int",sortType:h},{name:"ratio",type:"float"},{name:"distributed_copies",type:"float"},{name:"time_added",type:"int"},{name:"tracker_host"}]}),columns:[{id:"queue",header:_("#"),width:30,sortable:true,renderer:c,dataIndex:"queue"},{id:"name",header:_("Name"),width:150,sortable:true,renderer:e,dataIndex:"name"},{header:_("Size"),width:75,sortable:true,renderer:fsize,dataIndex:"total_size"},{header:_("Progress"),width:150,sortable:true,renderer:i,dataIndex:"progress"},{header:_("Seeders"),width:60,sortable:true,renderer:a,dataIndex:"num_seeds"},{header:_("Peers"),width:60,sortable:true,renderer:d,dataIndex:"num_peers"},{header:_("Down Speed"),width:80,sortable:true,renderer:g,dataIndex:"download_payload_rate"},{header:_("Up Speed"),width:80,sortable:true,renderer:g,dataIndex:"upload_payload_rate"},{header:_("ETA"),width:60,sortable:true,renderer:ftime,dataIndex:"eta"},{header:_("Ratio"),width:60,sortable:true,renderer:b,dataIndex:"ratio"},{header:_("Avail"),width:60,sortable:true,renderer:b,dataIndex:"distributed_copies"},{header:_("Added"),width:80,sortable:true,renderer:fdate,dataIndex:"time_added"},{header:_("Tracker"),width:120,sortable:true,renderer:f,dataIndex:"tracker_host"}],region:"center",cls:"deluge-torrents",stripeRows:true,autoExpandColumn:"name",deferredRender:false,autoScroll:true,margins:"5 5 0 0",stateful:true},j);Ext.deluge.TorrentGrid.superclass.constructor.call(this,j)},initComponent:function(){Ext.deluge.TorrentGrid.superclass.initComponent.call(this);Deluge.Events.on("torrentRemoved",this.onTorrentRemoved,this);Deluge.Events.on("logout",this.onDisconnect,this);this.on("rowcontextmenu",function(j,m,l){l.stopEvent();var k=j.getSelectionModel();if(!k.hasSelection()){k.selectRow(m)}Deluge.Menus.Torrent.showAt(l.getPoint())})},getTorrent:function(j){return this.getStore().getAt(j)},getSelected:function(){return this.getSelectionModel().getSelected()},getSelections:function(){return this.getSelectionModel().getSelections()},update:function(k,j){if(j){this.getStore().loadData({torrents:Ext.values(k)})}else{this.getStore().loadData({torrents:Ext.values(k)})}},onDisconnect:function(){this.getStore().removeAll()},onTorrentRemoved:function(k){var j=this.getSelectionModel();Ext.each(k,function(m){var l=this.getStore().getById(m);if(j.isSelected(l)){j.deselectRow(this.getStore().indexOf(l))}this.getStore().remove(l)},this)}});Deluge.Torrents=new Ext.deluge.TorrentGrid()})();Deluge.UI={errorCount:0,initialize:function(){this.MainPanel=new Ext.Panel({id:"mainPanel",iconCls:"x-deluge-main-panel",title:"Deluge",layout:"border",tbar:Deluge.Toolbar,items:[Deluge.Sidebar,Deluge.Details,Deluge.Torrents],bbar:Deluge.Statusbar});this.Viewport=new Ext.Viewport({layout:"fit",items:[this.MainPanel]});Deluge.Events.on("connect",this.onConnect,this);Deluge.Events.on("disconnect",this.onDisconnect,this);Deluge.Client=new Ext.ux.util.RpcClient({url:"/json"});for(var a in Deluge.Plugins){a=Deluge.Plugins[a];a.enable()}Ext.QuickTips.init();Deluge.Client.on("connected",function(b){Deluge.Login.show()},this,{single:true});this.update=this.update.bind(this)},update:function(){var a=Deluge.Sidebar.getFilters();Deluge.Client.web.update_ui(Deluge.Keys.Grid,a,{success:this.onUpdate,failure:this.onUpdateError,scope:this});Deluge.Details.update()},onUpdateError:function(a){if(this.errorCount==2){Ext.MessageBox.show({title:"Lost Connection",msg:"The connection to the webserver has been lost!",buttons:Ext.MessageBox.OK,icon:Ext.MessageBox.ERROR})}this.errorCount++},onUpdate:function(a){if(!a.connected){Deluge.Events.fire("disconnect")}Deluge.Torrents.update(a.torrents);Deluge.Statusbar.update(a.stats);Deluge.Sidebar.update(a.filters);this.errorCount=0},onConnect:function(){if(!this.running){this.running=setInterval(this.update,2000);this.update()}},onDisconnect:function(){this.stop()},onPluginEnabled:function(a){Deluge.Client.web.get_plugin_resources(a,{success:this.onGotPluginResources,scope:this})},onGotPluginResources:function(b){var a=(Deluge.debug)?b.debug_scripts:b.scripts;Ext.each(a,function(c){Ext.ux.JSLoader({url:c,onLoad:this.onPluginLoaded,pluginName:b.name})},this)},onPluginDisabled:function(a){Deluge.Plugins[a].disable()},onPluginLoaded:function(a){if(!Deluge.Plugins[a.pluginName]){return}Deluge.Plugins[a.pluginName].enable()},stop:function(){if(this.running){clearInterval(this.running);this.running=false;Deluge.Torrents.getStore().removeAll()}}};Ext.onReady(function(a){Deluge.UI.initialize()}); \ No newline at end of file diff --git a/deluge/ui/web/js/deluge-all/.build b/deluge/ui/web/js/deluge-all/.build new file mode 100644 index 000000000..0887ff2ed --- /dev/null +++ b/deluge/ui/web/js/deluge-all/.build @@ -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" diff --git a/deluge/ui/web/js/Deluge.Add.File.js b/deluge/ui/web/js/deluge-all/Deluge.Add.File.js similarity index 100% rename from deluge/ui/web/js/Deluge.Add.File.js rename to deluge/ui/web/js/deluge-all/Deluge.Add.File.js diff --git a/deluge/ui/web/js/Deluge.Add.Infohash.js b/deluge/ui/web/js/deluge-all/Deluge.Add.Infohash.js similarity index 100% rename from deluge/ui/web/js/Deluge.Add.Infohash.js rename to deluge/ui/web/js/deluge-all/Deluge.Add.Infohash.js diff --git a/deluge/ui/web/js/Deluge.Add.Url.js b/deluge/ui/web/js/deluge-all/Deluge.Add.Url.js similarity index 100% rename from deluge/ui/web/js/Deluge.Add.Url.js rename to deluge/ui/web/js/deluge-all/Deluge.Add.Url.js diff --git a/deluge/ui/web/js/Deluge.Add.js b/deluge/ui/web/js/deluge-all/Deluge.Add.js similarity index 99% rename from deluge/ui/web/js/Deluge.Add.js rename to deluge/ui/web/js/deluge-all/Deluge.Add.js index a884fc8b8..d8ff062fa 100644 --- a/deluge/ui/web/js/Deluge.Add.js +++ b/deluge/ui/web/js/deluge-all/Deluge.Add.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(); diff --git a/deluge/ui/web/js/Deluge.Client.js b/deluge/ui/web/js/deluge-all/Deluge.Client.js similarity index 100% rename from deluge/ui/web/js/Deluge.Client.js rename to deluge/ui/web/js/deluge-all/Deluge.Client.js diff --git a/deluge/ui/web/js/Deluge.ConnectionManager.js b/deluge/ui/web/js/deluge-all/Deluge.ConnectionManager.js similarity index 100% rename from deluge/ui/web/js/Deluge.ConnectionManager.js rename to deluge/ui/web/js/deluge-all/Deluge.ConnectionManager.js diff --git a/deluge/ui/web/js/Deluge.Details.Details.js b/deluge/ui/web/js/deluge-all/Deluge.Details.Details.js similarity index 100% rename from deluge/ui/web/js/Deluge.Details.Details.js rename to deluge/ui/web/js/deluge-all/Deluge.Details.Details.js diff --git a/deluge/ui/web/js/Deluge.Details.Files.js b/deluge/ui/web/js/deluge-all/Deluge.Details.Files.js similarity index 100% rename from deluge/ui/web/js/Deluge.Details.Files.js rename to deluge/ui/web/js/deluge-all/Deluge.Details.Files.js diff --git a/deluge/ui/web/js/Deluge.Details.Options.js b/deluge/ui/web/js/deluge-all/Deluge.Details.Options.js similarity index 100% rename from deluge/ui/web/js/Deluge.Details.Options.js rename to deluge/ui/web/js/deluge-all/Deluge.Details.Options.js diff --git a/deluge/ui/web/js/Deluge.Details.Peers.js b/deluge/ui/web/js/deluge-all/Deluge.Details.Peers.js similarity index 100% rename from deluge/ui/web/js/Deluge.Details.Peers.js rename to deluge/ui/web/js/deluge-all/Deluge.Details.Peers.js diff --git a/deluge/ui/web/js/Deluge.Details.Status.js b/deluge/ui/web/js/deluge-all/Deluge.Details.Status.js similarity index 99% rename from deluge/ui/web/js/Deluge.Details.Status.js rename to deluge/ui/web/js/deluge-all/Deluge.Details.Status.js index ad2c3a859..b718df820 100644 --- a/deluge/ui/web/js/Deluge.Details.Status.js +++ b/deluge/ui/web/js/deluge-all/Deluge.Details.Status.js @@ -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' }); diff --git a/deluge/ui/web/js/Deluge.Details.js b/deluge/ui/web/js/deluge-all/Deluge.Details.js similarity index 100% rename from deluge/ui/web/js/Deluge.Details.js rename to deluge/ui/web/js/deluge-all/Deluge.Details.js diff --git a/deluge/ui/web/js/Deluge.EditTrackers.js b/deluge/ui/web/js/deluge-all/Deluge.EditTrackers.js similarity index 100% rename from deluge/ui/web/js/Deluge.EditTrackers.js rename to deluge/ui/web/js/deluge-all/Deluge.EditTrackers.js diff --git a/deluge/ui/web/js/Deluge.Events.js b/deluge/ui/web/js/deluge-all/Deluge.Events.js similarity index 100% rename from deluge/ui/web/js/Deluge.Events.js rename to deluge/ui/web/js/deluge-all/Deluge.Events.js diff --git a/deluge/ui/web/js/Deluge.Formatters.js b/deluge/ui/web/js/deluge-all/Deluge.Formatters.js similarity index 100% rename from deluge/ui/web/js/Deluge.Formatters.js rename to deluge/ui/web/js/deluge-all/Deluge.Formatters.js diff --git a/deluge/ui/web/js/Deluge.Keys.js b/deluge/ui/web/js/deluge-all/Deluge.Keys.js similarity index 100% rename from deluge/ui/web/js/Deluge.Keys.js rename to deluge/ui/web/js/deluge-all/Deluge.Keys.js diff --git a/deluge/ui/web/js/Deluge.Login.js b/deluge/ui/web/js/deluge-all/Deluge.Login.js similarity index 100% rename from deluge/ui/web/js/Deluge.Login.js rename to deluge/ui/web/js/deluge-all/Deluge.Login.js diff --git a/deluge/ui/web/js/Deluge.Menus.js b/deluge/ui/web/js/deluge-all/Deluge.Menus.js similarity index 100% rename from deluge/ui/web/js/Deluge.Menus.js rename to deluge/ui/web/js/deluge-all/Deluge.Menus.js diff --git a/deluge/ui/web/js/Deluge.MoveStorage.js b/deluge/ui/web/js/deluge-all/Deluge.MoveStorage.js similarity index 100% rename from deluge/ui/web/js/Deluge.MoveStorage.js rename to deluge/ui/web/js/deluge-all/Deluge.MoveStorage.js diff --git a/deluge/ui/web/js/Deluge.MultiOptionsManager.js b/deluge/ui/web/js/deluge-all/Deluge.MultiOptionsManager.js similarity index 100% rename from deluge/ui/web/js/Deluge.MultiOptionsManager.js rename to deluge/ui/web/js/deluge-all/Deluge.MultiOptionsManager.js diff --git a/deluge/ui/web/js/Deluge.OptionsManager.js b/deluge/ui/web/js/deluge-all/Deluge.OptionsManager.js similarity index 100% rename from deluge/ui/web/js/Deluge.OptionsManager.js rename to deluge/ui/web/js/deluge-all/Deluge.OptionsManager.js diff --git a/deluge/ui/web/js/Deluge.Plugin.js b/deluge/ui/web/js/deluge-all/Deluge.Plugin.js similarity index 100% rename from deluge/ui/web/js/Deluge.Plugin.js rename to deluge/ui/web/js/deluge-all/Deluge.Plugin.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Bandwidth.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Bandwidth.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Bandwidth.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Bandwidth.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Cache.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Cache.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Cache.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Cache.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Daemon.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Daemon.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Daemon.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Daemon.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Downloads.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Downloads.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Downloads.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Downloads.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Encryption.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Encryption.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Encryption.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Encryption.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Interface.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Interface.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Interface.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Interface.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Network.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Network.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Network.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Network.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Notification.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Notification.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Notification.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Notification.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Other.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Other.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Other.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Other.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Plugins.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Plugins.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Plugins.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Plugins.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Proxy.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Proxy.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Proxy.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Proxy.js diff --git a/deluge/ui/web/js/Deluge.Preferences.Queue.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.Queue.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.Queue.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.Queue.js diff --git a/deluge/ui/web/js/Deluge.Preferences.js b/deluge/ui/web/js/deluge-all/Deluge.Preferences.js similarity index 100% rename from deluge/ui/web/js/Deluge.Preferences.js rename to deluge/ui/web/js/deluge-all/Deluge.Preferences.js diff --git a/deluge/ui/web/js/Deluge.Remove.js b/deluge/ui/web/js/deluge-all/Deluge.Remove.js similarity index 100% rename from deluge/ui/web/js/Deluge.Remove.js rename to deluge/ui/web/js/deluge-all/Deluge.Remove.js diff --git a/deluge/ui/web/js/Deluge.Sidebar.js b/deluge/ui/web/js/deluge-all/Deluge.Sidebar.js similarity index 100% rename from deluge/ui/web/js/Deluge.Sidebar.js rename to deluge/ui/web/js/deluge-all/Deluge.Sidebar.js diff --git a/deluge/ui/web/js/Deluge.Statusbar.js b/deluge/ui/web/js/deluge-all/Deluge.Statusbar.js similarity index 100% rename from deluge/ui/web/js/Deluge.Statusbar.js rename to deluge/ui/web/js/deluge-all/Deluge.Statusbar.js diff --git a/deluge/ui/web/js/Deluge.Toolbar.js b/deluge/ui/web/js/deluge-all/Deluge.Toolbar.js similarity index 100% rename from deluge/ui/web/js/Deluge.Toolbar.js rename to deluge/ui/web/js/deluge-all/Deluge.Toolbar.js diff --git a/deluge/ui/web/js/Deluge.Torrents.js b/deluge/ui/web/js/deluge-all/Deluge.Torrents.js similarity index 100% rename from deluge/ui/web/js/Deluge.Torrents.js rename to deluge/ui/web/js/deluge-all/Deluge.Torrents.js diff --git a/deluge/ui/web/js/Deluge.UI.js b/deluge/ui/web/js/deluge-all/Deluge.UI.js similarity index 100% rename from deluge/ui/web/js/Deluge.UI.js rename to deluge/ui/web/js/deluge-all/Deluge.UI.js diff --git a/deluge/ui/web/js/Deluge.js b/deluge/ui/web/js/deluge-all/Deluge.js similarity index 100% rename from deluge/ui/web/js/Deluge.js rename to deluge/ui/web/js/deluge-all/Deluge.js diff --git a/deluge/ui/web/js/ext-extensions-debug.js b/deluge/ui/web/js/ext-extensions-debug.js index 7eca42e56..ff8a64392 100644 --- a/deluge/ui/web/js/ext-extensions-debug.js +++ b/deluge/ui/web/js/ext-extensions-debug.js @@ -1,19 +1,1370 @@ -/** - * Copyright (c) 2008, Steven Chim - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/*! + * 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( + '
' + ); + ts.rowHolder.disableFormats = true; + ts.rowHolder.compile(); + + ts.rowBody = new Ext.Template( + '', + '{cells}', + (this.enableRowBody ? '' : ''), + '
{body}
' + ); + 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(); + } +}); +/*! + * 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; +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); +} +/*! + * 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; +/*! + * 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; +/*! + * 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 + *

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.

+ *

+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
+
+ * @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: + *

+// 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']
+    })
+});
+
+ */ + /** + * @cfg {String} defaultText + * The default {@link #text} value. This will be used anytime the status bar is cleared with the + * useDefaults:true 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 useDefaults:true option (defaults to ''). + */ + /** + * @cfg {String} text + * A string that will be initially 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 {@link #defaultText} + * will be used. + */ + /** + * @cfg {String} iconCls + * A CSS class that will be initially set as the status bar icon and is + * expected to provide a background image (defaults to ''). + * Example usage:

+// 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'
+});
+
+ */ + + /** + * @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 {@link #iconCls} applied when calling + * {@link #showBusy} (defaults to 'x-status-busy'). + * It can be overridden at any time by passing the iconCls + * argument into {@link #showBusy}. + */ + busyIconCls : 'x-status-busy', + /** + * @cfg {String} busyText + * The default {@link #text} applied when calling + * {@link #showBusy} (defaults to 'Loading...'). + * It can be overridden at any time by passing the text + * argument into {@link #showBusy}. + */ + busyText : 'Loading...', + /** + * @cfg {Number} autoClear + * The number of milliseconds to wait after setting the status via + * {@link #setStatus} before automatically clearing the status + * text and icon (defaults to 5000). Note that this only applies + * when passing the clear argument to {@link #setStatus} + * since that is the only way to defer clearing the status. This can + * be overridden by specifying a different wait value in + * {@link #setStatus}. Calls to {@link #clearStatus} + * 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 + * ' '). If there are no other items in the toolbar using + * an empty string ('') for this value would end up in the toolbar + * height collapsing since the empty string will not maintain the toolbar + * height. Use '' 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:
    + *
  • text {String} : (optional) The status text to display. If not specified, any current + * status text will remain unchanged.
  • + *
  • iconCls {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.
  • + *
  • clear {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 true 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:
      + *
    • wait {Number} : (optional) The number of milliseconds to wait before clearing + * (defaults to {@link #autoClear}).
    • + *
    • anim {Number} : (optional) False to clear the status immediately once the callback + * executes (defaults to true which fades the status out).
    • + *
    • useDefaults {Number} : (optional) False to completely clear the status text and iconCls + * (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).
    • + *
+ * Example usage:

+// 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
+    }
+});
+
+ * @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:
    + *
  • anim {Boolean} : (optional) True to clear the status by fading out the status element (defaults + * to false which clears immediately).
  • + *
  • useDefaults {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).
  • + *
+ * @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 + * text and/or iconCls 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); Ext.namespace("Ext.ux.form"); /** @@ -78,1025 +1429,809 @@ Ext.ux.form.ToggleField = Ext.extend(Ext.form.Field, { } }); Ext.reg('togglefield', Ext.ux.form.ToggleField); +/*! + * 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'); /** - * Ext.ux.form.Spinner Class - * - * @author Steven Chim - * @version Spinner.js 2008-08-27 v0.35 - * - * @class Ext.ux.form.Spinner - * @extends Ext.form.TriggerField - */ -Ext.ux.form.Spinner = function(config){ - Ext.ux.form.Spinner.superclass.constructor.call(this, config); - this.addEvents({ - 'spin' : true, - 'spinup' : true, - 'spindown' : true - }); - this.initStrategy(); -} - -Ext.extend(Ext.ux.form.Spinner, Ext.form.TriggerField, { - triggerClass : 'x-form-spinner-trigger', - splitterClass : 'x-form-spinner-splitter', - - alternateKey : Ext.EventObject.shiftKey, - strategy : undefined, - - //private - onRender : function(ct, position){ - Ext.ux.form.Spinner.superclass.onRender.call(this, ct, position); - - this.splitter = this.wrap.createChild({tag:'div', cls:this.splitterClass, style:'width:13px; height:2px;'}); - this.splitter.show().setRight( (Ext.isIE) ? 1 : 2 ); - this.splitter.show().setTop(10); - - 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.initSpinner(); - }, - - //private - initTrigger : function(){ - this.trigger.addClassOnOver('x-form-trigger-over'); - this.trigger.addClassOnClick('x-form-trigger-click'); - }, - - //private - initSpinner : function(){ - 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); - this.repeater.on("click", this.onTriggerClick, this, {preventDefault:true}); - this.trigger.on("mouseover", this.onMouseOver, this, {preventDefault:true}); - this.trigger.on("mouseout", this.onMouseOut, this, {preventDefault:true}); - this.trigger.on("mousemove", this.onMouseMove, this, {preventDefault:true}); - this.trigger.on("mousedown", this.onMouseDown, this, {preventDefault:true}); - this.trigger.on("mouseup", this.onMouseUp, this, {preventDefault:true}); - this.wrap.on("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); - }, - - initStrategy: function() { - /* - jsakalos suggestion - http://extjs.com/forum/showthread.php?p=121850#post121850 */ - if('object' == typeof this.strategy && this.strategy.xtype) { - switch(this.strategy.xtype) { - case 'number': - this.strategy = new Ext.ux.form.Spinner.NumberStrategy(this.strategy); - break; - - case 'date': - this.strategy = new Ext.ux.form.Spinner.DateStrategy(this.strategy); - break; - - case 'time': - this.strategy = new Ext.ux.form.Spinner.TimeStrategy(this.strategy); - break; - - default: - delete(this.strategy); - break; - } - delete(this.strategy.xtype); - } - - if(this.strategy == undefined){ - this.strategy = new Ext.ux.form.Spinner.NumberStrategy(); - } - }, - - //private - onMouseOver : function(){ - if(this.disabled){ - return; - } - var middle = this.getMiddle(); - this.__tmphcls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown'; - this.trigger.addClass(this.__tmphcls); - }, - - //private - onMouseOut : function(){ - this.trigger.removeClass(this.__tmphcls); - }, - - //private - onMouseMove : function(){ - if(this.disabled){ - return; - } - var middle = this.getMiddle(); - if( ((Ext.EventObject.getPageY() > middle) && this.__tmphcls == "x-form-spinner-overup") || - ((Ext.EventObject.getPageY() < middle) && this.__tmphcls == "x-form-spinner-overdown")){ - } - }, - - //private - onMouseDown : function(){ - if(this.disabled){ - return; - } - var middle = this.getMiddle(); - this.__tmpccls = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown'; - this.trigger.addClass(this.__tmpccls); - }, - - //private - onMouseUp : function(){ - this.trigger.removeClass(this.__tmpccls); - }, - - //private - onTriggerClick : function(){ - if(this.disabled || this.getEl().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.getEl().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.strategy.onSpinUp(this); - } - this.fireEvent("spin", this); - this.fireEvent("spinup", this); - this.fireEvent("change", this); - }, - - //private - onSpinDown : function(){ - if(this.isSpinnable() == false) { - return; - } - if(Ext.EventObject.shiftKey == true){ - this.onSpinDownAlternate(); - return; - }else{ - this.strategy.onSpinDown(this); - } - this.fireEvent("spin", this); - this.fireEvent("spindown", this); - this.fireEvent("change", this); - }, - - //private - onSpinUpAlternate : function(){ - if(this.isSpinnable() == false) { - return; - } - this.strategy.onSpinUpAlternate(this); - this.fireEvent("spin", this); - this.fireEvent("spinup", this); - this.fireEvent("change", this); - }, - - //private - onSpinDownAlternate : function(){ - if(this.isSpinnable() == false) { - return; - } - this.strategy.onSpinDownAlternate(this); - this.fireEvent("spin", this); - this.fireEvent("spindown", this); - this.fireEvent("change", this); - }, - - setValue: function(value) { - value = this.strategy.fixBoundries(value); - Ext.ux.form.Spinner.superclass.setValue.call(this, value); - } - -}); - -Ext.reg('uxspinner', Ext.ux.form.Spinner); - -/** - * Copyright (c) 2008, Steven Chim - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * @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 ['sort-asc', 'sort-desc']) + */ + sortClasses : ['sort-asc', 'sort-desc'], + /** + * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to 'Sort Ascending') + */ + sortAscText : 'Sort Ascending', + /** + * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to 'Sort Descending') + */ + sortDescText : 'Sort Descending', -/*** - * Abstract Strategy - */ -Ext.ux.form.Spinner.Strategy = function(config){ - Ext.apply(this, config); -}; + constructor : function(tree, config) { + if(!Ext.isObject(config)) { + config = { + property: tree.columns[0].dataIndex || 'text', + folderSort: true + } + } -Ext.extend(Ext.ux.form.Spinner.Strategy, Ext.util.Observable, { - defaultValue : 0, - minValue : undefined, - maxValue : undefined, - incrementValue : 1, - alternateIncrementValue : 5, - validationTask : new Ext.util.DelayedTask(), - - onSpinUp : function(field){ - this.spin(field, false, false); - }, + Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this, arguments); - onSpinDown : function(field){ - this.spin(field, true, false); - }, + this.tree = tree; + tree.on('headerclick', this.onHeaderClick, this); + tree.ddAppendOnly = true; - onSpinUpAlternate : function(field){ - this.spin(field, false, true); - }, + me = this; + this.defaultSortFn = function(n1, n2){ - onSpinDownAlternate : function(field){ - this.spin(field, true, true); - }, + 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'; - spin : function(field, down, alternate){ - this.validationTask.delay(500, function(){field.validate()}); - //extend - }, + 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; + } + }; - fixBoundries : function(value){ - return value; - //overwrite - } - -}); + tree.on('afterrender', this.onAfterTreeRender, this, {single: true}); + tree.on('headermenuclick', this.onHeaderMenuClick, this); + }, -/*** - * Concrete Strategy: Numbers - */ -Ext.ux.form.Spinner.NumberStrategy = function(config){ - Ext.ux.form.Spinner.NumberStrategy.superclass.constructor.call(this, config); -}; + 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'); + }, -Ext.extend(Ext.ux.form.Spinner.NumberStrategy, Ext.ux.form.Spinner.Strategy, { + onHeaderMenuClick : function(c, id, index) { + if(id === 'asc' || id === 'desc') { + this.onHeaderClick(c, null, index); + return false; + } + }, - allowDecimals : true, - decimalPrecision : 2, - - spin : function(field, down, alternate){ - Ext.ux.form.Spinner.NumberStrategy.superclass.spin.call(this, field, down, alternate); + onHeaderClick : function(c, el, i) { + if(c && !this.tree.headersDisabled){ + var me = this; - var v = parseFloat(field.getValue()); - var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; + 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; - (down == true) ? v -= incr : v += incr ; - v = (isNaN(v)) ? this.defaultValue : v; - v = this.fixBoundries(v); - field.setRawValue(v); - }, + this.tree.root.cascade(function(n) { + if(!n.isLeaf()) { + me.updateSort(me.tree, n); + } + }); - fixBoundries : function(value){ - var v = value; + this.updateSortIcon(i, c.dir); + } + }, - if(this.minValue != undefined && v < this.minValue){ - v = this.minValue; - } - if(this.maxValue != undefined && v > this.maxValue){ - v = this.maxValue; - } - - return this.fixPrecision(v); - }, - // private - fixPrecision : function(value){ - var nan = isNaN(value); - if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){ - return nan ? '' : value; - } - return Number(value).toFixed(this.decimalPrecision); + 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]); } -}); - - -/*** - * Concrete Strategy: Date +});/*! + * Ext JS Library 3.1.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license */ -Ext.ux.form.Spinner.DateStrategy = function(config){ - Ext.ux.form.Spinner.DateStrategy.superclass.constructor.call(this, config); -}; - -Ext.extend(Ext.ux.form.Spinner.DateStrategy, Ext.ux.form.Spinner.Strategy, { - defaultValue : new Date(), - format : "Y-m-d", - incrementValue : 1, - incrementConstant : Date.DAY, - alternateIncrementValue : 1, - alternateIncrementConstant : Date.MONTH, - - spin : function(field, down, alternate){ - Ext.ux.form.Spinner.DateStrategy.superclass.spin.call(this); - - var v = field.getRawValue(); - - v = Date.parseDate(v, this.format); - var dir = (down == true) ? -1 : 1 ; - var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; - var dtconst = (alternate == true) ? this.alternateIncrementConstant : this.incrementConstant; - - if(typeof this.defaultValue == 'string'){ - this.defaultValue = Date.parseDate(this.defaultValue, this.format); - } - - v = (v) ? v.add(dtconst, dir*incr) : this.defaultValue; - - v = this.fixBoundries(v); - field.setRawValue(Ext.util.Format.date(v,this.format)); - }, - - //private - fixBoundries : function(date){ - var dt = date; - var min = (typeof this.minValue == 'string') ? Date.parseDate(this.minValue, this.format) : this.minValue ; - var max = (typeof this.maxValue == 'string') ? Date.parseDate(this.maxValue, this.format) : this.maxValue ; - - if(this.minValue != undefined && dt < min){ - dt = min; - } - if(this.maxValue != undefined && dt > max){ - dt = max; - } - - return dt; - } - -}); - -/*** - * Concrete Strategy: Time +/** + * @class Ext.tree.ColumnResizer + * @extends Ext.util.Observable */ -Ext.ux.form.Spinner.TimeStrategy = function(config){ - Ext.ux.form.Spinner.TimeStrategy.superclass.constructor.call(this, config); -}; +Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { + /** + * @cfg {Number} minWidth The minimum width the column can be dragged to. + * Defaults to 14. + */ + minWidth: 14, -Ext.extend(Ext.ux.form.Spinner.TimeStrategy, Ext.ux.form.Spinner.DateStrategy, { - format : "H:i", - incrementValue : 1, - incrementConstant : Date.MINUTE, - alternateIncrementValue : 1, - alternateIncrementConstant : Date.HOUR -}); + constructor: function(config){ + Ext.apply(this, config); + Ext.tree.ColumnResizer.superclass.constructor.call(this); + }, -Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, { - lines:false, - borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell - cls:'x-column-tree', + 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); + } +});/*! + * 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, - onRender : function(){ - Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments); - this.headers = this.body.createChild( - {cls:'x-tree-headers'},this.innerCt.dom); - - var cols = this.columns, c; - var totalWidth = 0; - - for(var i = 0, len = cols.length; i < len; i++){ - c = cols[i]; - totalWidth += c.width; - this.headers.createChild({ - cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''), - cn: { - cls:'x-tree-hd-text', - html: c.header - }, - style:'width:'+(c.width-this.borderWidth)+'px;' - }); - } - this.headers.createChild({cls:'x-clear'}); - // prevent floats from wrapping when clipped - this.headers.setWidth(totalWidth); - this.innerCt.setWidth(totalWidth); - } -}); - -Ext.tree.ColumnTreeNode = Ext.extend(Ext.tree.TreeNode, { - - setColumnValue: function(index, value) { - var t = this.getOwnerTree(); - var oldValue = this[t.columns[index].dataIndex]; - this[t.columns[index].dataIndex] = value; - this.attributes[[t.columns[index].dataIndex]] = value; - if (this.rendered) { - this.ui.onColumnValueChange(this, index, value, oldValue); - } - this.fireEvent('columnvaluechange', this, index, value, oldValue); - } -}); - -Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { - focus: Ext.emptyFn, // prevent odd scrolling behavior - - onColumnValueChange: function(n, colIndex, value, oldValue) { - if (this.rendered) { - var c = n.getOwnerTree().columns[colIndex]; - this.columnNodes[colIndex].innerHTML = (c.renderer ? c.renderer(value, n, null) : value); - } - }, - 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() : ''; - var t = n.getOwnerTree(); - var cols = t.columns; - var bw = t.borderWidth; - var c = cols[0]; - - var cb = typeof a.checked == 'boolean'; - var href = a.href ? a.href : Ext.isGecko ? "" : "#"; + buf = [ + '', + '', + '', + '', this.indentMarkup, "", + '', + '', + '', + '', (c.tpl ? c.tpl.apply(a) : a[c.dataIndex] || c.text), '', + '' + ]; - var buf = [ - '
  • ', - '
    ', - '',this.indentMarkup,"", - '', - '', - cb ? ('' : '/>')) : '', - '', - '', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"", - "
    "]; - for(var i = 1, len = cols.length; i < len; i++){ - c = cols[i]; + for(i = 1, len = cols.length; i < len; i++){ + c = cols[i]; + buf.push( + '', + '
    ', + (c.tpl ? c.tpl.apply(a) : a[c.dataIndex]), + '
    ', + '' + ); + } - buf.push('
    ', - '
    ',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"
    ", - "
    "); - } - buf.push( - '
    ', - '', - "
  • "); + buf.push( + '', + '' + ); + for(i = 0, len = cols.length; i'); + } + buf.push(''); if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ - this.wrap = Ext.DomHelper.insertHtml("beforeBegin", - n.nextSibling.ui.getEl(), buf.join("")); + this.wrap = Ext.DomHelper.insertHtml("beforeBegin", n.nextSibling.ui.getEl(), buf.join('')); }else{ - this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join("")); + this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join('')); } this.elNode = this.wrap.childNodes[0]; - this.ctNode = this.wrap.childNodes[1]; + 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]; - var index = 3; - if(cb){ - this.checkbox = cs[3]; - // fix for IE6 - this.checkbox.defaultChecked = this.checkbox.checked; - index++; - } - this.anchor = cs[index]; - this.columnNodes = [cs[index].firstChild]; - for(var i = 1, len = cols.length; i < len; i++){ - this.columnNodes[i] = this.elNode.childNodes[i].firstChild; - } + 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.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, +Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { + isTreeGridNodeUI: true, // private - initComponent: function(){ - Ext.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.form.FileUploadField} this - * @param {String} value The file value returned by the underlying file input field - */ - 'fileselected' - ); - }, - - // private - onRender : function(ct, position){ - Ext.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.fileInput = this.wrap.createChild({ - id: this.getFileInputId(), - name: this.name||this.getId(), - cls: 'x-form-file', - tag: 'input', - type: 'file', - size: 1 - }); - - var btnCfg = Ext.applyIf(this.buttonCfg || {}, { - text: this.buttonText - }); - this.button = new Ext.Button(Ext.apply(btnCfg, { - renderTo: this.wrap - })); - - if(this.buttonOnly){ - this.el.hide(); - this.wrap.setWidth(this.button.getEl().getWidth()); + render : function(){ + if(!this.rendered){ + this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom; + this.node.expanded = true; } - this.fileInput.on('change', function(){ - var v = this.fileInput.dom.value; - this.setValue(v); - this.fireEvent('fileselected', this, v); - }, this); - }, - - // private - getFileInputId: function(){ - return this.id+'-file'; - }, - - // private - onResize : function(w, h){ - Ext.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); + 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); } }, - - // private - preFocus : Ext.emptyFn, - - // private - getResizeEl : function(){ - return this.wrap; + + destroy : function(){ + if(this.elNode){ + Ext.dd.Registry.unregister(this.elNode.id); + } + delete this.node; }, - - // private - getPositionEl : function(){ - return this.wrap; - }, - - // private - alignErrorIcon : function(){ - this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); - } -}); -Ext.reg('fileuploadfield', Ext.form.FileUploadField); - + collapse : Ext.emptyFn, + expand : Ext.emptyFn +});/*! + * Ext JS Library 3.1.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ /** - * Ext.ux.FullProgressBar Class - * - * @author Damien Churchill - * @version 1.2 - * - * @class Ext.deluge.ProgressBar - * @extends Ext.ProgressBar - * @constructor - * @param {Object} config Configuration options - */ -Ext.ux.FullProgressBar = Ext.extend(Ext.ProgressBar, { - initComponent: function() { - Ext.ux.FullProgressBar.superclass.initComponent.call(this); - }, - - updateProgress: function(value, text, animate) { - this.value = value || 0; - if (text) { - this.updateText(text); - } - - if (this.rendered) { - var w = Math.floor(value*this.el.dom.firstChild.offsetWidth / 100.0); - this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate)); - if (this.textTopEl) { - //textTopEl should be the same width as the bar so overflow will clip as the bar moves - this.textTopEl.removeClass('x-hidden').setWidth(w); - } - } - this.fireEvent('update', this, value, text); - return this; - } -}); -Ext.reg('fullprogressbar', Ext.ux.FullProgressBar); + * @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); + } +});/*! + * 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]; + } -// Allow radiogroups to be treated as a single form element. -Ext.override(Ext.form.RadioGroup, { + // 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; + } + } + } + }); - afterRender: function() { - var that = this; - this.items.each(function(i) { - that.relayEvents(i, ['check']); - }); - Ext.form.RadioGroup.superclass.afterRender.call(this) + 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); +})(); +/*! + * 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( + '
    ', + '
    ', + '
    ', + '', + '', + '', + '', + '', + '
    ', + '
    ', + this.enableHdMenu ? '' : '', + '{header}', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ', + '
    ' + ); + } + + if(!this.colgroupTpl) { + this.colgroupTpl = new Ext.XTemplate( + '' + ); + } }, - getName: function() { - return this.items.first().getName(); + 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; }, - getValue: function() { - var v; - v = this.items.first().getGroupValue(); - //this.items.each(function(item) { - // v = item.getRawValue(); - // return !item.getValue(); - //}); - return v; + 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); + } }, - setValue: function(v) { - if (!this.items.each) return; - this.items.each(function(item) { - var checked = (item.el.getValue() == String(v)); - if (item.rendered) { - item.el.dom.checked = checked; - item.el.dom.defaultChecked = checked; - item.wrap[checked ? 'addClass' : 'removeClass'](item.checkedCls); - } - }); + 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 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.ux.form.SpinnerGroup = Ext.extend(Ext.form.CheckboxGroup, { - - // private - defaultType: 'uxspinner', - - // private - groupCls: 'x-form-uxspinner-group', - - colCfg: {}, - - // private - onRender : function(ct, position){ - if(!this.el){ - var panelCfg = { - cls: this.groupCls, - layout: 'column', - border: false, - renderTo: ct - }; - var colCfg = Ext.apply({ - defaultType: this.defaultType, - layout: 'form', - border: false, - labelWidth: 60, - defaults: { - hideLabel: true, - anchor: '100%' - } - }, this.colCfg); - - if(this.items[0].items){ - - // The container has standard ColumnLayout configs, so pass them in directly - - Ext.apply(panelCfg, { - layoutConfig: {columns: this.items.length}, - defaults: this.defaults, - items: this.items - }) - for(var i=0, len=this.items.length; i0 && i%rows==0){ - ri++; - } - if(this.items[i].fieldLabel){ - this.items[i].hideLabel = false; - } - cols[ri].items.push(this.items[i]); - }; - }else{ - for(var i=0, len=this.items.length; i