Changeset 12770


Ignore:
Timestamp:
Aug 27, 2011, 9:35:19 PM (10 years ago)
Author:
jordan
Message:

(trunk web) in Transmission.refilter(), only refilter/resort the torrents that have changed since the last update. This makes the web client scale reasonably well even up to 1000s of torrents.

Location:
trunk/web
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/web/index.html

    r12739 r12770  
    209209
    210210                <div id="torrent_container">
    211                         <ul class="torrent_list" id="torrent_list"><li style="display: none;"></li></ul>
     211                        <ul class="torrent_list" id="torrent_list"></ul>
    212212                </div>
    213213
  • trunk/web/javascript/common.js

    r12739 r12770  
    204204Prefs._SortByRatio        = 'ratio';
    205205Prefs._SortByState        = 'state';
    206 Prefs._SortByTracker      = 'tracker';
    207206
    208207Prefs._TurtleState        = 'turtle-state';
  • trunk/web/javascript/torrent.js

    r12766 r12770  
    9696// fields used in the inspector which need to be periodically refreshed
    9797Torrent.Fields.StatsExtra = [
    98         'activityDate',
    9998        'desiredAvailable',
    10099        'downloadDir',
     
    125124        setField: function(o, name, value)
    126125        {
    127                 var changed = !(name in o) || (o[name] !== value);
    128                 if (changed)
    129                         o[name] = value;
    130                 return changed;
     126                if (o[name] === value)
     127                        return false;
     128                o[name] = value;
     129                return true;
    131130        },
    132131
     
    182181        {
    183182                if (this.refreshFields(data))
    184                         $(this).trigger('dataChanged');
     183                        $(this).trigger('dataChanged', this);
    185184        },
    186185
     
    240239        getActivity: function() { return this.getDownloadSpeed() + this.getUploadSpeed(); },
    241240        getPercentDoneStr: function() { return Transmission.fmt.percentString(100*this.getPercentDone()); },
    242         getPercentDone: function() {
    243                 var finalSize = this.getSizeWhenDone();
    244                 if (!finalSize) return 1.0;
    245                 var left = this.getLeftUntilDone();
    246                 if (!left) return 1.0;
    247                 return (finalSize - left) / finalSize;
    248         },
     241        getPercentDone: function() { return this.fields.percentDone; },
    249242        getStateString: function() {
    250243                switch(this.getStatus()) {
     
    399392};
    400393
     394Torrent.compareTorrents = function(a, b, sortMethod, sortDirection)
     395{
     396        var i;
     397
     398        switch(sortMethod)
     399        {
     400                case Prefs._SortByActivity:
     401                        i = Torrent.compareByActivity(a,b);
     402                        break;
     403                case Prefs._SortByAge:
     404                        i = Torrent.compareByAge(a,b);
     405                        break;
     406                case Prefs._SortByQueue:
     407                        i = Torrent.compareByQueue(a,b);
     408                        break;
     409                case Prefs._SortByProgress:
     410                        i = Torrent.compareByProgress(a,b);
     411                        break;
     412                case Prefs._SortByState:
     413                        i = Torrent.compareByState(a,b);
     414                        break;
     415                case Prefs._SortByRatio:
     416                        i = Torrent.compareByRatio(a,b);
     417                        break;
     418                default:
     419                        i = Torrent.compareByName(a,b);
     420                        break;
     421        }
     422
     423        if (sortDirection === Prefs._SortDescending)
     424                i = -i;
     425
     426        return i;
     427};
     428
    401429/**
    402430 * @param torrents an array of Torrent objects
  • trunk/web/javascript/transmission.js

    r12766 r12770  
    2626
    2727                // Initialize the implementation fields
    28                 this._current_search         = '';
    29                 this._torrents               = { };
    30                 this._rows                   = [ ];
     28                this.filterText    = '';
     29                this._torrents     = { };
     30                this._rows         = [ ];
     31                this.dirtyTorrents = { };
    3132
    3233                // Initialize the clutch preferences
     
    6667                jQuery.event.props.push("dataTransfer");
    6768
     69                $(document).delegate('#torrent_list > li', 'click', function(ev) {tr.setSelectedRow(ev.currentTarget.row);});
     70                $(document).delegate('#torrent_list > li', 'dblclick', function(e) {tr.toggleInspector();});
     71       
    6872                $('#torrent_upload_form').submit(function() { $('#upload_confirm_button').click(); return false; });
    6973
     
    234238                var tr = this;
    235239                var search_box = $('#torrent_search');
    236                 search_box.bind('keyup click', function() {tr.setSearch(this.value);});
     240                search_box.bind('keyup click', function() {
     241                        tr.setFilterText(this.value);
     242                });
    237243                if (!$.browser.safari)
    238244                {
     
    243249                                        $(this).addClass('blur');
    244250                                        this.value = 'Filter';
    245                                         tr.setSearch(null);
     251                                        tr.setFilterText(null);
    246252                                }
    247253                        }).bind('focus', function() {
     
    916922        },
    917923
    918         setSearch: function(search) {
    919                 this._current_search = search ? search.trim() : null;
    920                 this.refilter();
     924        setFilterText: function(search) {
     925                this.filterText = search ? search.trim() : null;
     926                this.refilter(true);
    921927        },
    922928
    923929        setSortMethod: function(sort_method) {
    924930                this.setPref(Prefs._SortMethod, sort_method);
    925                 this.refilter();
     931                this.refilter(true);
    926932        },
    927933
    928934        setSortDirection: function(direction) {
    929935                this.setPref(Prefs._SortDirection, direction);
    930                 this.refilter();
     936                this.refilter(true);
    931937        },
    932938
     
    10361042
    10371043
    1038         onTorrentChanged: function(ev)
    1039         {
     1044        onTorrentChanged: function(tor)
     1045        {
     1046                var id = tor.getId();
     1047
     1048                // update our dirty fields
     1049                this.dirtyTorrents[id] = true;
     1050
     1051                // enqueue a filter refresh
    10401052                this.refilterSoon();
    10411053       
     
    10581070                                var tr = this;
    10591071                                t = tr._torrents[id] = new Torrent(o);
    1060                                 $(t).bind('dataChanged',function(ev) {tr.onTorrentChanged(ev);});
     1072                                this.dirtyTorrents[id] = true;
     1073                                $(t).bind('dataChanged',function(ev,tor) {tr.onTorrentChanged(tor);});
    10611074                                if(!('name' in t.fields) || !('status' in t.fields)) // missing some fields...
    10621075                                        needinfo.push(id);
     
    19531966        },
    19541967
    1955         refilter: function()
    1956         {
     1968        sortRows: function(rows)
     1969        {
     1970                var i, tor, row,
     1971                    id2row = {},
     1972                    torrents = [];
     1973
     1974                for (i=0; row=rows[i]; ++i) {
     1975                        tor = row.getTorrent();
     1976                        torrents.push(tor);
     1977                        id2row[ tor.getId() ] = row;
     1978                }
     1979
     1980                Torrent.sortTorrents(torrents, this[Prefs._SortMethod],
     1981                                               this[Prefs._SortDirection]);
     1982
     1983                for (i=0; tor=torrents[i]; ++i)
     1984                        rows[i] = id2row[ tor.getId() ];
     1985        },
     1986
     1987        refilter: function(rebuildEverything)
     1988        {
     1989                var i, id, t, row, sel;
     1990
    19571991                clearTimeout(this.refilterTimer);
    19581992                delete this.refilterTimer;
    19591993
    1960                 // make a filtered, sorted array of our torrents
     1994                // get a temporary lookup table of selected torrent ids
     1995                sel = { };
     1996                for (i=0; row=this._rows[i]; ++i)
     1997                        if (row.isSelected())
     1998                                sel[row.getTorrentId()] = row;
     1999
     2000                if (rebuildEverything)
     2001                        for (id in this._torrents)
     2002                                this.dirtyTorrents[id] = ~0;
     2003
     2004                // rows that overlap with dirtyTorrents need to be refiltered.
     2005                // those that don't are 'clean' and don't need refiltering.
     2006                var dirty_rows = [];
     2007                var clean_rows = [];
     2008                for (i=0; row=this._rows[i]; ++i) {
     2009                        if(row.getTorrentId() in this.dirtyTorrents)
     2010                                dirty_rows.push(row);
     2011                        else
     2012                                clean_rows.push(row);
     2013                }
     2014
     2015                // remove the dirty rows from the dom
     2016                var elementsToRemove = $.map(dirty_rows.slice(0), function(r) {
     2017                        return r.getElement();
     2018                });
     2019                $(elementsToRemove).remove();
     2020
     2021                // drop any dirty rows that don't pass the filter test
     2022                var tmp = [];
    19612023                var filter_mode = this[Prefs._FilterMode];
    1962                 var filter_text = this._current_search;
     2024                var filter_text = this.filterText;
    19632025                var filter_tracker = this.filterTracker;
    1964                 var keep = $.grep(this.getAllTorrents(), function(t) {
    1965                         return t.test(filter_mode, filter_text, filter_tracker);
    1966                 });
    1967                 Torrent.sortTorrents(keep, this[Prefs._SortMethod], this[Prefs._SortDirection]);
    1968 
    1969                 // maybe rebuild the rows
    1970                 if (this._force_refilter || !this.matchesTorrentList(keep))
     2026                for (i=0; row=dirty_rows[i]; ++i) {
     2027                        t = row.getTorrent();
     2028                        if (t.test(filter_mode, filter_text, filter_tracker))
     2029                                tmp.push(row);
     2030                        delete this.dirtyTorrents[t.getId()];
     2031                }
     2032                dirty_rows = tmp;
     2033
     2034                // make new rows for dirty torrents that pass the filter test
     2035                // but don't already have a row
     2036                var renderer = this.torrentRenderer;
     2037                for (id in this.dirtyTorrents) {
     2038                        t = this._torrents[id];
     2039                        if (t.test(filter_mode, filter_text, filter_tracker)) {
     2040                                var is_selected = t.getId() in sel;
     2041                                row = new TorrentRow(renderer, this, t, is_selected);
     2042                                row.getElement().row = row;
     2043                                dirty_rows.push(row);
     2044                        }
     2045                }
     2046
     2047                // sort the dirty rows
     2048                this.sortRows (dirty_rows);
     2049
     2050                // now we have two sorted arrays of rows
     2051                // and can do a simple two-way sorted merge.
     2052                var rows = []
     2053                var ci=0, cmax=clean_rows.length;
     2054                var di=0, dmax=dirty_rows.length;
     2055                var sort_method = this[Prefs._SortMethod];
     2056                var sort_direction = this[Prefs._SortDirection];
     2057                var list = this._torrent_list;
     2058                while (ci!=cmax || di!=dmax)
    19712059                {
    1972                         var old_sel = this.getSelectedTorrents();
    1973                         var new_sel_count = 0;
    1974 
    1975                         // make the new rows
    1976                         var tr = this;
    1977                         var rows = [ ];
    1978                         var fragment = document.createDocumentFragment();
    1979                         var renderer = this.torrentRenderer;
    1980                         for (var i=0, tor; tor=keep[i]; ++i)
    1981                         {
    1982                                 var is_selected = old_sel.indexOf(tor) !== -1;
    1983                                 var row = new TorrentRow(renderer, this, tor, is_selected);
    1984                                 row.setEven((i+1) % 2 == 0);
    1985                                 if (is_selected)
    1986                                         new_sel_count++;
    1987                                 if (!iPhone) {
    1988                                         var b = row.getToggleRunningButton();
    1989                                         if (b)
    1990                                                 $(b).click({r:row}, function(ev) {tr.onToggleRunningClicked(ev);});
    1991                                 }
    1992                                 $(row.getElement()).click({r: row}, function(ev) {tr.onRowClicked(ev,ev.data.r);})
    1993                                                    .dblclick(function() {tr.toggleInspector();});
    1994                                 fragment.appendChild(row.getElement());
     2060                        var push_clean;
     2061
     2062                        if (ci==cmax)
     2063                                push_clean = false;
     2064                        else if (di==dmax)
     2065                                push_clean = true;
     2066                        else {
     2067                                var ctor = clean_rows[ci].getTorrent();
     2068                                var dtor = dirty_rows[di].getTorrent();
     2069                                var c = Torrent.compareTorrents(ctor, dtor, sort_method, sort_direction);
     2070                                push_clean = (c < 0);
     2071                        }
     2072
     2073                        if (push_clean)
     2074                                rows.push(clean_rows[ci++]);
     2075                        else {
     2076                                var row = dirty_rows[di++];
     2077                                var e = row.getElement();
     2078                                if (ci !== cmax)
     2079                                        list.insertBefore(e, clean_rows[ci].getElement());
     2080                                else
     2081                                        list.appendChild(e);
    19952082                                rows.push(row);
    19962083                        }
    1997                         $('ul.torrent_list').empty();
    1998                         delete this._rows;
    1999                         this._rows = rows;
    2000                         this._torrent_list.appendChild(fragment);
    2001 
    2002                         if (old_sel.length !== new_sel_count)
    2003                                 this.selectionChanged();
    2004 
    2005                         delete this._force_refilter;
    2006                 }
     2084                }
     2085
     2086                // update our implementation fields
     2087                this._rows = rows;
     2088                this.dirtyTorrents = { };
     2089
     2090                // jquery's even/odd starts with 1 rather than 0, so invert the logic here
     2091                var elements = $.map(rows.slice(0), function(r){return r.getElement();});
     2092                $(elements).filter(":odd").addClass('even');
     2093                $(elements).filter(":even").removeClass('even');
    20072094
    20082095                // sync gui
     
    20112098        },
    20122099
    2013         setFilter: function(mode)
     2100        setFilterMode: function(mode)
    20142101        {
    20152102                // set the state
     
    20172104
    20182105                // refilter
    2019                 this.refilter();
     2106                this.refilter(true);
    20202107        },
    20212108
     
    20522139                                      + '<span class="filter-name">' + tr.getStateString(s) + '</span>'
    20532140                                      + '<span class="count">' + counts[s] + '</span>';
    2054                         $(div).click({'state':s}, function(ev) { tr.setFilter(ev.data.state); $('#filter-popup').dialog('close');});
     2141                        $(div).click({'state':s}, function(ev) {
     2142                                tr.setFilterMode(ev.data.state);
     2143                                $('#filter-popup').dialog('close');
     2144                        });
    20552145                        fragment.appendChild(div);
    20562146                }
     
    20722162                              + '<span class="filter-name">All</span>'
    20732163                              + '<span class="count">' + torrents.length + '</span>';
    2074                 $(div).click(function() {tr.setFilterTracker(null); $('#filter-popup').dialog('close');})
     2164                $(div).click(function() {
     2165                        tr.setFilterTracker(null);
     2166                        $('#filter-popup').dialog('close');
     2167                });
    20752168                fragment.appendChild(div);
    20762169                for (var i=0, name; name=names[i]; ++i) {
     
    20822175                                      + '<span class="filter-name">'+ name + '</span>'
    20832176                                      + '<span class="count">'+ o.count + '</span>';
    2084                         $(div).click({domain:o.domain}, function(ev) { tr.setFilterTracker(ev.data.domain); $('#filter-popup').dialog('close');});
     2177                        $(div).click({domain:o.domain}, function(ev) {
     2178                                tr.setFilterTracker(ev.data.domain);
     2179                                $('#filter-popup').dialog('close');
     2180                        });
    20852181                        fragment.appendChild(div);
    20862182                }
     
    21112207                $(id).addClass('selected').siblings().removeClass('selected');
    21122208
    2113                 this.refilterSoon();
     2209                this.refilter(true);
    21142210        },
    21152211
     
    22012297                this.torrentRenderer = compact ? new TorrentRendererCompact()
    22022298                                               : new TorrentRendererFull();
    2203                 this._force_refilter = true;
    2204                 this.refilter();
     2299                this.refilter(true);
    22052300        }
    22062301};
Note: See TracChangeset for help on using the changeset viewer.