Project

General

Profile

Download (6.52 KB) Statistics
| Branch: | Revision:

hydrilla-fixes-bundle / src / google_sheets_download.js @ bac96457

1
/**
2
 * SPDX-License-Identifier: LicenseRef-GPL-3.0-or-later-WITH-js-exceptions
3
 *
4
 * Make spreadsheets on drive.google.com browsable without nonfree js
5
 *
6
 * Copyright (C) 2021 Wojtek Kosior <koszko@koszko.org>
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * As additional permission under GNU GPL version 3 section 7, you
19
 * may distribute forms of that code without the copy of the GNU
20
 * GPL normally required by section 4, provided you include this
21
 * license notice and, in case of non-source distribution, a URL
22
 * through which recipients can access the Corresponding Source.
23
 * If you modify file(s) with this exception, you may extend this
24
 * exception to your version of the file(s), but you are not
25
 * obligated to do so. If you do not wish to do so, delete this
26
 * exception statement from your version.
27
 *
28
 * As a special exception to the GPL, any HTML file which merely
29
 * makes function calls to this code, and for that purpose
30
 * includes it by reference shall be deemed a separate work for
31
 * copyright law purposes. If you modify this code, you may extend
32
 * this exception to your version of the code, but you are not
33
 * obligated to do so. If you do not wish to do so, delete this
34
 * exception statement from your version.
35
 *
36
 * You should have received a copy of the GNU General Public License
37
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
38
 *
39
 * I, Wojtek Kosior, thereby promise not to sue for violation of this file's
40
 * license. Although I request that you do not make use of this code in a
41
 * proprietary program, I am not going to enforce this in court.
42
 */
43

    
44
/* Use with https://docs.google.com/spreadsheets/d/** */
45

    
46
/* Make the view scrollable. */
47

    
48
document.body.setAttribute("style",
49
			   "width: 100vw; height: 100vh; overflow: scroll;" +
50
			   (document.body.getAttribute("style") || ""));
51

    
52
let container = document.querySelectorAll(".waffle")[0];
53
let main_gid = null;
54

    
55
while (container) {
56
    container = container.parentElement;
57
    console.log(container);
58
    if (container === document.body || !container)
59
	break;
60

    
61
    const match = /([0-9]+)-grid-container/.exec(container.id);
62
    if (match)
63
	main_gid = match[1];
64

    
65
    container.setAttribute("style",
66
			   "width: fit-content; width: -moz-fit-content;");
67
}
68

    
69

    
70
/* Remove editor toolbars and bottom bar - these don't work anyway. */
71

    
72
const docs_chrome = document.getElementById("docs-chrome");
73
if (docs_chrome)
74
    docs_chrome.remove();
75
const grid_bottom_bar = document.getElementById("grid-bottom-bar");
76
if (grid_bottom_bar)
77
    grid_bottom_bar.remove();
78

    
79

    
80
/* Remove no Javascript warning. */
81

    
82
for (const no_js_warning of document.querySelectorAll("noscript"))
83
    no_js_warning.remove();
84

    
85

    
86
/* Get opengraph data. */
87

    
88
const og = {};
89

    
90
for (const node of document.head.childNodes) {
91
    if (node.tagName === "STYLE") {
92
	document.head.removeChild(node);
93
	continue;
94
    }
95

    
96
    if (node.tagName !== "META")
97
	continue;
98

    
99
    const match = /^og:(.+)/.exec(node.getAttribute("property"));
100
    if (!match)
101
	continue;
102

    
103
    og[match[1]] = node.getAttribute("content");
104
}
105

    
106

    
107
/* Construct download link. */
108

    
109
let download_link = null;
110

    
111
const match = new RegExp("/spreadsheets/d/([^/]+)").exec(document.URL);
112
if (match)
113
    download_link = `https://docs.google.com/spreadsheets/d/${match[1]}/export`;
114

    
115

    
116
/* Add title bar with sheet name and download button. */
117

    
118
const title_bar = document.createElement("div");
119
const title_heading = document.createElement("h1");
120
const title_text = document.createElement("span");
121
const main_download_button = document.createElement("a");
122

    
123
main_download_button.textContent = "download";
124
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;");
125

    
126
if (og.title) {
127
    title_text.textContent = og.title;
128
    title_heading.appendChild(title_text);
129
}
130

    
131
title_text.setAttribute("style", "margin-right: 10px;");
132

    
133
if (download_link) {
134
    main_download_button.setAttribute("href", download_link);
135
    title_heading.appendChild(main_download_button);
136
}
137

    
138
title_bar.setAttribute("style", "padding: 0 20px; color: #555;");
139

    
140
title_bar.appendChild(title_heading);
141

    
142
document.body.insertBefore(title_bar, document.body.firstElementChild);
143

    
144

    
145
/* Extract sheet data from a script that sets the `bootstrapData' variable. */
146

    
147
let data = null;
148
for (const script of document.scripts) {
149
    const match = /bootstrapData = ({([^;]|[^}];)+})/.exec(script.textContent);
150
    if (!match)
151
	continue;
152
    data = JSON.parse(match[1]);
153
}
154

    
155
/*
156
 * Add download buttons for individual sheets belonging to this spreadsheet.
157
 * Data schema has been observed by looking at various spreadsheets.
158
 */
159

    
160
function add_sheet_download(data)
161
{
162
    if (!Array.isArray(data))
163
	return;
164

    
165
    const gid = data[2];
166
    if (!["string", "number"].includes(typeof gid))
167
	return;
168

    
169
    const sheet_download_link = `${download_link}?gid=${gid}`;
170
    const sheet_download_button = document.createElement("a");
171

    
172
    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;");
173
    sheet_download_button.setAttribute("href", sheet_download_link);
174

    
175
    let sheet_name = null;
176
    if (Array.isArray(data[3]) &&
177
	data[3][0] && typeof data[3][0] === "object"
178
	&& Array.isArray(data[3][0][1]) &&
179
	Array.isArray(data[3][0][1][0]) &&
180
	typeof data[3][0][1][0][2] === "string") {
181

    
182
	const sheet_name = data[3][0][1][0][2];
183
	sheet_download_button.textContent = sheet_name;
184
        if (gid == main_gid)
185
	    title_text.textContent = `${title_text.textContent} - ${sheet_name}`;
186
    } else {
187
	sheet_download_button.textContent = `<sheet gid=${gid}>`;
188
    }
189

    
190
    title_bar.appendChild(sheet_download_button);
191
}
192

    
193
if (download_link) {
194
    for (const entry of data.changes.topsnapshot) {
195
	if (!Array.isArray(entry) || entry[0] !== 21350203 ||
196
	    typeof entry[1] !== "string")
197
	    continue;
198

    
199
	let entry_data = null;
200

    
201
	try {
202
	    entry_data = JSON.parse(entry[1]);
203
	} catch (e) {
204
	    console.log(e);
205
	    continue;
206
	}
207

    
208
	add_sheet_download(entry_data);
209
    }
210
}
(8-8/22)