/** * Copyright 2021 Wojtek Kosior * * This program is free software; you can redistribute it * and/or modify it under the terms of either: * - the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version, or * - the "A" license: <https://koszko.org/alicense.txt>; explained * at: <https://koszko.org/en/articles/my-new-license.html> * * As additional permission under GNU GPL version 3 section 7, you * may distribute forms of that code without the copy of the GNU * GPL normally required by section 4, provided you include this * license notice and, in case of non-source distribution, a URL * through which recipients can access the Corresponding Source. * If you modify file(s) with this exception, you may extend this * exception to your version of the file(s), but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. If you delete this * exception statement from all source files in the program, then * also delete it here. * * As a special exception to the GPL, any HTML file which merely * makes function calls to this code, and for that purpose * includes it by reference shall be deemed a separate work for * copyright law purposes. If you modify this code, you may extend * this exception to your version of the code, but you are not * obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ /* Make the view scrollable. */ document.body.setAttribute("style", "width: 100vw; height: 100vh; overflow: scroll;" + (document.body.getAttribute("style") || "")); let container = document.querySelectorAll(".waffle")[0]; let main_gid = null; while (container) { container = container.parentElement; console.log(container); if (container === document.body || !container) break; const match = /([0-9]+)-grid-container/.exec(container.id); if (match) main_gid = match[1]; container.setAttribute("style", "width: fit-content; width: -moz-fit-content;"); } /* Remove editor toolbars and bottom bar - these don't work anyway. */ const docs_chrome = document.getElementById("docs-chrome"); if (docs_chrome) docs_chrome.remove() const grid_bottom_bar = document.getElementById("grid-bottom-bar"); if (grid_bottom_bar) grid_bottom_bar.remove() /* Remove no Javascript warning. */ for (const no_js_warning of document.querySelectorAll("noscript")) no_js_warning.remove(); /* Get opengraph data. */ const og = {}; for (const node of document.head.childNodes) { if (node.tagName === "STYLE") { document.head.removeChild(node); continue; } if (node.tagName !== "META") continue; const match = /^og:(.+)/.exec(node.getAttribute("property")); if (!match) continue; og[match[1]] = node.getAttribute("content"); } /* Construct download link. */ let download_link = null; const match = new RegExp("/spreadsheets/d/([^/]+)").exec(document.URL); if (match) download_link = `https://docs.google.com/spreadsheets/d/${match[1]}/export`; /* Add title bar with sheet name and download button. */ const title_bar = document.createElement("div"); const title_heading = document.createElement("h1"); const title_text = document.createElement("span"); const main_download_button = document.createElement("a"); main_download_button.textContent = "download"; main_download_button.setAttribute("style", "border-radius: 10px; padding: 20px; color: #333; background-color: lightgreen; text-decoration: none; box-shadow: -4px 8px 8px #888; display: inline-block;"); if (og.title) { title_text.textContent = og.title; title_heading.appendChild(title_text); } title_text.setAttribute("style", "margin-right: 10px;"); if (download_link) { main_download_button.setAttribute("href", download_link); title_heading.appendChild(main_download_button); } title_bar.setAttribute("style", "padding: 0 20px; color: #555;"); title_bar.appendChild(title_heading); document.body.insertBefore(title_bar, document.body.firstElementChild); /* Extract sheet data from a script that sets the `bootstrapData' variable. */ let data = null; for (const script of document.scripts) { const match = /bootstrapData = ({([^;]|[^}];)+})/.exec(script.textContent); if (!match) continue; data = JSON.parse(match[1]); } /* * Add download buttons for individual sheets belonging to this spreadsheet. * Data schema has been observed by looking at various spreadsheets. */ function add_sheet_download(data) { if (!Array.isArray(data)) return; const gid = data[2]; if (!["string", "number"].includes(typeof gid)) return; const sheet_download_link = `${download_link}?gid=${gid}`; const sheet_download_button = document.createElement("a"); sheet_download_button.setAttribute("style", "border-radius: 5px; padding: 10px; color: #333; background-color: lightgreen; text-decoration: none; box-shadow: -4px 8px 8px #888; display: inline-block; margin: 0 5px 5px 0;"); sheet_download_button.setAttribute("href", sheet_download_link); let sheet_name = null; if (Array.isArray(data[3]) && data[3][0] && typeof data[3][0] === "object" && Array.isArray(data[3][0][1]) && Array.isArray(data[3][0][1][0]) && typeof data[3][0][1][0][2] === "string") { const sheet_name = data[3][0][1][0][2]; sheet_download_button.textContent = sheet_name; if (gid == main_gid) title_text.textContent = `${title_text.textContent} - ${sheet_name}`; } else { sheet_download_button.textContent = `<sheet gid=${gid}>`; } title_bar.appendChild(sheet_download_button); } if (download_link) { for (const entry of data.changes.topsnapshot) { if (!Array.isArray(entry) || entry[0] !== 21350203 || typeof entry[1] !== "string") continue; let entry_data = null; try { entry_data = JSON.parse(entry[1]); } catch (e) { console.log(e); continue; } add_sheet_download(entry_data); } }