3 * Copyright (c) 2008 Ishan Arora <ishan@qbittorrent.org> & Christophe Dumez <chris@qbittorrent.org>
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 /**************************************************************
26 Script : Dynamic Table
28 Authors : Ishan Arora & Christophe Dumez
29 Desc : Programmable sortable table
30 Licence : Open Source MIT Licence
32 **************************************************************/
36 var DynamicTableHeaderContextMenuClass = null;
37 var ProgressColumnWidth = -1;
39 var DynamicTable = new Class({
41 initialize: function() {},
43 setup: function(dynamicTableDivId, dynamicTableFixedHeaderDivId, contextMenu) {
44 this.dynamicTableDivId = dynamicTableDivId;
45 this.dynamicTableFixedHeaderDivId = dynamicTableFixedHeaderDivId;
46 this.fixedTableHeader = $(dynamicTableFixedHeaderDivId).getElements('tr')[0];
47 this.hiddenTableHeader = $(dynamicTableDivId).getElements('tr')[0];
48 this.tableBody = $(dynamicTableDivId).getElements('tbody')[0];
49 this.rows = new Hash();
50 this.selectedRows = [];
52 this.contextMenu = contextMenu;
53 this.sortedColumn = getLocalStorageItem('sorted_column_' + this.dynamicTableDivId, 0);
54 this.reverseSort = getLocalStorageItem('reverse_sort_' + this.dynamicTableDivId, '0');
56 this.loadColumnsOrder();
57 this.updateTableHeaders();
58 this.setupCommonEvents();
59 this.setupHeaderEvents();
60 this.setupHeaderMenu();
61 this.setSortedColumnIcon(this.sortedColumn, null, (this.reverseSort === '1'));
64 setupCommonEvents: function() {
65 var scrollFn = function() {
66 $(this.dynamicTableFixedHeaderDivId).getElements('table')[0].style.left = -$(this.dynamicTableDivId).scrollLeft + 'px';
69 $(this.dynamicTableDivId).addEvent('scroll', scrollFn);
71 // if the table exists within a panel
72 if ($(this.dynamicTableDivId).getParent('.panel')) {
73 var resizeFn = function() {
74 var panel = $(this.dynamicTableDivId).getParent('.panel');
75 var h = panel.getBoundingClientRect().height - $(this.dynamicTableFixedHeaderDivId).getBoundingClientRect().height;
76 $(this.dynamicTableDivId).style.height = h + 'px';
78 // Workaround due to inaccurate calculation of elements heights by browser
82 while (panel.clientWidth != panel.offsetWidth && n > 0) { // is panel vertical scrollbar visible ?
85 $(this.dynamicTableDivId).style.height = h + 'px';
88 this.lastPanelHeight = panel.getBoundingClientRect().height;
91 $(this.dynamicTableDivId).getParent('.panel').addEvent('resize', resizeFn);
93 this.lastPanelHeight = 0;
95 // Workaround. Resize event is called not always (for example it isn't called when browser window changes it's size)
97 var checkResizeFn = function() {
98 var panel = $(this.dynamicTableDivId).getParent('.panel');
99 if (this.lastPanelHeight != panel.getBoundingClientRect().height) {
100 this.lastPanelHeight = panel.getBoundingClientRect().height;
101 panel.fireEvent('resize');
105 setInterval(checkResizeFn, 500);
109 setupHeaderEvents: function() {
110 this.currentHeaderAction = '';
111 this.canResize = false;
113 var resetElementBorderStyle = function(el, side) {
114 if (side === 'left' || side !== 'right') {
115 el.setStyle('border-left-style', '');
116 el.setStyle('border-left-color', '');
117 el.setStyle('border-left-width', '');
119 if (side === 'right' || side !== 'left') {
120 el.setStyle('border-right-style', '');
121 el.setStyle('border-right-color', '');
122 el.setStyle('border-right-width', '');
126 var mouseMoveFn = function(e) {
127 var brect = e.target.getBoundingClientRect();
128 var mouseXRelative = e.event.clientX - brect.left;
129 if (this.currentHeaderAction === '') {
130 if (brect.width - mouseXRelative < 5) {
131 this.resizeTh = e.target;
132 this.canResize = true;
133 e.target.getParent("tr").style.cursor = 'col-resize';
135 else if ((mouseXRelative < 5) && e.target.getPrevious('[class=""]')) {
136 this.resizeTh = e.target.getPrevious('[class=""]');
137 this.canResize = true;
138 e.target.getParent("tr").style.cursor = 'col-resize';
141 this.canResize = false;
142 e.target.getParent("tr").style.cursor = '';
145 if (this.currentHeaderAction === 'drag') {
146 var previousVisibleSibling = e.target.getPrevious('[class=""]');
147 var borderChangeElement = previousVisibleSibling;
148 var changeBorderSide = 'right';
150 if (mouseXRelative > brect.width / 2) {
151 borderChangeElement = e.target;
152 this.dropSide = 'right';
155 this.dropSide = 'left';
158 e.target.getParent("tr").style.cursor = 'move';
160 if (!previousVisibleSibling) { // right most column
161 borderChangeElement = e.target;
163 if (mouseXRelative <= brect.width / 2)
164 changeBorderSide = 'left';
167 borderChangeElement.setStyle('border-' + changeBorderSide + '-style', 'solid');
168 borderChangeElement.setStyle('border-' + changeBorderSide + '-color', '#e60');
169 borderChangeElement.setStyle('border-' + changeBorderSide + '-width', 'initial');
171 resetElementBorderStyle(borderChangeElement, changeBorderSide === 'right' ? 'left' : 'right');
173 borderChangeElement.getSiblings('[class=""]').each(function(el) {
174 resetElementBorderStyle(el);
177 this.lastHoverTh = e.target;
178 this.lastClientX = e.event.clientX;
181 var mouseOutFn = function(e) {
182 resetElementBorderStyle(e.target);
185 var onBeforeStart = function(el) {
187 this.currentHeaderAction = 'start';
188 this.dragMovement = false;
189 this.dragStartX = this.lastClientX;
192 var onStart = function(el, event) {
193 if (this.canResize) {
194 this.currentHeaderAction = 'resize';
195 this.startWidth = this.resizeTh.getStyle('width').toFloat();
198 this.currentHeaderAction = 'drag';
199 el.setStyle('background-color', '#C1D5E7');
203 var onDrag = function(el, event) {
204 if (this.currentHeaderAction === 'resize') {
205 var width = this.startWidth + (event.page.x - this.dragStartX);
208 this.columns[this.resizeTh.columnName].width = width;
209 this.updateColumn(this.resizeTh.columnName);
213 var onComplete = function(el, event) {
214 resetElementBorderStyle(this.lastHoverTh);
215 el.setStyle('background-color', '');
216 if (this.currentHeaderAction === 'resize')
217 localStorage.setItem('column_' + this.resizeTh.columnName + '_width_' + this.dynamicTableDivId, this.columns[this.resizeTh.columnName].width);
218 if ((this.currentHeaderAction === 'drag') && (el !== this.lastHoverTh)) {
219 this.saveColumnsOrder();
220 var val = localStorage.getItem('columns_order_' + this.dynamicTableDivId).split(',');
221 val.erase(el.columnName);
222 var pos = val.indexOf(this.lastHoverTh.columnName);
223 if (this.dropSide === 'right') ++pos;
224 val.splice(pos, 0, el.columnName);
225 localStorage.setItem('columns_order_' + this.dynamicTableDivId, val.join(','));
226 this.loadColumnsOrder();
227 this.updateTableHeaders();
228 while (this.tableBody.firstChild)
229 this.tableBody.removeChild(this.tableBody.firstChild);
230 this.updateTable(true);
232 if (this.currentHeaderAction === 'drag') {
233 resetElementBorderStyle(el);
234 el.getSiblings('[class=""]').each(function(el) {
235 resetElementBorderStyle(el);
238 this.currentHeaderAction = '';
241 var onCancel = function(el) {
242 this.currentHeaderAction = '';
243 this.setSortedColumn(el.columnName);
246 var ths = this.fixedTableHeader.getElements('th');
248 for (var i = 0; i < ths.length; ++i) {
250 th.addEvent('mousemove', mouseMoveFn);
251 th.addEvent('mouseout', mouseOutFn);
257 onBeforeStart: onBeforeStart,
260 onComplete: onComplete,
266 setupDynamicTableHeaderContextMenuClass: function() {
267 if (!DynamicTableHeaderContextMenuClass) {
268 DynamicTableHeaderContextMenuClass = new Class({
269 Extends: ContextMenu,
270 updateMenuItems: function() {
271 for (var i = 0; i < this.dynamicTable.columns.length; ++i) {
272 if (this.dynamicTable.columns[i].caption === '')
274 if (this.dynamicTable.columns[i].visible !== '0')
275 this.setItemChecked(this.dynamicTable.columns[i].name, true);
277 this.setItemChecked(this.dynamicTable.columns[i].name, false);
284 showColumn: function(columnName, show) {
285 this.columns[columnName].visible = show ? '1' : '0';
286 localStorage.setItem('column_' + columnName + '_visible_' + this.dynamicTableDivId, show ? '1' : '0');
287 this.updateColumn(columnName);
290 setupHeaderMenu: function() {
291 this.setupDynamicTableHeaderContextMenuClass();
293 var menuId = this.dynamicTableDivId + '_headerMenu';
295 var ul = new Element('ul', {
297 class: 'contextMenu scrollableMenu'
300 var createLi = function(columnName, text) {
301 var html = '<a href="#' + columnName + '" ><img src="images/qbt-theme/checked.svg"/>' + escapeHtml(text) + '</a>';
302 return new Element('li', {
309 var onMenuItemClicked = function(element, ref, action) {
310 this.showColumn(action, this.columns[action].visible === '0');
313 for (var i = 0; i < this.columns.length; ++i) {
314 var text = this.columns[i].caption;
317 ul.appendChild(createLi(this.columns[i].name, text));
318 actions[this.columns[i].name] = onMenuItemClicked;
321 ul.inject(document.body);
323 this.headerContextMenu = new DynamicTableHeaderContextMenuClass({
324 targets: '#' + this.dynamicTableFixedHeaderDivId + ' tr',
333 this.headerContextMenu.dynamicTable = this;
336 initColumns: function() {},
338 newColumn: function(name, style, caption, defaultWidth, defaultVisible) {
340 column['name'] = name;
341 column['visible'] = getLocalStorageItem('column_' + name + '_visible_' + this.dynamicTableDivId, defaultVisible ? '1' : '0');
342 column['force_hide'] = false;
343 column['caption'] = caption;
344 column['style'] = style;
345 column['width'] = getLocalStorageItem('column_' + name + '_width_' + this.dynamicTableDivId, defaultWidth);
346 column['dataProperties'] = [name];
347 column['getRowValue'] = function(row, pos) {
348 if (pos === undefined)
350 return row['full_data'][this.dataProperties[pos]];
352 column['compareRows'] = function(row1, row2) {
353 if (this.getRowValue(row1) < this.getRowValue(row2))
355 else if (this.getRowValue(row1) > this.getRowValue(row2))
359 column['updateTd'] = function(td, row) {
360 td.innerHTML = this.getRowValue(row);
362 column['onResize'] = null;
363 this.columns.push(column);
364 this.columns[name] = column;
366 this.hiddenTableHeader.appendChild(new Element('th'));
367 this.fixedTableHeader.appendChild(new Element('th'));
370 loadColumnsOrder: function() {
371 var columnsOrder = [];
372 var val = localStorage.getItem('columns_order_' + this.dynamicTableDivId);
373 if (val === null || val === undefined) return;
374 val.split(',').forEach(function(v) {
375 if ((v in this.columns) && (!columnsOrder.contains(v)))
376 columnsOrder.push(v);
379 for (var i = 0; i < this.columns.length; ++i)
380 if (!columnsOrder.contains(this.columns[i].name))
381 columnsOrder.push(this.columns[i].name);
383 for (i = 0; i < this.columns.length; ++i)
384 this.columns[i] = this.columns[columnsOrder[i]];
387 saveColumnsOrder: function() {
389 for (var i = 0; i < this.columns.length; ++i) {
392 val += this.columns[i].name;
394 localStorage.setItem('columns_order_' + this.dynamicTableDivId, val);
397 updateTableHeaders: function() {
398 this.updateHeader(this.hiddenTableHeader);
399 this.updateHeader(this.fixedTableHeader);
402 updateHeader: function(header) {
403 var ths = header.getElements('th');
405 for (var i = 0; i < ths.length; ++i) {
408 th.setAttribute('title', this.columns[i].caption);
409 th.innerHTML = this.columns[i].caption;
410 th.setAttribute('style', 'width: ' + this.columns[i].width + 'px;' + this.columns[i].style);
411 th.columnName = this.columns[i].name;
412 th.addClass('column_' + th.columnName);
413 if ((this.columns[i].visible == '0') || this.columns[i].force_hide)
414 th.addClass('invisible');
416 th.removeClass('invisible');
420 getColumnPos: function(columnName) {
421 for (var i = 0; i < this.columns.length; ++i)
422 if (this.columns[i].name == columnName)
427 updateColumn: function(columnName) {
428 var pos = this.getColumnPos(columnName);
429 var visible = ((this.columns[pos].visible != '0') && !this.columns[pos].force_hide);
430 var ths = this.hiddenTableHeader.getElements('th');
431 var fths = this.fixedTableHeader.getElements('th');
432 var trs = this.tableBody.getElements('tr');
433 var style = 'width: ' + this.columns[pos].width + 'px;' + this.columns[pos].style;
435 ths[pos].setAttribute('style', style);
436 fths[pos].setAttribute('style', style);
439 ths[pos].removeClass('invisible');
440 fths[pos].removeClass('invisible');
441 for (var i = 0; i < trs.length; ++i)
442 trs[i].getElements('td')[pos].removeClass('invisible');
445 ths[pos].addClass('invisible');
446 fths[pos].addClass('invisible');
447 for (var j = 0; j < trs.length; ++j)
448 trs[j].getElements('td')[pos].addClass('invisible');
450 if (this.columns[pos].onResize !== null) {
451 this.columns[pos].onResize(columnName);
455 getSortedColunn: function() {
456 return localStorage.getItem('sorted_column_' + this.dynamicTableDivId);
459 setSortedColumn: function(column) {
460 if (column != this.sortedColumn) {
461 var oldColumn = this.sortedColumn;
462 this.sortedColumn = column;
463 this.reverseSort = '0';
464 this.setSortedColumnIcon(column, oldColumn, false);
468 this.reverseSort = this.reverseSort == '0' ? '1' : '0';
469 this.setSortedColumnIcon(column, null, (this.reverseSort === '1'));
471 localStorage.setItem('sorted_column_' + this.dynamicTableDivId, column);
472 localStorage.setItem('reverse_sort_' + this.dynamicTableDivId, this.reverseSort);
473 this.updateTable(false);
476 setSortedColumnIcon: function(newColumn, oldColumn, isReverse) {
477 var getCol = function(headerDivId, colName) {
478 var colElem = $$("#" + headerDivId + " .column_" + colName);
479 if (colElem.length == 1)
484 var colElem = getCol(this.dynamicTableFixedHeaderDivId, newColumn);
485 if (colElem !== null) {
486 colElem.addClass('sorted');
488 colElem.addClass('reverse');
490 colElem.removeClass('reverse');
492 var oldColElem = getCol(this.dynamicTableFixedHeaderDivId, oldColumn);
493 if (oldColElem !== null) {
494 oldColElem.removeClass('sorted');
495 oldColElem.removeClass('reverse');
499 getSelectedRowId: function() {
500 if (this.selectedRows.length > 0)
501 return this.selectedRows[0];
505 isRowSelected: function(rowId) {
506 return this.selectedRows.contains(rowId);
510 if (!MUI.ieLegacySupport)
513 var trs = this.tableBody.getElements('tr');
514 trs.each(function(el, i) {
519 el.removeClass('alt');
524 selectAll: function() {
527 var trs = this.tableBody.getElements('tr');
528 for (var i = 0; i < trs.length; ++i) {
530 this.selectedRows.push(tr.rowId);
531 if (!tr.hasClass('selected'))
532 tr.addClass('selected');
536 deselectAll: function() {
537 this.selectedRows.empty();
540 selectRow: function(rowId) {
541 this.selectedRows.push(rowId);
543 this.onSelectedRowChanged();
546 deselectRow: function(rowId) {
547 this.selectedRows.erase(rowId);
549 this.onSelectedRowChanged();
552 selectRows: function(rowId1, rowId2) {
554 if (rowId1 === rowId2) {
555 this.selectRow(rowId1);
561 this.tableBody.getElements('tr').each(function(tr) {
562 if ((tr.rowId == rowId1) || (tr.rowId == rowId2)) {
564 that.selectedRows.push(tr.rowId);
567 that.selectedRows.push(tr.rowId);
571 this.onSelectedRowChanged();
574 reselectRows: function(rowIds) {
576 this.selectedRows = rowIds.slice();
577 this.tableBody.getElements('tr').each(function(tr) {
578 if (rowIds.indexOf(tr.rowId) > -1)
579 tr.addClass('selected');
583 setRowClass: function() {
585 this.tableBody.getElements('tr').each(function(tr) {
586 if (that.isRowSelected(tr.rowId))
587 tr.addClass('selected');
589 tr.removeClass('selected');
593 onSelectedRowChanged: function() {},
595 updateRowData: function(data) {
596 var rowId = data['rowId'];
599 if (!this.rows.has(rowId)) {
601 this.rows.set(rowId, row);
602 row['full_data'] = {};
603 row['rowId'] = rowId;
606 row = this.rows.get(rowId);
611 row['full_data'][x] = data[x];
614 getFilteredAndSortedRows: function() {
615 var filteredRows = [];
617 var rows = this.rows.getValues();
619 for (var i = 0; i < rows.length; ++i) {
620 filteredRows.push(rows[i]);
621 filteredRows[rows[i].rowId] = rows[i];
624 filteredRows.sort(function(row1, row2) {
625 var column = this.columns[this.sortedColumn];
626 var res = column.compareRows(row1, row2);
627 if (this.reverseSort == '0')
635 getTrByRowId: function(rowId) {
636 var trs = this.tableBody.getElements('tr');
637 for (var i = 0; i < trs.length; ++i)
638 if (trs[i].rowId == rowId)
643 updateTable: function(fullUpdate) {
644 if (fullUpdate === undefined)
647 var rows = this.getFilteredAndSortedRows();
649 for (var i = 0; i < this.selectedRows.length; ++i)
650 if (!(this.selectedRows[i] in rows)) {
651 this.selectedRows.splice(i, 1);
655 var trs = this.tableBody.getElements('tr');
657 for (var rowPos = 0; rowPos < rows.length; ++rowPos) {
658 var rowId = rows[rowPos]['rowId'];
659 var tr_found = false;
660 for (var j = rowPos; j < trs.length; ++j)
661 if (trs[j]['rowId'] == rowId) {
665 trs[j].inject(trs[rowPos], 'before');
668 trs.splice(rowPos, 0, tmpTr);
671 if (tr_found) // row already exists in the table
672 this.updateRow(trs[rowPos], fullUpdate);
673 else { // else create a new row in the table
674 var tr = new Element('tr');
676 tr['rowId'] = rows[rowPos]['rowId'];
679 tr.addEvent('contextmenu', function(e) {
680 if (!this._this.isRowSelected(this.rowId)) {
681 this._this.deselectAll();
682 this._this.selectRow(this.rowId);
686 tr.addEvent('click', function(e) {
688 if (e.control || e.meta) {
689 // CTRL/CMD ⌘ key was pressed
690 if (this._this.isRowSelected(this.rowId))
691 this._this.deselectRow(this.rowId);
693 this._this.selectRow(this.rowId);
695 else if (e.shift && (this._this.selectedRows.length == 1)) {
696 // Shift key was pressed
697 this._this.selectRows(this._this.getSelectedRowId(), this.rowId);
701 this._this.deselectAll();
702 this._this.selectRow(this.rowId);
709 for (var k = 0; k < this.columns.length; ++k) {
710 var td = new Element('td');
711 if ((this.columns[k].visible == '0') || this.columns[k].force_hide)
712 td.addClass('invisible');
717 if (rowPos >= trs.length) {
718 tr.inject(this.tableBody);
722 tr.inject(trs[rowPos], 'before');
723 trs.splice(rowPos, 0, tr);
726 // Update context menu
727 if (this.contextMenu)
728 this.contextMenu.addTarget(tr);
730 this.updateRow(tr, true);
734 rowPos = rows.length;
736 while ((rowPos < trs.length) && (trs.length > 0)) {
737 trs[trs.length - 1].dispose();
742 setupTr: function(tr) {},
744 updateRow: function(tr, fullUpdate) {
745 var row = this.rows.get(tr.rowId);
746 var data = row[fullUpdate ? 'full_data' : 'data'];
748 var tds = tr.getElements('td');
749 for (var i = 0; i < this.columns.length; ++i) {
750 if (data.hasOwnProperty(this.columns[i].dataProperties[0]))
751 this.columns[i].updateTd(tds[i], row);
756 removeRow: function(rowId) {
757 this.selectedRows.erase(rowId);
758 var tr = this.getTrByRowId(rowId);
761 this.rows.erase(rowId);
770 var trs = this.tableBody.getElements('tr');
771 while (trs.length > 0) {
772 trs[trs.length - 1].dispose();
777 selectedRowsIds: function() {
778 return this.selectedRows.slice();
781 getRowIds: function() {
782 return this.rows.getKeys();
786 var TorrentsTable = new Class({
787 Extends: DynamicTable,
789 initColumns: function() {
790 this.newColumn('priority', '', '#', 30, true);
791 this.newColumn('state_icon', 'cursor: default', '', 22, true);
792 this.newColumn('name', '', 'QBT_TR(Name)QBT_TR[CONTEXT=TransferListModel]', 200, true);
793 this.newColumn('size', '', 'QBT_TR(Size)QBT_TR[CONTEXT=TransferListModel]', 100, true);
794 this.newColumn('total_size', '', 'QBT_TR(Total Size)QBT_TR[CONTEXT=TransferListModel]', 100, false);
795 this.newColumn('progress', '', 'QBT_TR(Done)QBT_TR[CONTEXT=TransferListModel]', 85, true);
796 this.newColumn('status', '', 'QBT_TR(Status)QBT_TR[CONTEXT=TransferListModel]', 100, true);
797 this.newColumn('num_seeds', '', 'QBT_TR(Seeds)QBT_TR[CONTEXT=TransferListModel]', 100, true);
798 this.newColumn('num_leechs', '', 'QBT_TR(Peers)QBT_TR[CONTEXT=TransferListModel]', 100, true);
799 this.newColumn('dlspeed', '', 'QBT_TR(Down Speed)QBT_TR[CONTEXT=TransferListModel]', 100, true);
800 this.newColumn('upspeed', '', 'QBT_TR(Up Speed)QBT_TR[CONTEXT=TransferListModel]', 100, true);
801 this.newColumn('eta', '', 'QBT_TR(ETA)QBT_TR[CONTEXT=TransferListModel]', 100, true);
802 this.newColumn('ratio', '', 'QBT_TR(Ratio)QBT_TR[CONTEXT=TransferListModel]', 100, true);
803 this.newColumn('category', '', 'QBT_TR(Category)QBT_TR[CONTEXT=TransferListModel]', 100, true);
804 this.newColumn('tags', '', 'QBT_TR(Tags)QBT_TR[CONTEXT=TransferListModel]', 100, true);
805 this.newColumn('added_on', '', 'QBT_TR(Added On)QBT_TR[CONTEXT=TransferListModel]', 100, true);
806 this.newColumn('completion_on', '', 'QBT_TR(Completed On)QBT_TR[CONTEXT=TransferListModel]', 100, false);
807 this.newColumn('tracker', '', 'QBT_TR(Tracker)QBT_TR[CONTEXT=TransferListModel]', 100, false);
808 this.newColumn('dl_limit', '', 'QBT_TR(Down Limit)QBT_TR[CONTEXT=TransferListModel]', 100, false);
809 this.newColumn('up_limit', '', 'QBT_TR(Up Limit)QBT_TR[CONTEXT=TransferListModel]', 100, false);
810 this.newColumn('downloaded', '', 'QBT_TR(Downloaded)QBT_TR[CONTEXT=TransferListModel]', 100, false);
811 this.newColumn('uploaded', '', 'QBT_TR(Uploaded)QBT_TR[CONTEXT=TransferListModel]', 100, false);
812 this.newColumn('downloaded_session', '', 'QBT_TR(Session Download)QBT_TR[CONTEXT=TransferListModel]', 100, false);
813 this.newColumn('uploaded_session', '', 'QBT_TR(Session Upload)QBT_TR[CONTEXT=TransferListModel]', 100, false);
814 this.newColumn('amount_left', '', 'QBT_TR(Remaining)QBT_TR[CONTEXT=TransferListModel]', 100, false);
815 this.newColumn('time_active', '', 'QBT_TR(Time Active)QBT_TR[CONTEXT=TransferListModel]', 100, false);
816 this.newColumn('save_path', '', 'QBT_TR(Save path)QBT_TR[CONTEXT=TransferListModel]', 100, false);
817 this.newColumn('completed', '', 'QBT_TR(Completed)QBT_TR[CONTEXT=TransferListModel]', 100, false);
818 this.newColumn('max_ratio', '', 'QBT_TR(Ratio Limit)QBT_TR[CONTEXT=TransferListModel]', 100, false);
819 this.newColumn('seen_complete', '', 'QBT_TR(Last Seen Complete)QBT_TR[CONTEXT=TransferListModel]', 100, false);
820 this.newColumn('last_activity', '', 'QBT_TR(Last Activity)QBT_TR[CONTEXT=TransferListModel]', 100, false);
822 this.columns['state_icon'].onclick = '';
823 this.columns['state_icon'].dataProperties[0] = 'state';
825 this.columns['num_seeds'].dataProperties.push('num_complete');
827 this.columns['num_leechs'].dataProperties.push('num_incomplete');
829 this.initColumnsFunctions();
832 initColumnsFunctions: function() {
835 this.columns['state_icon'].updateTd = function(td, row) {
836 var state = this.getRowValue(row);
841 state = "downloading";
861 case "queuedForChecking":
862 case "checkingResumeData":
874 var img_path = 'images/skin/' + state + '.svg';
876 if (td.getChildren('img').length) {
877 var img = td.getChildren('img')[0];
878 if (img.src.indexOf(img_path) < 0)
879 img.set('src', img_path);
882 td.adopt(new Element('img', {
889 this.columns['status'].updateTd = function(td, row) {
890 var state = this.getRowValue(row);
896 status = "QBT_TR(Downloading)QBT_TR[CONTEXT=TransferListDelegate]";
899 status = "QBT_TR(Stalled)QBT_TR[CONTEXT=TransferListDelegate]";
902 status = "QBT_TR(Downloading metadata)QBT_TR[CONTEXT=TransferListDelegate]";
905 status = "QBT_TR([F] Downloading)QBT_TR[CONTEXT=TransferListDelegate]";
908 status = "QBT_TR(Allocating)QBT_TR[CONTEXT=TransferListDelegate]";
912 status = "QBT_TR(Seeding)QBT_TR[CONTEXT=TransferListDelegate]";
915 status = "QBT_TR([F] Seeding)QBT_TR[CONTEXT=TransferListDelegate]";
919 status = "QBT_TR(Queued)QBT_TR[CONTEXT=TransferListDelegate]";
923 status = "QBT_TR(Checking)QBT_TR[CONTEXT=TransferListDelegate]";
925 case "queuedForChecking":
926 status = "QBT_TR(Queued for checking)QBT_TR[CONTEXT=TransferListDelegate]";
928 case "checkingResumeData":
929 status = "QBT_TR(Checking resume data)QBT_TR[CONTEXT=TransferListDelegate]";
932 status = "QBT_TR(Paused)QBT_TR[CONTEXT=TransferListDelegate]";
935 status = "QBT_TR(Completed)QBT_TR[CONTEXT=TransferListDelegate]";
938 status = "QBT_TR(Moving)QBT_TR[CONTEXT=TransferListDelegate]";
941 status = "QBT_TR(Missing Files)QBT_TR[CONTEXT=TransferListDelegate]";
944 status = "QBT_TR(Errored)QBT_TR[CONTEXT=TransferListDelegate]";
947 status = "QBT_TR(Unknown)QBT_TR[CONTEXT=HttpServer]";
950 td.set('html', status);
954 this.columns['priority'].updateTd = function(td, row) {
955 var priority = this.getRowValue(row);
956 td.set('html', priority < 1 ? '*' : priority);
959 this.columns['priority'].compareRows = function(row1, row2) {
960 var row1_val = this.getRowValue(row1);
961 var row2_val = this.getRowValue(row2);
966 if (row1_val < row2_val)
968 else if (row1_val > row2_val)
974 this.columns['name'].updateTd = function(td, row) {
975 td.set('html', escapeHtml(this.getRowValue(row)));
977 this.columns['category'].updateTd = this.columns['name'].updateTd;
980 this.columns['size'].updateTd = function(td, row) {
981 var size = this.getRowValue(row);
982 td.set('html', friendlyUnit(size, false));
986 this.columns['progress'].updateTd = function(td, row) {
987 var progress = this.getRowValue(row);
988 var progressFormated = (progress * 100).round(1);
989 if (progressFormated == 100.0 && progress != 1.0)
990 progressFormated = 99.9;
992 if (td.getChildren('div').length) {
993 var div = td.getChildren('div')[0];
996 div.setWidth(ProgressColumnWidth - 5);
998 if (div.getValue() != progressFormated)
999 div.setValue(progressFormated);
1002 if (ProgressColumnWidth < 0)
1003 ProgressColumnWidth = td.offsetWidth;
1004 td.adopt(new ProgressBar(progressFormated.toFloat(), {
1005 'width': ProgressColumnWidth - 5
1011 this.columns['progress'].onResize = function(columnName) {
1012 var pos = this.getColumnPos(columnName);
1013 var trs = this.tableBody.getElements('tr');
1014 ProgressColumnWidth = -1;
1015 for (var i = 0; i < trs.length; ++i) {
1016 var td = trs[i].getElements('td')[pos];
1017 if (ProgressColumnWidth < 0)
1018 ProgressColumnWidth = td.offsetWidth;
1020 this.columns[columnName].updateTd(td, this.rows.get(trs[i].rowId));
1025 this.columns['num_seeds'].updateTd = function(td, row) {
1026 var num_seeds = this.getRowValue(row, 0);
1027 var num_complete = this.getRowValue(row, 1);
1028 var html = num_seeds;
1029 if (num_complete != -1)
1030 html += ' (' + num_complete + ')';
1031 td.set('html', html);
1033 this.columns['num_seeds'].compareRows = function(row1, row2) {
1034 var num_seeds1 = this.getRowValue(row1, 0);
1035 var num_complete1 = this.getRowValue(row1, 1);
1037 var num_seeds2 = this.getRowValue(row2, 0);
1038 var num_complete2 = this.getRowValue(row2, 1);
1040 if (num_complete1 < num_complete2)
1042 else if (num_complete1 > num_complete2)
1044 else if (num_seeds1 < num_seeds2)
1046 else if (num_seeds1 > num_seeds2)
1052 this.columns['num_leechs'].updateTd = this.columns['num_seeds'].updateTd;
1053 this.columns['num_leechs'].compareRows = this.columns['num_seeds'].compareRows;
1056 this.columns['dlspeed'].updateTd = function(td, row) {
1057 var speed = this.getRowValue(row);
1058 td.set('html', friendlyUnit(speed, true));
1062 this.columns['upspeed'].updateTd = this.columns['dlspeed'].updateTd;
1065 this.columns['eta'].updateTd = function(td, row) {
1066 var eta = this.getRowValue(row);
1067 td.set('html', friendlyDuration(eta));
1071 this.columns['ratio'].updateTd = function(td, row) {
1072 var ratio = this.getRowValue(row);
1077 html = (Math.floor(100 * ratio) / 100).toFixed(2); //Don't round up
1078 td.set('html', html);
1082 this.columns['tags'].updateTd = this.columns['name'].updateTd;
1085 this.columns['added_on'].updateTd = function(td, row) {
1086 var date = new Date(this.getRowValue(row) * 1000).toLocaleString();
1087 td.set('html', date);
1091 this.columns['completion_on'].updateTd = function(td, row) {
1092 var val = this.getRowValue(row);
1093 if (val === 0xffffffff || val < 0)
1096 var date = new Date(this.getRowValue(row) * 1000).toLocaleString();
1097 td.set('html', date);
1102 this.columns['seen_complete'].updateTd = this.columns['completion_on'].updateTd;
1104 // dl_limit, up_limit
1105 this.columns['dl_limit'].updateTd = function(td, row) {
1106 var speed = this.getRowValue(row);
1108 td.set('html', '∞');
1110 td.set('html', friendlyUnit(speed, true));
1113 this.columns['up_limit'].updateTd = this.columns['dl_limit'].updateTd;
1115 // downloaded, uploaded, downloaded_session, uploaded_session, amount_left, completed, total_size
1116 this.columns['downloaded'].updateTd = this.columns['size'].updateTd;
1117 this.columns['uploaded'].updateTd = this.columns['size'].updateTd;
1118 this.columns['downloaded_session'].updateTd = this.columns['size'].updateTd;
1119 this.columns['uploaded_session'].updateTd = this.columns['size'].updateTd;
1120 this.columns['amount_left'].updateTd = this.columns['size'].updateTd;
1121 this.columns['amount_left'].updateTd = this.columns['size'].updateTd;
1122 this.columns['completed'].updateTd = this.columns['size'].updateTd;
1123 this.columns['total_size'].updateTd = this.columns['size'].updateTd;
1125 // save_path, tracker
1126 this.columns['save_path'].updateTd = this.columns['name'].updateTd;
1127 this.columns['tracker'].updateTd = this.columns['name'].updateTd;
1130 this.columns['max_ratio'].updateTd = this.columns['ratio'].updateTd;
1133 this.columns['last_activity'].updateTd = function(td, row) {
1134 var val = this.getRowValue(row);
1136 td.set('html', '∞');
1138 td.set('html', 'QBT_TR(%1 ago)QBT_TR[CONTEXT=TransferListDelegate]'.replace('%1', friendlyDuration((new Date()) / 1000 - val)));
1142 this.columns['time_active'].updateTd = function(td, row) {
1143 var time = this.getRowValue(row);
1144 td.set('html', friendlyDuration(time));
1148 applyFilter: function(row, filterName, categoryHash, filterTerms) {
1149 var state = row['full_data'].state;
1150 var name = row['full_data'].name.toLowerCase();
1151 var inactive = false;
1154 switch (filterName) {
1156 if (state != 'downloading' && !~state.indexOf('DL'))
1160 if (state != 'uploading' && state != 'forcedUP' && state != 'stalledUP' && state != 'queuedUP' && state != 'checkingUP')
1164 if (state != 'uploading' && !~state.indexOf('UP'))
1168 if (!~state.indexOf('paused'))
1172 if (~state.indexOf('paused'))
1179 if (state == 'stalledDL')
1180 r = (row['full_data'].upspeed > 0);
1182 r = state == 'metaDL' || state == 'downloading' || state == 'forcedDL' || state == 'uploading' || state == 'forcedUP';
1187 if (state != 'error' && state != "unknown" && state != "missingFiles")
1192 var categoryHashInt = parseInt(categoryHash);
1193 if (!isNaN(categoryHashInt)) {
1194 switch (categoryHashInt) {
1195 case CATEGORIES_ALL:
1196 break; // do nothing
1197 case CATEGORIES_UNCATEGORIZED:
1198 if (row['full_data'].category.length !== 0)
1200 break; // do nothing
1202 if (categoryHashInt !== genHash(row['full_data'].category))
1208 for (var i = 0; i < filterTerms.length; ++i) {
1209 if (name.indexOf(filterTerms[i]) === -1)
1217 getFilteredTorrentsNumber: function(filterName, categoryHash) {
1219 var rows = this.rows.getValues();
1221 for (var i = 0; i < rows.length; ++i)
1222 if (this.applyFilter(rows[i], filterName, categoryHash, null)) ++cnt;
1226 getFilteredTorrentsHashes: function(filterName, categoryHash) {
1227 var rowsHashes = [];
1228 var rows = this.rows.getValues();
1230 for (var i = 0; i < rows.length; ++i)
1231 if (this.applyFilter(rows[i], filterName, categoryHash, null))
1232 rowsHashes.push(rows[i]['rowId']);
1237 getFilteredAndSortedRows: function() {
1238 var filteredRows = [];
1240 var rows = this.rows.getValues();
1241 var filterText = $('torrentsFilterInput').value.trim().toLowerCase();
1242 var filterTerms = (filterText.length > 0) ? filterText.split(" ") : null;
1244 for (var i = 0; i < rows.length; ++i) {
1245 if (this.applyFilter(rows[i], selected_filter, selected_category, filterTerms)) {
1246 filteredRows.push(rows[i]);
1247 filteredRows[rows[i].rowId] = rows[i];
1251 filteredRows.sort(function(row1, row2) {
1252 var column = this.columns[this.sortedColumn];
1253 var res = column.compareRows(row1, row2);
1254 if (this.reverseSort == '0')
1259 return filteredRows;
1262 setupTr: function(tr) {
1263 tr.addEvent('dblclick', function(e) {
1265 this._this.deselectAll();
1266 this._this.selectRow(this.rowId);
1267 var row = this._this.rows.get(this.rowId);
1268 var state = row['full_data'].state;
1269 if (~state.indexOf('paused'))
1275 tr.addClass("torrentsTableContextMenuTarget");
1278 getCurrentTorrentHash: function() {
1279 return this.getSelectedRowId();
1282 onSelectedRowChanged: function() {
1283 updatePropertiesPanel();
1287 var TorrentPeersTable = new Class({
1288 Extends: DynamicTable,
1290 initColumns: function() {
1291 this.newColumn('country', '', 'QBT_TR(Country)QBT_TR[CONTEXT=PeerListWidget]', 22, true);
1292 this.newColumn('ip', '', 'QBT_TR(IP)QBT_TR[CONTEXT=PeerListWidget]', 80, true);
1293 this.newColumn('port', '', 'QBT_TR(Port)QBT_TR[CONTEXT=PeerListWidget]', 35, true);
1294 this.newColumn('connection', '', 'QBT_TR(Connection)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1295 this.newColumn('flags', '', 'QBT_TR(Flags)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1296 this.newColumn('client', '', 'QBT_TR(Client)QBT_TR[CONTEXT=PeerListWidget]', 140, true);
1297 this.newColumn('progress', '', 'QBT_TR(Progress)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1298 this.newColumn('dl_speed', '', 'QBT_TR(Down Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1299 this.newColumn('up_speed', '', 'QBT_TR(Up Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1300 this.newColumn('downloaded', '', 'QBT_TR(Downloaded)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1301 this.newColumn('uploaded', '', 'QBT_TR(Uploaded)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
1302 this.newColumn('relevance', '', 'QBT_TR(Relevance)QBT_TR[CONTEXT=PeerListWidget]', 30, true);
1303 this.newColumn('files', '', 'QBT_TR(Files)QBT_TR[CONTEXT=PeerListWidget]', 100, true);
1305 this.columns['country'].dataProperties.push('country_code');
1306 this.columns['flags'].dataProperties.push('flags_desc');
1307 this.initColumnsFunctions();
1310 initColumnsFunctions: function() {
1314 this.columns['country'].updateTd = function(td, row) {
1315 var country = this.getRowValue(row, 0);
1316 var country_code = this.getRowValue(row, 1);
1318 if (!country_code) {
1319 if (td.getChildren('img').length)
1320 td.getChildren('img')[0].dispose();
1324 var img_path = 'images/flags/' + country_code + '.svg';
1326 if (td.getChildren('img').length) {
1327 var img = td.getChildren('img')[0];
1328 img.set('src', img_path);
1329 img.set('class', 'flags');
1330 img.set('alt', country);
1331 img.set('title', country);
1334 td.adopt(new Element('img', {
1344 this.columns['ip'].compareRows = function(row1, row2) {
1345 var ip1 = this.getRowValue(row1);
1346 var ip2 = this.getRowValue(row2);
1348 var a = ip1.split(".");
1349 var b = ip2.split(".");
1351 for (var i = 0; i < 4; ++i) {
1359 // progress, relevance
1361 this.columns['progress'].updateTd = function(td, row) {
1362 var progress = this.getRowValue(row);
1363 var progressFormated = (progress * 100).round(1);
1364 if (progressFormated == 100.0 && progress != 1.0)
1365 progressFormated = 99.9;
1366 progressFormated += "%";
1367 td.set('html', progressFormated);
1370 this.columns['relevance'].updateTd = this.columns['progress'].updateTd;
1372 // dl_speed, up_speed
1374 this.columns['dl_speed'].updateTd = function(td, row) {
1375 var speed = this.getRowValue(row);
1379 td.set('html', friendlyUnit(speed, true));
1382 this.columns['up_speed'].updateTd = this.columns['dl_speed'].updateTd;
1384 // downloaded, uploaded
1386 this.columns['downloaded'].updateTd = function(td, row) {
1387 var downloaded = this.getRowValue(row);
1388 td.set('html', friendlyUnit(downloaded, false));
1391 this.columns['uploaded'].updateTd = this.columns['downloaded'].updateTd;
1395 this.columns['flags'].updateTd = function(td, row) {
1396 td.innerHTML = this.getRowValue(row, 0);
1397 td.title = this.getRowValue(row, 1);
1402 this.columns['files'].updateTd = function(td, row) {
1403 td.innerHTML = escapeHtml(this.getRowValue(row, 0).replace('\n', ';'));
1404 td.title = escapeHtml(this.getRowValue(row, 0));
1410 var SearchResultsTable = new Class({
1411 Extends: DynamicTable,
1413 initColumns: function() {
1414 this.newColumn('fileName', '', 'QBT_TR(Name)QBT_TR[CONTEXT=SearchResultsTable]', 500, true);
1415 this.newColumn('fileSize', '', 'QBT_TR(Size)QBT_TR[CONTEXT=SearchResultsTable]', 100, true);
1416 this.newColumn('nbSeeders', '', 'QBT_TR(Seeders)QBT_TR[CONTEXT=SearchResultsTable]', 100, true);
1417 this.newColumn('nbLeechers', '', 'QBT_TR(Leechers)QBT_TR[CONTEXT=SearchResultsTable]', 100, true);
1418 this.newColumn('siteUrl', '', 'QBT_TR(Search engine)QBT_TR[CONTEXT=SearchResultsTable]', 250, true);
1420 this.initColumnsFunctions();
1423 initColumnsFunctions: function() {
1424 var displayText = function(td, row) {
1425 var value = this.getRowValue(row);
1426 td.set('html', escapeHtml(value));
1428 var displaySize = function(td, row) {
1429 var size = this.getRowValue(row);
1430 td.set('html', friendlyUnit(size, false));
1432 var displayNum = function(td, row) {
1433 var value = escapeHtml(this.getRowValue(row));
1434 td.set('html', (value === "-1") ? "Unknown" : value);
1437 this.columns['fileName'].updateTd = displayText;
1438 this.columns['fileSize'].updateTd = displaySize;
1439 this.columns['nbSeeders'].updateTd = displayNum;
1440 this.columns['nbLeechers'].updateTd = displayNum;
1441 this.columns['siteUrl'].updateTd = displayText;
1444 getFilteredAndSortedRows: function() {
1445 var containsAll = function(text, searchTerms) {
1446 text = text.toLowerCase();
1447 for (var i = 0; i < searchTerms.length; ++i) {
1448 if (text.indexOf(searchTerms[i].toLowerCase()) === -1)
1455 var getSizeFilters = function() {
1456 var minSize = (searchSizeFilter.min > 0.00) ? (searchSizeFilter.min * Math.pow(1024, searchSizeFilter.minUnit)) : 0.00;
1457 var maxSize = (searchSizeFilter.max > 0.00) ? (searchSizeFilter.max * Math.pow(1024, searchSizeFilter.maxUnit)) : 0.00;
1459 if ((minSize > maxSize) && (maxSize > 0.00)) {
1471 var getSeedsFilters = function() {
1472 var minSeeds = (searchSeedsFilter.min > 0) ? searchSeedsFilter.min : 0;
1473 var maxSeeds = (searchSeedsFilter.max > 0) ? searchSeedsFilter.max : 0;
1475 if ((minSeeds > maxSeeds) && (maxSeeds > 0)) {
1477 minSeeds = maxSeeds;
1487 var filteredRows = [];
1488 var rows = this.rows.getValues();
1489 var searchTerms = searchPattern.toLowerCase().split(" ");
1490 var filterTerms = searchFilterPattern.toLowerCase().split(" ");
1491 var sizeFilters = getSizeFilters();
1492 var seedsFilters = getSeedsFilters();
1493 var searchInTorrentName = $('searchInTorrentName').get('value') === "names";
1495 if (searchInTorrentName || filterTerms.length || (searchSizeFilter.min > 0.00) || (searchSizeFilter.max > 0.00)) {
1496 for (var i = 0; i < rows.length; ++i) {
1499 if (searchInTorrentName && !containsAll(row.full_data.fileName, searchTerms)) continue;
1500 if (filterTerms.length && !containsAll(row.full_data.fileName, filterTerms)) continue;
1501 if ((sizeFilters.min > 0.00) && (row.full_data.fileSize < sizeFilters.min)) continue;
1502 if ((sizeFilters.max > 0.00) && (row.full_data.fileSize > sizeFilters.max)) continue;
1503 if ((seedsFilters.min > 0) && (row.full_data.nbSeeders < seedsFilters.min)) continue;
1504 if ((seedsFilters.max > 0) && (row.full_data.nbSeeders > seedsFilters.max)) continue;
1506 filteredRows.push(row);
1510 filteredRows = rows;
1513 filteredRows.sort(function(row1, row2) {
1514 var column = this.columns[this.sortedColumn];
1515 var res = column.compareRows(row1, row2);
1516 if (this.reverseSort == '0')
1522 return filteredRows;
1525 setupTr: function(tr) {
1526 tr.addClass("searchTableRow");
1530 var SearchPluginsTable = new Class({
1531 Extends: DynamicTable,
1533 initColumns: function() {
1534 this.newColumn('fullName', '', 'QBT_TR(Name)QBT_TR[CONTEXT=SearchPluginsTable]', 175, true);
1535 this.newColumn('version', '', 'QBT_TR(Version)QBT_TR[CONTEXT=SearchPluginsTable]', 100, true);
1536 this.newColumn('url', '', 'QBT_TR(Url)QBT_TR[CONTEXT=SearchPluginsTable]', 175, true);
1537 this.newColumn('enabled', '', 'QBT_TR(Enabled)QBT_TR[CONTEXT=SearchPluginsTable]', 100, true);
1539 this.initColumnsFunctions();
1542 initColumnsFunctions: function() {
1543 var displayText = function(td, row) {
1544 var value = this.getRowValue(row);
1545 td.set('html', escapeHtml(value));
1548 this.columns['fullName'].updateTd = displayText;
1549 this.columns['version'].updateTd = displayText;
1550 this.columns['url'].updateTd = displayText;
1551 this.columns['enabled'].updateTd = function(td, row) {
1552 var value = this.getRowValue(row);
1554 td.set('html', "Yes");
1555 td.getParent("tr").addClass("green");
1556 td.getParent("tr").removeClass("red");
1559 td.set('html', "No");
1560 td.getParent("tr").addClass("red");
1561 td.getParent("tr").removeClass("green");
1566 setupTr: function(tr) {
1567 tr.addClass("searchPluginsTableRow");
1571 var TorrentTrackersTable = new Class({
1572 Extends: DynamicTable,
1574 initColumns: function() {
1575 this.newColumn('tier', '', 'QBT_TR(#)QBT_TR[CONTEXT=TrackerListWidget]', 35, true);
1576 this.newColumn('url', '', 'QBT_TR(URL)QBT_TR[CONTEXT=TrackerListWidget]', 250, true);
1577 this.newColumn('status', '', 'QBT_TR(Status)QBT_TR[CONTEXT=TrackerListWidget]', 125, true);
1578 this.newColumn('peers', '', 'QBT_TR(Peers)QBT_TR[CONTEXT=TrackerListWidget]', 75, true);
1579 this.newColumn('seeds', '', 'QBT_TR(Seeds)QBT_TR[CONTEXT=TrackerListWidget]', 75, true);
1580 this.newColumn('leeches', '', 'QBT_TR(Leeches)QBT_TR[CONTEXT=TrackerListWidget]', 75, true);
1581 this.newColumn('downloaded', '', 'QBT_TR(Downloaded)QBT_TR[CONTEXT=TrackerListWidget]', 100, true);
1582 this.newColumn('message', '', 'QBT_TR(Message)QBT_TR[CONTEXT=TrackerListWidget]', 250, true);
1586 var TorrentFilesTable = new Class({
1587 Extends: DynamicTable,
1589 initColumns: function() {
1590 this.newColumn('checked', '', '', 50, true);
1591 this.newColumn('name', '', 'QBT_TR(Name)QBT_TR[CONTEXT=TrackerListWidget]', 300, true);
1592 this.newColumn('size', '', 'QBT_TR(Size)QBT_TR[CONTEXT=TrackerListWidget]', 75, true);
1593 this.newColumn('progress', '', 'QBT_TR(Progress)QBT_TR[CONTEXT=TrackerListWidget]', 100, true);
1594 this.newColumn('priority', '', 'QBT_TR(Download Priority)QBT_TR[CONTEXT=TrackerListWidget]', 150, true);
1595 this.newColumn('remaining', '', 'QBT_TR(Remaining)QBT_TR[CONTEXT=TrackerListWidget]', 75, true);
1596 this.newColumn('availability', '', 'QBT_TR(Availability)QBT_TR[CONTEXT=TrackerListWidget]', 75, true);
1598 this.initColumnsFunctions();
1601 initColumnsFunctions: function() {
1602 var displaySize = function(td, row) {
1603 var size = this.getRowValue(row);
1604 td.set('html', friendlyUnit(size, false));
1606 var displayPercentage = function(td, row) {
1607 var value = this.getRowValue(row);
1608 td.set('html', friendlyPercentage(value));
1611 this.columns['checked'].updateTd = function(td, row) {
1613 var value = this.getRowValue(row);
1615 if (isDownloadCheckboxExists(id)) {
1616 updateDownloadCheckbox(id, value);
1619 var treeImg = new Element('img', {
1620 src: 'images/L.gif',
1621 style: 'margin-bottom: -2px'
1623 td.adopt(treeImg, createDownloadCheckbox(row.rowId, value));
1627 this.columns['size'].updateTd = displaySize;
1629 this.columns['progress'].updateTd = function(td, row) {
1631 var value = this.getRowValue(row);
1633 var progressBar = $('pbf_' + id);
1634 if (progressBar === null) {
1635 td.adopt(new ProgressBar(value.toFloat(), {
1641 progressBar.setValue(value.toFloat());
1645 this.columns['priority'].updateTd = function(td, row) {
1647 var value = this.getRowValue(row);
1649 if (isPriorityComboExists(id))
1650 updatePriorityCombo(id, value);
1652 td.adopt(createPriorityCombo(id, value));
1655 this.columns['remaining'].updateTd = displaySize;
1656 this.columns['availability'].updateTd = displayPercentage;
1660 /*************************************************************/