Bureaucrats, editor, Interface administrators, Administrators (Semantic MediaWiki), Curators (Semantic MediaWiki), Editors (Semantic MediaWiki), Administrators
47,327
edits
No edit summary Tag: Manual revert |
No edit summary |
||
Line 1:
"use strict";
/**
* highlightTable.js
Line 89 ⟶ 91:
*/
(function ($, mw, OO, rs) {
self = {
/*
* Stores the current uncompressed data for the current page.
*/
data: null,
/*
* Perform initial checks on the page and browser.
*/
init: function init() {
var $tables = $('table.' + TABLE_CLASS),
hashedPageName = self.hashString(mw.config.get('wgPageName'));
// check we have some tables to interact with
if (!$tables.length)
return;
// check the browser
if (!rs.hasLocalStorage()) {
return;
}
self.data = self.load(hashedPageName, $tables.length);
self.initTables(hashedPageName, $tables);
},
/*
* Initialise table highlighting.
*
* @param hashedPageName The current page name as a hash.
* @param $tables A list of highlightable tables on the current page.
*/
initTables: function initTables(hashedPageName, $tables) {
$tables.each(function (tIndex) {
var $this = $(this),
$table = $this,
tblid = $this.data(TBLID),
// data cells
$cells = $this.find('td'),
$rows = $this.find('tr:has(td)'),
// don't rely on headers to find number of columns
// count them dynamically
columns = 1,
tableData = self.data[tIndex],
mode = 'cells',
initialised = false;
if (tblid) {
initialised = self.initNamed(tblid);
tableData = self.data[tblid] || {};
// initialise rows if necessary
var row_ids = $this.find('[data-rowid]').map(function () {
return $(this).data(ROWID);
});
row_ids.each(function (index, id) {
if (!tableData.hasOwnProperty(id)) {
tableData[id] = 0;
}
});
}
// Switching between either highlighting rows or cells
if (!$this.hasClass('individual')) {
mode
}
while ($cells.length > tableData.length)
// counting the column count
// necessary to determine colspan of reset button
$rows.each(function ()
var $this = $(this);
columns = Math.max(columns, $this.children('th,td').length);
});
$cells.each(function (cIndex) {
var $this = $(this);
if (tblid) cIndex = $this.data(ROWID);
var cellData = tableData[cIndex];
// forbid highlighting any cells/rows that have class nohighlight
if (!$this.hasClass('nohighlight')) {
// initialize highlighting based on localStorage, unless namedInit already did that
self.setHighlight($this, cellData);
self.setHighlight($this,
}).mouseout(function ()
self.setHighlight($this,
}).click(function (e) {
// don't toggle highlight when clicking links
if (e.target.tagName !== 'A' && e.target.tagName !== 'IMG') {
// 1 -> 0
// 0 -> 1
tableData[cIndex] = 1 - tableData[cIndex];
self.setHighlight($this, tableData[cIndex]);
if (tblid) {
} else
}
}
}
});
// if this is a named table, which wasn't initialised yet, make sure to save data to the named system
if (tblid && !initialised) {
// add
var button = new OO.ui.ButtonWidget({
label:
icon:
title:
classes: ['ht-reset'] // this class is targeted by other gadgets, be careful removing it
$cells.each(function (cIndex)
if (tblid) cIndex = $
tableData[cIndex] = 0;
self.setHighlight($(this),
}
if (tblid) {
self.saveNamed($table.data(TBLID));
} else {
self.save(hashedPageName, $tables.length);
}
});
// selected
var $label = $("<span>").addClass("ht-reset-counter").appendTo(button.$element.find(".oo-ui-labelElement-label"));
var $selectables =
var updtLabelFunc = function updtLabelFunc() {
self.updateLabel($selectables,
$selectables.click(updtLabelFunc);
button.$element.click(updtLabelFunc);
self.updateLabel($selectables, $label);
$this.append($('<tfoot>').append($('<tr>').append($('<th>').attr('colspan', columns).append(button.$element))));
});
},
/*
* Change the cell background color based on mouse events.
*
* @param $cell The cell element.
* @param val The value to control what class to add (if any).
* 0 -> light off (no class)
* 1 -> light on without hover
* 2 -> mouse over
*/
setHighlight: function setHighlight($cell, val) {
switch (val) {
// no highlighting
case 0:
$cell.removeClass(MOUSE_OVER_CLASS);
$cell.removeClass(LIGHT_ON_CLASS);
break;
// light
case
case 2:
break;
case 3:
}
},
/*
* Find selectable elements within the table.
*
* @param $table Table.
*/
getSelectables: function getSelectables($table) {
var selector;
if ($table.hasClass("individual")) selector = "> tbody > tr > td:not(.nohighlight)";else selector = "> tbody > tr:not(.nohighlight):has(td:not(.nohighlight))";
return $table.find(selector);
},
/*
* Update the selected elements counter.
*
* @param $eles Selectable elements.
* @param $label Selected elements counter element.
*/
updateLabel: function updateLabel($eles, $label) {
if ($eles.hasClass("highlight-over")) return;
$label.text(" (" + $eles.filter(".highlight-on").length + "/" + $eles.length + ")");
},
/*
* Merge the updated data for the current page into the data for other pages into local storage.
*
* @param hashedPageName A hash of the current page name.
*/
save: function save(hashedPageName) {
// load the existing data so we know where to save it
var curData = localStorage.getItem(STORAGE_KEY),
compressedData;
if (curData === null) {
curData = {};
} else {
curData = JSON.parse(curData);
curData = self.parse(curData);
}
// merge in our
curData[hashedPageName] = self.data;
compressedData =
// convert to a string and save to localStorage
compressedData = JSON.stringify(compressedData);
localStorage.setItem(STORAGE_KEY, compressedData);
},
/*
* Compress the entire data set using tha algoritm documented at the top of the page.
*
* @param data The data to compress.
*
* @return the compressed data.
*/
compress: function compress(data) {
var ret = {};
Object.keys(data).forEach(function (hashedPageName) {
var pageData = data[hashedPageName],
pageKey = hashedPageName.charAt(0);
if (!ret.hasOwnProperty(pageKey)) {
ret[pageKey] = {};
}
ret[pageKey][hashedPageName] = [];
pageData.forEach(function (tableData) {
var compressedTableData = '',
i,
j,
k;
for (i = 0; i < Math.ceil(tableData.length / 6); i += 1) {
k = tableData[6 * i];
for (j = 1; j < 6; j += 1) {
k = 2 * k + (6 * i + j < tableData.length ? tableData[6 * i + j] : 0);
}
compressedTableData += BASE_64_URL.charAt(k);
}
ret[pageKey][hashedPageName].push(compressedTableData);
});
ret[pageKey][hashedPageName] = ret[pageKey][hashedPageName].join(TABLE_SEPARATOR);
});
Object.keys(ret).forEach(function (pageKey) {
var hashKeys = Object.keys(ret[pageKey]),
hashedData = [];
hashKeys.forEach(function (key) {
var pageData = ret[pageKey][key];
hashedData.push(key + pageData);
});
hashedData = hashedData.join(PAGE_SEPARATOR);
ret[pageKey] = hashedData;
});
return ret;
},
/*
* Get the existing data for the current page.
*
* @param hashedPageName A hash of the current page name.
* @param numTables The number of tables on the current page. Used to ensure the loaded
* data matches the number of tables on the page thus handling cases
* where tables have been added or removed. This does not check the
* amount of rows in the given tables.
*
* @return The data for the current page.
*/
load: function load(hashedPageName, numTables) {
var data = localStorage.getItem(STORAGE_KEY),
pageData;
if (data === null) {
pageData = [];
} else {
data = JSON.parse(data);
data = self.parse(data);
if (data.hasOwnProperty(hashedPageName)) {
pageData = data[hashedPageName];
} else {
pageData = [];
}
}
// if more tables were added
// add extra arrays to store the data
// also populates if no existing data was
while (numTables > pageData.length) {
pageData.push([]);
}
// if tables were removed, remove data from the end of the list
// as there's no way to tell which was removed
while (numTables < pageData.length) {
pageData.pop();
}
return pageData;
},
/*
* Parse the compressed data as loaded from local storage using the algorithm desribed
* at the top of the page.
*
* @param data The data to parse.
*
* @return the parsed data.
*/
parse: function parse(data) {
var ret = {};
Object.keys(data).forEach(function (pageKey) {
var pageData = data[pageKey].split(PAGE_SEPARATOR);
pageData.forEach(function (tableData) {
var hashedPageName = tableData.substr(0, 8);
tableData = tableData.substr(8).split(TABLE_SEPARATOR);
ret[hashedPageName] = [];
tableData.forEach(function (rowData, index) {
var i, j, k;
ret[hashedPageName].push([]);
for (i = 0; i < rowData.length; i += 1) {
k = BASE_64_URL.indexOf(rowData.charAt(i));
// input
if (k < 0)
k =
}
for (j = 5; j >= 0; j -= 1) {
}
}
});
});
});
return ret;
},
/*
* Hash a string into a big endian 32 bit hex string. Used to hash page names.
*
* @param input The string to hash.
*
* @return the result of the hash.
*/
hashString: function hashString(input) {
var ret = 0,
table = [],
i,
j,
k;
// guarantee 8-bit chars
input = window.unescape(window.encodeURI(input));
// calculate the crc (cyclic redundancy check) for
// 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) * CASTAGNOLI_POLYNOMIAL;
}
table[i] = k;
}
// the actual
for (i = 0; i < input.length; i += 1) {
ret = ret
// make negative numbers unsigned
if (ret < 0) {
ret += UINT32_MAX;
}
// 32-bit hex string, padded on the left
ret = '0000000' + ret.toString(16).toUpperCase();
ret = ret.substr(ret.length - 8);
return ret;
},
/*
* Save highlighted rows for named tables, using the data-tableid attribute.
* Does not override values that are not present in the current table. This allows usethe use of a
* table ID on pages like [[Music]]
*
* @param tblid The table id for the table to initialise
*/
saveNamed: function saveNamed(tblid) {
// local storage key is prefixed by the generic storage key, to avoid local storage naming conflicts.
var lsKey = STORAGE_KEY + ':' + tblid,
data =
var $tbls = $('table.lighttable[data-tableid="' + tblid + '"]');
if (data === null)
data = {};
} else {
data = JSON.parse(data);
}
$tbls.find('[data-rowid]').each(function () {
var id =
if (!id)
data[id] =
});
localStorage.setItem(lsKey, JSON.stringify(data));
},
* Initialise a named table
*
* @param tblid The table id for the table to initialise
* @return Boolean true if successfully initialised, false if no named highlight data was available
initNamed: function initNamed(tblid) {
var lsKey = STORAGE_KEY +
var data = localStorage.getItem(lsKey);
var $tbls = $('table.lighttable[data-tableid="' + tblid + '"]');
if (data ===
// no data stored yet, so
return
var data = JSON.parse(data);
self.data[tblid] = data;
$tbls.find('[data-rowid]').each(function () {
var id = $(this).data(ROWID);
if (!id) return;
if
mw.log.warn('Reused rowid detected in
self.setHighlight($(this), Number(data[id]));
});
return true;
};
$(self.init);
/*
// sample data for testing the algorithm used
var data = {
// page1
'0FF47C63': [
[0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 1, 0, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 0, 0]
],
// page2
'02B75ABA': [
[0, 1, 0, 1, 1, 0],
[1, 1, 1, 0, 1, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 0, 0]
],
// page3
'0676470D': [
[1, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 1, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 1, 1]
]
};
console.log('input', data);
var compressedData = self.compress(data);
var parsedData = self.parse(compressedData);
})((void 0).jQuery, (void 0).mediaWiki, (void 0).OO, (void 0).rswiki);
|