* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
-function FileRow(torrent, i)
+function FileRow(torrent, depth, name, indices, even)
{
var fields = {
have: 0,
- index: 0,
- isDirty: false,
+ indices: [],
isWanted: true,
- priority: 0,
+ priorityLow: false,
+ priorityNormal: true,
+ priorityHigh: false,
me: this,
size: 0,
torrent: null
root: null
},
- initialize = function(torrent, i) {
+ initialize = function(torrent, depth, name, indices, even) {
fields.torrent = torrent;
- fields.index = i;
- createRow(torrent, i);
- },
-
- readAttributes = function(file) {
- if (fields.have !== file.bytesCompleted) {
- fields.have = file.bytesCompleted;
- fields.isDirty = true;
- }
- if (fields.size !== file.length) {
- fields.size = file.length;
- fields.isDirty = true;
- }
- if (fields.priority !== file.priority) {
- fields.priority = file.priority;
- fields.isDirty = true;
- }
- if (fields.isWanted !== file.wanted) {
- fields.isWanted = file.wanted;
- fields.isDirty = true;
- }
+ fields.indices = indices;
+ createRow(torrent, depth, name, even);
},
refreshWantedHTML = function()
e.toggleClass('complete', isDone());
$(e[0].checkbox).prop('checked', fields.isWanted);
},
- refreshPriorityHTML = function()
- {
- $(elements.priority_high_button ).toggleClass('selected', fields.priority === 1 );
- $(elements.priority_normal_button).toggleClass('selected', fields.priority === 0 );
- $(elements.priority_low_button ).toggleClass('selected', fields.priority === -1 );
- },
refreshProgressHTML = function()
{
var pct = 100 * (fields.size ? (fields.have / fields.size) : 1.0),
'%)' ].join('');
setTextContent(elements.progress, c);
},
- refreshHTML = function() {
- if (fields.isDirty) {
- fields.isDirty = false;
+ refreshImpl = function() {
+ var i,
+ file,
+ have = 0,
+ size = 0,
+ wanted = false,
+ low = false,
+ normal = false,
+ high = false;
+
+ // loop through the file_indices that affect this row
+ for (i=0; i<fields.indices.length; ++i) {
+ file = fields.torrent.getFile (fields.indices[i]);
+ have += file.bytesCompleted;
+ size += file.length;
+ wanted |= file.wanted;
+ switch (file.priority) {
+ case -1: low = true; break;
+ case 0: normal = true; break;
+ case 1: high = true; break;
+ }
+ }
+
+ if ((fields.have != have) || (fields.size != size)) {
+ fields.have = have;
+ fields.size = size;
refreshProgressHTML();
+ }
+
+ if (fields.isWanted !== wanted) {
+ fields.isWanted = wanted;
refreshWantedHTML();
- refreshPriorityHTML();
}
- },
- refresh = function() {
- readAttributes(fields.torrent.getFile(fields.index));
- refreshHTML();
+
+ if (fields.priorityLow !== low) {
+ fields.priorityLow = low;
+ $(elements.priority_low_button).toggleClass('selected', low);
+ }
+
+ if (fields.priorityNormal !== normal) {
+ fields.priorityNormal = normal;
+ $(elements.priority_normal_button).toggleClass('selected', normal);
+ }
+
+ if (fields.priorityHigh !== high) {
+ fields.priorityHigh = high;
+ $(elements.priority_high_button).toggleClass('selected', high);
+ }
},
isDone = function () {
return fields.have >= fields.size;
},
- createRow = function(torrent, i) {
- var file = torrent.getFile(i), e, name, root, box;
+ createRow = function(torrent, depth, name, even) {
+ var e, root, box;
root = document.createElement('li');
- root.id = 't' + fields.torrent.getId() + 'f' + fields.index;
- root.className = 'inspector_torrent_file_list_entry ' + ((i%2)?'odd':'even');
+ root.className = 'inspector_torrent_file_list_entry' + (even?'even':'odd');
elements.root = root;
e = document.createElement('input');
root.appendChild(box);
- name = file.name || 'Unknown';
- name = name.substring(name.lastIndexOf('/')+1);
- name = name.replace(/([\/_\.])/g, "$1");
e = document.createElement('div');
e.className = "inspector_torrent_file_list_entry_name";
setTextContent(e, name);
+ $(e).click(function(){ fireNameClicked(-1); });
root.appendChild(e);
e = document.createElement('div');
e.className = "inspector_torrent_file_list_entry_progress";
root.appendChild(e);
+ $(e).click(function(){ fireNameClicked(-1); });
elements.progress = e;
- refresh();
+ $(root).css('margin-left', '' + (depth*16) + 'px');
+
+ refreshImpl();
return root;
},
fireWantedChanged = function(do_want) {
- $(fields.me).trigger('wantedToggled',[ fields.me, do_want ]);
+ $(fields.me).trigger('wantedToggled',[ fields.indices, do_want ]);
},
firePriorityChanged = function(priority) {
- $(fields.me).trigger('priorityToggled',[ fields.me, priority ]);
+ $(fields.me).trigger('priorityToggled',[ fields.indices, priority ]);
+ },
+ fireNameClicked = function() {
+ $(fields.me).trigger('nameClicked',[ fields.me, fields.indices ]);
};
/***
this.getElement = function() {
return elements.root;
};
- this.getIndex = function() {
- return fields.index;
- };
this.isEditable = function () {
return (fields.torrent.getFileCount()>1) && !isDone();
};
- this.getPath = function () {
- var file = torrent.getFile(i);
- var path = file.name.replace(/\/\/+/g,'/');
- path = path.split('/').slice(0,-1);
- path.push('t' + fields.torrent.getId() + 'f' + fields.index);
- return path;
+ this.refresh = function() {
+ refreshImpl();
};
- initialize(torrent, i);
+ initialize(torrent, depth, name, indices, even);
};
else {
d = u = 0;
if(torrents.length == 1) {
- d = torrents[0].getDownloadedEver();
- u = torrents[0].getUploadedEver();
-
- if (d == 0)
- d = torrents[0].getHaveValid();
+ d = torrents[0].getDownloadedEver();
+ u = torrents[0].getUploadedEver();
+
+ if (d == 0)
+ d = torrents[0].getHaveValid();
}
else {
- for(i=0; t=torrents[i]; ++i) {
- d += t.getDownloadedEver();
- u += t.getUploadedEver();
- }
- }
+ for(i=0; t=torrents[i]; ++i) {
+ d += t.getDownloadedEver();
+ u += t.getUploadedEver();
+ }
+ }
str = fmt.size(u) + ' (Ratio: ' + fmt.ratioString( Math.ratio(u,d))+')';
}
setTextContent(e.uploaded_lb, str);
***** FILES PAGE
****/
- changeFileCommand = function(rows, command) {
+ changeFileCommand = function(fileIndices, command) {
var torrentId = data.file_torrent.getId();
- var rowIndices = $.map(rows.slice(0),function (row) {return row.getIndex();});
- data.controller.changeFileCommand(torrentId, rowIndices, command);
- },
-
- selectAllFiles = function() {
- changeFileCommand([], 'files-wanted');
- },
-
- deselectAllFiles = function() {
- changeFileCommand([], 'files-unwanted');
+ data.controller.changeFileCommand(torrentId, fileIndices, command);
},
- onFileWantedToggled = function(ev, row, want) {
- changeFileCommand([row], want?'files-wanted':'files-unwanted');
+ onFileWantedToggled = function(ev, fileIndices, want) {
+ changeFileCommand(fileIndices, want?'files-wanted':'files-unwanted');
},
- onFilePriorityToggled = function(ev, row, priority) {
+ onFilePriorityToggled = function(ev, fileIndices, priority) {
var command;
switch(priority) {
case -1: command = 'priority-low'; break;
case 1: command = 'priority-high'; break;
default: command = 'priority-normal'; break;
}
- changeFileCommand([row], command);
+ changeFileCommand(fileIndices, command);
+ },
+
+ onNameClicked = function(ev, fileRow, fileIndices) {
+ $(fileRow.getElement()).siblings().slideToggle();
},
clearFileList = function() {
$(data.elements.file_list).empty();
delete data.file_torrent;
+ delete data.file_torrent_n;
delete data.file_rows;
},
- updateFilesPage = function() {
- var i, j, n, sel, row, tor, fragment,
- box, complete, conn, connections, e, from, heirarchy, item,
- matches, parents, parentid, path, sum, subheirarchy, to, inner,
- file_list = data.elements.file_list,
- torrents = data.torrents;
-
- if (torrents.length !== 1) {
- clearFileList();
- return;
- }
-
- // build the file list
- tor = torrents[0];
+ createFileTreeModel = function (tor) {
+ var i, j, n, name, tokens, walk, tree, token, sub,
+ leaves = [ ],
+ tree = { children: { }, file_indices: [ ] };
- for (parentid in data.parents) {
- data.parents[parentid] = $('#'+parentid).children('li').css('display')
- }
- clearFileList();
- data.file_torrent = tor;
n = tor.getFileCount();
- data.file_rows = [];
- fragment = document.createDocumentFragment();
- heirarchy = {'/':[]}
-
for (i=0; i<n; ++i) {
- row = data.file_rows[i] = new FileRow(tor, i);
- fragment.appendChild(row.getElement());
- $(row).bind('wantedToggled',onFileWantedToggled);
- $(row).bind('priorityToggled',onFilePriorityToggled);
- path = row.getPath();
- if (path.length == 1) {
- heirarchy['/'].push(path[0]);
- } else {
- subheirarchy = heirarchy;
- for (j=0; j<path.length; j++) {
- if (j<(path.length-1)) {
- subheirarchy[path[j]] = (subheirarchy[path[j]] == undefined) ? {'/':[]} : subheirarchy[path[j]];
- subheirarchy[path[j]]['PARENT'] = (path[j-1] == undefined) ? [undefined] : [path[j-1].replace(/[\[\] ().]/g,'_')]
- subheirarchy = subheirarchy[path[j]];
- } else {
- parentid = (path[j-1] == undefined) ? [undefined] : [path[j-1].replace(/[\[\] ().]/g,'_')]
- subheirarchy['/'].push([path[j],parentid]);
- }
+ name = tor.getFile(i).name;
+ tokens = name.split('/');
+ walk = tree;
+ for (j=0; j<tokens.length; ++j) {
+ token = tokens[j];
+ sub = walk.children[token];
+ if (!sub) {
+ walk.children[token] = sub = {
+ name: token,
+ parent: walk,
+ children: { },
+ file_indices: [ ],
+ depth: j
+ };
}
+ walk = sub;
}
+ walk.file_index = i;
+ delete walk.children;
+ leaves.push (walk);
}
- console.log(heirarchy);
- /*
- At this point we have a single object containing the whole
- torrent heirarchy. The '/' key is file element ids as assigned above
- for the current level. Any other keys are array objects which are
- directories in the torrent heirarchy, this will also contain a '/'
- key and potentially more keys for nested directories.
- */
- connections = [];
- parents = {};
- var recursiveCreate = function(key,val) {
- if (key == "PARENT") { return; }
- if (typeof(val) == "object" &&! Array.isArray(val)) {
- row = document.createElement('li');
- row.id = key.replace(/[\[\] ().]/g,'_');
- row.classname = 'inspector_torrent_file_list_entry odd';
-
- e = document.createElement('input');
- e.type = 'checkbox';
- e.className = "file_wanted_control";
- e.title = 'Download file';
- e.checked = true;
- $(e).change(function() { $($(this).parent()).children('li').children('input').click() });
- row.appendChild(e);
-
- e = document.createElement('div');
- e.className = 'file-priority-radiobox';
- box = e;
-
- e = document.createElement('div');
- e.className = 'low';
- e.title = 'Low Priority';
- $(e).click(function(){ $($(this).parent().parent()).children('li').children('div').children('.low').click() });
- box.appendChild(e);
-
- e = document.createElement('div');
- e.className = 'normal';
- e.title = 'Normal Priority';
- $(e).click(function(){ $($(this).parent().parent()).children('li').children('div').children('.normal').click() });
- box.appendChild(e);
-
- e = document.createElement('div');
- e.title = 'High Priority';
- e.className = 'high';
- $(e).click(function(){ $($(this).parent().parent()).children('li').children('div').children('.high').click() });
- box.appendChild(e);
-
- row.appendChild(box);
-
- inner = document.createElement('div');
- inner.className = "inspector_torrent_file_list_entry_name";
- inner.innerHTML = key;
- row.appendChild(inner);
-
- complete = document.createElement('div');
- complete.className = "inspector_torrent_file_list_entry_progress";
- complete.innerHTML = " "
- row.appendChild(complete)
-
- fragment.appendChild(row);
- connections.push([key.replace(/[\[\] ().]/g,'_'),val['PARENT'][0]])
- } else {
- for (item in val) {
- parents[val[item][1][0]] = ""
- }
- connections.push.apply(connections,val)
- }
- if (!Array.isArray(val)) {
- $.each(val,function(key,val) { recursiveCreate(key,val) })
- }
- }
- $.each(heirarchy,function(key,val) { recursiveCreate(key,val) })
- file_list.appendChild(fragment);
- for (conn in connections) {
- from = connections[conn][0];
- to = connections[conn][1];
- if (to == undefined) { continue; }
- $('#'+from).appendTo($('#'+to));
- $('#'+from).css('margin-left','20px')
+
+ for (i=0; i<leaves.length; ++i) {
+ walk = leaves[i];
+ j = walk.file_index;
+ do {
+ walk.file_indices.push (j);
+ walk = walk.parent;
+ } while (walk);
}
- for (parentid in parents) {
- $($('.inspector_torrent_file_list_entry_name',$('#'+parentid))[0]).click(function() { $($(this).parent()).children('li').toggle(); })
- sum = 0;
- matches = $('#'+parentid).children('li').text().match(/\([^\.]+\)/g)
- if (matches == null) { continue; }
- matches.map(function(word) {return parseFloat(word.slice(1,-2)) }).map(function(perc) {sum+=perc})
- count = $('#'+parentid).children('li').text().match(/\([^\.]+\)/g).length
- totalcomplete = (sum/count).toFixed(1)
+ return tree;
+ },
- $($('.inspector_torrent_file_list_entry_progress',$('#'+parentid))[0]).text('('+totalcomplete+'%)')
+ addNodeToView = function (tor, parent, sub, i) {
+ var row;
+ row = new FileRow(tor, sub.depth, sub.name, sub.file_indices, i%2);
+ data.file_rows.push(row);
+ parent.appendChild(row.getElement());
+ $(row).bind('wantedToggled',onFileWantedToggled);
+ $(row).bind('priorityToggled',onFilePriorityToggled);
+ $(row).bind('nameClicked',onNameClicked);
+ }
+
+ addSubtreeToView = function (tor, parent, sub, i) {
+ var key, div;
+ div = document.createElement('div');
+ if (sub.parent)
+ addNodeToView (tor, div, sub, i++);
+ if (sub.children)
+ for (key in sub.children)
+ i = addSubtreeToView (tor, div, sub.children[key]);
+ parent.appendChild(div);
+ return i;
+ },
+
+ updateFilesPage = function() {
+ var i, n, tor, fragment, tree,
+ file_list = data.elements.file_list,
+ torrents = data.torrents;
+
+ // only show one torrent at a time
+ if (torrents.length !== 1) {
+ clearFileList();
+ return;
}
- for (parentid in data.parents) {
- $('#'+parentid).children('li').css('display',data.parents[parentid])
+
+ tor = torrents[0];
+ n = tor ? tor.getFileCount() : 0;
+ if (tor!=data.file_torrent || n==data.file_torrent_n) {
+ // rebuild the file list...
+ clearFileList();
+ data.file_torrent = tor;
+ data.file_torrent_n = n;
+ data.file_rows = [ ];
+ fragment = document.createDocumentFragment();
+ tree = createFileTreeModel (tor);
+ addSubtreeToView (tor, fragment, tree, 0);
+ file_list.appendChild (fragment);
+ } else {
+ // ...refresh the already-existing file list
+ for (i=0, n=data.file_rows.length; i<n; ++i)
+ data.file_rows[i].refresh();
}
- data.parents = parents;
},
/****
data.elements.peers_list = $('#inspector_peers_list')[0];
data.elements.trackers_list = $('#inspector_trackers_list')[0];
- data.elements.have_lb = $('#inspector-info-have')[0];
- data.elements.availability_lb = $('#inspector-info-availability')[0];
- data.elements.downloaded_lb = $('#inspector-info-downloaded')[0];
- data.elements.uploaded_lb = $('#inspector-info-uploaded')[0];
- data.elements.state_lb = $('#inspector-info-state')[0];
- data.elements.running_time_lb = $('#inspector-info-running-time')[0];
- data.elements.remaining_time_lb = $('#inspector-info-remaining-time')[0];
- data.elements.last_activity_lb = $('#inspector-info-last-activity')[0];
- data.elements.error_lb = $('#inspector-info-error')[0];
- data.elements.size_lb = $('#inspector-info-size')[0];
- data.elements.foldername_lb = $('#inspector-info-location')[0];
- data.elements.hash_lb = $('#inspector-info-hash')[0];
- data.elements.privacy_lb = $('#inspector-info-privacy')[0];
- data.elements.origin_lb = $('#inspector-info-origin')[0];
- data.elements.comment_lb = $('#inspector-info-comment')[0];
+ data.elements.have_lb = $('#inspector-info-have')[0];
+ data.elements.availability_lb = $('#inspector-info-availability')[0];
+ data.elements.downloaded_lb = $('#inspector-info-downloaded')[0];
+ data.elements.uploaded_lb = $('#inspector-info-uploaded')[0];
+ data.elements.state_lb = $('#inspector-info-state')[0];
+ data.elements.running_time_lb = $('#inspector-info-running-time')[0];
+ data.elements.remaining_time_lb = $('#inspector-info-remaining-time')[0];
+ data.elements.last_activity_lb = $('#inspector-info-last-activity')[0];
+ data.elements.error_lb = $('#inspector-info-error')[0];
+ data.elements.size_lb = $('#inspector-info-size')[0];
+ data.elements.foldername_lb = $('#inspector-info-location')[0];
+ data.elements.hash_lb = $('#inspector-info-hash')[0];
+ data.elements.privacy_lb = $('#inspector-info-privacy')[0];
+ data.elements.origin_lb = $('#inspector-info-origin')[0];
+ data.elements.comment_lb = $('#inspector-info-comment')[0];
data.elements.name_lb = $('#torrent_inspector_name')[0];
- // file page's buttons
- $('#select-all-files').click(selectAllFiles);
- $('#deselect-all-files').click(deselectAllFiles);
-
// force initial 'N/A' updates on all the pages
updateInspector();
updateInfoPage();