1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-25 01:03:01 +00:00

(trunk web) #5013 "WebUI Inspector file tree" -- possible reimplementation.

This commit is contained in:
Jordan Lee 2012-09-23 22:05:21 +00:00
parent 7b7da2d315
commit 53ef843cbd
2 changed files with 187 additions and 238 deletions

View file

@ -5,14 +5,15 @@
* 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
@ -26,29 +27,10 @@ function FileRow(torrent, i)
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()
@ -58,12 +40,6 @@ function FileRow(torrent, i)
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),
@ -75,29 +51,65 @@ function FileRow(torrent, i)
'%)' ].join('');
setTextContent(elements.progress, c);
},
refreshHTML = function() {
if (fields.isDirty) {
fields.isDirty = false;
refreshProgressHTML();
refreshWantedHTML();
refreshPriorityHTML();
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();
}
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);
}
},
refresh = function() {
readAttributes(fields.torrent.getFile(fields.index));
refreshHTML();
},
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');
@ -135,28 +147,32 @@ function FileRow(torrent, i)
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 ]);
};
/***
@ -166,19 +182,12 @@ function FileRow(torrent, i)
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);
};

View file

@ -456,183 +456,127 @@ function Inspector(controller) {
***** 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);
data.controller.changeFileCommand(torrentId, fileIndices, command);
},
selectAllFiles = function() {
changeFileCommand([], 'files-wanted');
onFileWantedToggled = function(ev, fileIndices, want) {
changeFileCommand(fileIndices, want?'files-wanted':'files-unwanted');
},
deselectAllFiles = function() {
changeFileCommand([], 'files-unwanted');
},
onFileWantedToggled = function(ev, row, want) {
changeFileCommand([row], 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;
},
createFileTreeModel = function (tor) {
var i, j, n, name, tokens, walk, tree, token, sub,
leaves = [ ],
tree = { children: { }, file_indices: [ ] };
n = tor.getFileCount();
for (i=0; i<n; ++i) {
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);
}
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);
}
return tree;
},
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, j, n, sel, row, tor, fragment,
box, complete, conn, connections, e, from, heirarchy, item,
matches, parents, parentid, path, sum, subheirarchy, to, inner,
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;
}
// build the file list
tor = torrents[0];
for (parentid in data.parents) {
data.parents[parentid] = $('#'+parentid).children('li').css('display')
}
n = tor ? tor.getFileCount() : 0;
if (tor!=data.file_torrent || n==data.file_torrent_n) {
// rebuild the file list...
clearFileList();
data.file_torrent = tor;
n = tor.getFileCount();
data.file_torrent_n = n;
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]);
}
}
}
}
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 = "&nbsp;"
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) })
tree = createFileTreeModel (tor);
addSubtreeToView (tor, fragment, tree, 0);
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')
} else {
// ...refresh the already-existing file list
for (i=0, n=data.file_rows.length; i<n; ++i)
data.file_rows[i].refresh();
}
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)
$($('.inspector_torrent_file_list_entry_progress',$('#'+parentid))[0]).text('('+totalcomplete+'%)')
}
for (parentid in data.parents) {
$('#'+parentid).children('li').css('display',data.parents[parentid])
}
data.parents = parents;
},
/****
@ -847,10 +791,6 @@ function Inspector(controller) {
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();