diff options
Diffstat (limited to 'src/docs-google-com-fix-sheets-display/google_sheets_download.js')
-rw-r--r-- | src/docs-google-com-fix-sheets-display/google_sheets_download.js | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/docs-google-com-fix-sheets-display/google_sheets_download.js b/src/docs-google-com-fix-sheets-display/google_sheets_download.js new file mode 100644 index 0000000..c4861c2 --- /dev/null +++ b/src/docs-google-com-fix-sheets-display/google_sheets_download.js @@ -0,0 +1,210 @@ +/** + * SPDX-License-Identifier: LicenseRef-GPL-3.0-or-later-WITH-js-exceptions + * + * Make spreadsheets on drive.google.com browsable without nonfree js + * + * Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * I, Wojtek Kosior, thereby promise not to sue for violation of this file's + * license. Although I request that you do not make use of this code in a + * proprietary program, I am not going to enforce this in court. + */ + +/* Use with https://docs.google.com/spreadsheets/d/** */ + +/* 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); + } +} |