diff options
Diffstat (limited to 'content/sgoogle_drive_browse/google_drive_browse_folder.js')
-rw-r--r-- | content/sgoogle_drive_browse/google_drive_browse_folder.js | 373 |
1 files changed, 0 insertions, 373 deletions
diff --git a/content/sgoogle_drive_browse/google_drive_browse_folder.js b/content/sgoogle_drive_browse/google_drive_browse_folder.js deleted file mode 100644 index 7843b15..0000000 --- a/content/sgoogle_drive_browse/google_drive_browse_folder.js +++ /dev/null @@ -1,373 +0,0 @@ -/** - * 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. - */ - -/* Use with https://drive.google.com/drive/folders/*** */ - -/* Define how to handle various mime types used by Google. */ - -const known_mimes = { - "application/vnd.google-apps.folder": { - links: id => ({ - view: `https://drive.google.com/drive/folders/${id}` - }), - type: "folder", - is_folder: true - }, - "application/vnd.google-apps.shortcut": { - links: id => ({ - view: `https://drive.google.com/drive/folders/${id}` - }), - type: "shortcut", - is_folder: true - }, - "application/vnd.google-apps.document": { - links: id => ({ - view: `https://docs.google.com/document/d/${id}`, - download: `https://docs.google.com/document/d/${id}/export?format=odt`, - }), - type: "Google text document", - new_mime: "application/vnd.oasis.opendocument.text" - }, - "application/vnd.google-apps.spreadsheet": { - links: id => ({ - view: `https://docs.google.com/spreadsheets/d/${id}`, - download: `https://docs.google.com/spreadsheets/d/${id}/export?format=ods` - }), - type: "Google spreadsheet", - new_mime: "application/vnd.oasis.opendocument.spreadsheet" - }, - "application/vnd.google-apps.presentation": { - links: id => ({ - view: `https://docs.google.com/presentation/d/${id}`, - download: `https://docs.google.com/presentation/d/${id}/export/pptx` - }), - type: "Google presentation", - new_mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation" - }, - "application/vnd.google-apps.drawing": { - links: id => ({ - view: `https://docs.google.com/drawings/d/${id}`, - download: `https://docs.google.com/drawings/d/${id}/export/jpeg` - }), - type: "Google drawing", - new_mime: "image/jpeg" - }, - "application/vnd.google-apps.script": { - links: id => ({ - download: `https://script.google.com/feeds/download/export?format=json&id=${id}` - }), - type: "Google script", - new_mime: "application/vnd.google-apps.script+json" - }, - "application/vnd.google-apps.jam": { - links: id => ({ - download: `https://jamboard.google.com/export?id=${id}` - }), - type: "Google jam", - new_mime: "application/pdf" - } -}; - -/* - * Human-friendly names defined here will be displayed to the user instead of - * raw mime types. Please add more here. - */ -let mime_display_overrides = { - "image/jpeg": "JPEG image", - "application/octet-stream": "binary data", - "application/pdf": "PDF document", - "application/rar": "RAR archive", - "application/zip": "ZIP archive" -}; - -let default_link_producer = id => ({ - view: `https://drive.google.com/file/d/${id}`, - download: `https://drive.google.com/uc?export=download&id=${id}` -}); - -for (const [mime, display_name] of Object.entries(mime_display_overrides)) { - known_mimes[mime] = { - links: default_link_producer, - type: display_name, - new_mime: mime - } -} - -delete mime_display_overrides; - -function get_mime_info(mime) { - return known_mimes[mime] || { - links: default_link_producer, - type: mime || "", - new_mime: mime || "application/octet-stream" - } -} - -/* Prepare folder contents data as well as data regarding the folder itself. */ - -const content = new Map(); - -function add_content_item(item) -{ - const old_item = content.get(item.id) || {}; - Object.assign(old_item, item); - content.set(item.id, old_item); -} - -const this_folder = {}; - -function replace_string_escape(match, group) -{ - return String.fromCharCode(`0x${group}`); -} - -function try_parse(data, replace_x_escapes) -{ - if (!data) - return null; - - if (replace_x_escapes) - data = data.replaceAll(/\\x([0-9a-f]{2})/g, replace_string_escape); - - try { - return JSON.parse(data); - } catch (e) { - console.log(e); - } - - return null; -} - -function process_file_data(file_data, callback) -{ - if (!Array.isArray(file_data) || !typeof file_data[0] === "string") { - console.log("cannot process the following file data object:", - file_data); - return; - } - - const result = {id: file_data[0], folders: []}; - - if (Array.isArray(file_data[1])) { - for (const item of file_data[1]) { - if (typeof item === "string" && item !== this_folder.id) - result.folders.push(item); - } - } - if (typeof file_data[2] === "string") - result.filename = file_data[2]; - if (typeof file_data[3] === "string" && file_data[3].search("/") >= 0) - result.mime = file_data[3]; - if (typeof file_data[9] === "number") - result.date1 = new Date(file_data[9]); - if (typeof file_data[10] === "number") - result.date2 = new Date(file_data[10]); - - callback(result); -} - -/* - * By searching for scripts with calls to AF_initDataCallback we get about 7 - * matches. All arguments to this function seem to be arrays parseable as JSON, - * but their contents are very different and only two of those ~7 arrays - * actually hold some data useful to us. Here we try to filter out the other - * cases and then extract the useful data. - */ -function process_af_init_data(data) -{ - if (!Array.isArray(data) || !/^driveweb/.test(data[0]) || - !Array.isArray(data[1])) - return; - - /* First useful "kind" of object we can encounter is this folder's data. */ - if (typeof data[1][0] === "string") { - process_file_data(data[1], item => Object.assign(this_folder, item)); - return; - } - - /* - * Second "kind" of object holds data about all items in the folder. - * Folders and Files are stored in separate lists (doesn't matter to us, - * since we distinguish them based on mime type anyway). - */ - for (const data_sub of data[1]) { - if (!Array.isArray(data_sub) || !/^driveweb/.test(data_sub[0]) || - !Array.isArray(data_sub[1])) - continue; - for (const item of data_sub[1]) - process_file_data(item, add_content_item); - } -} - -/* - * Folder items data actually exists in both of the 2 kinds of scripts we search - * for. In case of both of the regexes below we call `process_file_data' in the - * end. As a result we process the same file multiple time as it appears in 2 - * scripts. This is, however, a good thing, because it makes a change less - * likely to break our fix. - */ - -const ivd_data_regex = /\s*window\s*\['_DRIVE_ivd'\]\s*=\s*'(\\x5b[^']+)'(.*)$/; -const af_init_data_regex = /AF_initDataCallback\s*\(.+data\s*:\s*(\[.*\])[^\]]+$/; - -for (const script of document.scripts) { - const ivd_data_match = ivd_data_regex.exec(script.textContent); - if (ivd_data_match) { - const ivd_data = try_parse(ivd_data_match[1], true); - if (ivd_data && Array.isArray(ivd_data) && Array.isArray(ivd_data[0])) { - for (const item of ivd_data[0]) - process_file_data(item, add_content_item); - } - } - - const af_init_data_match = af_init_data_regex.exec(script.textContent); - if (af_init_data_match) { - const af_init_data = try_parse(af_init_data_match[1], false); - if (af_init_data) - process_af_init_data(af_init_data); - } -} - -/* Construct our own user interface. */ - -const body = document.createElement("body"); -const folders = document.createElement("div"); -const files = document.createElement("div"); -const folders_heading = document.createElement("h2"); -const files_heading = document.createElement("h2"); - -folders_heading.textContent = "Folders"; -files_heading.textContent = "Files"; - -let has_folders = false; -let has_files = false; - -folders.appendChild(folders_heading); -files.appendChild(files_heading); - -body.setAttribute("style", "width: 100vw; height: 100vh; overflow: scroll; color: #555; margin: 15px; -webkit-user-select: initial;"); - -const drive_folder_regex = /application\/vnd.google-apps.(folder|shortcut)/; - -function add_item_to_view(item_data) -{ - const item_div = document.createElement("div"); - const item_heading = document.createElement("h4"); - - item_div.setAttribute("style", "border: 2px solid #999; border-radius: 8px; padding: 10px; display: inline-block; margin: 2px;"); - - let item_heading_style = "margin: 8px 4px;"; - - if (item_data.filename) { - item_heading.textContent = item_data.filename; - } else { - item_heading.textContent = "(no name)"; - item_heading_style += " font-style:italic;"; - } - item_heading.setAttribute("style", item_heading_style); - item_div.appendChild(item_heading); - - const mime_info = get_mime_info(item_data.mime); - - if (mime_info.type) { - const type_div = document.createElement("div"); - type_div.setAttribute("style", "margin-bottom: 5px;"); - type_div.textContent = mime_info.type; - - item_div.appendChild(type_div); - } - - if (mime_info.is_folder) - has_folders = true; - else - has_files = true; - - const links = {}; - if (item_data.id) - Object.assign(links, mime_info.links(item_data.id)); - - if (links.view) { - const view_button = document.createElement("a"); - view_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: 5px;"); - view_button.textContent = "view"; - view_button.href = links.view; - - item_div.appendChild(view_button); - } - - if (links.download) { - const download_button = document.createElement("a"); - 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: 5px;"); - download_button.textContent = "download"; - download_button.href = links.download; - download_button.type = mime_info.new_mime; - - item_div.appendChild(download_button); - } - - (mime_info.is_folder ? folders : files).appendChild(item_div); -} - -for (const item of content.values()) - add_item_to_view(item); - -if (this_folder.filename) { - const heading = document.createElement("h1"); - heading.textContent = this_folder.filename; - body.appendChild(heading); -} -if (this_folder.folders && this_folder.folders.length > 0) { - for (const parent_id of this_folder.folders) { - const up_button = document.createElement("a"); - let text = "go up"; - - up_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: 5px;"); - up_button.href = `https://drive.google.com/drive/folders/${parent_id}`; - if (this_folder.folders.length > 1) - text = `${text} (${parent_id})`; - up_button.textContent = text; - - body.appendChild(up_button); - } -} -if (has_folders) - body.appendChild(folders); -if (has_files) - body.appendChild(files); -if (!(has_files || has_folders)) { - const no_files_message = document.createElement("h3"); - no_files_message.textContent = "No files found."; - body.appendChild(no_files_message); -} - -if (document.body.firstChild.id !== "af-error-container") - document.documentElement.replaceChild(body, document.body); |