- var e = document.createElement( 'div' );
- e.className = 'torrent_name';
- top_e.appendChild( e );
- element._name_container = e;
-
- // Create the 'peer details'
- e = document.createElement( 'div' );
- e.className = 'torrent_peer_details';
- top_e.appendChild( e );
- element._peer_details_container = e;
-
- //Create a progress bar container
- top_a = document.createElement( 'div' );
- top_a.className = 'torrent_progress_bar_container';
- element._progress_bar_container = top_a;
-
- // Create the 'in progress' bar
- e = document.createElement( 'div' );
- e.className = 'torrent_progress_bar incomplete';
- e.style.width = '0%';
- top_a.appendChild( e );
- element._progress_complete_container = e;
-
- // Create the 'incomplete' bar (initially hidden)
- e = document.createElement( 'div' );
- e.className = 'torrent_progress_bar incomplete';
- e.style.display = 'none';
- top_a.appendChild( e );
- element._progress_incomplete_container = e;
-
- //Add the progress bar container to the torrent
- top_e.appendChild(top_a);
-
- // Add the pause/resume button - don't specify the
- // image or alt text until the 'refresh()' function
- // (depends on torrent state)
- var image = document.createElement( 'div' );
- image.className = 'torrent_pause';
- e = document.createElement( 'a' );
- e.appendChild( image );
- top_e.appendChild( e );
- element._pause_resume_button_image = image;
- if (!iPhone) $(e).bind('click', function(e) { element._torrent.clickPauseResumeButton(e); });
-
- // Create the 'progress details'
- e = document.createElement( 'div' );
- e.className = 'torrent_progress_details';
- top_e.appendChild( e );
- element._progress_details_container = e;
-
- // Set the torrent click observer
- element.bind('click', function(e){ element._torrent.clickTorrent(e) });
-
- // Safari hack - first torrent needs to be moved down for some reason. Seems to be ok when
- // using
's in straight html, but adding through the DOM gets a bit odd.
- if ($.browser.safari)
- this._element.css('margin-top', '7px');
-
- this.initializeTorrentFilesInspectorGroup( fileListParent );
-
// Update all the labels etc
this.refresh(data);
-
- // insert the element
- transferListParent.appendChild(top_e);
- },
-
- initializeTorrentFilesInspectorGroup: function( fileListParent ) {
- var e = document.createElement( 'ul' );
- e.className = 'inspector_torrent_file_list inspector_group';
- e.style.display = 'none';
- fileListParent.appendChild( e );
- this._fileList = e;
- },
-
- fileList: function() {
- return $(this._fileList);
},
buildTrackerStats: function(trackerStats) {
@@ -221,19 +124,7 @@ Torrent.prototype =
*
*--------------------------------------------*/
- /* Return the DOM element for this torrent (a element) */
- element: function() {
- return this._element;
- },
-
- setElement: function( element ) {
- this._element = element;
- element._torrent = this;
- element[0]._torrent = this;
- this.refreshHTML( );
- },
-
- activity: function() { return this._download_speed + this._upload_speed; },
+ activity: function() { return this.downloadSpeed() + this.uploadSpeed(); },
comment: function() { return this._comment; },
completed: function() { return this._completed; },
creator: function() { return this._creator; },
@@ -246,14 +137,14 @@ Torrent.prototype =
|| this.peersSendingToUs() > 0
|| this.webseedsSendingToUs() > 0
|| this.state() == Torrent._StatusCheck; },
+ isStopped: function() { return this.state() === Torrent._StatusStopped; },
isActive: function() { return this.state() != Torrent._StatusStopped; },
isDownloading: function() { return this.state() == Torrent._StatusDownload; },
isFinished: function() { return this._isFinishedSeeding; },
+ isDone: function() { return this._leftUntilDone < 1; },
isSeeding: function() { return this.state() == Torrent._StatusSeed; },
name: function() { return this._name; },
queuePosition: function() { return this._queue_position; },
- isQueued: function() { return ( this.state() == Torrent._StatusSeedWait )
- || ( this.state() == Torrent._StatusDownloadWait ); },
webseedsSendingToUs: function() { return this._webseeds_sending_to_us; },
peersSendingToUs: function() { return this._peers_sending_to_us; },
peersGettingFromUs: function() { return this._peers_getting_from_us; },
@@ -283,96 +174,23 @@ Torrent.prototype =
trackerStats: function() { return this._trackerStats; },
uploadSpeed: function() { return this._upload_speed; },
uploadTotal: function() { return this._upload_total; },
- showFileList: function() {
- if(this.fileList().is(':visible'))
- return;
- this.ensureFileListExists();
- this.refreshFileView();
- this.fileList().show();
- },
- hideFileList: function() { this.fileList().hide(); },
- seedRatioLimit: function(){
+ seedRatioLimit: function(controller){
switch( this._seed_ratio_mode ) {
- case Torrent._RatioUseGlobal: return this._controller.seedRatioLimit();
+ case Torrent._RatioUseGlobal: return controller.seedRatioLimit();
case Torrent._RatioUseLocal: return this._seed_ratio_limit;
default: return -1;
}
},
+ getErrorMessage: function() {
+ if( this._error == Torrent._ErrTrackerWarning )
+ return 'Tracker returned a warning: ' + this._error_string;
+ if( this._error == Torrent._ErrTrackerError )
+ return 'Tracker returned an error: ' + this._error_string;
+ if( this._error == Torrent._ErrLocalError )
+ return 'Error: ' + this._error_string;
+ return null;
+ },
- /*--------------------------------------------
- *
- * E V E N T F U N C T I O N S
- *
- *--------------------------------------------*/
-
- /*
- * Process a click event on this torrent
- */
- clickTorrent: function( event )
- {
- // Prevents click carrying to parent element
- // which deselects all on click
- event.stopPropagation();
- // but still hide the context menu if it is showing
- $('#jqContextMenu').hide();
-
- var torrent = this;
-
- // 'Apple' button emulation on PC :
- // Need settable meta-key and ctrl-key variables for mac emulation
- var meta_key = event.metaKey;
- var ctrl_key = event.ctrlKey;
- if (event.ctrlKey && navigator.appVersion.toLowerCase().indexOf("mac") == -1) {
- meta_key = true;
- ctrl_key = false;
- }
-
- // Shift-Click - Highlight a range between this torrent and the last-clicked torrent
- if (iPhone) {
- if ( torrent.isSelected() )
- torrent._controller.showInspector();
- torrent._controller.setSelectedTorrent( torrent, true );
-
- } else if (event.shiftKey) {
- torrent._controller.selectRange( torrent, true );
- // Need to deselect any selected text
- window.focus();
-
- // Apple-Click, not selected
- } else if (!torrent.isSelected() && meta_key) {
- torrent._controller.selectTorrent( torrent, true );
-
- // Regular Click, not selected
- } else if (!torrent.isSelected()) {
- torrent._controller.setSelectedTorrent( torrent, true );
-
- // Apple-Click, selected
- } else if (torrent.isSelected() && meta_key) {
- torrent._controller.deselectTorrent( torrent, true );
-
- // Regular Click, selected
- } else if (torrent.isSelected()) {
- torrent._controller.setSelectedTorrent( torrent, true );
- }
-
- torrent._controller.setLastTorrentClicked(torrent);
- },
-
- /*
- * Process a click event on the pause/resume button
- */
- clickPauseResumeButton: function( event )
- {
- // prevent click event resulting in selection of torrent
- event.stopPropagation();
-
- // either stop or start the torrent
- var torrent = this;
- if( torrent.isActive( ) )
- torrent._controller.stopTorrent( torrent );
- else
- torrent._controller.startTorrent( torrent );
- },
/*--------------------------------------------
*
@@ -380,22 +198,18 @@ Torrent.prototype =
*
*--------------------------------------------*/
- refreshMetaData: function(data) {
+ fireDataChanged: function()
+ {
+ $(this).trigger('dataChanged',[]);
+ },
+
+ refreshMetaData: function(data)
+ {
this.initMetaData( data );
- this.ensureFileListExists();
- this.refreshFileView();
- this.refreshHTML( );
+ this.fireDataChanged();
},
- refresh: function(data) {
- this.refreshData( data );
- this.refreshHTML( );
- },
-
- /*
- * Refresh display
- */
- refreshData: function(data)
+ refresh: function(data)
{
if( this.needsMetaData() && ( data.metadataPercentComplete >= 1 ) )
transmission.refreshMetaData( [ this._id ] );
@@ -430,6 +244,8 @@ Torrent.prototype =
if (data.fileStats)
this.refreshFileModel( data );
+
+ this.fireDataChanged();
},
refreshFileModel: function(data) {
@@ -444,275 +260,6 @@ Torrent.prototype =
}
},
- getErrorMessage: function()
- {
- if( this._error == Torrent._ErrTrackerWarning )
- return 'Tracker returned a warning: ' + this._error_string;
- if( this._error == Torrent._ErrTrackerError )
- return 'Tracker returned an error: ' + this._error_string;
- if( this._error == Torrent._ErrLocalError )
- return 'Error: ' + this._error_string;
- return null;
- },
-
- formatUL: function() {
- return 'UL: ' + Transmission.fmt.speedBps(this._upload_speed);
- },
- formatDL: function() {
- return 'DL: ' + Transmission.fmt.speedBps(this._download_speed);
- },
-
- getPeerDetails: function()
- {
- var c;
-
- var compact_mode = this._controller[Prefs._CompactDisplayState];
- if(( c = this.getErrorMessage( )))
- return c;
-
- var st = this.state( );
- switch( st )
- {
- case Torrent._StatusStopped:
- case Torrent._StatusCheckWait:
- case Torrent._StatusDownloadWait:
- case Torrent._StatusSeedWait:
- c = this.stateStr( );
- break;
-
- case Torrent._StatusDownload:
- var a = [ ];
- if(!compact_mode)
- a.push( 'Downloading from', this.peersSendingToUs(), 'of', this._peers_connected, 'peers', '-' );
- a.push( this.formatDL(), this.formatUL() );
- c = a.join(' ');
- break;
-
- case Torrent._StatusSeed:
- if(compact_mode){
- c = this.formatUL();
- } else {
- // 'Seeding to 13 of 22 peers - UL: 36.2 KiB/s'
- c = [ 'Seeding to', this.peersGettingFromUs(), 'of', this._peers_connected, 'peers', '-', this.formatUL() ].join(' ');
- }
- break;
-
- case Torrent._StatusCheck:
- // 'Verifying local data (40% tested)'
- c = [ 'Verifying local data (', Transmission.fmt.percentString( 100.0 * this._recheckProgress ), '% tested)' ].join('');
- break;
- }
- return c;
- },
-
- refreshHTML: function() {
- var c;
- var e;
- var progress_details;
- var root = this._element;
- var MaxBarWidth = 100; // reduce this to make the progress bar shorter (%)
- var compact_mode = this._controller[Prefs._CompactDisplayState];
- var compact = '';
- if(compact_mode){
- compact = ' compact';
- root._peer_details_container.style.display = 'none';
- } else {
- root._peer_details_container.style.display = 'block';
- }
-
- root._progress_details_container.className = 'torrent_progress_details'+compact
- root._progress_bar_container.className = 'torrent_progress_bar_container'+compact;
- root._name_container.className = 'torrent_name'+compact;
-
- setInnerHTML( root._name_container, this._name );
-
- // Add the progress bar
- var notDone = this._leftUntilDone > 0;
-
- // Fix for situation
- // when a verifying/downloading torrent gets state seeding
- if( this._state === Torrent._StatusSeeding )
- notDone = false ;
-
- if( this.needsMetaData() ){
- var metaPercentComplete = this._metadataPercentComplete * 100;
- progress_details = [ "Magnetized transfer - retrieving metadata (",
- Transmission.fmt.percentString( metaPercentComplete ),
- "%)" ].join('');
-
- var empty = "";
- if(metaPercentComplete == 0)
- empty = "empty";
-
- root._progress_complete_container.style.width = metaPercentComplete + "%";
- root._progress_complete_container.className = 'torrent_progress_bar in_progress meta ' + empty+compact;
- root._progress_incomplete_container.style.width = 100 - metaPercentComplete + "%"
- root._progress_incomplete_container.className = 'torrent_progress_bar incomplete compact meta'+compact;
- root._progress_incomplete_container.style.display = 'block';
- }
- else if( notDone )
- {
- // Create the 'progress details' label
- // Eg: '101 MiB of 631 MiB (16.02%) - 2 hr remaining'
-
- c = [ Transmission.fmt.size( this._sizeWhenDone - this._leftUntilDone ),
- ' of ', Transmission.fmt.size( this._sizeWhenDone ),
- ' (', this.getPercentDoneStr(), '%)' ];
- if( this.isActive( ) ) {
- c.push( ' - ' );
- if (this._eta < 0 || this._eta >= Torrent._InfiniteTimeRemaining )
- c.push( 'remaining time unknown' );
- else
- c.push( Transmission.fmt.timeInterval(this._eta) + ' remaining' );
- }
- progress_details = c.join('');
-
- // Figure out the percent completed
- var css_completed_width = ( this.getPercentDone() * MaxBarWidth ).toTruncFixed( 2 );
-
- // Update the 'in progress' bar
- e = root._progress_complete_container;
- c = [ 'torrent_progress_bar'+compact,
- this.isActive() ? 'in_progress' : 'incomplete_stopped' ];
- if(css_completed_width === 0) { c.push( 'empty' ); }
- e.className = c.join(' ');
- e.style.width = css_completed_width + '%';
-
- // Update the 'incomplete' bar
- e = root._progress_incomplete_container;
- e.className = 'torrent_progress_bar incomplete'
- e.style.width = (MaxBarWidth - css_completed_width) + '%';
- e.style.display = 'block';
- }
- else
- {
- // Create the 'progress details' label
-
- if( this._size == this._sizeWhenDone )
- {
- // seed: '698.05 MiB'
- c = [ Transmission.fmt.size( this._size ) ];
- }
- else
- {
- // partial seed: '127.21 MiB of 698.05 MiB (18.2%)'
- c = [ Transmission.fmt.size( this._sizeWhenDone ), ' of ', Transmission.fmt.size( this._size ),
- ' (', Transmission.fmt.percentString( 100.0 * this._sizeWhenDone / this._size ), '%)' ];
- }
-
- // append UL stats: ', uploaded 8.59 GiB (Ratio: 12.3)'
- c.push( ', uploaded ', Transmission.fmt.size( this._upload_total ),
- ' (Ratio ', Transmission.fmt.ratioString( this._upload_ratio ), ')' );
-
- // maybe append remaining time
- if( this.isActive( ) && this.seedRatioLimit( ) > 0 )
- {
- c.push(' - ');
-
- if (this._eta < 0 || this._eta >= Torrent._InfiniteTimeRemaining )
- c.push( 'remaining time unknown' );
- else
- c.push( Transmission.fmt.timeInterval(this._eta), ' remaining' );
- }
-
- progress_details = c.join('');
-
- var status = this.isActive() ? 'complete' : 'complete_stopped';
-
- if(this.isActive() && this.seedRatioLimit() > 0){
- status = 'complete seeding'
- var seedRatioRatio = this._upload_ratio / this.seedRatioLimit();
- var seedRatioPercent = Math.round( seedRatioRatio * 100 * MaxBarWidth ) / 100;
-
- // Set progress to percent seeded
- root._progress_complete_container.style.width = seedRatioPercent + '%';
-
- // Update the 'incomplete' bar
- root._progress_incomplete_container.className = 'torrent_progress_bar incomplete seeding'
- root._progress_incomplete_container.style.display = 'block';
- root._progress_incomplete_container.style.width = MaxBarWidth - seedRatioPercent + '%';
- }
- else
- {
- // Hide the 'incomplete' bar
- root._progress_incomplete_container.className = 'torrent_progress_bar incomplete'
- root._progress_incomplete_container.style.display = 'none';
-
- // Set progress to maximum
- root._progress_complete_container.style.width = MaxBarWidth + '%';
- }
-
- // Update the 'in progress' bar
- e = root._progress_complete_container;
- e.className = 'torrent_progress_bar ' + status;
- }
-
- var hasError = this.getErrorMessage( ) != undefined;
- // Update the progress details
- if(compact_mode){
- progress_details = this.getPeerDetails();
- $(root._progress_details_container).toggleClass('error',hasError);
- } else {
- $(root._peer_details_container).toggleClass('error',hasError);
- }
- setInnerHTML( root._progress_details_container, progress_details );
-
- if( compact ){
- var width = root._progress_details_container.offsetLeft - root._name_container.offsetLeft;
- root._name_container.style.width = width + 'px';
- }
- else {
- root._name_container.style.width = '100%';
- }
-
- // Update the peer details and pause/resume button
- e = root._pause_resume_button_image;
- if ( this.state() === Torrent._StatusStopped ) {
- e.alt = 'Resume';
- e.className = "torrent_resume"+compact;
- } else {
- e.alt = 'Pause';
- e.className = "torrent_pause"+compact;
- }
-
- setInnerHTML( root._peer_details_container, this.getPeerDetails( ) );
-
- this.refreshFileView( );
- },
-
- refreshFileView: function() {
- if( this._file_view.length )
- for( var i=0; i= this._size;
- },
-
- isEditable: function () {
- return (this._torrent._file_model.length>1) && !this.isDone();
- },
-
- readAttributes: function(file_data) {
- if( file_data.index !== undefined && file_data.index !== this._index ) {
- this._index = file_data.index;
- this._dirty = true;
- }
- if( file_data.bytesCompleted !== undefined && file_data.bytesCompleted !== this._done ) {
- this._done = file_data.bytesCompleted;
- this._dirty = true;
- }
- if( file_data.length !== undefined && file_data.length !== this._size ) {
- this._size = file_data.length;
- this._dirty = true;
- }
- if( file_data.priority !== undefined && file_data.priority !== this._prio ) {
- this._prio = file_data.priority;
- this._dirty = true;
- }
- if( file_data.wanted !== undefined && file_data.wanted !== this._wanted ) {
- this._wanted = file_data.wanted;
- this._dirty = true;
- }
- },
-
- element: function() {
- return $(this._element);
- },
-
- domElement: function() {
- return this._element;
- },
-
- setPriority: function(prio) {
- if (this.isEditable()) {
- var cmd;
- switch( prio ) {
- case 1: cmd = 'priority-high'; break;
- case -1: cmd = 'priority-low'; break;
- default: cmd = 'priority-normal'; break;
- }
- this._prio = prio;
- this._dirty = true;
- this._torrent._controller.changeFileCommand( cmd, this._torrent, this );
- }
- },
-
- setWanted: function(wanted, process) {
- this._dirty = true;
- this._wanted = wanted;
- if(!iPhone)
- this.element().toggleClass( 'skip', !wanted );
- if (process) {
- var command = wanted ? 'files-wanted' : 'files-unwanted';
- this._torrent._controller.changeFileCommand(command, this._torrent, this);
- }
- },
-
- toggleWanted: function() {
- if (this.isEditable())
- this.setWanted( !this._wanted, true );
- },
-
- refreshHTML: function() {
- if( this._dirty ) {
- this._dirty = false;
- this.refreshProgressHTML();
- this.refreshWantedHTML();
- this.refreshPriorityHTML();
- }
- },
-
- refreshProgressHTML: function() {
- var c = [ Transmission.fmt.size(this._done),
- ' of ',
- Transmission.fmt.size(this._size),
- ' (',
- this._size ? Transmission.fmt.percentString(100 * this._done / this._size) : '100',
- '%)' ].join('');
- setInnerHTML(this._progress[0], c);
- },
-
- refreshWantedHTML: function() {
- var e = this.domElement();
- var c = [ e.classNameConst ];
- if(!this._wanted) { c.push( 'skip' ); }
- if(this.isDone()) { c.push( 'complete' ); }
- e.className = c.join(' ');
- },
-
- refreshPriorityHTML: function() {
- var e = this._priority_control;
- var c = [ e.classNameConst ];
- switch( this._prio ) {
- case 1 : c.push( 'high' ); break;
- case -1 : c.push( 'low' ); break;
- default : c.push( 'normal' ); break;
- }
- e.className = c.join(' ');
- },
-
- fileWantedControlClicked: function(event) {
- this.toggleWanted();
- },
-
- filePriorityControlClicked: function(event, element) {
- var x = event.pageX;
- while (element !== null) {
- x = x - element.offsetLeft;
- element = element.offsetParent;
- }
-
- var prio;
- if(iPhone)
- {
- if( x < 8 ) prio = -1;
- else if( x < 27 ) prio = 0;
- else prio = 1;
- }
- else
- {
- if( x < 12 ) prio = -1;
- else if( x < 23 ) prio = 0;
- else prio = 1;
- }
- this.setPriority( prio );
- }
-};
diff --git a/web/javascript/transmission.js b/web/javascript/transmission.js
index 67bbbaa21..2b7afd3f0 100644
--- a/web/javascript/transmission.js
+++ b/web/javascript/transmission.js
@@ -51,8 +51,6 @@ Transmission.prototype =
$('#block_update_button').bind('click', function(e){ tr.blocklistUpdateClicked(e); return false; });
$('#stats_close_button').bind('click', function(e){ tr.closeStatsClicked(e); return false; });
$('.inspector_tab').bind('click', function(e){ tr.inspectorTabClicked(e, this); });
- $('.file_wanted_control').live('click', function(e){ tr.fileWantedClicked(e, this); });
- $('.file_priority_control').live('click', function(e){ tr.filePriorityClicked(e, this); });
$('#files_select_all').live('click', function(e){ tr.filesSelectAllClicked(e, this); });
$('#files_deselect_all').live('click', function(e){ tr.filesDeselectAllClicked(e, this); });
$('#open_link').bind('click', function(e){ tr.openTorrentClicked(e); });
@@ -224,6 +222,15 @@ Transmission.prototype =
});
},
+ setCompactMode: function( is_compact )
+ {
+ this.torrentRenderer = is_compact ? new TorrentRendererCompact( )
+ : new TorrentRendererFull( );
+ $('ul.torrent_list li').remove();
+ this._rows = [];
+ this.refilter();
+ },
+
/*
* Load the clutch prefs and init the GUI according to those prefs
*/
@@ -251,6 +258,8 @@ Transmission.prototype =
if( !iPhone && this[Prefs._CompactDisplayState] )
$('#compact_view').selectMenuItem();
+
+ this.setCompactMode( this[Prefs._CompactDisplayState] );
},
/*
@@ -362,9 +371,13 @@ Transmission.prototype =
boundingRightPad: 20,
boundingBottomPad: 5,
onContextMenu: function(e) {
- var closestRow = $(e.target).closest('.torrent')[0]._torrent;
- if(!closestRow.isSelected())
- tr.setSelectedTorrent( closestRow, true );
+ var closest_row = $(e.target).closest('.torrent')[0];
+ for( var i=0, row; row = tr._rows[i]; ++i ) {
+ if( row.getElement() === closest_row ) {
+ tr.setSelectedRow( row );
+ break;
+ }
+ }
return true;
}
});
@@ -421,45 +434,56 @@ Transmission.prototype =
{
var torrents = [ ];
for( var i=0, row; row=this._rows[i]; ++i )
- if( row._torrent && ( row[0].style.display != 'none' ) )
- torrents.push( row._torrent );
+ if( row.isVisible( ) )
+ torrents.push( row.getTorrent( ) );
return torrents;
},
+ getSelectedRows: function()
+ {
+ var s = [ ];
+
+ for( var i=0, row; row=this._rows[i]; ++i )
+ if( row.isSelected( ) )
+ s.push( row );
+
+ return s;
+ },
+
getSelectedTorrents: function()
{
- var v = this.getVisibleTorrents( );
- var s = [ ];
- for( var i=0, row; row=v[i]; ++i )
- if( row.isSelected( ) )
- s.push( row );
+ var s = this.getSelectedRows( );
+
+ for( var i=0, row; row=s[i]; ++i )
+ s[i] = s[i].getTorrent();
+
return s;
},
- getDeselectedTorrents: function() {
- var visible_torrent_ids = jQuery.map(this.getVisibleTorrents(), function(t) { return t.id(); } );
- var s = [ ];
- jQuery.each( this.getAllTorrents( ), function() {
- var visible = (-1 != jQuery.inArray(this.id(), visible_torrent_ids));
- if (!this.isSelected() || !visible)
- s.push( this );
- } );
- return s;
+ getDeselectedTorrents: function()
+ {
+ var ret = { };
+ for( var key in this._torrents )
+ ret[ key ] = this._torrents[key];
+ var sel = this.getSelectedTorrents( );
+ for( var i=0, tor; tor=sel[i]; ++i )
+ delete ret[ tor.id() ];
+ return ret;
},
getVisibleRows: function()
{
var rows = [ ];
for( var i=0, row; row=this._rows[i]; ++i )
- if( row[0].style.display != 'none' )
+ if( row.isVisible( ) )
rows.push( row );
return rows;
},
- getTorrentIndex: function( rows, torrent )
+ getRowIndex: function( rows, row )
{
- for( var i=0, row; row=rows[i]; ++i )
- if( row._torrent == torrent )
+ for( var i=0, r; r=rows[i]; ++i )
+ if( r === row )
return i;
return null;
},
@@ -479,8 +503,8 @@ Transmission.prototype =
var scrollTop = container.scrollTop( );
var innerHeight = container.innerHeight( );
- var offsetTop = e[0].offsetTop;
- var offsetHeight = e.outerHeight( );
+ var offsetTop = e.offsetTop;
+ var offsetHeight = $(e).outerHeight( );
if( offsetTop < scrollTop )
container.scrollTop( offsetTop );
@@ -501,72 +525,57 @@ Transmission.prototype =
*
*--------------------------------------------*/
- setSelectedTorrent: function( torrent, doUpdate ) {
- this.deselectAll( );
- this.selectTorrent( torrent, doUpdate );
+ setSelectedRow: function( row ) {
+ var rows = this.getSelectedRows( );
+ for( var i=0, r; r=rows[i]; ++i )
+ this.deselectRow( r );
+ this.selectRow( row );
},
- selectElement: function( e, doUpdate ) {
- e.addClass('selected');
- if( doUpdate )
- this.selectionChanged( );
- },
- selectRow: function( rowIndex, doUpdate ) {
- this.selectElement( this._rows[rowIndex], doUpdate );
- },
- selectTorrent: function( torrent, doUpdate ) {
- if( torrent._element )
- this.selectElement( torrent._element, doUpdate );
+ selectRow: function( row ) {
+ row.setSelected( true );
+ this.callSelectionChangedSoon();
},
- deselectElement: function( e, doUpdate ) {
- e.removeClass('selected');
- if( doUpdate )
- this.selectionChanged( );
- },
- deselectTorrent: function( torrent, doUpdate ) {
- if( torrent._element )
- this.deselectElement( torrent._element, doUpdate );
+ deselectRow: function( row ) {
+ row.setSelected( false );
+ this.callSelectionChangedSoon();
},
- selectAll: function( doUpdate ) {
+ selectAll: function( ) {
var tr = this;
for( var i=0, row; row=tr._rows[i]; ++i )
- tr.selectElement( row );
- if( doUpdate )
- tr.selectionChanged();
+ tr.selectRow( row );
+ this.callSelectionChangedSoon();
},
- deselectAll: function( doUpdate ) {
- var tr = this;
- for( var i=0, row; row=tr._rows[i]; ++i )
- tr.deselectElement( row );
- tr._last_torrent_clicked = null;
- if( doUpdate )
- tr.selectionChanged( );
+ deselectAll: function( ) {
+ for( var i=0, row; row=this._rows[i]; ++i )
+ this.deselectRow( row );
+ this.callSelectionChangedSoon();
+ this._last_row_clicked = null;
},
/*
* Select a range from this torrent to the last clicked torrent
*/
- selectRange: function( torrent, doUpdate )
+ selectRange: function( row )
{
- if( !this._last_torrent_clicked )
+ if( this._last_row_clicked === null )
{
- this.selectTorrent( torrent );
+ this.selectRow( row );
}
else // select the range between the prevous & current
{
var rows = this.getVisibleRows( );
- var i = this.getTorrentIndex( rows, this._last_torrent_clicked );
- var end = this.getTorrentIndex( rows, torrent );
+ var i = this.getRowIndex( rows, this._last_row_clicked );
+ var end = this.getRowIndex( rows, row );
var step = i < end ? 1 : -1;
for( ; i!=end; i+=step )
- this.selectRow( i );
- this.selectRow( i );
+ this.selectRow( this._rows[i] );
+ this.selectRow( this._rows[i] );
}
- if( doUpdate )
- this.selectionChanged( );
+ this.callSelectionChangedSoon( );
},
selectionChanged: function()
@@ -574,6 +583,13 @@ Transmission.prototype =
this.updateButtonStates();
this.updateInspector();
this.updateSelectedData();
+ this.selectionChangedTimer = null;
+ },
+
+ callSelectionChangedSoon: function()
+ {
+ if( this.selectionChangedTimer === null )
+ this.selectionChangedTimer = setTimeout(function(o) { o.selectionChanged(); }, 200, this);
},
/*--------------------------------------------
@@ -588,28 +604,28 @@ Transmission.prototype =
keyDown: function(event)
{
var tr = this;
- var sel = tr.getSelectedTorrents( );
+ var sel = tr.getSelectedRows( );
var rows = tr.getVisibleRows( );
var i = -1;
if( event.keyCode == 40 ) // down arrow
{
- var t = sel.length ? sel[sel.length-1] : null;
- i = t==null ? null : tr.getTorrentIndex(rows,t)+1;
+ var r = sel.length ? sel[sel.length-1] : null;
+ i = r==null ? null : tr.getRowIndex(rows,r)+1;
if( i == rows.length || i == null )
i = 0;
}
else if( event.keyCode == 38 ) // up arrow
{
- var t = sel.length ? sel[0] : null
- i = t==null ? null : tr.getTorrentIndex(rows,t)-1;
+ var r = sel.length ? sel[0] : null
+ i = r==null ? null : tr.getRowIndex(rows,r)-1;
if( i == -1 || i == null )
i = rows.length - 1;
}
if( 0<=i && i 1 ) {
+ var command = wanted ? 'files-wanted' : 'files-unwanted';
+ this.changeFileCommand( command, rows );
}
- return files_list;
},
toggleFilterClicked: function(event) {
@@ -1237,7 +1222,7 @@ Transmission.prototype =
// Figure out which menu has been clicked
switch ($element.parent()[0].id) {
- // Display the preferences dialog
+ // Display the preferences dialog
case 'footer_super_menu':
if ($element[0].id == 'preferences') {
$('div#prefs_container div#pref_error').hide();
@@ -1255,7 +1240,7 @@ Transmission.prototype =
$element.selectMenuItem();
else
$element.deselectMenuItem();
- this.refreshDisplay( );
+ this.setCompactMode( this[Prefs._CompactDisplayState] );
}
else if ($element[0].id == 'homepage') {
window.open('http://www.transmissionbt.com/');
@@ -1336,11 +1321,6 @@ Transmission.prototype =
return false; // to prevent the event from bubbling up
},
- setLastTorrentClicked: function( torrent )
- {
- this._last_torrent_clicked = torrent;
- },
-
/*
* Update the inspector with the latest data for the selected torrents
*/
@@ -1407,7 +1387,7 @@ Transmission.prototype =
setInnerHTML( tab.creator, na );
setInnerHTML( tab.download_dir, na );
setInnerHTML( tab.error, na );
- this.updateVisibleFileLists();
+ this.updateFileList();
this.updatePeersLists();
this.updateTrackersLists();
$("#torrent_inspector_size, .inspector_row > div:contains('N/A')").css('color', '#666');
@@ -1491,27 +1471,67 @@ Transmission.prototype =
this.updatePeersLists();
this.updateTrackersLists();
$(".inspector_row > div:contains('N/A')").css('color', '#666');
- this.updateVisibleFileLists();
+ this.updateFileList();
},
- fileListIsVisible: function() {
- return this._inspector_tab_files.className.indexOf('selected') != -1;
+ onFileWantedToggled: function( row, want ) {
+ var command = want ? 'files-wanted' : 'files-unwanted';
+ this.changeFileCommand( command, [ row ] );
},
-
- updateVisibleFileLists: function() {
- if( this.fileListIsVisible( ) === true ) {
- var selected = this.getSelectedTorrents();
- jQuery.each( selected, function() { this.showFileList(); } );
- jQuery.each( this.getDeselectedTorrents(), function() { this.hideFileList(); } );
- // Check if we need to display the select all buttions
- if ( !selected.length ) {
- if ( $("#select_all_button_container").is(':visible') )
- $("#select_all_button_container").hide();
- } else {
- if ( !$("#select_all_button_container").is(':visible') )
- $("#select_all_button_container").show();
- }
+ onFilePriorityToggled: function( row, priority ) {
+ var command;
+ switch( priority ) {
+ case -1: command = 'priority-low'; break;
+ case 1: command = 'priority-high'; break;
+ default: command = 'priority-normal'; break;
}
+ this.changeFileCommand( command, [ row ] );
+ },
+ clearFileList: function() {
+ $(this._inspector_file_list).empty();
+ delete this._files_torrent;
+ delete this._files;
+ },
+ updateFileList: function() {
+
+ // if the file list is hidden, clear the list
+ if( this._inspector_tab_files.className.indexOf('selected') == -1 ) {
+ this.clearFileList( );
+ return;
+ }
+
+ // if not torrent is selected, clear the list
+ var selected_torrents = this.getSelectedTorrents( );
+ if( selected_torrents.length != 1 ) {
+ this.clearFileList( );
+ return;
+ }
+
+ // if the active torrent hasn't changed, noop
+ var torrent = selected_torrents[0];
+ if( this._files_torrent === torrent )
+ return;
+
+ // build the file list
+ this.clearFileList( );
+ this._files_torrent = torrent;
+ var n = torrent._file_model.length;
+ this._files = new Array( n );
+ var fragment = document.createDocumentFragment( );
+ var tr = this;
+ for( var i=0; i 0)
tr.remote.loadTorrentFiles( refresh_files_for );
@@ -1796,16 +1819,14 @@ Transmission.prototype =
},
updateTorrentsFileData: function( torrents ){
- var tr = this;
- var listIsVisible = tr.fileListIsVisible( );
- jQuery.each( torrents, function() {
- var t = tr._torrents[this.id];
- if (t) {
- t.refreshFileModel(this);
- if( listIsVisible && t.isSelected())
- t.refreshFileView();
+ for( var i=0, o; o=torrents[i]; ++i ) {
+ var t = this._torrents[o.id];
+ if( t !== null ) {
+ t.refreshFileModel( o );
+ if( t === this._files_torrent )
+ this.refreshFileView();
}
- } );
+ }
},
initializeAllTorrents: function(){
@@ -1813,19 +1834,63 @@ Transmission.prototype =
this.remote.getInitialDataFor( null ,function(torrents) { tr.addTorrents(torrents); } );
},
+ onRowClicked: function( ev, row )
+ {
+ // Prevents click carrying to parent element
+ // which deselects all on click
+ event.stopPropagation();
+ // but still hide the context menu if it is showing
+ $('#jqContextMenu').hide();
+
+ // 'Apple' button emulation on PC :
+ // Need settable meta-key and ctrl-key variables for mac emulation
+ var meta_key = event.metaKey;
+ var ctrl_key = event.ctrlKey;
+ if (event.ctrlKey && navigator.appVersion.toLowerCase().indexOf("mac") == -1) {
+ meta_key = true;
+ ctrl_key = false;
+ }
+
+ // Shift-Click - selects a range from the last-clicked row to this one
+ if (iPhone) {
+ if ( row.isSelected() )
+ this.showInspector();
+ this.setSelectedRow( row, true );
+
+ } else if (event.shiftKey) {
+ this.selectRange( row, true );
+ // Need to deselect any selected text
+ window.focus();
+
+ // Apple-Click, not selected
+ } else if (!row.isSelected() && meta_key) {
+ this.selectRow( row, true );
+
+ // Regular Click, not selected
+ } else if (!row.isSelected()) {
+ this.setSelectedRow( row, true );
+
+ // Apple-Click, selected
+ } else if (row.isSelected() && meta_key) {
+ this.deselectRow( row );
+
+ // Regular Click, selected
+ } else if (row.isSelected()) {
+ this.setSelectedRow( row, true );
+ }
+
+ this._last_row_clicked = row;
+ },
+
addTorrents: function( new_torrents )
{
- var transferFragment = document.createDocumentFragment( );
- var fileFragment = document.createDocumentFragment( );
+ var tr = this;
for( var i=0, row; row=new_torrents[i]; ++i ) {
- var new_torrent = new Torrent( transferFragment, fileFragment, this, row );
- this._torrents[new_torrent.id()] = new_torrent;
+ var t = new Torrent( this, row );
+ this._torrents[t.id()] = t;
}
- this._inspector_file_list.appendChild( fileFragment );
- this._torrent_list.appendChild( transferFragment );
-
this.refilter( );
},
@@ -1839,7 +1904,7 @@ Transmission.prototype =
if(torrent) {
removedAny = true;
- var e = torrent.element();
+ var e = torrent.view;
if( e ) {
var row_index;
for( var i=0, row; row = tr._rows[i]; ++i ) {
@@ -1855,8 +1920,6 @@ Transmission.prototype =
e.remove();
}
- torrent.hideFileList();
- torrent.deleteFiles();
delete tr._torrents[torrent.id()];
}
});
@@ -1866,9 +1929,9 @@ Transmission.prototype =
refreshDisplay: function( )
{
- var torrents = this.getVisibleTorrents();
- for( var i=0; torrents[i]; ++i )
- torrents[i].refreshHTML();
+ var rows = this.getVisibleRows( );
+ for( var i=0, row; row=rows[i]; ++i )
+ row.render( this );
},
/*
@@ -1877,12 +1940,8 @@ Transmission.prototype =
setTorrentBgColors: function( )
{
var rows = this.getVisibleRows( );
- for( var i=0, row; row=rows[i]; ++i ) {
- var wasEven = row[0].className.indexOf('even') != -1;
- var isEven = ((i+1) % 2 == 0);
- if( wasEven != isEven )
- row.toggleClass('even', isEven);
- }
+ for( var i=0, row; row=rows[i]; ++i )
+ row.setEven((i+1) % 2 == 0);
},
updateStatusbar: function()
@@ -2068,8 +2127,8 @@ Transmission.prototype =
var tr = this;
this.remote.stopTorrents( torrent_ids, function(){ tr.refreshTorrents(torrent_ids )} );
},
- changeFileCommand: function(command, torrent, file) {
- this.remote.changeFileCommand(command, torrent, file)
+ changeFileCommand: function(command, rows) {
+ this.remote.changeFileCommand(command, rows);
},
hideiPhoneAddressbar: function(timeInSeconds) {
@@ -2114,6 +2173,16 @@ Transmission.prototype =
****
***/
+ onToggleRunningClicked: function( ev )
+ {
+ var torrent = ev.data.r.getTorrent( );
+
+ if( torrent.isStopped( ) )
+ this.startTorrent( torrent );
+ else
+ this.stopTorrent( torrent );
+ },
+
refilter: function()
{
// decide which torrents to keep showing
@@ -2129,23 +2198,39 @@ Transmission.prototype =
// make a backup of the selection
var sel = this.getSelectedTorrents( );
- this.deselectAll( );
- // hide the ones we're not keeping
+ // add rows it there aren't enough
+ if( this._rows.length < keep.length ) {
+ var tr = this;
+ var fragment = document.createDocumentFragment( );
+ while( this._rows.length < keep.length ) {
+ var row = new TorrentRow( this, this.torrentRenderer );
+ if( !iPhone ) {
+ var b = row.getToggleRunningButton( );
+ if( b !== null ) {
+ $(b).bind('click', {r:row}, function(e) { tr.onToggleRunningClicked(e); });
+ }
+ }
+ $(row.getElement()).bind('click',{r: row}, function(ev){ tr.onRowClicked(ev,ev.data.r);});
+ fragment.appendChild( row.getElement() );
+ this._rows.push( row );
+ }
+ this._torrent_list.appendChild(fragment);
+ }
+
+ // hide rows if there are too many
for( var i=keep.length, e; e=this._rows[i]; ++i ) {
delete e._torrent;
- e[0].style.display = 'none';
+ e.setVisible(false);
}
// show the ones we're keeping
- sel.sort( Torrent.compareById );
for( var i=0, len=keep.length; i