All pastes #804033 Raw Edit

Mine

public text v1 · immutable
·published 2007-12-03 01:23 UTC
rendered paste body
/*
 *	Copyright © Dave Perrett and Malcolm Jarvis
 *	This code is licensed under the GPL version 2.
 *	For more details, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 * Class Transmission
 */

function Transmission(){
    // Constants
	this._FilterAll              = 'all';
	this._FilterSeeding          = 'seeding';
	this._FilterDownloading      = 'downloading';
	this._FilterPaused           = 'paused';
	this._SortAscending          = 'ascending';
	this._SortDescending         = 'descending';
	this._EncryptionPreferred    = 'preferred';
	this._EncryptionRequired     = 'required';
	this._current_filter         = this._FilterAll;
	this._current_sort_method    = 'queue_order';
	this._current_sort_direction = this._SortAscending;
	this._current_search         = '';

    this.initialize();
} 
 
Transmission.prototype = {

    /*
     * Constructor
     */
    initialize: function() {
	
		// Before we do anything, browser compatability test
		if ($.browser.msie) {
			$('div.torrent_footer').hide();
			$('div#unsupported_browser').show();
			return;
		}
		
		// Initialize the helper classes
		this.remote = new TransmissionRemote(this);
		
        /*
         * Private Variables
         */
		this._filter_visible     = false;
		this._inspector_visible  = false;
		this._speed_limit_active = false;
		
		// Initialise the torrent lists
        this._torrents            = new Hash();
        this._selected_torrents   = new Hash();
        this._num_paused_torrents = 0;
        this._num_active_torrents = 0;
        this._num_selected_paused_torrents = 0;
        this._num_selected_active_torrents = 0;
		
		// Pre-load the images
		this.preloadImages();
        
        // Get the initial settings from the remote server
        this.remote.request('requestSettings');
        
        // Observe key presses
		$(document).bind('keydown', {transmission: this}, this.keyDown);

		// Buttons
		$('#pause_all_link').bind('click', {transmission: this}, this.releasePauseAllButton);
		$('#resume_all_link').bind('click', {transmission: this}, this.releaseResumeAllButton);
		$('#pause_selected_link').bind('click', {transmission: this}, this.releasePauseSelectedButton);
		$('#resume_selected_link').bind('click', {transmission: this}, this.releaseResumeSelectedButton);
		$('#open_link').bind('click', {transmission: this}, this.releaseOpenButton);
		$('#remove_link').bind('click', {transmission: this}, this.releaseRemoveButton);
		$('#filter_toggle_link').bind('click', {transmission: this}, this.releaseFilterToggleButton);
		$('#inspector_link').bind('click', {transmission: this}, this.releaseInspectorButton);
		$('#filter_all_link').bind('click', {transmission: this}, this.releaseFilterAllButton);
		$('#filter_downloading_link').bind('click', {transmission: this}, this.releaseFilterDownloadingButton);
		$('#filter_seeding_link').bind('click', {transmission: this}, this.releaseFilterSeedingButton);
		$('#filter_paused_link').bind('click', {transmission: this}, this.releaseFilterPausedButton);
		$('#upload_confirm_button').bind('click', {transmission: this}, this.releaseUploadConfirmButton);
		$('#upload_cancel_button').bind('click', {transmission: this}, this.releaseUploadCancelButton);
		$('#prefs_save_button').bind('click', {transmission: this}, this.releasePrefsSaveButton);
		$('#prefs_cancel_button').bind('click', {transmission: this}, this.releasePrefsCancelButton);
		$('#speed_limit_button').bind('click', {transmission: this}, this.releaseSpeedLimitButton);
		
		// Inspector tabs
		$('#inspector_tab_info').bind('click', {transmission: this}, this.releaseInspectorTab);
		$('#inspector_tab_activity').bind('click', {transmission: this}, this.releaseInspectorTab);
		
		// Setup the search box
		this.setupSearchBox();
		
		// Set up the right-click context menu
		this.createContextMenu();
		
		// Setup the footer settings menu
		this.createSettingsMenu();
		
		// Setup the preference box
		this.setupPrefs();
    },
    

    /*--------------------------------------------
     * 
     *  S E T T E R S   /   G E T T E R S
     * 
     *--------------------------------------------*/
    
    /*
     * Return the current filter
     */
    currentFilter: function() {
        return this._current_filter;
    },
    
    /*
     * Set the current filter
     */
    setCurrentFilter: function(filter) {
        this._current_filter = filter;
    },
    
    /*
     * Return the current sort method
     */
    currentSortMethod: function() {
        return this._current_sort_method;
    },
    
    /*
     * Set the current sort method
     */
    setCurrentSortMethod: function(sort_method) {
        this._current_sort_method = sort_method;
    },
    
    /*
     * Return the current sort direction
     */
    currentSortDirection: function() {
        return this._current_sort_direction;
    },
    
    /*
     * Set the current sort direction
     */
    setCurrentSortDirection: function(sort_direction) {
        this._current_sort_direction = sort_direction;
    },
    
    /*
     * Return the current search criteria
     */
    currentSearch: function() {
        return this._current_search;
    },
    
    /*
     * Return the last torrent clicked
     */
    lastTorrentClicked: function() {
        return this._last_torrent_clicked;
    },
    
    /*
     * Set the last torrent clicked
     */
    setLastTorrentClicked: function(torrent) {
        this._last_torrent_clicked = torrent;
    },
    
    /*
     * Return the highest selected (ie closest to the top) torrent in the list
     */
    highestSelected: function() {
        return this._highest_selected;
    },
    
    /*
     * Set the highest selected (ie closest to the top) torrent in the list
     */
    setHighestSelected: function(torrent) {
        this._highest_selected = torrent;
    },
    
    /*
     * Return the lowest selected (ie closest to the bottom) torrent in the list
     */
    lowestSelected: function() {
        return this._lowest_selected;
    },
    
    /*
     * Set the lowest selected (ie closest to the bottom) torrent in the list
     */
    setLowestSelected: function(torrent) {
        this._lowest_selected = torrent;
    },
    
    /*
     * Return the number of selected torrents
     */
    numTorrents: function() {
		return this._torrents.length();
    },
    
    /*
     * Return the number of selected torrents
     */
    numSelectedTorrents: function() {
		return this._selected_torrents.length();
    },
    
    /*
     * Return the number of selected active torrents
     */
    numSelectedActiveTorrents: function() {
		return this._num_selected_active_torrents;
    },
    
    /*
     * Return the list of selected torrents
     */
    selectedTorrents: function() {
		return this._selected_torrents;
    },




    
    /*--------------------------------------------
     * 
     *  E V E N T   F U N C T I O N S
     * 
     *--------------------------------------------*/
    
    /*
     * Process key event
     */
    keyDown: function(event) {
		
		var transmission = event.data.transmission;
        var selected_torrent;

        // Down Arrow Key
        if (event.keyCode == 40 && transmission._lowest_selected != null) {
            selected_torrent = transmission._lowest_selected;
            if (selected_torrent.nextTorrent() != null) {
                selected_torrent = selected_torrent.nextTorrent();
            }
            transmission.deselectAll();
            selected_torrent.select();
            transmission._last_torrent_clicked = selected_torrent;
            
        // Up Arrow key	
        } else if (event.keyCode == 38) {
            selected_torrent = transmission._highest_selected;
            if (selected_torrent.previousTorrent() != null) {
                selected_torrent = selected_torrent.previousTorrent();
            }
            transmission.deselectAll();
            selected_torrent.select();
            transmission._last_torrent_clicked = selected_torrent;
		}
    },
    
    /*
     * Register the specified torrent as selected
     */
    selectTorrent: function(torrent) {
		
		// Figure out if this is the highest selected torrent
		if (this._highestSelected == null || 
			this._highestSelected.position() > torrent.position()) {
			this.setHighestSelected(torrent);
		}
		
		// Figure out if this is the lowest selected torrent
		if (this._lowestSelected == null || 
			this._lowestSelected.position() < torrent.position()) {
			this.setLowestSelected(torrent);
		}	
		
		// Store this in the list of selected torrents	
        if (!this._selected_torrents.hasKey(torrent.id())) {
			this._selected_torrents.set(torrent.id(), torrent);
		}
		
		// Enable/disabled buttons based on the selection
		if (torrent.isActive()) {
			this._num_selected_active_torrents++;
		} else {
			this._num_selected_paused_torrents++;
		}
		this.updateButtonStates();

		// Display in Inspector
		this.updateInspector();
    },
    
    /*
     * Register the specified torrent as de-selected
     */
    deselectTorrent: function(torrent, ignore_inspector_update) {
	
		var temp_torrent;
		var found;
		
		if (ignore_inspector_update == null) {
			ignore_inspector_update = false
		}
		
		// De-select the torrent via css
		torrent.element().removeClass('selected');
		
		// Remove this from the list of selected torrents
		transmission._selected_torrents.remove(torrent.id());
		
		if (! ignore_inspector_update) {
		
			// May need to re-calculate the controllers highest selected torrent :
			// work down the list until the next selected torrent
			if (torrent == transmission._highestSelected) {
				temp_torrent = torrent._next_torrent;
				found = false;
				while (found == false && temp_torrent != null) {
					if 	(temp_torrent.isSelected()) {
						found = true;
						transmission._highestSelected = temp_torrent;
					}
					temp_torrent = temp_torrent.nextTorrent();
				}
			}
		
			// May need to re-calculate the controllers lowest selected torrent :
			// work down the list until the next selected torrent
			if (torrent == transmission._lowestSelected) {
				temp_torrent = torrent._previous_torrent;
				found = false;
				while (found == false && temp_torrent != null) {
					if 	(temp_torrent.isSelected()) {
						found = true;
						transmission._lowestSelected = temp_torrent;
					}
					temp_torrent = temp_torrent.previousTorrent();
				}
			}
			
			// Enable/disabled buttons based on the selection
			if (torrent.isActive()) {
				transmission._num_selected_active_torrents--;
			} else {
				transmission._num_selected_paused_torrents--;
			}
			transmission.updateButtonStates();
		
			// Display in Inspector
			if (!ignore_inspector_update) {
				transmission.updateInspector();
			}
		}
    },

	/*
	 * Process a mouse-up event on the 'pause all' button
	 */
	releasePauseAllButton: function(event) {
		event.data.transmission.remote.pauseTorrents([]);
	},

	/*
	 * Process a mouse-up event on the 'resume all' button
	 */
	releaseResumeAllButton: function(event) {
		event.data.transmission.remote.resumeTorrents([]);
	},

	/*
	 * Process a mouse-up event on the 'pause selected' button
	 */
	releasePauseSelectedButton: function(event) {
		event.data.transmission.pauseSelectedTorrents();
	},

	/*
	 * Process a mouse-up event on the 'resume selected' button
	 */
	releaseResumeSelectedButton: function(event) {
		event.data.transmission.resumeSelectedTorrents();
	},

	/*
	 * Process a mouse-up event on the 'open' button
	 */
	releaseOpenButton: function(event) {
		event.data.transmission.uploadTorrentFile();	
	},

	/*
	 * Process a mouse-up event on the 'open' button
	 */
	releaseUploadCancelButton: function(event) {
		$('#upload_container').hide();
	},

	/*
	 * Process a mouse-up event on the 'open' button
	 */
	releaseUploadConfirmButton: function(event) {
		var url_data = jQuery.fieldValue($("#torrent_upload_url")[0]);
		if (url_data.length == 0)
			event.data.transmission.uploadTorrentFile(true);
		else
			event.data.transmission.remote.addTorrentByURL();
	},

	/*
	 * Process a mouse-up event on the 'cancel' button in the preferences dialog
	 */
	releasePrefsCancelButton: function(event) {
		$('#prefs_container').hide();
		
		// Reset all the settings
		transmission.remote.request('resetPrefs');
	},

	/*
	 * Process a mouse-up event on the 'open' button
	 */
	releasePrefsSaveButton: function(event) {
		event.data.transmission.remote.savePrefs();
	},

	/*
	 * Process a mouse-up event on the 'remove' button
	 */
	releaseRemoveButton: function(event) {	
		event.data.transmission.remote.removeSelectedTorrents();
	},

	/*
	 * Process a mouse-up event on the 'inspector' button
	 */
	releaseInspectorButton: function(event) {
		event.data.transmission.toggleInspector();
	},

	/*
	 * Process a mouse-up event on an 'inspector' tab
	 */
	releaseInspectorTab: function(event) {
		
		// Unselect all the tabs, select the clicked tab, and display the appropriate info
		var tab_ids = ['inspector_tab_info', 'inspector_tab_activity'];
        for (i=0; i<tab_ids.length; i++) {
			if (this.id == tab_ids[i]) {
				$('#' + tab_ids[i]).addClass('selected');
				$('#' + tab_ids[i] + '_container').show();
			} else {
				$('#' + tab_ids[i]).removeClass('selected');
				$('#' + tab_ids[i] + '_container').hide();
			}
		}
	},
	
    /*
     * Process a mouse-up event on the 'filter' button
     */
	releaseFilterToggleButton: function(event) {
		event.data.transmission.toggleFilter();
	},

	/*
	 * Process a mouse-up event on the 'filter all' button
	 */
	releaseFilterAllButton: function(event) {	
		event.data.transmission.remote.filterTorrents(event.data.transmission._FilterAll);
		$(this).parent().siblings().removeClass('selected');
		$(this).parent().addClass('selected');
	},

	/*
	 * Process a mouse-up event on the 'filter downloading' button
	 */
	releaseFilterDownloadingButton: function(event) {
		event.data.transmission.remote.filterTorrents(event.data.transmission._FilterDownloading);
		$(this).parent().siblings().removeClass('selected');
		$(this).parent().addClass('selected');
	},

	/*
	 * Process a mouse-up event on the 'filter seeding' button
	 */
	releaseFilterSeedingButton: function(event) {	
		event.data.transmission.remote.filterTorrents(event.data.transmission._FilterSeeding);
		$(this).parent().siblings().removeClass('selected');
		$(this).parent().addClass('selected');
	},

	/*
	 * Process a mouse-up event on the 'filter paused' button
	 */
	releaseFilterPausedButton: function(event) {
		event.data.transmission.remote.filterTorrents(event.data.transmission._FilterPaused);
		$(this).parent().siblings().removeClass('selected');
		$(this).parent().addClass('selected');
	},

	/*
	 * Process a mouse-up event on the 'filter paused' button
	 */
	releaseSpeedLimitButton: function(event) {
		event.data.transmission.toggleSpeedLimit();
	},

	/*
	 * Turn the periodic ajax-refresh on & off
	 */
	togglePeriodicRefresh: function(state) {
		if (state && this._periodic_refresh == null) {
			this._periodic_refresh = setInterval('transmission.remote.reloadTorrents()', this._refresh_rate * 1000);
		} else {
			clearInterval(this._periodic_refresh);
			this._periodic_refresh = null;
		}
	},

	/*
	 * Do nothing - used for ajax calls that don't need to do anything on return
	 */
	ignore: function() {},
	
	

    /*--------------------------------------------
     * 
     *  I N T E R F A C E   F U N C T I O N S
     * 
     *--------------------------------------------*/
    
    /*
     * Setup the initial settings, and request the list of torrents from the server
     */
	initializeSettings: function(settings) {
		
		// Set the filter
		this._current_filter = settings.filter;	
		$('#filter_' + settings.filter + '_link').parent().addClass('selected');
		
		// Set the sort_method
		this._current_sort_method = settings.sort_method;
		$('#sort_by_' + settings.sort_method).selectMenuItem();
		
		// Set the sort_direction
		this._current_sort_direction = settings.sort_direction;
		if (settings.sort_direction == this._SortDescending) {
			$('#reverse_sort_order').selectMenuItem();
		}
		
		// Update the preferences
		this.updatePrefs(settings);		
		
		// Show the filter if necessary
		if (settings.show_filter) {
			this.showFilter(true);
		}
		
		// Show the inspector if necessary
		if (settings.show_inspector) {
			this.showInspector(true);
		}

		// Request the list of torrents from the server
		this.remote.request('refreshTorrents', null, this._current_filter);

		// Create a periodical executer to refresh the list
		this.togglePeriodicRefresh(true);
    },
    
    /*
     * Set up the preference validation
     */
    setupPrefs: function() {
		// Make sure only integers are input for speed limit & port options		
		$('div.preference input[@type=text]:not(#download_location)').blur( function() {
			this.value = this.value.replace(/[^0-9]/gi, '');
			if (this.value == '') {
				if ($(this).is('#refresh_rate')) {
					this.value = 5;
				} else {
					this.value = 0;
				}
			}
		});
    },
    
    /*
     * Process the preferences window with the provided values
     */
	updatePrefs: function(settings) {
		
		$('div#download_location input')[0].value      = settings.download_location;
		$('div#port input')[0].value                   = settings.port;
		$('div#auto_start input')[0].checked           = settings.auto_start;
		$('input#limit_download')[0].checked           = settings.limit_download;
		$('input#download_rate')[0].value              = settings.download_rate;
		$('input#limit_upload')[0].checked             = settings.limit_upload;
		$('input#upload_rate')[0].value                = settings.upload_rate;
		$('form#prefs_form input#over_ride_rate')[0].value = settings.over_ride_rate;
		$('input#over_ride_download_rate')[0].value    = settings.over_ride_download_rate;
		$('input#over_ride_upload_rate')[0].value      = settings.over_ride_upload_rate;
		$('input#refresh_rate')[0].value               = settings.refresh_rate;
		$('div#encryption input')[0].checked           = (settings.encryption == transmission._EncryptionRequired);
		
		// Set the download rate
		$('#limited_download_rate')[0].innerHTML = 'Limit (' + settings.download_rate + ' KB/s)';
		if (settings.limit_download) {
			$('#limited_download_rate').deselectMenuSiblings().selectMenuItem();			
		} else {
			$('#unlimited_download_rate').deselectMenuSiblings().selectMenuItem();
		}
		
		// Set the upload rate
		$('#limited_upload_rate')[0].innerHTML = 'Limit (' + settings.upload_rate + ' KB/s)';
		if (settings.limit_upload) {
			$('#limited_upload_rate').deselectMenuSiblings().selectMenuItem();			
		} else {
			$('#unlimited_upload_rate').deselectMenuSiblings().selectMenuItem();
		}
		
		// Turn on speed-limit over-ride if necessary
		if (settings.over_ride_rate) {
			transmission.activateSpeedLimit(false);
		} else {
			transmission.deactivateSpeedLimit(false);
		}
		
		// Update the refresh rate and force the new value to be used next refresh
		transmission._refresh_rate = parseInt(settings.refresh_rate);
		if (transmission._periodic_refresh) {
			transmission.togglePeriodicRefresh(false);
			transmission.togglePeriodicRefresh(true);
		}
		
		$('#prefs_container').hide();	
	},
    
    /*
     * Display an error if a preference update request fails
     */
    preferenceError: function(error_msg) {
		$('div#prefs_container h2.dialog_heading').hide();
		$('div#prefs_container div#pref_error')[0].innerHTML = error_msg;
		$('div#prefs_container div#pref_error').show();
	},
    
    /*
     * Select all torrents in the list
     */
    selectAll: function() {
		this._torrents.invoke('select');
    },
    
    /*
     * De-select all torrents in the list
     */
    deselectAll: function() {
		this._torrents.each(this.deselectTorrent, true);
        
        // reset the highest and lowest selected
        this._highest_selected = null;
        this._lowest_selected = null;
		this._num_selected_active_torrents = 0;
		this._num_selected_paused_torrents = 0;
    },
    
    /*
     * Set up the search box
     */
    setupSearchBox: function() {
		var search_box = $('#torrent_search');
		search_box[0].value = 'filter';
		search_box.bind('keyup', {transmission: this}, function(event) {
			event.data.transmission._current_search = this.value.trim();
		}).bind('blur', {transmission: this}, function(event) {
			if (this.value == '') {
				$(this).addClass('blur');
				this.value = 'filter';
				event.data.transmission._current_search = '';
			}
		}).bind('focus', {}, function(event) {
			if ($(this).is('.blur')) {
				this.value = '';
				$(this).removeClass('blur');
			}
		});
    },
    
    /*
     * Create the torrent right-click menu
     */
	createContextMenu: function() {
		
		var bindings = {
			context_pause_selected:    this.pauseSelectedTorrents,
			context_resume_selected:   this.resumeSelectedTorrents,
			context_remove:            this.remote.removeSelectedTorrents,
			context_toggle_inspector:  this.toggleInspector
		};
		
		// Setup the context menu
		$('ul#torrent_list').contextMenu('torrent_context_menu', {
			bindings:          bindings,
			menuStyle:         Menu.context.menu_style,
			itemStyle:         Menu.context.item_style,
			itemHoverStyle:    Menu.context.item_hover_style,
			itemDisabledStyle: Menu.context.item_disabled_style,
			boundingElement:   $('div#torrent_container'),
			boundingRightPad:  20,
			boundingBottomPad: 5
		});
	},
    
    /*
     * Create the footer settings menu
     */
	createSettingsMenu: function() {
		$('#settings_menu').transMenu({
			direction: 'up',
			onClick: this.processSettingsMenuEvent
		});
		
		// Make initial menu selections (TODO - do this with data from the daemon?)
		$('#unlimited_download_rate').selectMenuItem();
		$('#unlimited_upload_rate').selectMenuItem();
	},
    
    /*
     * Enable/disable the button states
     */
	updateButtonStates: function() {
		if (this._num_selected_active_torrents == 0) {
			$('li#pause_selected a').addClass('disabled');
			$('li.context_pause_selected').addClass('disabled');		
		} else {
			$('li#pause_selected a.disabled').removeClass('disabled');
			$('li.context_pause_selected').removeClass('disabled');
		}
		
		if (this._num_selected_paused_torrents == 0) {
			$('li#resume_selected a').addClass('disabled');
			$('li.context_resume_selected').addClass('disabled');
		} else {
			$('li#resume_selected a.disabled').removeClass('disabled');
			$('li.context_resume_selected').removeClass('disabled');
		}
		
		if (this.numSelectedTorrents() == 0) {
			$('li#remove a').addClass('disabled');
		} else {
			$('li#remove a.disabled').removeClass('disabled');
		}
		
		if (this._num_active_torrents == 0) {
			$('li#pause_all a').addClass('disabled');
		} else {
			$('li#pause_all a.disabled').removeClass('disabled');
		}
		
		if (this._num_paused_torrents == 0) {
			$('li#resume_all a').addClass('disabled');
		} else {
			$('li#resume_all a.disabled').removeClass('disabled');
		}
		
		return true;
	},
    
    /*
     * Process an event in the footer-menu
     */
	processSettingsMenuEvent: function(event) {
		// Don't use 'this' in the function to avoid confusion (this != transmission instance)
		var element = this;
		
		// Figure out which menu has been clicked
		switch ($(element).parent()[0].id) {
			
			// Display the preferences dialog
			case 'footer_super_menu':
				if ($(element)[0].id == 'preferences') {
					$('div#prefs_container div#pref_error').hide();
					$('div#prefs_container h2.dialog_heading').show();
					$('#prefs_container').show();
				}
				break;
			
			// Limit the download rate
			case 'footer_download_rate_menu':
				var rate = (this.innerHTML).replace(/[^0-9]/ig, '');
				if ($(this).is('#unlimited_download_rate')) {
					$(this).deselectMenuSiblings().selectMenuItem();
					$('div.preference input#limit_download')[0].checked = false;
					rate = -1;
				} else {
					$('#limited_download_rate')[0].innerHTML = 'Limit (' + rate + ' KB/s)';
					$('#limited_download_rate').deselectMenuSiblings().selectMenuItem();
					$('div.preference input#download_rate')[0].value = rate;
					$('div.preference input#limit_download')[0].checked = true;
				}
				transmission.remote.request('setDownloadRate', rate);
				break;
			
			// Limit the upload rate
			case 'footer_upload_rate_menu':
				var rate = (this.innerHTML).replace(/[^0-9]/ig, '');
				if ($(this).is('#unlimited_upload_rate')) {
					$(this).deselectMenuSiblings().selectMenuItem();
					$('div.preference input#limit_upload')[0].checked = false;
					rate = -1;
				} else {
					$('#limited_upload_rate')[0].innerHTML = 'Limit (' + rate + ' KB/s)';
					$('#limited_upload_rate').deselectMenuSiblings().selectMenuItem();
					$('div.preference input#upload_rate')[0].value = rate;
					$('div.preference input#limit_upload')[0].checked = true;
				}
				transmission.remote.request('setUploadRate', rate);
				break;
			
			// Sort the torrent list 
			case 'footer_sort_menu':
				var sort_method = transmission._current_sort_method;
				var sort_direction = transmission._current_sort_direction;
				
				// The 'reverse sort' option state can be toggled on/off independant of the other options
				if ($(this).is('#reverse_sort_order')) {
					if ($(this).menuItemIsSelected()) {
						$(this).deselectMenuItem();
						sort_direction = transmission._SortAscending;
					} else {
						$(this).selectMenuItem();
						sort_direction = transmission._SortDescending;
					}	
				// Otherwise, deselect all other options (except reverse-sort) and select this one				
				} else {
					$(this).parent().find('span.selected').each( function() {
						if (! $(this).parent().is('#reverse_sort_order')) {
							$(this).parent().deselectMenuItem();
						}
					});
					$(this).selectMenuItem();
					sort_method = $(this)[0].id.replace(/sort_by_/, '');
				}
				transmission.remote.sortTorrents(sort_method, sort_direction);
				break;
		}
	},
    
    /*
     * Select a range from this torrent to the last clicked torrent
     */
    selectRange: function(torrent) {
        
		if (!this._last_torrent_clicked) {
			torrent.select();
			
        // The last clicked torrent is above this one in the list
        } else if (this._last_torrent_clicked.position() < torrent.position()) {
            // if the last clicked torrent is not selected, walk down the
            // list until we find one that is
            while (!this._last_torrent_clicked.isSelected() && 
                    this._last_torrent_clicked.nextTorrent() != null) {
                this._last_torrent_clicked = this._last_torrent_clicked.nextTorrent();
            }
            
            while (torrent != null && torrent.position() > this._last_torrent_clicked.position()) {
                torrent.select();
                torrent = torrent.previousTorrent();
            }
        
        // The last clicked torrent is below this one in the list
        } else if (this._last_torrent_clicked.position() > torrent.position()) {
            // if the last clicked torrent is not selected, walk up the
            // list until we find one that is
            while (!this._last_torrent_clicked.isSelected() && 
                    this._last_torrent_clicked.previousTorrent() != null) {
                this._last_torrent_clicked = this._last_torrent_clicked.previousTorrent();
            }
            
            while (torrent != null && torrent.position() < this._last_torrent_clicked.position()) {
                torrent.select();
                torrent = torrent.nextTorrent();
            }
        }
    },
    
    /*
     * Load a list of torrents into the application
     */
    addTorrents: function(torrent_list, previous_torrent) {
        var torrent_data;
        var torrent;
		
		// Clear the inspector
		this.deselectAll();
		this.updateInspector();
		
		var num_existing_torrents = this._torrents.length();
		var num_new_torrents = torrent_list.length;
        for (i=0; i<num_new_torrents; i++) {
            torrent_data = torrent_list[i];
            torrent_data.position = i+1+num_existing_torrents;
            torrent = new Torrent(torrent_data);
			
            // Set the controller
            torrent.setController(this);
            
            // Set this torrent's neighbours
            if (previous_torrent != null) {
                torrent.setPreviousTorrent(previous_torrent);
                previous_torrent.setNextTorrent(torrent);
            }
            
            // Add to the collection
            this._torrents.set(torrent.id(), torrent);
            
			// Keep track of torrent statuses
			if (torrent.isActive()) {
				this._num_active_torrents++;
			} else {
				this._num_paused_torrents++;
			}
			
            previous_torrent = torrent;
        }
    },
    
    /*
     * Update the inspector with the latest data for the curently selected torrents
     */
	updateInspector: function() {
		if (this._inspector_visible) {
			var torrent_count = this.numSelectedTorrents();
			
			// If only one torrent is selected, update all fields
			if (torrent_count == 1) {
				torrent = this._selected_torrents.first();
				$('#torrent_inspector_name')[0].innerHTML			= torrent._name;
				$('#torrent_inspector_size')[0].innerHTML			= Math.formatBytes(torrent._size);
				$('#torrent_inspector_tracker')[0].innerHTML		= torrent._tracker['address']+
															  		':'+torrent._tracker['port']+
															  		torrent._tracker['announce'];

				$('#torrent_inspector_hash')[0].innerHTML			= torrent._hash;
				$('#torrent_inspector_state')[0].innerHTML			= torrent._state;
				$('#torrent_inspector_ratio')[0].innerHTML			= torrent.ratio();
				$('#torrent_inspector_uploaded')[0].innerHTML		= Math.formatBytes(torrent._upload_total);
				$('#torrent_inspector_downloaded')[0].innerHTML		= Math.formatBytes(torrent._download_total);
				$('#torrent_inspector_upload_to')[0].innerHTML		= torrent._peers_downloading;
				$('#torrent_inspector_download_from')[0].innerHTML	= torrent._peers_uploading;
				$('#torrent_inspector_swarm_speed')[0].innerHTML	= torrent._swarm_speed;
				$('#torrent_inspector_total_seeders')[0].innerHTML	= torrent._total_seeders;
				$('#torrent_inspector_total_leechers')[0].innerHTML	= torrent._total_leechers;		
		
				if (torrent._error_message && torrent._error_message != '') {
					$('#torrent_inspector_error')[0].innerHTML		= torrent._error_message;
				} else {
					$('#torrent_inspector_error')[0].innerHTML		= 'N/A';
				}
				if (torrent._comment && torrent._comment != '') {
					$('#torrent_inspector_comment')[0].innerHTML	= torrent._comment;
				} else {
					$('#torrent_inspector_comment')[0].innerHTML	= 'N/A';
				}
				if (torrent._creator && torrent._creator != '') {
					$('#torrent_inspector_creator')[0].innerHTML	= torrent._creator;
				} else {
					$('#torrent_inspector_creator')[0].innerHTML	= 'N/A';
				}

				if (torrent._private == 1) {
					$('#torrent_inspector_secure')[0].innerHTML 	= 'Private Torrent';
				} else {
					$('#torrent_inspector_secure')[0].innerHTML		= 'Public Torrent';
				}

				$('#torrent_inspector_creator_date')[0].innerHTML	= Math.formatTimestamp(torrent._creator_date);
			
			// Otherwise, just update up/down totals
			} else {
				var total_upload = 0;
				var total_download = 0;
				for (i=0; i<torrent_count; i++) {
					total_upload += this._selected_torrents.itemByIndex(i)._upload_total;
					total_download += this._selected_torrents.itemByIndex(i)._download_total;
				}
				if (torrent_count == 0) {
					$('#torrent_inspector_name')[0].innerHTML			= 'No Torrent Selected';
				} else {
					$('#torrent_inspector_name')[0].innerHTML			= torrent_count + ' Torrents Selected';
				}
				$('#torrent_inspector_size')[0].innerHTML			= '';
				$('#torrent_inspector_tracker')[0].innerHTML		= 'N/A';
				$('#torrent_inspector_hash')[0].innerHTML			= 'N/A';
				$('#torrent_inspector_state')[0].innerHTML			= 'N/A';
				$('#torrent_inspector_ratio')[0].innerHTML			= 'N/A';
				$('#torrent_inspector_uploaded')[0].innerHTML		= Math.formatBytes(total_upload);
				$('#torrent_inspector_downloaded')[0].innerHTML		= Math.formatBytes(total_download);
				$('#torrent_inspector_upload_to')[0].innerHTML		= 'N/A';
				$('#torrent_inspector_download_from')[0].innerHTML	= 'N/A';
				$('#torrent_inspector_swarm_speed')[0].innerHTML	= 'N/A';
				$('#torrent_inspector_total_seeders')[0].innerHTML	= 'N/A';
				$('#torrent_inspector_total_leechers')[0].innerHTML	= 'N/A';
				$('#torrent_inspector_creator')[0].innerHTML		= 'N/A';
				$('#torrent_inspector_comment')[0].innerHTML		= 'N/A';
				$('#torrent_inspector_creator_date')[0].innerHTML	= 'N/A';
				$('#torrent_inspector_secure')[0].innerHTML			= 'N/A';
				$('#torrent_inspector_error')[0].innerHTML			= 'N/A';
			}
		}
	},
    
    /*
     * Toggle the visibility of the inspector (used by the context menu)
     */
	toggleInspector: function() {
		if (transmission._inspector_visible) {
			transmission.hideInspector();
		} else {
			transmission.showInspector();
		}
	},
    
    /*
     * Show the inspector
	 * dont_inform_server is used when the filter is setup initially on startup
     */
	showInspector: function(dont_inform_server) {
		$('#torrent_filter_bar')[0].style.right = $('#torrent_inspector').width() + 'px';
		$('#torrent_container')[0].style.right = $('#torrent_inspector').width() + 'px';
		$('#torrent_inspector').show();
		transmission._inspector_visible = true;
		transmission.updateInspector();
		
		$('ul li#context_toggle_inspector')[0].innerHTML = 'Hide Inspector';

		// Tell the server about this action
		if (! dont_inform_server) {
			transmission.remote.setPreference('show_inspector', true);
		}
	},
    
    /*
     * Hide the inspector
     */
	hideInspector: function() {
		$('#torrent_filter_bar')[0].style.right = '0px';
		$('#torrent_container')[0].style.right = '0px';
		$('#torrent_inspector').hide();
		transmission._inspector_visible = false;
		
		$('ul li#context_toggle_inspector')[0].innerHTML = 'Show Inspector';

		// Tell the server about this action
		transmission.remote.setPreference('show_inspector', false);
	},
    
    /*
     * Toggle the speed limit switch
     */
	toggleSpeedLimit: function() {
		if (transmission._speed_limit_active) {
			transmission.deactivateSpeedLimit(true);
		} else {
			transmission.activateSpeedLimit(true);
		}
	},
    
    /*
     * Turn the speed limit on
     */
	activateSpeedLimit: function(informServer) {
		$('#speed_limit_button').css('backgroundImage', "url('/images/buttons/footer_speed_limit_button_blue.png')");
		transmission._speed_limit_active = true;
		$('form#prefs_form input#over_ride_rate')[0].value = 1;
		
		if (informServer) {
        	transmission.remote.request('setOverRide', 1);
		}
	},
    
    /*
     * Turn the speed limit off
     */
	deactivateSpeedLimit: function(informServer) {
		$('#speed_limit_button').css('backgroundImage', "url('/images/buttons/footer_speed_limit_button.png')");
		transmission._speed_limit_active = false;
		$('form#prefs_form input#over_ride_rate')[0].value = 0;
		
		if (informServer) {
        	transmission.remote.request('setOverRide', 0);
		}
	},
	
    /*
     * Toggle the visibility of the filter bar
     */
	toggleFilter: function() {		
		if (transmission._filter_visible) {
			transmission.hideFilter();
		} else {
			transmission.showFilter();
		}
	},
	
    /*
     * Show the filter bar
	 * dont_inform_server is used when the filter is setup initially on startup
     */
	showFilter: function(dont_inform_server) {
		var container_top = parseInt($('#torrent_container').css('top')) + $('#torrent_filter_bar').height();
		$('#torrent_container').css('top', container_top + 'px');
		$('#torrent_filter_bar').show();
		transmission._filter_visible = true;
		if (! dont_inform_server) {
			transmission.remote.setPreference('show_filter', true);
		}
	},
	
    /*
     * Hide the filter bar
     */
	hideFilter: function() {
		var container_top = parseInt($('#torrent_container').css('top')) - $('#torrent_filter_bar').height();
		$('#torrent_container').css('top', container_top + 'px');
		$('#torrent_filter_bar').hide();
		transmission._filter_visible = false;
		transmission.remote.setPreference('show_filter', false);
	},

    /*
     * Remove all the torrents from the interface to force a re-sort
     */
    refreshAndSortTorrents: function(data) {
		$('#upload_container').hide();
		transmission.removeTorrents(transmission._torrents.keys().clone());
		transmission.refreshTorrents(data);
	},

    /*
     * Load a list of torrents into the application
     */
    refreshTorrents: function(data) {
        var torrent_data;
        var torrent_ids = transmission._torrents.keys().clone();
        var new_torrents = [];
		var torrent_list = data.torrent_list;
		var last_torrent;
			
		this._num_active_torrents = 0;
		this._num_paused_torrents = 0;
		this._num_selected_active_torrents = 0;
		this._num_selected_paused_torrents = 0;

		// If the length of the new torrent_list isn't equal to the number of torrents in
		// the browser (if a torrent has been added/deleted/filtered for example), we
		// need to clear the list & force a re-sort
		if (torrent_ids.length > 0 && torrent_ids.length != torrent_list.length) {
			transmission.removeTorrents(transmission._torrents.keys().clone());
			torrent_ids = [];
		}

        for (i=0; i<torrent_list.length; i++) {
            torrent_data = torrent_list[i];
	
			// If this torrent already exists, refresh it & remove this ID from torrent_ids
			if (torrent_ids.inArray(torrent_data.id)) {
				var torrent = transmission._torrents.item(torrent_data.id)
				torrent.refresh(torrent_data);
				if (torrent.isActive()) {
					this._num_active_torrents++;
					if (torrent.isSelected()) {
						this._num_selected_active_torrents++;
					}
				} else {
					this._num_paused_torrents++;
					if (torrent.isSelected()) {
						this._num_selected_paused_torrents++;
					}
				}
				torrent_ids.remove(torrent_data.id);
				last_torrent = torrent;
			
			// Otherwise, this is a new torrent - add it
			} else {
				new_torrents.push(torrent_data);
			}
        }
		
		// Add any torrents that aren't already being displayed
		if (new_torrents.length > 0) {
			transmission.addTorrents(new_torrents, last_torrent);
		}
		
		// Remove any torrents that are displayed but not in the refresh list
		// The 'update_only' flag is sent went pausing/resuming torrents
		if (torrent_ids.length > 0) {
			transmission.removeTorrents(torrent_ids);
		}
		
		// Update the alternating torrent background colors
		transmission.setTorrentBgColors();
		
		// Update global upload and download speed display
		transmission.setGlobalSpeeds(torrent_list.length,  data.total_upload_rate, data.total_download_rate);
		
		// Update the button states
		transmission.updateButtonStates();
		
		// Update the inspector
		transmission.updateInspector();
    },

    /*
     * Load a list of torrents into the application
     */
    removeTorrents: function(torrent_id_list) {
		var torrent;
		
		if (torrent_id_list.length != 0) {
        	for (i=0; i<torrent_id_list.length; i++) {	
				torrent = transmission._torrents.item(torrent_id_list[i]);
				
				// Keep the torrent chain intact
				if (torrent.previousTorrent()) {
					torrent.previousTorrent().setNextTorrent(torrent.nextTorrent());
				}
				if (torrent.nextTorrent()) {
					torrent.nextTorrent().setPreviousTorrent(torrent.previousTorrent());
				}
				
				// Remove the torrent from the list
				torrent.element().remove();
				transmission._torrents.remove(torrent_id_list[i]);
				transmission._selected_torrents.remove(torrent_id_list[i]);
        	}
		}
		
		// Set the background colors
		transmission.setTorrentBgColors();
		
		// Clear the inspector
		transmission.deselectAll();
		transmission.updateInspector();
		transmission.setGlobalSpeeds(this._torrents.length());
    },
    
    /*
     * Set the alternating background colors for torrents
     */
    setTorrentBgColors: function() {
		for (i=0; i<this._torrents.length(); i++) {	
			torrent = this._torrents.itemByIndex(i);
			if ((i+1) % 2 == 0) {
				torrent.element().addClass('even');
			} else {
				torrent.element().removeClass('even');
			}
		}
    },
    
    /*
     * Set the global up and down speed in the interface
     */
    setGlobalSpeeds: function(num_torrents, global_up_speed, global_down_speed) {
		$('#torrent_global_transfer')[0].innerHTML = num_torrents + ' Transfers';
		if (global_up_speed != null) {
			$('#torrent_global_upload')[0].innerHTML = 'Total UL: ' + Math.formatBytes(global_up_speed, true) + '/s';
		}
		if (global_down_speed != null) {
			$('#torrent_global_download')[0].innerHTML = 'Total DL: ' + Math.formatBytes(global_down_speed, true) + '/s';
		}
    },
    
    /*
     * Select a torrent file to upload
     */
    uploadTorrentFile: function(confirmed) {
	
		// Display the upload dialog
		if (! confirmed) {
			$('#upload_container').show();
			
		// Submit the upload form			
		} else {
			// Set the form action with the appropriate params
			$('#torrent_upload_form')[0].action = 'remote/?action=uploadTorrent&param=[]' + 
					'&filter=' + transmission._current_filter +
					'&sort_method=' + transmission._current_sort_method +
					'&sort_direction=' + transmission._current_sort_direction +
					'&search=' + transmission._current_search;
		
			// Submit the form
			$('#torrent_upload_form')[0].submit();
		
			// Disable the periodic refresh call
			transmission.togglePeriodicRefresh(false);
		}
    },
    
    /*
     * Pause any currently selected torrents
     */
    pauseSelectedTorrents: function() {
		if (transmission.numSelectedTorrents() > 0) {				
			transmission.remote.pauseTorrents(transmission._selected_torrents.keys());
		}
    },
    
    /*
     * Resume any currently selected torrents
     */
    resumeSelectedTorrents: function() {
		if (transmission.numSelectedTorrents() > 0) {				
			transmission.remote.resumeTorrents(transmission._selected_torrents.keys());
		}		
    },	
    
    /*
     * Pre-load the images
     */
    preloadImages: function() {
		$.preloadImages(
			'images/buttons/filter.png',
			'images/buttons/filter_on.png',
			'images/buttons/footer_action_button.png',
			'images/buttons/footer_speed_limit_button.png',
			'images/buttons/footer_speed_limit_button_blue.png',
			'images/buttons/footer_speed_limit_button_graphite.png',
			'images/buttons/footer_speed_limit_button_on.png',
			'images/buttons/info.png',
			'images/buttons/info_off.png',
			'images/buttons/info_on.png',
			'images/buttons/info_toolbar_on.png',
			'images/buttons/open.png',
			'images/buttons/open_on.png',
			'images/buttons/pause_all.png',
			'images/buttons/pause_all_disabled.png',
			'images/buttons/pause_all_on.png',
			'images/buttons/pause_off.png',
			'images/buttons/pause_on.png',
			'images/buttons/pause_selected.png',
			'images/buttons/pause_selected_disabled.png',
			'images/buttons/pause_selected_on.png',
			'images/buttons/remove.png',
			'images/buttons/remove_disabled.png',
			'images/buttons/remove_on.png',
			'images/buttons/resume_all.png',
			'images/buttons/resume_all_disabled.png',
			'images/buttons/resume_all_on.png',
			'images/buttons/resume_off.png',
			'images/buttons/resume_on.png',
			'images/buttons/resume_selected.png',
			'images/buttons/resume_selected_disabled.png',
			'images/buttons/resume_selected_on.png',
			'images/graphics/browser_firefox.gif',
			'images/graphics/browser_opera.gif',
			'images/graphics/browser_safari.gif',
			'images/graphics/dialog_top.png',
			'images/graphics/filter_bar_bg.png',
			'images/graphics/filter_hover_left.png',
			'images/graphics/filter_hover_right.png',
			'images/graphics/filter_selected_left.png',
			'images/graphics/filter_selected_right.png',
			'images/graphics/footer_bg.png',
			'images/graphics/logo.png',
			'images/graphics/menu_bg.gif',
			'images/graphics/menu_divider.png',
			'images/graphics/pause_all_disabled.png',
			'images/graphics/status_bar_background.png',
			'images/graphics/status_bar_divider.png',
			'images/progress/complete.png',
			'images/progress/complete_stopped.png',
			'images/progress/in_progress.png',
			'images/progress/incomplete.png',
			'images/progress/incomplete_stopped.png'
		);	
	}
}