diff --git a/explorer/htm/BeamExplorer.htm b/explorer/htm/BeamExplorer.htm index e9346bcd1..04be9134d 100644 --- a/explorer/htm/BeamExplorer.htm +++ b/explorer/htm/BeamExplorer.htm @@ -1,29 +1,32 @@ - + + + @@ -34,2612 +37,2576 @@ Beam Smart Explorer - + - - - -
-
- - - - - - - - - - - - - - - -
- - -
- - -
- -

Loading...

-
- -
- - - - - - - -
- - + + + +
+
+ + + + + + + + + + + + + + + +
+ + +
+ + +
+ +

Loading...

+
+ +
+ + + + + + + +
+ + - + - // PROCESS A BLOCK/KERNEL SEARCH REQUEST - function submitSearch() { - // Get search string - let query = document.getElementById("SearchField").value; - // Simplistic test to check if search string is rather block height or kernel id - let key = (query.length < 10) ? "&id=" : "&kernel="; - // Load the corresponding URL - window.location.href = UrlSelf("block", key + query); - } - - // ADD EXPAND OPTIONS TO ALL TEXT OF TYPE HASH OR METADATA (CSS WILL DISPLAYED THEM TRUNCATED BY DEFAULT) - function addExpandOptions() { - // Text of type Hash (truncated/expanded) - for (let myHash of document.querySelectorAll(".cid, .kernelId, .blob, .commitment")) { - // Check if the text contains a link - let myClass = (myHash.querySelector("a")) ? "truncated withLink" : "truncated"; - // Add a span to truncate the text, and another span to hold the clickable symbol - let myHTML = myHash.innerHTML; - myHash.innerHTML = "" + myHTML + ""; - // Add event listener on the symbol - myHash.querySelector(".expand").addEventListener("click", expandHash); - } - // Text of type Metadata (reduced/full) - for (let myMetadata of document.querySelectorAll(".metadata")) { - // Add a span to truncate the text, and another span to hold the clickable symbol - let myHTML = myMetadata.innerHTML; - myMetadata.innerHTML = "" + myHTML + ""; - // Add event listener on the symbol - myMetadata.querySelector(".extend").addEventListener("click", expandMetadata); - } - } - - function expandHash(e) { - // Get element, table and column number of the event - let input = e.currentTarget; - // If item is in table, expand the whole column - if (input.closest('table')) { - let col = input.closest('th,td').cellIndex; - let table = input.closest('table'); - // Loop on all cells in that column - for (let myRow of table.rows) { - // Remove all truncated classes in the cell - for (let myItem of myRow.cells[col].querySelectorAll(".truncated")) { - myItem.classList.remove("truncated"); - myItem.classList.add("expanded"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", expandHash); - myItem.nextElementSibling.addEventListener("click", truncateHash); - } - } - } else { - // Expand only the item and its siblings - for (let myItem of input.parentNode.querySelectorAll(".truncated")) { - myItem.classList.remove("truncated"); - myItem.classList.add("expanded"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", expandHash); - myItem.nextElementSibling.addEventListener("click", truncateHash); - } - } - } - - function truncateHash(e) { - // Get element, table and column number of the event - let input = e.currentTarget; - // If item is in table, truncate the whole column - if (input.closest('table')) { - let col = input.closest('th,td').cellIndex; - let table = input.closest('table'); - // Loop on all cells in that column - for (let myRow of table.rows) { - // Remove all expanded classes in the cell - for (let myItem of myRow.cells[col].querySelectorAll(".expanded")) { - myItem.classList.remove("expanded"); - myItem.classList.add("truncated"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", truncateHash); - myItem.nextElementSibling.addEventListener("click", expandHash); - } - } - } else { - // Expand only the item and its siblings - for (let myItem of input.parentNode.querySelectorAll(".expanded")) { - myItem.classList.remove("expanded"); - myItem.classList.add("truncated"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", truncateHash); - myItem.nextElementSibling.addEventListener("click", expandHash); - } - } - } - - function expandMetadata(e) { - // Expand selected metadata only - let input = e.currentTarget; - for (let myItem of input.parentNode.querySelectorAll(".reduced")) { - myItem.classList.remove("reduced"); - myItem.classList.add("full"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", expandMetadata); - myItem.nextElementSibling.addEventListener("click", truncateMetadata); - } - } - - function truncateMetadata(e) { - // Truncate selected metadata only - let input = e.currentTarget; - for (let myItem of input.parentNode.querySelectorAll(".full")) { - myItem.classList.remove("full"); - myItem.classList.add("reduced"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", truncateMetadata); - myItem.nextElementSibling.addEventListener("click", expandMetadata); - } - } - - function expandEverything() { - // Expand all truncated Hashes - for (let myItem of document.querySelectorAll(".truncated")) { - myItem.classList.remove("truncated"); - myItem.classList.add("expanded"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", expandHash); - myItem.nextElementSibling.addEventListener("click", truncateHash); - } - // Expand all truncated Metadata - for (let myItem of document.querySelectorAll(".reduced")) { - myItem.classList.remove("reduced"); - myItem.classList.add("full"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", expandMetadata); - myItem.nextElementSibling.addEventListener("click", truncateMetadata); - } - // Expand all small collapsible blocks - for (let myItem of document.querySelectorAll(".collapsible-checkbox")) { - if (myItem.nextElementSibling.classList.contains("small")) { - myItem.checked = true; - } - } - // Switch action of the main clickable symbol - let icon = document.querySelector("#ExpandAll"); - icon.setAttribute("href", "javascript:truncateEverything();"); - icon.setAttribute("title", "Collapse everything"); - } - - function truncateEverything() { - // Truncate all Hashes - for (let myItem of document.querySelectorAll(".expanded")) { - myItem.classList.remove("expanded"); - myItem.classList.add("truncated"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", truncateHash); - myItem.nextElementSibling.addEventListener("click", expandHash); - } - // Truncate all Metadata - for (let myItem of document.querySelectorAll(".full")) { - myItem.classList.remove("full"); - myItem.classList.add("reduced"); - // Switch action of the clickable symbols - myItem.nextElementSibling.removeEventListener("click", truncateMetadata); - myItem.nextElementSibling.addEventListener("click", expandMetadata); - } - // Collapse all small collapsible blocks - for (let myItem of document.querySelectorAll(".collapsible-checkbox")) { - if (myItem.nextElementSibling.classList.contains("small")) { - myItem.checked = false; - } - } - // Switch action of the main clickable symbol - let icon = document.querySelector("#ExpandAll"); - icon.setAttribute("href", "javascript:expandEverything();"); - icon.setAttribute("title", "Expand everything"); - } - - // ALLOW ADDING FILTERS TO FIRST ROW OF ALL LONG TABLES - function addFilterOption() { - for (let myTable of document.getElementsByTagName("table")) { - // Ignore tables of certain specific classes - if (!myTable.classList.contains("tableTotals") && !myTable.classList.contains("tableSummary") && !myTable.closest('.divTableStatus')) { - let myRow = myTable.rows[0]; - // If all cells of the first row are or have a span with a "th" class, then mark the row as header. - // We also enforce the use of to ensure that the row won't be included in the tbodies that will be filtered and sorted. - // And since Chrome 83 (as in Beam Wallet) can only do 'sticky' on 'th' elements, we also change all its 'td' into 'th'. - if ((myRow.querySelectorAll("td > span.th").length + myRow.querySelectorAll("th").length) == myRow.cells.length) { - // If that first row is not already inside a thead (which means it's implicitely inside a tbody), then create a thead and move the row inside it. - if (myRow.parentNode.nodeName != "THEAD") { - let myTHead = myTable.createTHead(); - myTHead.appendChild(myRow); - } - // Mark the row as header - myRow.classList.add("headerRow"); - // Define all cells in this first row as 'th' (remark: any attribute of those cells are lost, but we don't care) - for (let myTd of myRow.cells) { - let myTh = document.createElement("th"); - myTh.innerHTML = myTd.innerHTML; - myTd.replaceWith(myTh); - } - // Add filters only to tables with more than a 5 lines - if (myTable.rows.length > 5) { - myRow.classList.add("filtersOff"); - myRow.addEventListener("click", addSortAndSearch); - myRow.title = "Click to activate filters"; // This is for the tooltip - } else { - myRow.classList.add("filtersNot"); - } - } - } - } - } - - // ADD SORT AND SEARCH FILTERS TO FIRST ROW OF A GIVEN TABLE - function addSortAndSearch(e) - { - // Get element of the event (the first row) - let myRow = e.currentTarget; - - // Replace previous class and remove event and title (tooltip) - myRow.classList.remove("filtersOff"); - myRow.classList.add("filtersOn"); - myRow.removeEventListener("click", addSortAndSearch); - myRow.removeAttribute("title"); - - // Apply to each cell of the first row (whether 'th' or 'td', it doesn't matter) - for (let myHeader of myRow.cells) { - // Wrap each header text into a div (will be clickable for sorting), - // and then add a search input field below it. - let myHTML = myHeader.innerHTML; - myHeader.innerHTML = "\ -
\n\ -
" + myHTML + "
\n\ -
\n\ -
\n"; - // Add event listeners to all elements - myHeader.querySelector(".sortable").addEventListener("click", sortTable); - myHeader.querySelector(".search").addEventListener("keyup", filterTable); - } - } - - // SORT ROWS BY CLICKING ON HEADERS (first ascending, then descending) - function sortTable(e) { - // Declare variables - let input, col, headRow, table, nbr_tbodies, dir, items, switching, i, x, y, xVal, yVal, shouldSwitch, switchcount = 0; - - // Get element, table and column number of the event - input = e.currentTarget; - col = input.closest('th,td').cellIndex; - headRow = input.closest('tr'); - table = input.closest('table'); - nbr_tbodies = table.tBodies.length; - - // Set the default sorting direction as descending - // (or switch to ascending if already sorted descending) - dir = (input.classList.contains("desc")) ? "asc" : "desc"; - - // Reset sorting classes and titles in all headers of this table - for (let myHeader of headRow.getElementsByClassName("sortable")) { - myHeader.classList.remove("asc"); - myHeader.classList.remove("desc"); - myHeader.title = "Sort column"; - } - - // Get the list of rows or tbodies to be sorted - // (Remark: There is always at least one tbody, even if not explictely defined) - items = (nbr_tbodies > 1) ? table.tBodies : table.tBodies[0].rows; - // Make an array with the items to be sorted - let itemsArray = Array.from(items); - - // Make an array from the content of the cells in the selected column - let colArray = itemsArray.map(function(item, index) { - // Get the content of the cell in the column being sorted - // (for tbodies with multiple lines, we concatenate the content of their cells in that column) - let val = ""; - let myCell; - if (nbr_tbodies > 1) { - // Loop on all rows of the tbody - for (let trow of item.rows) { - // Concatenate content of each cell - if (val != "") {val += "\n"}; - val += trow.cells[col].innerText; - } - } else { - // Get content of the cell - val = item.cells[col].innerText; - } - // Remove comas (just for numbers to be recognized as such). - // And add its initial index (it will allow reordering the actual table rows or tbodies). - let valArray = [val.replace(/,/g,''), index]; - return valArray; - }); - - // We initialise two collators for sorting: - // One for "alphabetical sorting" (character by character, insensitive to case and accents). - // One for "natural sorting" (same, except numbers are treated numerically: "Asset-1" < "Asset-2" < "Asset-10"). - let collator1 = Intl.Collator("en-US", {numeric: false, sensitivity: "base", usage: "sort", ignorePunctuation: "false"}); - let collator2 = Intl.Collator("en-US", {numeric: true, sensitivity: "base", usage: "sort", ignorePunctuation: "false"}); - - // Sort the array (in ascending order by default) - colArray.sort(function([a,b],[c,d]) { - // Compare as numbers - if (!isNaN(a) && !isNaN(c)) {return a - c} - // Compare as address - else if (/^[a-fA-F0-9]*$/.test(a) && /^[a-fA-F0-9]*$/.test(c)) {return collator1.compare(a,c)} - // Compare as other strings - else {return collator2.compare(a,c)} - }); - // Reverse the array for descending order - if (dir == "desc") {colArray.reverse();} - - // Get the 'parent' of the sorted items (i.e. the tbody in the case of rows, or the table in the case of tbodies) - // Remark: The parent of a row is always a tbody, even if not explictely defined. - let parent = items[0].parentNode; - // Loop through the array of sorted cells to reorder the table rows or tbodies - for (let sortedCol of colArray) { - // Move each corresponding row or tbody to the end of the table. - parent.appendChild(itemsArray[sortedCol[1]]); - } - - // Update class and title in header by adding the sorting direction - input.classList.add(dir); - input.title = (dir == "desc") ? "Sort ascending" : "Sort descending"; - } - - // FILTER ROWS (OR GROUPS OF ROWS) - function filterTable(e) { - // Declare variables - let table, tbodies, rows, trows, headers, input, filter, i, ii, j, k, td, txtValue, status; - - // Get tbodies and rows - table = e.currentTarget.closest('table'); - tbodies = table.tBodies; - rows = table.rows; - headers = rows[0].cells; - - // If multiple tbodies are present, filtering will be done by tbodies instead of rows - // (the inner rows in each tbody thus always being kept together) - - // Initialize results arrays with all 1 (i.e. all rows or tbodies are visible) - let results_tbodies = new Array(tbodies.length).fill(1); - let results_rows = new Array(rows.length).fill(1); - - // Always check again all header cells (so that filters on multiple columns can be combined!) - for (j = 0; j < (headers.length); j++) { - // Get search input field - input = headers[j].querySelector(".search"); - // Get search query (we use toUpperCase() to perform a case-insensitive search) - filter = input.value.toUpperCase(); - // Apply search query only if it's non-empty - if (filter != "") { - // Loop on all rows within all tbodies - for (k = 0; k < (tbodies.length); k++) { - // Only test cells if the tbody is not already marked as hidden - if (results_tbodies[k] != 0) { - // In case of multiple tbodies, start by hidding the tbody - // (it will be displayed back if at least one of its rows matches the search query) - if (tbodies.length > 1) { results_tbodies[k] = 0 } - // Loop on all rows of the tbody - trows = tbodies[k].rows; - for (i = 0; i < (trows.length); i++) { - // Get the overall index of the row in the table (independent of tbodies) - // Remark: i and ii will be equal in the case of one single tbody - ii = trows[i].rowIndex; - // Only test the cell if the row is not already marked as hidden - if (results_rows[ii] != 0) { - // Get cell - td = trows[i].cells[j]; - if (td) { - // Get cell content - txtValue = td.textContent || td.innerText; - // If there are multiple tbodies, then filter by tbodies - if (tbodies.length > 1) { - // Mark tbody back as visible as soon as one of its rows matches - // (and then stop looping on its rows) - if (txtValue.toUpperCase().indexOf(filter) != -1) { - results_tbodies[k] = 1; - break; - } - // If there is only one tbody, then filter by rows - } else { - // Hide row if there is no match - if (txtValue.toUpperCase().indexOf(filter) == -1) { - results_rows[ii] = 0; - } - } - } - } - } - } - } - } - } - // Special hack to avoid a display bug on Chrome 83 (when used as Beam Wallet DApp): - // If a tbody is hidden, then each of its rows must also be hidden! - if (tbodies.length > 1) { - // Loop on all tbodies - for (k = 0; k < (tbodies.length); k++) { - // Loop on all rows of the tbody - trows = tbodies[k].rows; - for (i = 0; i < (trows.length); i++) { - // Get the overall index of the row in the table (independent of tbodies) - ii = trows[i].rowIndex; - // Set status of the row identical to the tbody it belongs to - results_rows[ii] = results_tbodies[k]; - } - } - } - // Apply final results by setting the visibility of all tbodies and rows - for (k = 0; k < (tbodies.length); k++) { - status = (results_tbodies[k] == 0) ? "collapse" : "visible"; - tbodies[k].style.visibility = status; - } - for (ii = 1; ii < (rows.length); ii++) { - status = (results_rows[ii] == 0) ? "collapse" : "visible"; - rows[ii].style.visibility = status; - } - } - - // SCROLL DOWN TO BOTTOM OR BACK TO TOP - window.onscroll = function() {scrollFunction()}; - // When the user scrolls away 40px from top or bottom of the page, show the up or down buttons - function scrollFunction() { - let myTopButton = document.getElementById("TopButton"); - if (document.body.scrollTop > 40 || document.documentElement.scrollTop > 40) { - myTopButton.style.display = "block"; - } else { - myTopButton.style.display = "none"; - } - let myBottomButton = document.getElementById("BottomButton"); - if (Math.abs(document.body.scrollHeight - document.body.scrollTop - document.body.clientHeight) > 40 || Math.abs(document.documentElement.scrollHeight - document.documentElement.scrollTop - document.documentElement.clientHeight) > 40) { - myBottomButton.style.display = "block"; - } else { - myBottomButton.style.display = "none"; - } - } - // Scroll to the top of the document - function topFunction() { - document.body.scrollTop = 0; // For Safari - document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera - } - // Scroll to the bottom of the document - function bottomFunction() { - document.body.scrollTop = Math.abs(document.body.scrollHeight - document.body.clientHeight); // For Safari - document.documentElement.scrollTop = Math.abs(document.documentElement.scrollHeight - document.documentElement.clientHeight); // For Chrome, Firefox, IE and Opera - } - - // TOGGLE DARK MODE - function toggleDarkMode() { - let mode = ""; - // For dark mode, we add a class to root - let rootclass = document.querySelector(":root").classList; - if ( rootclass.contains("dark") ) { rootclass.remove("dark") } - else { rootclass.add("dark"); mode = "dark"; } - // Store mode in localStorage (to allow retrieving it across pages) - localStorage.setItem("mode", mode); - } - + +