Last active
May 2, 2023 02:05
-
-
Save Roytangrb/7df5bd7c0321debb9df8e539662b08ea to your computer and use it in GitHub Desktop.
Tampermonkey script to save Leetcode question No., name, URL and current code in editor to a download file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Save My Solution | |
| // @namespace https://gist.github.com/Roytangrb/7df5bd7c0321debb9df8e539662b08ea | |
| // @version 2.2 | |
| // @updateURL https://gist.githubusercontent.com/Roytangrb/7df5bd7c0321debb9df8e539662b08ea/raw/save-my-solution.js | |
| // @downloadURL https://gist.githubusercontent.com/Roytangrb/7df5bd7c0321debb9df8e539662b08ea/raw/save-my-solution.js | |
| // @description Save Leetcode question No., name, URL and current code in editor to a download file | |
| // @author Roy Tang | |
| // @match https://leetcode.com/problems/* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=leetcode.com | |
| // @run-at document-end | |
| // @grant none | |
| // ==/UserScript== | |
| (function () { | |
| "use strict"; | |
| var $ = document.querySelector.bind(document); | |
| var $$ = document.querySelectorAll.bind(document); | |
| var BTN_GROUP_DIV_ID = "fj8agdsl344"; | |
| var DOWNLOAD_BTN_ID = "fj8agdsl345"; | |
| var COPY_TITLE_BTN_ID = "fj8agdsl346"; | |
| var TEXT_COLOR = "rgba(239, 241, 246, .75)"; | |
| var AUTHOR = "RT"; | |
| // To support a new lanuage, add an entry here: | |
| var LANG_SUPPORTED = Object.freeze({ | |
| python3: { | |
| commentPrefix: "# ", | |
| extension: ".py", | |
| }, | |
| rust: { | |
| commentPrefix: "// ", | |
| extension: ".rs", | |
| }, | |
| c: { | |
| commentPrefix: "// ", | |
| extension: ".c", | |
| }, | |
| }); | |
| function getUrl() { | |
| return window.location.href; | |
| } | |
| function isEditView() { | |
| var url = getUrl(); | |
| var r = /^https:\/\/leetcode\.com\/problems\/[^/]*(\/description)?\/?$/; | |
| return r.test(url); | |
| } | |
| function getQuestionTitle() { | |
| return document.title.split(" - ")[0]; | |
| } | |
| function getQuestionNumberString() { | |
| const titleEl = Array.from($$("#qd-content span")).find(function (el) { | |
| return (el.textContent + "").includes("."); | |
| }); | |
| return !!titleEl ? titleEl.textContent.split(".")[0].trim() : "9999"; | |
| } | |
| function getSelectedLanguage() { | |
| return $( | |
| "#qd-content div:nth-child(3) button:first-of-type" | |
| ).textContent.toLowerCase(); | |
| } | |
| function isLangSupported() { | |
| return LANG_SUPPORTED.hasOwnProperty(getSelectedLanguage()); | |
| } | |
| function getFilename() { | |
| var url = getUrl(); | |
| var kebabName = url.match(/^.*problems\/([^/]*)\/?.*$/)[1]; | |
| var lcNumber = getQuestionNumberString(); | |
| var extension = LANG_SUPPORTED[getSelectedLanguage()].extension; | |
| return lcNumber + "-" + kebabName + extension; | |
| } | |
| function getOrCreateBtnGroup() { | |
| var container = $("#" + BTN_GROUP_DIV_ID); | |
| if (container) { | |
| return container; | |
| } | |
| container = document.createElement("div"); | |
| container.id = BTN_GROUP_DIV_ID; | |
| container.style.display = "flex"; | |
| container.style.position = "absolute"; | |
| container.style.top = "15px"; | |
| container.style.left = "150px"; | |
| container.style.zIndex = "9999"; | |
| document.body.appendChild(container); | |
| return container; | |
| } | |
| function createButton(id, innerText) { | |
| var btn = document.createElement("button"); | |
| btn.style.marginLeft = "10px"; | |
| btn.style.color = TEXT_COLOR; | |
| btn.innerText = innerText; | |
| btn.id = id; | |
| return btn; | |
| } | |
| function insertDownloadBtn() { | |
| var container = getOrCreateBtnGroup(); | |
| var btn = createButton(DOWNLOAD_BTN_ID, "Download my code"); | |
| container.append(btn); | |
| return btn; | |
| } | |
| function insertCopyTitleBtn() { | |
| var container = getOrCreateBtnGroup(); | |
| var btn = createButton(COPY_TITLE_BTN_ID, "Copy title"); | |
| container.append(btn); | |
| return btn; | |
| } | |
| function unescapeSlash(contents) { | |
| var replacements = { "\\\\": "\\", "\\n": "\n", '\\"': '"' }; | |
| return contents.replace(/\\(\\|n|")/g, function (replace) { | |
| return replacements[replace]; | |
| }); | |
| } | |
| function getCodeTextFromStorage() { | |
| var prefix = getQuestionNumberString() + "_"; | |
| var suffix = getSelectedLanguage() + "_code"; | |
| for (var i = 0; i < localStorage.length; i++) { | |
| var key = localStorage.key(i); | |
| if (key.startsWith(prefix) && key.endsWith(suffix)) { | |
| // unescape multi-line content and remove quotes | |
| return unescapeSlash(localStorage.getItem(key)).slice(1, -1); | |
| } | |
| } | |
| console.warn("Code not found in localStorage"); | |
| return ""; | |
| } | |
| // Get code lines from dom | |
| function getCodeLinesFromDom() { | |
| var lines = []; | |
| var translates = [ | |
| [/\u00a0/g, " "], // replace with single space | |
| [/\u200b/g, ""], // remove zero width space | |
| ]; | |
| $$(".view-lines .view-line").forEach(function (line) { | |
| lines.push( | |
| translates.reduce(function (str, rule) { | |
| return str.replace(rule[0], rule[1]); | |
| }, line.textContent) | |
| ); | |
| }); | |
| return lines; | |
| } | |
| function generateFileHeader() { | |
| var commentPrefix = LANG_SUPPORTED[getSelectedLanguage()].commentPrefix; | |
| var lines = [ | |
| "Author: " + AUTHOR, | |
| "Date: " + new Date().toISOString(), | |
| "URL: " + getUrl(), | |
| ]; | |
| return ( | |
| lines.reduce(function (s, line) { | |
| return s + commentPrefix + line + "\n"; | |
| }, "") + "\n\n" | |
| ); | |
| } | |
| function download(filename, url) { | |
| var elm = document.createElement("a"); | |
| elm.setAttribute("href", url); | |
| elm.setAttribute("download", filename); | |
| document.body.appendChild(elm); | |
| elm.click(); | |
| document.body.removeChild(elm); | |
| setTimeout(function () { | |
| URL.revokeObjectURL(url); | |
| }, 100); | |
| } | |
| function main() { | |
| console.log("Tampermonkey: save my leetcode solution"); | |
| if (!isEditView()) { | |
| console.log("not in edit view"); | |
| return; | |
| } | |
| var downloadBtn = insertDownloadBtn(); | |
| downloadBtn.addEventListener("click", function () { | |
| if (!isLangSupported()) { | |
| alert( | |
| "The selected language is not supported. Please modify the userscript." | |
| ); | |
| return; | |
| } | |
| // var codeText = getCodeLinesFromDom().join("\n"); | |
| var codeText = getCodeTextFromStorage(); | |
| var filename = getFilename(); | |
| var file = new File([generateFileHeader() + codeText], filename, { | |
| type: "text/plain", | |
| }); | |
| var objUrl = URL.createObjectURL(file); | |
| download(filename, objUrl); | |
| }); | |
| var copyTitleBtn = insertCopyTitleBtn(); | |
| copyTitleBtn.addEventListener("click", function () { | |
| navigator.clipboard | |
| .writeText(getQuestionTitle()) | |
| .then(function () { | |
| copyTitleBtn.innerText = "Copied"; | |
| }) | |
| .catch(console.error); | |
| }); | |
| } | |
| // TODO: on document ready | |
| setTimeout(main, 5000); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment