diff options
Diffstat (limited to 'abs/core/local-website/htdocs/linhes/js/table_sort.js')
-rw-r--r-- | abs/core/local-website/htdocs/linhes/js/table_sort.js | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/abs/core/local-website/htdocs/linhes/js/table_sort.js b/abs/core/local-website/htdocs/linhes/js/table_sort.js new file mode 100644 index 0000000..26208b1 --- /dev/null +++ b/abs/core/local-website/htdocs/linhes/js/table_sort.js @@ -0,0 +1,569 @@ +/** + * Javascript Table sorting lib + * + * @url $URL: http://cvs.mythtv.org/svn/branches/release-0-22-fixes/mythplugins/mythweb/js/table_sort.js $ + * @date $Date: 2008-06-22 02:21:08 +0000 (Sun, 22 Jun 2008) $ + * @version $Revision: 17560 $ + * @author $Author: kormoc $ + * @license LGPL + * +/**/ + +document.observe('dom:loaded', function () { +// Not a dom v3 supported browser? + if (typeof document.body.textContent == 'undefined') { + // Let's work around for IE + if (typeof document.body.innerText != 'undefined') { + HTMLElement.prototype.__defineGetter__('textContent', function () { + return this.innerText; + }); + } + // And now Safari + else { + HTMLElement.prototype.__defineGetter__('textContent', function () { + return this.innerHTML; + }); + } + } +}); + +var SortableTables = { + tables: [], + callback_presort: null, + callback_postsort: null, + callback_preload: null, + callback_postload: null, + + currently_sorting_table: null, + + start: function () { + $$('table[sortable=true]').each(SortableTables.setupTable); + }, + + setupTable: function (table) { + if (this.callback_preload) + this.callback_preload(table); + // We require a unique id + if (table.id == '' || ( this.tables && this.tables[table.id] )) + return; + SortableTables.tables[table.id] = new SortableTable(table); + if (this.callback_postload) + this.callback_postload(table); + }, + + sort: function (table_id, header_index) { + // 25% speed boost on large tables (900 rows+) by not allowing multiple sorts at the same time. + if (this.currently_sorting_table != null) + return; + if (this.callback_presort) + this.callback_presort(table_id, heder_index); + if (this.tables[table_id].callback_presort) + this.tables[table_id].callback_presort(table_id, header_index); + this.tables[table_id].sort(header_index); + if (this.tables[table_id].callback_postsort) + this.tables[table_id].callback_postsort(table_id, header_index); + if (this.callback_postsort) + this.callback_postsort(table_id, header_index); + } +}; + +document.observe('dom:loaded', SortableTables.start); + +var SortableTable = Class.create({ + initialize: function (table) { + // Class vars + this.table = null; + this.header = null; + this.body = null; + this.rows = null; + + this.cache_sort_functions = new Array(); + this.cache_has_attribute = new Array(); + this.cache_sorted_index = null; + this.cache_previous_sorts = new Array(); + this.cache_sort_hash = new Array(); + this.cache_sort_direction = false; + + this.multisort = false; + this.zebra_stripe = false; + + this.sort_cell_index = null; + this.sort_header_index = null; + + this.callback_presort = null; + this.callback_postsort = null; + + // Make sure the table is valid for sorting + if (table.tBodies[0].rows.length < 2) + return; + + this.table = $(table); + this.header = this.table.tHead.rows[table.tHead.rows.length-1]; + this.body = this.table.tBodies[0] + this.rows = this.body.rows; + + this.multisort = this.table.readAttribute('multisort'); + + if (this.rows[0].className.match(/even/) || this.rows[0].className.match(/odd/)) + this.zebra_stripe = true; + + // Setup the header with links + var cells = this.header.cells; + var cells_count = cells.length; + var cell_coloffset = 0; + for (var i = 0; i < cells_count; i++) { + var cell = cells[i]; + var cell_colspan = 1; + var cell_index = i + cell_coloffset; + var header_index = i; + this.cache_sort_hash[i] = cell_index; + // Handle colspans... + if (cell.hasAttribute('colspan')) + cell_coloffset += cell.readAttribute('colspan')-1; + + if (cell.readAttribute('sortable') == 'false') + continue; + // This should fix a bug with safari (as of version 2.0.2 at least), which does not understand cellIndex correctly + cell.setAttribute('onclick','SortableTables.sort("'+this.table.id+'","'+header_index+'");return false;'); + cell.addClassName('link'); + // Heat the cache... + this.cache_has_attribute[cell_index] = false; + // Precache the sort function... + this.cache_sort_functions[cell_index] = this.getSortFunction(header_index, cell_index); + } + }, + + getSortFunction: function (header_index) { + var cell_index = this.cache_sort_hash[header_index]; + // Default sort function to use + var sort_function = 'sortCaseInsensitive'; + // Do we have sort_values? + this.cache_has_attribute[header_index] = this.rows[0].cells[cell_index].hasAttribute('sort_value'); + if (this.header.cells[header_index].hasAttribute('sort_hint')) + sort_function = this.header.cells[header_index].readAttribute('sort_hint'); + else { + if (this.cache_has_attribute[header_index]) + var value = this.rows[0].cells[cell_index].readAttribute('sort_value'); + else + var value = this.rows[0].cells[cell_index].textContent; + + // Figure out the function to use... + if (value.match(/^\w+ \d+[,] \d\d\d\d/)) + sort_function = 'sortEnglishDate'; + else if (value.match(/^\w+[,] \w+ \d+[,] \d+[:]\d+ \w+$/)) + sort_function = 'sortLongEnglishDate'; + else if (value.match(/^\d+ days$/) || value=='Today' || value=='Tomorrow' || value=='Yesterday') + sort_function = 'sortExpiration'; + else if (value.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) + sort_function = 'sortDate'; + else if (value.match(/^\d\d[\/-]\d\d[\/-]\d\d$/)) + sort_function = 'sortDate'; + else if (value.match(/^\W+ \d+$/)) + sort_function = 'sortMonthDay'; + else if (value.match(/^[\d\.]+[%]*$/)) + sort_function = 'sortNumeric'; + else if (value.match(/^[£$]/)) + sort_function = 'sortCurrency'; + } + return eval('SortableTableSorts.'+sort_function); + }, + + sort: function (header_index) { + this.sort_header_index = header_index; + this.sort_cell_index = this.cache_sort_hash[header_index] + + // Spawn into an array + var row_length = this.rows.length; + var rows = new Array(); + for (var i=0; i<row_length; i++) + rows.push(this.rows[i]); + + // Check to see if we're already sorted, and if so, just invert it + if (this.cache_previous_sorts.last() == this.sort_cell_index) + rows.reverse(); + else { + SortableTables.currently_sorting_table = this; + var sort_function = SortableTables.tables[this.table.id].doSort; + rows.sort(sort_function); + SortableTables.currently_sorting_table = null; + } + + // Move back to the table... + for (var i=0; i<row_length; i++) + this.body.appendChild(rows[i]); + + // Setup the arrows... + this.doHeaderArrows(); + // Fix any zebra striping + this.fixZebraStripes(); + // Remove the current index fromt he previous sorts and make sure it's uniq + this.cache_previous_sorts = this.cache_previous_sorts.without(this.sort_cell_index).uniq(); + // Add in the sorted col + this.cache_previous_sorts.push(this.sort_cell_index); + }, + + doSort: function (a, b, cell_index) { + // Are we overriding the cell index (for multisort) + if (!cell_index) + cell_index = SortableTables.currently_sorting_table.sort_cell_index; + + // Use the sort hint? + if (SortableTables.currently_sorting_table.cache_has_attribute[cell_index]) { + var value_a = a.cells[cell_index].readAttribute('sort_value'); + var value_b = b.cells[cell_index].readAttribute('sort_value'); + } + else { + var value_a = a.cells[cell_index].textContent; + var value_b = b.cells[cell_index].textContent; + } + + if ( value_a != value_b) + var comparison = SortableTables.currently_sorting_table.cache_sort_functions[cell_index](value_a,value_b); + else + var comparison = 0; + + if (SortableTables.currently_sorting_table.multisort && !comparison && SortableTables.currently_sorting_table.cache_previous_sorts.length) { + var multisort_index = SortableTables.currently_sorting_table.cache_previous_sorts.pop(); + comparison = SortableTables.currently_sorting_table.doSort(a, b, multisort_index); + SortableTables.currently_sorting_table.cache_previous_sorts.push(multisort_index); + } + return comparison; + }, + + doHeaderArrows: function () { + // Remove any current span arrows + $$('#'+this.table.id+' thead span.sortArrow').each(function (span) {span.update(' ');}); + var arrow_span = this.header.cells[this.sort_header_index].select('span.sortArrow').reduce(); + if (!arrow_span) { + arrow_span = new Element('span', { 'class': 'sortArrow'}); + this.header.cells[this.sort_header_index].insert(arrow_span); + } + if (this.cache_previous_sorts.last() == this.sort_cell_index && this.cache_sort_direction) { + arrow_span.update(' ↑'); + this.cache_sort_direction = false; + } + else { + arrow_span.update(' ↓'); + this.cache_sort_direction = true; + } + }, + + fixZebraStripes: function () { + if (!this.zebra_stripe) + return; + var length = this.rows.length; + var even = false; + for (var i=0; i<length; i++) { + var row = this.rows[i]; + if (even) + row.className = row.className.replace(/odd/g, 'even'); + else + row.className = row.className.replace(/even/g, 'odd'); + even = !even; + } + } +}); + +var SortableTableSorts = { +// Sort: DD-MM-YY or DD-MM-YYYY +// y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX + sortDate: function (a,b) { + if (a.length == 10) + dt1 = a.substr(6,4)+a.substr(3,2)+a.substr(0,2); + else { + yr = a.substr(6,2); + if (parseInt(yr) < 50) + yr = '20'+yr; + else + yr = '19'+yr; + dt1 = yr+a.substr(3,2)+a.substr(0,2); + } + if (b.length == 10) + dt2 = b.substr(6,4)+b.substr(3,2)+b.substr(0,2); + else { + yr = b.substr(6,2); + if (parseInt(yr) < 50) + yr = '20'+yr; + else + yr = '19'+yr; + dt2 = yr+b.substr(3,2)+b.substr(0,2); + } + if (dt1==dt2) + return 0; + if (dt1<dt2) + return -1; + return 1; + }, + +// Sort MM-DD-YY or MM-DD-YYYY +// y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX + sortDateUs: function (a,b) { + if (a.length == 10) + dt1 = a.substr(6,4)+a.substr(0,2)+a.substr(3,2); + else { + yr = a.substr(6,2); + if (parseInt(yr) < 50) + yr = '20'+yr; + else + yr = '19'+yr; + dt1 = yr+a.substr(0,2)+a.substr(3,2); + } + if (b.length == 10) + dt2 = b.substr(6,4)+b.substr(0,2)+b.substr(3,2); + else { + yr = b.substr(6,2); + if (parseInt(yr) < 50) + yr = '20'+yr; + else + yr = '19'+yr; + dt2 = yr+b.substr(0,2)+b.substr(3,2); + } + if (dt1 == dt2) + return 0; + if (dt1 < dt2) + return -1; + return 1; + }, + +// This function handles Mon day, year sorting +// Falls back to alpha-comparison, if date-comparison can't make a decision. + sortEnglishDate: function (a,b) { + if (a == 'none' || a.length == 0) + return -1; + if (b == 'none' || b.length == 0) + return 1; + + var aa_mon = SortableTableSorts.englishMonthToNumber (a.substr(0,3)); + aa = a.substr(4); + var aa_year = parseFloat(aa.substr(aa.length-4)); + var aa_day = parseFloat(aa.substr(0, aa.length-6)); + + var bb_mon = SortableTableSorts.englishMonthToNumber (b.substr(0,3)); + bb = b.substr(4); + var bb_year = parseFloat(bb.substr(bb.length-4)); + var bb_day = parseFloat(bb.substr(0, bb.length-6)); + if (!aa_year || !aa_mon || !aa_day || !bb_year || !bb_mon || !bb_day) + return SortableTableSorts.sortCaseSensitive(a,b); + if (aa_year < bb_year) + return -1; + if (aa_year > bb_year) + return 1; + if (aa_mon < bb_mon) + return -1; + if (aa_mon > bb_mon) + return 1; + if (aa_day < bb_day) + return -1; + return 1; + }, + +// This function handles Mon day, year sorting +// Jan 21, 2006 + sortEnglishDateInvert: function (a,b) { + return SortableTableSorts.sortEnglishDate(b,a); + }, + +// This function sorts based on month day format +// Jan 21 + sortMonthDay: function (a, b) { + if (a.length == 0) + return -1; + if (b.length == 0) + return 1; + + var a_mon = SortableTableSorts.englishMonthToNumber (a.substr(0,3)); + a = parseFloat(a.substr(4)); + var b_mon = SortableTableSorts.englishMonthToNumber (b.substr(0,3)); + b = parseFloat(b.substr(4)); + if (a_mon < b_mon) + return -1; + if (a_mon > b_mon) + return 1; + if (a < b) + return -1; + return 1; + }, + +// This function handles DDD, Mon Day, Hour:Min A/PM +// we also assume it's the same year, as we can't figure it out otherwise + sortLongEnglishDate: function (a,b) { + var aa = a.substr(5).split(/,/); + var bb = b.substr(5).split(/,/); + var aatmp = aa[1].split(/:/); + var aa_hour = aatmp[0]; + var aa_min = aatmp[1].substr(0,2); + if (aatmp[1].substr(3).toLowerCase() == 'pm') + aa_hour += 12; + var bbtmp = bb[1].split(/:/); + var bb_hour = bbtmp[0]; + var bb_min = bbtmp[1].substr(0,2); + if (bbtmp[1].substr(3).toLowerCase() == 'pm') + bb_hour += 12; + aa = aa[0]; + bb = bb[0]; + if (aa == bb) + return 0; + if (aa == 'none' || aa.length == 0) + return -1; + if (bb == 'none' || bb.length == 0) + return 1; + var aa_mon = SortableTableSorts.englishMonthToNumber (aa.substr(0,3)); + var aa_day = aa.substr(4); + var bb_mon = SortableTableSorts.englishMonthToNumber (bb.substr(0,3)); + var bb_day = bb.substr(4); + if (!aa_mon || !aa_day || !bb_mon || !bb_day) + return -1; + if (aa_mon < bb_mon) + return -1; + if (aa_mon > bb_mon) + return 1; + if (aa_day < bb_day) + return -1; + if (aa_day > bb_day) + return 1; + if (aa_hour < bb_hour) + return -1; + if (aa_hour > bb_hour) + return 1; + if (aa_minute < bb_minute) + return -1; + return 1; + }, + +// This function sorts expiration dates, such as in the quote list + sortExpiration: function (a,b) { + a = a.split(/ /)[0]; + b = b.split(/ /)[0]; + switch (a) { + case 'Yesterday': a = -1; break; + case 'Today': a = 0; break; + case 'Tomorrow': a = 1; break; + default: a = parseFloat(a); + } + switch (b) { + case 'Yesterday': b = -1; break; + case 'Today': b = 0; break; + case 'Tomorrow': b = 1; break; + default: b = parseFloat(b); + } + + if (a > b) + return 1; + if (a < b) + return -1; + return 0; + }, + + englishMonthToNumber: function (mon) { + switch (mon.toLowerCase()) { + case 'jan' : return 1; + case 'feb' : return 2; + case 'mar' : return 3; + case 'apr' : return 4; + case 'may' : return 5; + case 'jun' : return 6; + case 'jul' : return 7; + case 'aug' : return 8; + case 'sep' : return 9; + case 'oct' : return 10; + case 'nov' : return 11; + case 'dec' : return 12; + } + return 0; + }, + + sortCurrency: function (a,b) { + return parseFloat(a.replace(/[^0-9.]/g,'')) - parseFloat(b.replace(/[^0-9.]/g,'')); + }, + +// A non number or a blank is always less then a number. + sortNumeric: function (a,b) { + b = parseFloat(b); + if (isNaN(b)) + return 1; + a = parseFloat(a); + if (isNaN(a)) + return -1; + return a-b; + }, + +// A non number or a blank is always less then a number. + sortNumericInvert: function (a,b) { + return SortableTableSorts.sortNumeric(b,a); + }, + + sortCaseInsensitive: function (a,b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + + if (a > b) + return 1; + if (a < b) + return -1; + return 0; + }, + + sortCaseSensitive: function (a,b) { + if (a > b) + return 1; + if (a < b) + return -1; + return 0; + }, + + sortMythwebPlayTime: function (a,b) { + if (a == b) + return 0; + + var a_hrs = a.match(/([0-9]*) hr/); + var a_min = a.match(/([0-9]*) min/); + var a_val = 0; + if (a_hrs) + a_val += a_hrs[1]*60; + if (a_min) + a_val += a_min[1]*1; + + var b_hrs = b.match(/([0-9]*) hr/); + var b_min = b.match(/([0-9]*) min/); + var b_val = 0; + if (b_hrs) + b_val += b_hrs[1]*60; + if (b_min) + b_val += b_min[1]*1; + + return SortableTableSorts.sortNumeric(a_val, b_val); + }, + + sortMythwebChannel: function (a,b) { + if (a == b) + return 0; + var a_channel = a.match(/([0-9]*) -/); + + if (a_channel) + a_channel = a_channel[1]; + else + a_channel = 0; + + var b_channel = b.match(/([0-9]*) -/); + + if (b_channel) + b_channel = b_channel[1]; + else + b_channel = 0; + + return SortableTableSorts.sortNumeric(a_channel, b_channel); + }, + +// Some compat wrappers + date_us: function (a,b) { + return SortableTableSorts.sortDateUs(a,b); + }, + + numeric: function (a,b) { + return SortableTableSorts.sortNumeric(a,b); + }, + + numeric_invert: function (a,b) { + return SortableTableSorts.sortNumericInvert(a,b); + } +}; |