Line 1: |
Line 1: |
− | // <nowiki> | + | /** <pre> |
− | if ($("table.lighttable").length) { | + | * highlightTable.js |
− | mw.loader.load( 'ext.gadget.highlightTable-core' );
| + | * |
− | } | + | * Description: |
− | // </nowiki> | + | * Adds row highlighting to tables |
| + | * |
| + | * Version 1.0: Row highlighting - Quarenon |
| + | * Version 1.1: Update from pengLocations.js v1.0 - Quarenon |
| + | * Version 2.0: pengLocations v2.1, Granular cookie - Saftzie |
| + | * Version 2.1: Made compatible with jquery.tablesorter - Cqm |
| + | */ |
| + | |
| + | ;(function ($, mw) { |
| + | |
| + | 'use strict'; |
| + | |
| + | function highlightTable() { |
| + | |
| + | // requires CSS classes named in lightOnClass and mouseOverClass |
| + | var wgPageName = mw.config.get('wgPageName'), |
| + | cookiePrefix = 'lightTable', |
| + | tableClass = 'lighttable', |
| + | lightOnClass = 'highlight-on', |
| + | mouseOverClass = 'highlight-over', |
| + | base64url = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', |
| + | pageSeparator = '!', |
| + | tableSeparator = '.', |
| + | hashPageName, |
| + | rows = [], |
| + | columns, |
| + | tables, |
| + | cookie; |
| + | |
| + | // hash a string into a 32-bit hex string, msb first |
| + | function crc32c(s) { |
| + | var polynomial = 0x04C11DB7, // Castagnoli polynomial |
| + | retVal, |
| + | table = [], |
| + | i, |
| + | j, |
| + | k; |
| + | |
| + | // guarantee 8-bit chars |
| + | s = window.unescape(window.encodeURI(s)); |
| + | |
| + | // calculate the crc for all 8-bit data |
| + | // bit-wise operations discard anything left of bit 31 |
| + | for (i = 0; i < 256; i += 1) { |
| + | k = (i << 24); |
| + | for (j = 0; j < 8; j += 1) { |
| + | k = (k << 1) ^ ((k >>> 31) * polynomial); |
| + | } |
| + | table[i] = k; |
| + | } |
| + | |
| + | // the actual calculation |
| + | retVal = 0; |
| + | for (i = 0; i < s.length; i += 1) { |
| + | retVal = (retVal << 8) ^ table[(retVal >>> 24) ^ s.charCodeAt(i)]; |
| + | } |
| + | |
| + | // make negative numbers unsigned |
| + | if (retVal < 0) { |
| + | retVal += 4294967296; |
| + | } |
| + | // 32-bit hex string, padded on the left |
| + | retVal = '0000000' + retVal.toString(16).toUpperCase(); |
| + | retVal = retVal.substr(retVal.length - 8); |
| + | |
| + | return retVal; |
| + | } |
| + | |
| + | // change the row bg color based on mouse events |
| + | function setHighlight(el, val) { |
| + | $(el).removeClass(mouseOverClass).removeClass(lightOnClass); |
| + | switch (val) { |
| + | case 1: // light on |
| + | $(el).addClass(lightOnClass); |
| + | break; |
| + | case 2: // mouse-over |
| + | $(el).addClass(mouseOverClass); |
| + | break; |
| + | default: // same as case 0, light off |
| + | } |
| + | } |
| + | |
| + | // load the cookie and parse it for the page |
| + | // cookie info is saved in 1 of 16 browser cookies, based on page name hash |
| + | // global cookie[][] supports multiple tables and multiple pages |
| + | // uses global hashPageName |
| + | function loadCookie(numTables) { |
| + | var cookieName = cookiePrefix + '-' + hashPageName.charAt(0), |
| + | pageCookies, |
| + | tableCookies, |
| + | iPage, |
| + | iTable, |
| + | i, |
| + | j, |
| + | k; |
| + | |
| + | cookie = []; |
| + | if ($.cookie(cookieName) !== null) { |
| + | pageCookies = $.cookie(cookieName).split(pageSeparator); |
| + | for (iPage = 0; iPage < pageCookies.length; iPage += 1) { |
| + | if (hashPageName === pageCookies[iPage].substr(0, 8)) { |
| + | tableCookies = pageCookies[iPage].substr(8).split(tableSeparator); |
| + | // trim the cookie array of arrays, if needed |
| + | while (tableCookies.length > numTables) { |
| + | tableCookies.pop(); |
| + | } |
| + | // extract the row info per table |
| + | // use Base64url to compress 6 rows to 1 character |
| + | for (iTable = 0; iTable < tableCookies.length; iTable += 1) { |
| + | cookie[iTable] = []; |
| + | for (i = 0; i < tableCookies[iTable].length; i += 1) { |
| + | k = base64url.indexOf(tableCookies[iTable].charAt(i)); |
| + | if (k < 0) { // input validation |
| + | k = 0; |
| + | } |
| + | for (j = 5; j >= 0; j -= 1) { |
| + | cookie[iTable][6 * i + j] = (k & 0x1); |
| + | k >>= 1; |
| + | } |
| + | } |
| + | } |
| + | } |
| + | } |
| + | } |
| + | |
| + | // initialize the cookie array of arrays, if needed |
| + | while (cookie.length < numTables) { |
| + | cookie.push([]); |
| + | } |
| + | } |
| + | |
| + | // save/update the cookie for page reloads |
| + | // cookie info is saved in 1 of 16 browser cookies, based on page name hash |
| + | // global cookie[][] supports multiple tables and multiple pages |
| + | // uses global hashPageName |
| + | function saveCookie() { |
| + | var cookieName = cookiePrefix + '-' + hashPageName.charAt(0), |
| + | pageCookies, |
| + | tableCookies, |
| + | iPage, |
| + | iTable, |
| + | i, |
| + | j, |
| + | k, |
| + | updated; |
| + | |
| + | // create the cookie for the tables on the current page |
| + | // use Base64url to compress 6 rows to 1 character |
| + | tableCookies = hashPageName; |
| + | for (iTable = 0; iTable < cookie.length; iTable += 1) { |
| + | if (iTable > 0) { |
| + | tableCookies += tableSeparator; |
| + | } |
| + | for (i = 0; i < Math.ceil(cookie[iTable].length / 6); i += 1) { |
| + | k = cookie[iTable][6 * i]; |
| + | for (j = 1; j < 6; j += 1) { |
| + | k = 2 * k + ((6 * i + j < cookie[iTable].length) ? cookie[iTable][6 * i + j] : 0); |
| + | } |
| + | tableCookies += base64url.charAt(k); |
| + | } |
| + | } |
| + | |
| + | updated = 0; |
| + | pageCookies = []; |
| + | if ($.cookie(cookieName) !== null) { |
| + | // get all the page cookies |
| + | // another page might have updated them since this page |
| + | pageCookies = $.cookie(cookieName).split(pageSeparator); |
| + | // update the page cookie if it already exists |
| + | for (iPage = 0; iPage < pageCookies.length; iPage += 1) { |
| + | if (hashPageName === pageCookies[iPage].substr(0, 8)) { |
| + | updated = 1; |
| + | pageCookies[iPage] = tableCookies; |
| + | } |
| + | } |
| + | } |
| + | // add the page cookie if it doesn't exist yet |
| + | if (updated === 0) { |
| + | pageCookies.push(tableCookies); |
| + | } |
| + | |
| + | // set path to / so it works for /wiki/, /index.php, etc |
| + | $.cookie(cookieName, pageCookies.join(pageSeparator), { |
| + | expires: 7, |
| + | path: '/' |
| + | }); |
| + | } |
| + | |
| + | tables = $('table.' + tableClass); |
| + | // don't bother doing anything unless there's really something to do |
| + | if (tables.length > 0) { |
| + | // hash the page name to an 8-char hex string |
| + | hashPageName = crc32c(wgPageName); |
| + | loadCookie(tables.length); |
| + | |
| + | tables.each(function (iTable) { |
| + | rows[iTable] = $(this).find('tr:has(td)'); // data rows |
| + | |
| + | // init or trim the cookie array of rows, if needed |
| + | while (cookie[iTable].length < rows[iTable].length) { |
| + | cookie[iTable].push(0); |
| + | } |
| + | while (cookie[iTable].length > rows[iTable].length) { |
| + | cookie[iTable].pop(); |
| + | } |
| + | |
| + | // don't rely on headers to find # of columns |
| + | // count them dynamically |
| + | columns = 1; |
| + | |
| + | rows[iTable].each(function (iRow) { |
| + | // update column count as we go |
| + | // a smarter approach would count colspans, but this is good for now |
| + | columns = Math.max(columns, $(this).children('th,td').length); |
| + | |
| + | // initialize highlighting based on the cookie |
| + | setHighlight(this, cookie[iTable][iRow]); |
| + | |
| + | // set mouse events |
| + | $(this).mouseover(function () { |
| + | setHighlight(this, 2); |
| + | }).mouseout(function () { |
| + | setHighlight(this, cookie[iTable][iRow]); |
| + | }).click(function (e) { |
| + | // don't toggle highlight when clicking links |
| + | if ((e.target.tagName !== 'A') && (e.target.tagName !== 'IMG')) { |
| + | cookie[iTable][iRow] = 1 - cookie[iTable][iRow]; |
| + | setHighlight(this, cookie[iTable][iRow]); |
| + | saveCookie(); |
| + | } |
| + | }); |
| + | }); |
| + | |
| + | // add a button for reset |
| + | $(this).append( |
| + | $('<tfoot/>') |
| + | .append( |
| + | $('<tr/>') |
| + | .append( |
| + | $('<th/>') |
| + | .attr('colspan', columns) |
| + | .append( |
| + | $('<input>') |
| + | .attr({ |
| + | 'type': 'button', |
| + | 'value': 'Reset' |
| + | }) |
| + | .click(function () { |
| + | rows[iTable].each(function (iRow) { |
| + | cookie[iTable][iRow] = 0; |
| + | setHighlight(this, 0); |
| + | }); |
| + | saveCookie(); |
| + | }) |
| + | ) |
| + | ) |
| + | ) |
| + | ); |
| + | }); |
| + | } |
| + | } |
| + | |
| + | $(highlightTable); |
| + | |
| + | }(this.jQuery, this.mediaWiki)); |
| + | |
| + | /* </pre> */ |