Difference between revisions of "MediaWiki:Common.js"
Line 4: | Line 4: | ||
( function() { | ( function() { | ||
var $e = function (tag, text, classes, id) { | var $e = function (tag, text, classes, id) { | ||
− | + | var d = document.createElement(tag); | |
− | + | if (classes !== undefined) { | |
− | d.textContent = (text || ""); | + | d.classList.add(...classes); |
− | + | } | |
− | + | if(id) { | |
− | + | d.id = id; | |
− | + | } | |
− | + | d.textContent = (text || ""); | |
− | + | return d; | |
− | + | }; | |
− | + | // Function to streamline getElementById | |
− | + | var $i = function (id, top) { | |
− | + | if(top === undefined) { | |
− | + | top = document; | |
− | + | } | |
− | + | return top.getElementById(id); | |
− | + | }; | |
− | + | // Function to streamline querySelector | |
− | + | var $q = function (query, top) { | |
− | + | if(top === undefined) { | |
− | + | top = document; | |
− | + | } | |
− | + | return top.querySelector(query); | |
− | + | }; | |
− | + | // Function to streamline querySelectorAll | |
− | + | var $a = function (query, top) { | |
− | + | if(top === undefined) { | |
− | + | top = document; | |
− | + | } | |
− | + | return top.querySelectorAll(query); | |
+ | }; | ||
+ | // Function to look up a CSS variable (not needed) | ||
+ | /*var $v = function (variable) { | ||
+ | return window.getComputedStyle(document.documentElement).getPropertyValue(variable).trim(); | ||
+ | };*/ | ||
+ | // Function to set CSS variables | ||
+ | var $sv = function (variable, value) { | ||
+ | document.documentElement.style.setProperty(variable, value); | ||
+ | }; | ||
+ | // Look for a Div that indicates we're a lexicon page | ||
+ | var lexiconInfo = $i("lexiconInfo"); | ||
+ | // Swappers have these two attributes. | ||
+ | var swapperNexus = $a("input[data-swapper][data-swap-interval]"); | ||
Line 44: | Line 57: | ||
swapperNexus.forEach(function(nex) { | swapperNexus.forEach(function(nex) { | ||
// Define variables inside here to hold stuff for the setInterval loop. | // Define variables inside here to hold stuff for the setInterval loop. | ||
− | var overlord = $i("swap-override") | + | var overlord = $i("swap-override"); // A checkbox to pause ALL swapping on a page |
− | + | var inverter = $i("swap-inverter"); // A checkbox to invert ALL animations (check to animate) | |
− | + | var ds = nex.dataset; | |
− | + | var ident = ds.swapper; // Name | |
− | + | var rep = parseInt(ds.swapInterval); // Duration (in milliseconds) | |
setInterval(function(){ | setInterval(function(){ | ||
var x = nex.checked; | var x = nex.checked; | ||
Line 54: | Line 67: | ||
$a("[data-swap-nexus=\"" + ident + "\"]").forEach(function(d) { | $a("[data-swap-nexus=\"" + ident + "\"]").forEach(function(d) { | ||
// Rotate out the text! | // Rotate out the text! | ||
− | var swap = d.dataset.swap.split(" ") | + | var swap = d.dataset.swap.split(" "); |
− | + | var newText = swap.shift(); | |
swap.push(newText); | swap.push(newText); | ||
d.textContent = newText; | d.textContent = newText; | ||
Line 69: | Line 82: | ||
// OVERRIDE LINK TITLES (in certain circumstances) | // OVERRIDE LINK TITLES (in certain circumstances) | ||
// | // | ||
− | $a("span[title][data-title-override] a[title]").forEach( x => x.removeAttribute("title") ); | + | $a("span[title][data-title-override] a[title]").forEach( (x) => x.removeAttribute("title") ); |
Line 77: | Line 90: | ||
if(lexiconInfo !== null) { | if(lexiconInfo !== null) { | ||
// Set variables | // Set variables | ||
− | + | var lexicon = JSON.parse(lexiconInfo.textContent); | |
− | + | // Raw info, Array | |
− | + | var lexiconWords = lexicon.map((x) => x[0]); | |
− | + | // Array of lexicon keys (derivation-sort-keys) | |
− | + | var longestWord = Math.max(...lexicon.map(function(x) { | |
− | + | var z = (x[1].word || x[0]); | |
− | + | return z.length; | |
− | + | })); | |
− | + | // Length of the longest word in the lexicon | |
− | + | var lexx = new Map(lexicon); | |
− | + | // Map with the lexicon info | |
− | + | var lexiMain = $i("lexicon"); | |
− | + | // Div holding the lexicon words, etc, including padding. | |
− | + | var lexiDiv = $q(".lexi", lexiMain); | |
− | + | // Div holding only the lexicon words, etc. | |
− | + | var lexiDivDataset = lexiDiv.dataset; | |
− | + | // Simplifying an oft-used property | |
− | + | var sortMain; | |
− | + | var sortWord; | |
− | + | var sortCat; | |
− | + | var sortDef; | |
− | + | var checkPrevSorts; | |
− | + | var doSort; | |
− | + | // Functions for sorting the lexicon | |
− | + | var less = -1; | |
− | + | var more = 1; | |
− | + | var flagShowDerivs = true; | |
− | + | // Used by the sorting functions | |
+ | var prevSorts = new Set(); | ||
+ | // Holds previously-used sorts | ||
+ | var Em; | ||
+ | var maxRowsPx; | ||
+ | var numMaxRows; | ||
+ | var allRowsPx; | ||
+ | // Various variables used in determining the lexicon paddings' heights | ||
+ | var doRewrite; | ||
+ | var heartbeat; | ||
+ | // Functions for rewriting info to page depending on (sort and) scroll position | ||
+ | var tooBig = (lexiconWords.length > 500); | ||
+ | // Flag that notes if we should limit info displayed by scroll position | ||
+ | var undef = 0; | ||
+ | var uncr = 0; | ||
+ | var deriv = 0; | ||
+ | var uncat = 0; | ||
+ | var cats = new Map(); | ||
+ | // Used to fill out information in the lexicon infobox | ||
+ | var tempo; | ||
+ | var tempy; | ||
// Set CSS variables for lexicon grids | // Set CSS variables for lexicon grids | ||
− | $sv("--rows-num", "repeat(auto, " + | + | tempo = lexx.size + 2; |
− | $sv("--cols-num", | + | $sv("--rows-num", "repeat(auto, " + tempo.toString() + ")"); |
− | $sv("--header-cols-num", | + | tempo = longestWord + 2; |
+ | tempy = tempo + 5; | ||
+ | $sv("--cols-num", tempo.toString() + "em 5em calc(100% - " + tempy.toString() + "em)"); | ||
+ | $sv("--header-cols-num", tempo.toString() + "em 5em calc(100% - " + tempy.toString() + "em)"); | ||
// Create an element to determine how large a lexicon row will be, in pixels | // Create an element to determine how large a lexicon row will be, in pixels | ||
Line 123: | Line 159: | ||
// Function to sort by word (and derivation) | // Function to sort by word (and derivation) | ||
sortMain = function (a, b) { | sortMain = function (a, b) { | ||
− | var x = lexx.get(a).sort | + | var x = lexx.get(a).sort; |
+ | var y = lexx.get(b).sort; | ||
if(x < y) { | if(x < y) { | ||
return less; | return less; | ||
− | } | + | } |
+ | if (x > y) { | ||
return more; | return more; | ||
} | } | ||
− | + | if(prevSorts.size) { | |
+ | return checkPrevSorts(a, b); | ||
+ | } | ||
+ | return 0; | ||
}; | }; | ||
// Function to sort by word, ignoring if its a derivation or not | // Function to sort by word, ignoring if its a derivation or not | ||
sortWord = function (a, b) { | sortWord = function (a, b) { | ||
− | var x = lexx.get(a) | + | var x = lexx.get(a); |
+ | var y = lexx.get(b); | ||
x = x.selfSort || x.sort; | x = x.selfSort || x.sort; | ||
y = y.selfSort || y.sort; | y = y.selfSort || y.sort; | ||
if(x < y) { | if(x < y) { | ||
return less; | return less; | ||
− | } | + | } |
+ | if (x > y) { | ||
return more; | return more; | ||
} | } | ||
− | + | if(prevSorts.size) { | |
+ | return checkPrevSorts(a, b); | ||
+ | } | ||
+ | return 0; | ||
}; | }; | ||
// Function to sort by category | // Function to sort by category | ||
sortCat = function (a, b) { | sortCat = function (a, b) { | ||
− | var x = lexx.get(a).cat | + | var x = lexx.get(a).cat; |
+ | var y = lexx.get(b).cat; | ||
if(x < y) { | if(x < y) { | ||
return less; | return less; | ||
− | } | + | } |
+ | if (x > y) { | ||
return more; | return more; | ||
} | } | ||
− | + | if(prevSorts.size) { | |
+ | return checkPrevSorts(a, b); | ||
+ | } | ||
+ | return 0; | ||
}; | }; | ||
// Function to sort by definition | // Function to sort by definition | ||
sortDef = function (a, b) { | sortDef = function (a, b) { | ||
− | var x = lexx.get(a) | + | var x = lexx.get(a); |
+ | var y = lexx.get(b); | ||
// Change these to uppercase to eliminate case-based sorting errors | // Change these to uppercase to eliminate case-based sorting errors | ||
− | x = (x.defSort || x.def).toUpperCase(); | + | x = (x.defSort || x.def); |
− | y = (y.defSort || y.def).toUpperCase(); | + | x = x.toUpperCase(); |
+ | y = (y.defSort || y.def); | ||
+ | y = y.toUpperCase(); | ||
if(x < y) { | if(x < y) { | ||
return less; | return less; | ||
− | } | + | } |
+ | if (x > y) { | ||
return more; | return more; | ||
} | } | ||
− | + | if(prevSorts.size) { | |
+ | return checkPrevSorts(a, b); | ||
+ | } | ||
+ | return 0; | ||
}; | }; | ||
// When search terms are equal, use previous sort criteria (if any). | // When search terms are equal, use previous sort criteria (if any). | ||
checkPrevSorts = function(a, b) { | checkPrevSorts = function(a, b) { | ||
− | var val = 0 | + | var val = 0; |
− | + | var test; | |
− | saved = [...prevSorts.values()]; | + | var rv; |
+ | // save prevSorts | ||
+ | var prev = prevSorts; | ||
+ | // put all previous sorts in an array format | ||
+ | var saved = [...prevSorts.values()]; | ||
// Clear prevSorts so we don't loop infinitely. | // Clear prevSorts so we don't loop infinitely. | ||
prevSorts = new Set(); | prevSorts = new Set(); | ||
// Go through previous sorts in a last-in first-out method. | // Go through previous sorts in a last-in first-out method. | ||
while(saved.length > 0) { | while(saved.length > 0) { | ||
− | + | test = saved.pop(); | |
// Test the sort | // Test the sort | ||
rv = test(a, b); | rv = test(a, b); | ||
Line 189: | Line 251: | ||
prevSorts = prev; | prevSorts = prev; | ||
return val; | return val; | ||
− | } | + | }; |
// Function that rewrites the page to match the newly sorted content | // Function that rewrites the page to match the newly sorted content | ||
doRewrite = function(count) { | doRewrite = function(count) { | ||
// Variables used in this function | // Variables used in this function | ||
− | var cols = 0 | + | var cols = 0; |
+ | var id; | ||
+ | var info; | ||
+ | var special; | ||
// Make sure we got a number to work with | // Make sure we got a number to work with | ||
− | (count === undefined) | + | if (count === undefined) { |
+ | count = Math.max(0, Math.round(Number(lexiDivDataset.top) / Em)); | ||
+ | } | ||
// Go through each Div one at a time | // Go through each Div one at a time | ||
$a("div", lexiDiv).forEach(function(node) { | $a("div", lexiDiv).forEach(function(node) { | ||
− | var txt | + | var txt; |
+ | var cL = node.classList; | ||
// There are three columns | // There are three columns | ||
switch(cols) { | switch(cols) { | ||
Line 226: | Line 294: | ||
// Reset column to 0 | // Reset column to 0 | ||
cols = 0; | cols = 0; | ||
+ | break; | ||
} | } | ||
// Set the text | // Set the text | ||
node.textContent = txt; | node.textContent = txt; | ||
// Remove any classes that may exist, aside from the first (word, cat or def) | // Remove any classes that may exist, aside from the first (word, cat or def) | ||
− | [...cL].slice(1).forEach(c => cL.remove(c)); | + | node = [...cL]; |
+ | node.slice(1).forEach((c) => cL.remove(c)); | ||
// If special is set, add it as a class | // If special is set, add it as a class | ||
if(special) { | if(special) { | ||
Line 242: | Line 312: | ||
// Go through each word and add a new property .sort | // Go through each word and add a new property .sort | ||
lexiconWords.forEach(function(id) { | lexiconWords.forEach(function(id) { | ||
− | var info = lexx.get(id) | + | var info = lexx.get(id); |
− | + | // The spreadsheet works best when Zs are used to shunt something to the end of the line | |
− | + | // In javascript, Zs actuallly move you above all lowercase letters | |
− | + | // So, we change Zs to ~s to get the desired effect | |
− | + | var sort = id.replace(/Z/g, "~"); | |
− | |||
// Create the sort property | // Create the sort property | ||
info.sort = sort; | info.sort = sort; | ||
Line 260: | Line 329: | ||
// Function that is called by the sort button | // Function that is called by the sort button | ||
doSort = function(doNotRewrite) { | doSort = function(doNotRewrite) { | ||
− | var dir = $q("select[name=\"dir\"]").value | + | var dir = $q("select[name=\"dir\"]").value; |
− | + | var what = $q("select[name=\"sort\"]").value; | |
− | + | var lexiMainClasses = lexiMain.classList; | |
− | + | var sortFunction; | |
− | |||
− | |||
− | |||
// Change the value of "less" and "more" and change classes on the lexicon to indicate this | // Change the value of "less" and "more" and change classes on the lexicon to indicate this | ||
if(dir === "asc") { | if(dir === "asc") { | ||
Line 298: | Line 364: | ||
break; | break; | ||
default: | default: | ||
− | console.log("A strange error occurred while trying to sort: [" + what + "]"); | + | window.console.log("A strange error occurred while trying to sort: [" + what + "]"); |
return; | return; | ||
} | } | ||
Line 308: | Line 374: | ||
prevSorts.add(sortFunction); | prevSorts.add(sortFunction); | ||
// Trigger rewrite | // Trigger rewrite | ||
− | (doNotRewrite === true) | + | if (doNotRewrite === true) { |
+ | doRewrite(); | ||
+ | } | ||
}; | }; | ||
// Add the above to the sort button | // Add the above to the sort button | ||
− | $i("dosort").addEventListener("click", function (e) { doSort(false) }); | + | $i("dosort").addEventListener("click", function (e) { doSort(false); }); |
// Sort right away. | // Sort right away. | ||
doSort(true); | doSort(true); | ||
Line 324: | Line 392: | ||
lexiconWords.slice(0, numMaxRows).forEach(function(id) { | lexiconWords.slice(0, numMaxRows).forEach(function(id) { | ||
// Get all the info we need | // Get all the info we need | ||
− | var info = lexx.get(id) | + | var info = lexx.get(id); |
− | + | var w = info.word || id; | |
− | + | var extra = ( | |
− | + | info.special ? [info.special] : [] | |
− | //console.log([w, id, sort]); | + | ); |
+ | //window.console.log([w, id, sort]); | ||
// Add the word to the page | // Add the word to the page | ||
lexiDiv.append( | lexiDiv.append( | ||
Line 341: | Line 410: | ||
$sv("--flex-top", "0px"); | $sv("--flex-top", "0px"); | ||
// Determine how muxh empty space lies beneath the printed rows and set the CSS variable accordingly | // Determine how muxh empty space lies beneath the printed rows and set the CSS variable accordingly | ||
− | $sv("--flex-bottom", | + | tooBig = allRowsPx - maxRowsPx; |
+ | $sv("--flex-bottom", tooBig.toString() + "px"); | ||
// Create a function to periodically check the scroll state | // Create a function to periodically check the scroll state | ||
heartbeat = function() { | heartbeat = function() { | ||
− | var changed = lexiDivDataset.changed | + | var changed = lexiDivDataset.changed; |
− | + | var scrollTop; | |
− | + | var clientHeightTwice; | |
+ | var top; | ||
+ | var bottom; | ||
if(changed !== "true") { | if(changed !== "true") { | ||
// Nothing has changed. | // Nothing has changed. | ||
Line 382: | Line 454: | ||
lexiDivDataset.top = top.toString(); | lexiDivDataset.top = top.toString(); | ||
lexiDivDataset.scrollTop = scrollTop.toString(); | lexiDivDataset.scrollTop = scrollTop.toString(); | ||
− | } | + | }; |
// Start the heartbeat | // Start the heartbeat | ||
lexiDivDataset.changed = "false"; | lexiDivDataset.changed = "false"; | ||
Line 408: | Line 480: | ||
// No special padding/row-adjustments necessary, just print the entire lexicon | // No special padding/row-adjustments necessary, just print the entire lexicon | ||
lexx.forEach(function(info, id) { | lexx.forEach(function(info, id) { | ||
− | var w = info.word || id | + | var w = (info.word || id); |
− | + | var extra = ( | |
− | + | info.special ? [info.special] : [] | |
− | //console.log([w, id, sort]); | + | ); |
+ | //window.console.log([w, id, sort]); | ||
lexiDiv.append( | lexiDiv.append( | ||
$e("div", w, ["word", ...extra]), | $e("div", w, ["word", ...extra]), | ||
Line 429: | Line 502: | ||
$q("span.lex_box_all").textContent = lexicon.length.toString(); | $q("span.lex_box_all").textContent = lexicon.length.toString(); | ||
lexx.forEach(function(info) { | lexx.forEach(function(info) { | ||
− | var cat = info.cat || "uncat" | + | var cat = (info.cat || "uncat"); |
− | + | var ncat = (cats.get(cat) || 0); | |
− | + | var def = info.def; | |
− | + | var word = info.word; | |
− | + | var dv = (info.special === "deriv"); | |
// Check definitions | // Check definitions | ||
if(!def) { | if(!def) { |
Revision as of 18:58, 27 November 2019
/* Any JavaScript here will be loaded for all users on every page load. */ // Function to streamline element creation ( function() { var $e = function (tag, text, classes, id) { var d = document.createElement(tag); if (classes !== undefined) { d.classList.add(...classes); } if(id) { d.id = id; } d.textContent = (text || ""); return d; }; // Function to streamline getElementById var $i = function (id, top) { if(top === undefined) { top = document; } return top.getElementById(id); }; // Function to streamline querySelector var $q = function (query, top) { if(top === undefined) { top = document; } return top.querySelector(query); }; // Function to streamline querySelectorAll var $a = function (query, top) { if(top === undefined) { top = document; } return top.querySelectorAll(query); }; // Function to look up a CSS variable (not needed) /*var $v = function (variable) { return window.getComputedStyle(document.documentElement).getPropertyValue(variable).trim(); };*/ // Function to set CSS variables var $sv = function (variable, value) { document.documentElement.style.setProperty(variable, value); }; // Look for a Div that indicates we're a lexicon page var lexiconInfo = $i("lexiconInfo"); // Swappers have these two attributes. var swapperNexus = $a("input[data-swapper][data-swap-interval]"); // // TEXT SWAP-OUT ANIMATION // if(swapperNexus.length) { // Check each swapper separately. swapperNexus.forEach(function(nex) { // Define variables inside here to hold stuff for the setInterval loop. var overlord = $i("swap-override"); // A checkbox to pause ALL swapping on a page var inverter = $i("swap-inverter"); // A checkbox to invert ALL animations (check to animate) var ds = nex.dataset; var ident = ds.swapper; // Name var rep = parseInt(ds.swapInterval); // Duration (in milliseconds) setInterval(function(){ var x = nex.checked; if(!overlord.checked && (inverter.checked ? x : !x) ) { // Pause when checked $a("[data-swap-nexus=\"" + ident + "\"]").forEach(function(d) { // Rotate out the text! var swap = d.dataset.swap.split(" "); var newText = swap.shift(); swap.push(newText); d.textContent = newText; d.dataset.swap = swap.join(" "); }); } }, rep); }); } // // OVERRIDE LINK TITLES (in certain circumstances) // $a("span[title][data-title-override] a[title]").forEach( (x) => x.removeAttribute("title") ); // // HANDLE LEXICONS // if(lexiconInfo !== null) { // Set variables var lexicon = JSON.parse(lexiconInfo.textContent); // Raw info, Array var lexiconWords = lexicon.map((x) => x[0]); // Array of lexicon keys (derivation-sort-keys) var longestWord = Math.max(...lexicon.map(function(x) { var z = (x[1].word || x[0]); return z.length; })); // Length of the longest word in the lexicon var lexx = new Map(lexicon); // Map with the lexicon info var lexiMain = $i("lexicon"); // Div holding the lexicon words, etc, including padding. var lexiDiv = $q(".lexi", lexiMain); // Div holding only the lexicon words, etc. var lexiDivDataset = lexiDiv.dataset; // Simplifying an oft-used property var sortMain; var sortWord; var sortCat; var sortDef; var checkPrevSorts; var doSort; // Functions for sorting the lexicon var less = -1; var more = 1; var flagShowDerivs = true; // Used by the sorting functions var prevSorts = new Set(); // Holds previously-used sorts var Em; var maxRowsPx; var numMaxRows; var allRowsPx; // Various variables used in determining the lexicon paddings' heights var doRewrite; var heartbeat; // Functions for rewriting info to page depending on (sort and) scroll position var tooBig = (lexiconWords.length > 500); // Flag that notes if we should limit info displayed by scroll position var undef = 0; var uncr = 0; var deriv = 0; var uncat = 0; var cats = new Map(); // Used to fill out information in the lexicon infobox var tempo; var tempy; // Set CSS variables for lexicon grids tempo = lexx.size + 2; $sv("--rows-num", "repeat(auto, " + tempo.toString() + ")"); tempo = longestWord + 2; tempy = tempo + 5; $sv("--cols-num", tempo.toString() + "em 5em calc(100% - " + tempy.toString() + "em)"); $sv("--header-cols-num", tempo.toString() + "em 5em calc(100% - " + tempy.toString() + "em)"); // Create an element to determine how large a lexicon row will be, in pixels document.body.append($e("div", "", [], "hiddenEm")); Em = $i("hiddenEm").clientHeight * 2.5; // Row-height is 2.5em. // Determine the maximum number of rows to keep loaded numMaxRows = Math.max(Math.floor(window.innerHeight / Em) * 5, 100); // Determine the height of the filled rows maxRowsPx = numMaxRows * Em; // Determine the height that would be generated IF all rows were shown at once allRowsPx = lexiconWords.length * Em; // Function to sort by word (and derivation) sortMain = function (a, b) { var x = lexx.get(a).sort; var y = lexx.get(b).sort; if(x < y) { return less; } if (x > y) { return more; } if(prevSorts.size) { return checkPrevSorts(a, b); } return 0; }; // Function to sort by word, ignoring if its a derivation or not sortWord = function (a, b) { var x = lexx.get(a); var y = lexx.get(b); x = x.selfSort || x.sort; y = y.selfSort || y.sort; if(x < y) { return less; } if (x > y) { return more; } if(prevSorts.size) { return checkPrevSorts(a, b); } return 0; }; // Function to sort by category sortCat = function (a, b) { var x = lexx.get(a).cat; var y = lexx.get(b).cat; if(x < y) { return less; } if (x > y) { return more; } if(prevSorts.size) { return checkPrevSorts(a, b); } return 0; }; // Function to sort by definition sortDef = function (a, b) { var x = lexx.get(a); var y = lexx.get(b); // Change these to uppercase to eliminate case-based sorting errors x = (x.defSort || x.def); x = x.toUpperCase(); y = (y.defSort || y.def); y = y.toUpperCase(); if(x < y) { return less; } if (x > y) { return more; } if(prevSorts.size) { return checkPrevSorts(a, b); } return 0; }; // When search terms are equal, use previous sort criteria (if any). checkPrevSorts = function(a, b) { var val = 0; var test; var rv; // save prevSorts var prev = prevSorts; // put all previous sorts in an array format var saved = [...prevSorts.values()]; // Clear prevSorts so we don't loop infinitely. prevSorts = new Set(); // Go through previous sorts in a last-in first-out method. while(saved.length > 0) { test = saved.pop(); // Test the sort rv = test(a, b); // Check if we're unequal if(rv) { // Clear saved (to end the loop) saved = []; // Save the sort value val = rv; } } // Restore prevSorts prevSorts = prev; return val; }; // Function that rewrites the page to match the newly sorted content doRewrite = function(count) { // Variables used in this function var cols = 0; var id; var info; var special; // Make sure we got a number to work with if (count === undefined) { count = Math.max(0, Math.round(Number(lexiDivDataset.top) / Em)); } // Go through each Div one at a time $a("div", lexiDiv).forEach(function(node) { var txt; var cL = node.classList; // There are three columns switch(cols) { // Column 1: the word itself case 0: // Set up the variables so we don't need to check again for the next two columns id = lexiconWords[count]; info = lexx.get(id); special = info.special; // Word may be in info.word or it may be the sorting id. txt = (info.word || id); // Advance to next column next time cols++; break; case 1: // Category is straightforward txt = info.cat; // Advance to next column next time cols++; break; case 2: // Category is straightforward txt = info.def; // Advance to next row next time count++; // Reset column to 0 cols = 0; break; } // Set the text node.textContent = txt; // Remove any classes that may exist, aside from the first (word, cat or def) node = [...cL]; node.slice(1).forEach((c) => cL.remove(c)); // If special is set, add it as a class if(special) { cL.add(special); } }); // Mark on the main Div if we're sorting by derivations lexiMain.classList.toggle("derivationsMarked", flagShowDerivs); }; // Go through each word and add a new property .sort lexiconWords.forEach(function(id) { var info = lexx.get(id); // The spreadsheet works best when Zs are used to shunt something to the end of the line // In javascript, Zs actuallly move you above all lowercase letters // So, we change Zs to ~s to get the desired effect var sort = id.replace(/Z/g, "~"); // Create the sort property info.sort = sort; // Do the same process for the .selfSort property (used by derviations) if(info.selfSort) { info.selfSort = info.selfSort.replace(/Z/g, "~"); } // Save the info lexx.set(id, info); }); // Function that is called by the sort button doSort = function(doNotRewrite) { var dir = $q("select[name=\"dir\"]").value; var what = $q("select[name=\"sort\"]").value; var lexiMainClasses = lexiMain.classList; var sortFunction; // Change the value of "less" and "more" and change classes on the lexicon to indicate this if(dir === "asc") { less = -1; more = 1; lexiMainClasses.add("asc"); lexiMainClasses.remove("desc"); } else { less = 1; more = -1; lexiMainClasses.add("desc"); lexiMainClasses.remove("asc"); } // "Zero out" this flag flagShowDerivs = false; // Set sortFunction to the correct sorting function switch(what) { case "word": sortFunction = sortMain; // Re-enable the flag because we use it here. flagShowDerivs = true; break; case "noDeriv": sortFunction = sortWord; break; case "cat": sortFunction = sortCat; break; case "def": sortFunction = sortDef; break; default: window.console.log("A strange error occurred while trying to sort: [" + what + "]"); return; } // Remove the sort from previous sorts (if it's there) prevSorts.delete(sortFunction); // Sort words via the chosen function lexiconWords.sort(sortFunction); // Add the sort to previous sorts prevSorts.add(sortFunction); // Trigger rewrite if (doNotRewrite === true) { doRewrite(); } }; // Add the above to the sort button $i("dosort").addEventListener("click", function (e) { doSort(false); }); // Sort right away. doSort(true); // Check to see if we need to limit the number of rows onscreen if(tooBig) { // Yes, we need to limit // Save initial position as "at the top" lexiDivDataset.top = "0"; lexiDivDataset.scrollTop = "0"; // Go through the words up to the predetermined limit lexiconWords.slice(0, numMaxRows).forEach(function(id) { // Get all the info we need var info = lexx.get(id); var w = info.word || id; var extra = ( info.special ? [info.special] : [] ); //window.console.log([w, id, sort]); // Add the word to the page lexiDiv.append( $e("div", w, ["word", ...extra]), $e("div", info.cat, ["cat", ...extra]), $e("div", info.def, ["def", ...extra]) ); }); // Note if we have derivations lexiMain.classList.toggle("derivationsMarked", flagShowDerivs); // Set CSS variables to have no padding on top $sv("--flex-top", "0px"); // Determine how muxh empty space lies beneath the printed rows and set the CSS variable accordingly tooBig = allRowsPx - maxRowsPx; $sv("--flex-bottom", tooBig.toString() + "px"); // Create a function to periodically check the scroll state heartbeat = function() { var changed = lexiDivDataset.changed; var scrollTop; var clientHeightTwice; var top; var bottom; if(changed !== "true") { // Nothing has changed. if(changed === "setting") { lexiDivDataset.changed = "false"; } return; } // Where are we scrolled to? scrollTop = lexiMain.scrollTop; // How far up should the padding end? clientHeightTwice = lexiMain.clientHeight * 2; // What is the current amount of top-padding? top = parseInt(lexiDivDataset.top); // Determine if we're scrolled within range of an edge if(scrollTop >= top && scrollTop + clientHeightTwice - maxRowsPx <= top) { // No need to change padding and words return; } // Note that we're changing stuff. lexiDivDataset.changed = "setting"; // Determine new padding for top top = Math.min(allRowsPx - maxRowsPx, Math.max(0, scrollTop - clientHeightTwice)); // Clip to a row boundary top -= top % Em; // New padding for bottom, too bottom = Math.max(0, allRowsPx - (top + maxRowsPx)); // Rewrite rows starting at the proper position doRewrite(Math.max(0, Math.round(top / Em))); // Set the top and bottom paddings by CSS variables $sv("--flex-top", top.toString() + "px"); $sv("--flex-bottom", bottom.toString() + "px"); // Save the top padding and the current position lexiDivDataset.top = top.toString(); lexiDivDataset.scrollTop = scrollTop.toString(); }; // Start the heartbeat lexiDivDataset.changed = "false"; setInterval(heartbeat, 300); // Start the listener for the scroll event lexiMain.addEventListener("scroll", function(event) { // When the visible rows are moved down by changing // the height of the optimistic row above them, the browser automatically // scrolls them back into view, which in turn creates another render // becuase the onScroll is called, resulting in an infinite loop. // To solve this, we get the snapshot before the DOM is updated // and check for a mismatch between the scrollTop before and after. // If such a mismatch exists, it means that the scroll // was done by the browser, and not the user, and therefore // we apply the scrollTop from the snapshot. if (lexiDivDataset.changed !== "setting") { lexiDivDataset.changed = "true"; } else { lexiDivDataset.changed = "false"; // Reset scrollTop lexiMain.scrollTop = Number(lexiDivDataset.scrollTop); } }); } else { // No special padding/row-adjustments necessary, just print the entire lexicon lexx.forEach(function(info, id) { var w = (info.word || id); var extra = ( info.special ? [info.special] : [] ); //window.console.log([w, id, sort]); lexiDiv.append( $e("div", w, ["word", ...extra]), $e("div", info.cat, ["cat", ...extra]), $e("div", info.def, ["def", ...extra]) ); }); // Note if we have derivations lexiMain.classList.toggle("derivationsMarked", flagShowDerivs); } // // Lexicon Infobox // if($q("span.lex_box_all") !== null) { // total number of words $q("span.lex_box_all").textContent = lexicon.length.toString(); lexx.forEach(function(info) { var cat = (info.cat || "uncat"); var ncat = (cats.get(cat) || 0); var def = info.def; var word = info.word; var dv = (info.special === "deriv"); // Check definitions if(!def) { undef++; } else if (!word) { uncr++; } // Check categories if(cat === "uncat") { uncat++; } else { if(cat === "#") { cat = "number"; } ncat++; cats.set(cat, ncat); if(!word) { uncr++; } } // Check derivations if(dv) { deriv++; } }); // undefined words $q("span.lex_box_undef").textContent = undef.toString(); // uncreated words (definition and/or category, though) $q("span.lex_box_uncr").textContent = uncr.toString(); // derivations $q("span.lex_box_deriv").textContent = deriv.toString(); // uncategorized words $q("span.lex_box_uncat").textContent = uncat.toString(); // words in categories cats.forEach(function(n, cat) { $q("span.lex_box_cat_" + cat).textContent = n.toString(); }); } } }() );