]> granicus.if.org Git - transmission/commitdiff
(trunk web) #5013 "WebUI Inspector file tree" -- possible reimplementation.
authorJordan Lee <jordan@transmissionbt.com>
Sun, 23 Sep 2012 22:05:21 +0000 (22:05 +0000)
committerJordan Lee <jordan@transmissionbt.com>
Sun, 23 Sep 2012 22:05:21 +0000 (22:05 +0000)
web/javascript/file-row.js
web/javascript/inspector.js

index fe2e9d5eacb9002ddce6be41d77d451ed5843ef2..cc2f66dfb852aa31c5fa28c716b260b4821f377f 100644 (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;
+       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');
@@ -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);
 };
index ef5c3adff22d1347b3a85095167c7f364896f38c..122fc9f51664e4d42e0224dc548b3d332622c4c4 100644 (file)
@@ -222,18 +222,18 @@ function Inspector(controller) {
         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);
@@ -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);
-    },
-
-    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 = "&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) })
-        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;
     },
 
     /****
@@ -830,27 +774,23 @@ function Inspector(controller) {
         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();