aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/docs-google-com-fix-sheets-display/google_sheets_download.js351
-rw-r--r--src/docs-google-com-fix-sheets-display/index.json4
2 files changed, 239 insertions, 116 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
index c4861c2..e4cd905 100644
--- a/src/docs-google-com-fix-sheets-display/google_sheets_download.js
+++ b/src/docs-google-com-fix-sheets-display/google_sheets_download.js
@@ -1,9 +1,9 @@
/**
* SPDX-License-Identifier: LicenseRef-GPL-3.0-or-later-WITH-js-exceptions
*
- * Make spreadsheets on drive.google.com browsable without nonfree js
+ * Make spreadsheets on docs.google.com browsable without nonfree js.
*
- * Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
+ * Copyright (C) 2021,2022 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
@@ -43,168 +43,291 @@
/* Use with https://docs.google.com/spreadsheets/d/** */
-/* Make the view scrollable. */
+"use strict";
-document.body.setAttribute("style",
- "width: 100vw; height: 100vh; overflow: scroll;" +
- (document.body.getAttribute("style") || ""));
+const current_params = new URLSearchParams(window.location.search);
+const work_url = new URL(window.location.href);
+let reload_needed = false;
-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;");
+/*
+ * URLs ending with "/pubhtml/sheet?<something>" allow displaying a single sheet
+ * from a document. If we're on one of those URLs, reload to a corresponding URL
+ * ending with "/pub".
+ */
+if (work_url.pathname.endsWith("/pubhtml/sheet")) {
+ reload_needed = true;
+ work_url.pathname = work_url.pathname.replace(/pubhtml\/sheet$/, "pub");
}
+/*
+ * The "widget=true" parameter in the URL may cause preview to be
+ * unavailable. If this is the case, reload without it.
+ */
+const widget_setting = current_params.get("widget");
+if (widget_setting !== null && widget_setting.toLowerCase() == "true") {
+ if (work_url.pathname.endsWith("/pub") ||
+ work_url.pathname.endsWith("/pubhtml"))
+ reload_needed = true;
+}
-/* Remove editor toolbars and bottom bar - these don't work anyway. */
+/*
+ * When reloading, get rid or parameters other than "gid" - we don't use them
+ * anyway.
+ */
+if (reload_needed) {
+ const gid = current_params.get("gid");
+ if (gid === null)
+ work_url.search = "";
+ else
+ work_url.search = new URLSearchParams({gid});
+
+ window.location.href = work_url;
+}
-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();
+function initial_gid() {
+ let gid = null;
+ const param_strings_to_try = [
+ window.location.hash.substring(1),
+ window.location.search.substring(1)
+ ];
-/* Remove no Javascript warning. */
+ for (const params in param_strings_to_try)
+ gid = gid || new URLSearchParams(params).get("gid");
-for (const no_js_warning of document.querySelectorAll("noscript"))
- no_js_warning.remove();
+ return gid;
+}
+/* For URLs where path ends with "/edit". */
+function PageHandler_Edit() {
+}
-/* Get opengraph data. */
+PageHandler_Edit.prototype.get_sheets_count = function() {
+ /* 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]);
+ }
-const og = {};
+ let count = 0;
-for (const node of document.head.childNodes) {
- if (node.tagName === "STYLE") {
- document.head.removeChild(node);
- continue;
+ for (const entry of data.changes.topsnapshot) {
+ if (Array.isArray(entry) && entry[0] === 21350203)
+ count++;
}
- if (node.tagName !== "META")
- continue;
+ return count;
+}
- const match = /^og:(.+)/.exec(node.getAttribute("property"));
- if (!match)
- continue;
+PageHandler_Edit.prototype.make_preview = function() {
+ /* Make sure the view is scrollable. */
+ document.body.style.width = "100vw";
+ document.body.style.height = "100vh";
+ document.body.style.overflow = "scroll";
- og[match[1]] = node.getAttribute("content");
-}
+ let container = document.querySelectorAll(".waffle")[0];
+ let main_gid = null;
+ while (container) {
+ container = container.parentElement;
+ console.log(container);
+ if (container === document.body || !container)
+ break;
-/* Construct download link. */
+ const match = /([0-9]+)-grid-container/.exec(container.id);
+ if (match)
+ main_gid = match[1];
-let download_link = null;
+ container.removeAttribute("style");
+ container.style.width = "-moz-fit-content";
+ container.style.width = "fit-content";
+ }
-const match = new RegExp("/spreadsheets/d/([^/]+)").exec(document.URL);
-if (match)
- download_link = `https://docs.google.com/spreadsheets/d/${match[1]}/export`;
+ /*
+ * Hide editor bottom bar - it doesn't work anyway. Also hide no Javascript
+ * warning (if any).
+ */
+ for (const selector of [
+ "#grid-bottom-bar",
+ ".jfk-butterBar-warning",
+ "noscript"
+ ]) {
+ for (const element of document.querySelectorAll(selector))
+ element.style.display = "none";
+ }
+ /* Hide non-working tool bars and widgets above the sheet grid. */
+ const editor_container = document.getElementById("docs-editor-container");
+ for (const element of editor_container.parentElement.children) {
+ if (element !== editor_container)
+ element.style.display = "none";
+ }
-/* Add title bar with sheet name and download button. */
+ /* Add information about additional sheets that are not visible. */
+ try {
+ const subsheets_count = this.get_sheets_count();
+ if (subsheets_count > 1) {
+ const notice = document.createElement("aside");
-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");
+ notice.style.display = "block";
+ notice.style.backgroundColor = "white";
+ notice.style.margin = "5px";
+ notice.style.fontStyle = "italic";
-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;");
+ notice.innerText = `This document contains ${subsheets_count - 1} additional subsheet(s). Download to view.`;
-if (og.title) {
- title_text.textContent = og.title;
- title_heading.appendChild(title_text);
+ editor_container.prepend(notice);
+ }
+ } catch(e) {
+ console.error(e);
+ }
}
-title_text.setAttribute("style", "margin-right: 10px;");
-
-if (download_link) {
- main_download_button.setAttribute("href", download_link);
- title_heading.appendChild(main_download_button);
+PageHandler_Edit.prototype.get_doc_title = function() {
+ const title_span = document.getElementById("docs-title-input-label-inner");
+ return title_span ? title_span.innerText : "<unknown document>";
}
-title_bar.setAttribute("style", "padding: 0 20px; color: #555;");
+PageHandler_Edit.prototype.get_download_link = function() {
+ const link = new URL(window.location.href);
+ link.pathname = link.pathname.replace(/edit$/, "export");
+ link.search = new URLSearchParams({format: "ods"});
+
+ return "" + link;
+}
-title_bar.appendChild(title_heading);
+/* For URLs where path ends with "/pub", "/htmlview" and "/pubhtml". */
+function PageHandler_PubHtmlviewPubhtml() {
+}
-document.body.insertBefore(title_bar, document.body.firstElementChild);
+PageHandler_PubHtmlviewPubhtml.prototype.make_preview = function() {
+ /* Remove the "Updated automatically every 5 minutes" text. */
+ for (const elem of [...document.querySelectorAll("#footer>.dash+a~*")])
+ elem.remove();
+ /*
+ * If there is more than one sheet, make sheets switchable through button
+ * clicks.
+ */
+ const views = [...document.querySelectorAll("#sheets-viewport>[id]")];
-/* Extract sheet data from a script that sets the `bootstrapData' variable. */
+ function get_button(sheet_view) {
+ return document.getElementById(`sheet-button-${sheet_view.id}`);
+ }
-let data = null;
-for (const script of document.scripts) {
- const match = /bootstrapData = ({([^;]|[^}];)+})/.exec(script.textContent);
- if (!match)
- continue;
- data = JSON.parse(match[1]);
-}
+ function show_view(sheet_view) {
+ for (const processed_view of views) {
+ const button = get_button(processed_view);
-/*
- * Add download buttons for individual sheets belonging to this spreadsheet.
- * Data schema has been observed by looking at various spreadsheets.
- */
+ processed_view.style.display = processed_view === sheet_view ?
+ "initial": "none";
-function add_sheet_download(data)
-{
- if (!Array.isArray(data))
- return;
+ if (button !== null)
+ button.style.textDecoration = processed_view === sheet_view ?
+ "underline" : "";
+ }
+ }
- const gid = data[2];
- if (!["string", "number"].includes(typeof gid))
- return;
+ for (const sheet_view of views) {
+ const button = get_button(sheet_view);
+ if (button !== null)
+ button.addEventListener("click", () => show_view(sheet_view));
+ }
- const sheet_download_link = `${download_link}?gid=${gid}`;
- const sheet_download_button = document.createElement("a");
+ /* Make one of the sheets visible from the beginning. */
+ if (views.length > 0) {
+ let initial_view = views[0];
- 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);
+ const gid = "" + initial_gid();
- 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") {
+ for (const sheet_view of views) {
+ if (sheet_view.id === gid)
+ initial_view = sheet_view;
+ }
- 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}>`;
+ show_view(initial_view);
}
- title_bar.appendChild(sheet_download_button);
+ /* Hide doc title (we'll be replacing it with our own element). */
+ const doc_title_elem = document.getElementById("doc-title");
+ if (doc_title_elem !== null)
+ doc_title_elem.style.display = "none";
}
-if (download_link) {
- for (const entry of data.changes.topsnapshot) {
- if (!Array.isArray(entry) || entry[0] !== 21350203 ||
- typeof entry[1] !== "string")
- continue;
+PageHandler_PubHtmlviewPubhtml.prototype.get_doc_title = function() {
+ const title_span = document.querySelector("#doc-title .name");
+ return title_span ? title_span.innerText : "<unknown document>";
+}
+
+PageHandler_PubHtmlviewPubhtml.prototype.get_download_link = function() {
+ if (!window.location.pathname.endsWith("/pub"))
+ return null;
+
+ const link = new URL(window.location.href);
+ link.search = new URLSearchParams({output: "ods"});
+
+ return "" + link;
+}
+
+function main() {
+ let page_handler = null;
- let entry_data = null;
+ if (window.location.pathname.endsWith("/edit"))
+ page_handler = new PageHandler_Edit();
+ else if (window.location.pathname.endsWith("/pub") ||
+ window.location.pathname.endsWith("/htmlview") ||
+ window.location.pathname.endsWith("/pubhtml"))
+ page_handler = new PageHandler_PubHtmlviewPubhtml();
+ if (page_handler === null) {
+ console.error("Unknown type of docs page.");
+ } else {
try {
- entry_data = JSON.parse(entry[1]);
- } catch (e) {
- console.log(e);
- continue;
+ page_handler.make_preview();
+ } catch(e) {
+ console.error(e);
+ }
+
+ /* Add our own title&download bar */
+ const title = page_handler.get_doc_title();
+ const download_link = page_handler.get_download_link();
+
+ const doc_heading = document.createElement("h3");
+ const title_text = document.createElement("span");
+ const download_button = document.createElement("a");
+
+ title_text.style.margin = "0 20px";
+ title_text.textContent = title;
+ doc_heading.append(title_text);
+
+ download_button.style.borderRadius = "5px";
+ download_button.style.padding = "10px";
+ download_button.style.color = "#333";
+ download_button.style.backgroundColor = "lightgreen";
+ download_button.style.textDecoration = "none";
+ download_button.style.boxShadow = "-4px 8px 8px #888";
+ download_button.style.display = "inline-block";
+ download_button.textContent = "download";
+
+ if (download_link === null) {
+ download_button.style.backgroundColor = "lightgray";
+ download_button.style.userSelect = "none";
+ download_button.style.color = "#606060";
+ download_button.textContent = "download unavailable";
+ } else {
+ download_button.href = download_link;
}
- add_sheet_download(entry_data);
+ doc_heading.append(download_button);
+
+ doc_heading.style.padding = "0 20px; color: #555";
+ document.body.prepend(doc_heading);
}
}
+
+if (!reload_needed)
+ main();
diff --git a/src/docs-google-com-fix-sheets-display/index.json b/src/docs-google-com-fix-sheets-display/index.json
index 92fee67..f7fc848 100644
--- a/src/docs-google-com-fix-sheets-display/index.json
+++ b/src/docs-google-com-fix-sheets-display/index.json
@@ -20,8 +20,8 @@
"identifier": "docs-google-com-fix-sheets-display",
"long_name": "Google Sheets display&download fix",
"uuid": "ad496895-3302-4045-8f2d-ecac8f151d5b",
- "version": [2022, 2, 21],
- "revision": 2,
+ "version": [2022, 8, 16],
+ "revision": 1,
"description": "Make spreadsheets on docs.google.com viewable and downloadable without relying on site-served JavaScript.",
"dependencies": [],
"scripts": [{"file": "google_sheets_download.js"}],