├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── FUNDING.yml ├── LICENSE ├── README.md ├── assets └── logo.png ├── dist ├── extensionPopup.css ├── extensionPopupManager.js ├── index.html ├── main.js ├── main.js.LICENSE.txt ├── modules │ ├── AnalyticsManager.js │ ├── BufferManager │ │ └── GoogleSheetsBufferManager.js │ ├── ContainerManager.js │ ├── ContentBuilder │ │ ├── EditorialContentBuilder.js │ │ ├── TableContentBuilder.js │ │ └── TagsContentBuilder.js │ ├── DataFetcher │ │ ├── GoogleSheetsDataFetcher.js │ │ ├── GoogleSheetsDataGrabber.js │ │ └── LocalStorageDataFetcher.js │ ├── ElementGenerator │ │ ├── ElementHelperClass.js │ │ ├── TableContentElementGenerator.js │ │ └── TagContentElementGenerator.js │ ├── ElementModifier │ │ ├── CompanySwipperElementModifier.js │ │ ├── EditorialPageElementModifier.js │ │ ├── ProblemTableElementModifier.js │ │ ├── ProblemTagsElementModifier.js │ │ ├── TagPageProblemTableElementModifier.js │ │ └── TopProblemFoldoutElementModifier.js │ ├── Objects.js │ ├── ProblemSorter.js │ └── Unlocker │ │ ├── CompaniesProblemUnlocker.js │ │ ├── EditorialUnlocker.js │ │ ├── ProblemTableUnlocker.js │ │ ├── ProblemTagsUnlocker.js │ │ ├── TagPageProblemTableUnlocker.js │ │ └── TopProblemUnlocker.js └── style.css ├── manifest.json ├── package-lock.json └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/* linguist-documentation=false 2 | dist/* linguist-vendored=false 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - Browser [e.g. chrome, safari] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [31b4] 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Bence Szilagyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 🚨MAKE SURE TO KEEP YOUR EXTENSION UP TO DATE, WITH A ⭐️ ON THIS REPO🚨 2 | ## 👨‍🔧Help me maintenance this extension with a [☕ coffee](https://github.com/sponsors/31b4) 3 | 4 | 5 | # Leetcode Premium Bypass 6 | 7 | Unlock Leetcode's premium features without a subscription using the Leetcode Premium Bypass Chrome extension. 8 | 9 | - Credits: 10 | - Original creator of the extension: [@Edwardsoen](https://github.com/Edwardsoen) 11 | - A helpful contributor: [@Gourav-21](https://github.com/Gourav-21) 12 | ## Video Tutorial 13 | Watch the installation guide on [X](https://twitter.com/31b4_/status/1748694097433395416). 14 | 15 | ## Known Issues with solutions 16 | - Editorial now working on dynamic UI: https://github.com/31b4/Leetcode-Premium-Bypass/issues/14 17 | - Failed to load extension: https://github.com/31b4/Leetcode-Premium-Bypass/issues/44 18 | 19 | ## Table of Contents 20 | - [Installation](#installation) 21 | - [Step 1: Download and Unzip](#step-1-download-and-unzip) 22 | - [Step 2: Enable Developer Mode](#step-2-enable-developer-mode) 23 | - [Step 3: Load the Extension](#step-3-load-the-extension) 24 | - [Usage](#usage) 25 | - [Step 4: Test the Extension](#step-4-test-the-extension) 26 | - [Troubleshooting](#troubleshooting) 27 | 28 | ## Installation 29 | 30 | ### Step 1: Download and Unzip 31 | 32 | 1. Download the latest release [here](https://github.com/31b4/Leetcode-Premium-Bypass/releases/latest). 33 | 2. Unzip the downloaded file. 34 | 35 | ### Step 2: Enable Developer Mode 36 | 37 | 1. Open your Chrome browser. 38 | 2. Navigate to 'chrome://extensions/' or go to 'Settings' -> 'Extensions' on the left sidebar. 39 | 3. Turn on Developer Mode using the toggle switch. 40 | 41 | ![Developer Mode](https://github.com/31b4/Leetcode-Premium-Bypass/assets/75566095/951ffb80-fa25-43ad-a211-d0bdf250606f) 42 | 43 | ### Step 3: Load the Extension 44 | 45 | 1. Click on 'Load unpacked.' 46 | 47 | ![Load Unpacked](https://github.com/31b4/Leetcode-Premium-Bypass/assets/75566095/3732c9af-b928-46d2-89a0-e0be17c46169) 48 | 49 | 2. Select the unzipped folder from Step 1. 50 | 51 | ![Select Folder](https://github.com/31b4/Leetcode-Premium-Bypass/assets/75566095/d55e46a0-510e-4090-9844-321a07e99154) 52 | 53 | ## Usage 54 | 55 | ### Step 4: Test the Extension 56 | 57 | 1. Go to [Leetcode](https://leetcode.com/problemset/all/). 58 | 2. Refresh the page. 59 | 3. If you see green Frequency bars, it's working. 60 | 4. Click on companies to see the most common questions. 61 | 5. Find a premium question, and refresh the page if its title is not green. 62 | 6. If it's green, click on it and enjoy :D 63 | 64 | ![Extension in Action](https://github.com/31b4/Leetcode-Premium-Bypass/assets/75566095/e4d41e19-af43-4c3c-9476-256a5ab7e159) 65 | ### Troubleshooting 66 | **If it's not working, feel free to write on [Issues](https://github.com/31b4/Leetcode-Premium-Bypass/issues); I'm here to help.** 67 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gourav-21/Leetcode-Premium-Bypass/b3263a04243bbce6a12dcac0abdedd26b694456a/assets/logo.png -------------------------------------------------------------------------------- /dist/extensionPopup.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Montserrat', sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | background-color: #ee9b1a; 6 | color: #2c2c2c; 7 | } 8 | 9 | .popup-container { 10 | width: 300px; 11 | padding: 20px; 12 | } 13 | 14 | h1 { 15 | font-size: 20px; 16 | margin-bottom: 15px; 17 | } 18 | 19 | .section { 20 | margin-bottom: 20px; 21 | } 22 | 23 | 24 | 25 | ul { 26 | list-style: none; 27 | padding: 0; 28 | } 29 | 30 | li { 31 | margin-bottom: 5px; 32 | } 33 | 34 | a { 35 | font-size: 16px; 36 | text-decoration: none; 37 | color: #4c4c4c; 38 | } 39 | -------------------------------------------------------------------------------- /dist/extensionPopupManager.js: -------------------------------------------------------------------------------- 1 | 2 | function fetchLatestUpdate() { 3 | let range = "metadata!A:B" 4 | let url = `https://sheets.googleapis.com/v4/spreadsheets/1ilv8yYAIcggzTkehjuB_dsRI4LUxjkTPZz4hsBKJvwo/values/${range}?key=AIzaSyDDAE3rf1fjLGKM0FUHQeTcsmS6fCQjtDs` 5 | fetch(url) 6 | .then(data => data.json()) 7 | } 8 | 9 | function setTextToElement(data) { 10 | let strings = data["values"] 11 | for(let i =0; i <= strings.length-1; i ++) { 12 | let text = strings[i][0] + ": " + strings[i][1] 13 | let element = getSpan(text) 14 | let parent = document.getElementById("data-update-data") 15 | parent.appendChild(element) 16 | parent.appendChild(document.createElement('br')) 17 | } 18 | return data["values"][0][1] 19 | } 20 | 21 | function getSpan(text){ 22 | let span = document.createElement("span") 23 | span.textContent = text; 24 | return span 25 | } 26 | 27 | window.addEventListener('click',function(e){ 28 | if(e.target.href!==undefined){ 29 | chrome.tabs.create({url:e.target.href}) 30 | } 31 | }) 32 | 33 | fetchLatestUpdate() 34 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | async function waitForStableHTML() { 2 | return new Promise(resolve => { 3 | let isDOMStable = false; 4 | let iterationCount = 0; 5 | const maxIterations = 30; 6 | 7 | // Callback function to be executed when mutations are observed 8 | const mutationCallback = () => { 9 | // Set isDOMStable to false, indicating that there has been a change 10 | isDOMStable = false; 11 | }; 12 | 13 | // Create a new MutationObserver with the callback function 14 | const observer = new MutationObserver(mutationCallback); 15 | 16 | // Start observing mutations in the document 17 | observer.observe(document, { childList: true, subtree: true }); 18 | 19 | // Polling function to check for stability 20 | const pollForStability = () => { 21 | iterationCount++; 22 | 23 | if (isDOMStable || iterationCount >= maxIterations) { 24 | // If stable or reached max iterations, disconnect the observer and resolve the promise 25 | observer.disconnect(); 26 | resolve(); 27 | } else { 28 | // If not stable, continue polling after a short delay 29 | setTimeout(pollForStability, 100); // Adjust the delay as needed 30 | } 31 | }; 32 | 33 | // Start the polling mechanism 34 | pollForStability(); 35 | }); 36 | } 37 | 38 | async function waitAndContinue() { 39 | console.log("Waiting for stable HTML"); 40 | await waitForStableHTML(); 41 | console.log("End"); 42 | } 43 | 44 | (async () => { 45 | console.log("Start"); 46 | await waitAndContinue() 47 | var e = { 48 | 856: function(e) { 49 | e.exports = function() { 50 | "use strict"; 51 | const { 52 | entries: e, 53 | setPrototypeOf: t, 54 | isFrozen: n, 55 | getPrototypeOf: i, 56 | getOwnPropertyDescriptor: a 57 | } = Object; 58 | let { 59 | freeze: r, 60 | seal: o, 61 | create: l 62 | } = Object, { 63 | apply: s, 64 | construct: c 65 | } = "undefined" != typeof Reflect && Reflect; 66 | s || (s = function(e, t, n) { 67 | return e.apply(t, n) 68 | }), r || (r = function(e) { 69 | return e 70 | }), o || (o = function(e) { 71 | return e 72 | }), c || (c = function(e, t) { 73 | return new e(...t) 74 | }); 75 | const d = w(Array.prototype.forEach), 76 | h = w(Array.prototype.pop), 77 | m = w(Array.prototype.push), 78 | u = w(String.prototype.toLowerCase), 79 | p = w(String.prototype.toString), 80 | g = w(String.prototype.match), 81 | f = w(String.prototype.replace), 82 | b = w(String.prototype.indexOf), 83 | y = w(String.prototype.trim), 84 | E = w(RegExp.prototype.test), 85 | T = (C = TypeError, function() { 86 | for (var e = arguments.length, t = new Array(e), n = 0; n < e; n++) t[n] = arguments[n]; 87 | return c(C, t) 88 | }); 89 | var C; 90 | 91 | function w(e) { 92 | return function(t) { 93 | for (var n = arguments.length, i = new Array(n > 1 ? n - 1 : 0), a = 1; a < n; a++) i[a - 1] = arguments[a]; 94 | return s(e, t, i) 95 | } 96 | } 97 | 98 | function v(e, i, a) { 99 | var r; 100 | a = null !== (r = a) && void 0 !== r ? r : u, t && t(e, null); 101 | let o = i.length; 102 | for (; o--;) { 103 | let t = i[o]; 104 | if ("string" == typeof t) { 105 | const e = a(t); 106 | e !== t && (n(i) || (i[o] = e), t = e) 107 | } 108 | e[t] = !0 109 | } 110 | return e 111 | } 112 | 113 | function D(t) { 114 | const n = l(null); 115 | for (const [i, a] of e(t)) n[i] = a; 116 | return n 117 | } 118 | 119 | function M(e, t) { 120 | for (; null !== e;) { 121 | const n = a(e, t); 122 | if (n) { 123 | if (n.get) return w(n.get); 124 | if ("function" == typeof n.value) return w(n.value) 125 | } 126 | e = i(e) 127 | } 128 | return function(e) { 129 | return console.warn("fallback value for", e), null 130 | } 131 | } 132 | const A = r(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "shadow", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]), 133 | N = r(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]), 134 | k = r(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]), 135 | L = r(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]), 136 | S = r(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]), 137 | _ = r(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]), 138 | R = r(["#text"]), 139 | P = r(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "pattern", "placeholder", "playsinline", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "xmlns", "slot"]), 140 | x = r(["accent-height", "accumulate", "additive", "alignment-baseline", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]), 141 | O = r(["accent", "accentunder", "align", "bevelled", "close", "columnsalign", "columnlines", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lspace", "lquote", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]), 142 | I = r(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]), 143 | B = o(/\{\{[\w\W]*|[\w\W]*\}\}/gm), 144 | F = o(/<%[\w\W]*|[\w\W]*%>/gm), 145 | U = o(/\${[\w\W]*}/gm), 146 | H = o(/^data-[\-\w.\u00B7-\uFFFF]/), 147 | j = o(/^aria-[\-\w]+$/), 148 | z = o(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i), 149 | q = o(/^(?:\w+script|data):/i), 150 | G = o(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g), 151 | $ = o(/^html$/i); 152 | var W = Object.freeze({ 153 | 154 | __proto__: null, 155 | MUSTACHE_EXPRESSION: B, 156 | ERB_EXPRESSION: F, 157 | TEMPLATE_LITERAL_EXPRESSION: U, 158 | DATA_ATTRIBUTE: H, 159 | ARIA_ATTRIBUTE: j, 160 | IS_ALLOWED_URI: z, 161 | IS_SCRIPT_OR_DATA: q, 162 | ATTRIBUTE_WHITESPACE: G, 163 | DOCTYPE_NAME: $ 164 | 165 | }); 166 | const Y = () => "undefined" == typeof window ? null : window; 167 | return function t() { 168 | let n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : Y(); 169 | const i = e => t(e); 170 | if (i.version = "3.0.3", i.removed = [], !n || !n.document || 9 !== n.document.nodeType) return i.isSupported = !1, i; 171 | const a = n.document, 172 | o = a.currentScript; 173 | let { 174 | document: l 175 | } = n; 176 | const { 177 | DocumentFragment: s, 178 | HTMLTemplateElement: c, 179 | Node: C, 180 | Element: w, 181 | NodeFilter: B, 182 | NamedNodeMap: F = n.NamedNodeMap || n.MozNamedAttrMap, 183 | HTMLFormElement: U, 184 | DOMParser: H, 185 | trustedTypes: j 186 | } = n, q = w.prototype, G = M(q, "cloneNode"), K = M(q, "nextSibling"), V = M(q, "childNodes"), X = M(q, "parentNode"); 187 | if ("function" == typeof c) { 188 | const e = l.createElement("template"); 189 | e.content && e.content.ownerDocument && (l = e.content.ownerDocument) 190 | } 191 | let J, Q = ""; 192 | const { 193 | implementation: Z, 194 | createNodeIterator: ee, 195 | createDocumentFragment: te, 196 | getElementsByTagName: ne 197 | } = l, { 198 | importNode: ie 199 | } = a; 200 | let ae = {}; 201 | i.isSupported = "function" == typeof e && "function" == typeof X && Z && void 0 !== Z.createHTMLDocument; 202 | const { 203 | 204 | 205 | MUSTACHE_EXPRESSION: re, 206 | ERB_EXPRESSION: oe, 207 | TEMPLATE_LITERAL_EXPRESSION: le, 208 | DATA_ATTRIBUTE: se, 209 | ARIA_ATTRIBUTE: ce, 210 | IS_SCRIPT_OR_DATA: de, 211 | ATTRIBUTE_WHITESPACE: he 212 | } = W; 213 | let { 214 | IS_ALLOWED_URI: me 215 | } = W, ue = null; 216 | const pe = v({}, [...A, ...N, ...k, ...S, ...R]); 217 | let ge = null; 218 | const fe = v({}, [...P, ...x, ...O, ...I]); 219 | let be = Object.seal(Object.create(null, { 220 | tagNameCheck: { 221 | writable: !0, 222 | configurable: !1, 223 | enumerable: !0, 224 | value: null 225 | }, 226 | attributeNameCheck: { 227 | writable: !0, 228 | configurable: !1, 229 | enumerable: !0, 230 | value: null 231 | }, 232 | allowCustomizedBuiltInElements: { 233 | writable: !0, 234 | configurable: !1, 235 | enumerable: !0, 236 | value: !1 237 | } 238 | })), 239 | ye = null, 240 | Ee = null, 241 | Te = !0, 242 | Ce = !0, 243 | we = !1, 244 | ve = !0, 245 | De = !1, 246 | Me = !1, 247 | Ae = !1, 248 | Ne = !1, 249 | ke = !1, 250 | Le = !1, 251 | Se = !1, 252 | _e = !0, 253 | Re = !1, 254 | Pe = !0, 255 | xe = !1, 256 | Oe = {}, 257 | Ie = null; 258 | const Be = v({}, ["annotation-xml", "audio", "colgroup", "desc", "foreignobject", "head", "iframe", "math", "mi", "mn", "mo", "ms", "mtext", "noembed", "noframes", "noscript", "plaintext", "script", "style", "svg", "template", "thead", "title", "video", "xmp"]); 259 | let Fe = null; 260 | const Ue = v({}, ["audio", "video", "img", "source", "image", "track"]); 261 | let He = null; 262 | const je = v({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]), 263 | ze = "http://www.w3.org/1998/Math/MathML", 264 | qe = "http://www.w3.org/2000/svg", 265 | Ge = "http://www.w3.org/1999/xhtml"; 266 | let $e = Ge, 267 | We = !1, 268 | Ye = null; 269 | const Ke = v({}, [ze, qe, Ge], p); 270 | let Ve; 271 | const Xe = ["application/xhtml+xml", "text/html"]; 272 | let Je, Qe = null; 273 | const Ze = l.createElement("form"), 274 | et = function(e) { 275 | return e instanceof RegExp || e instanceof Function 276 | }, 277 | tt = function(e) { 278 | if (!Qe || Qe !== e) { 279 | if (e && "object" == typeof e || (e = {}), e = D(e), Ve = Ve = -1 === Xe.indexOf(e.PARSER_MEDIA_TYPE) ? "text/html" : e.PARSER_MEDIA_TYPE, Je = "application/xhtml+xml" === Ve ? p : u, ue = "ALLOWED_TAGS" in e ? v({}, e.ALLOWED_TAGS, Je) : pe, ge = "ALLOWED_ATTR" in e ? v({}, e.ALLOWED_ATTR, Je) : fe, Ye = "ALLOWED_NAMESPACES" in e ? v({}, e.ALLOWED_NAMESPACES, p) : Ke, He = "ADD_URI_SAFE_ATTR" in e ? v(D(je), e.ADD_URI_SAFE_ATTR, Je) : je, Fe = "ADD_DATA_URI_TAGS" in e ? v(D(Ue), e.ADD_DATA_URI_TAGS, Je) : Ue, Ie = "FORBID_CONTENTS" in e ? v({}, e.FORBID_CONTENTS, Je) : Be, ye = "FORBID_TAGS" in e ? v({}, e.FORBID_TAGS, Je) : {}, Ee = "FORBID_ATTR" in e ? v({}, e.FORBID_ATTR, Je) : {}, Oe = "USE_PROFILES" in e && e.USE_PROFILES, Te = !1 !== e.ALLOW_ARIA_ATTR, Ce = !1 !== e.ALLOW_DATA_ATTR, we = e.ALLOW_UNKNOWN_PROTOCOLS || !1, ve = !1 !== e.ALLOW_SELF_CLOSE_IN_ATTR, De = e.SAFE_FOR_TEMPLATES || !1, Me = e.WHOLE_DOCUMENT || !1, ke = e.RETURN_DOM || !1, Le = e.RETURN_DOM_FRAGMENT || !1, Se = e.RETURN_TRUSTED_TYPE || !1, Ne = e.FORCE_BODY || !1, _e = !1 !== e.SANITIZE_DOM, Re = e.SANITIZE_NAMED_PROPS || !1, Pe = !1 !== e.KEEP_CONTENT, xe = e.IN_PLACE || !1, me = e.ALLOWED_URI_REGEXP || z, $e = e.NAMESPACE || Ge, be = e.CUSTOM_ELEMENT_HANDLING || {}, e.CUSTOM_ELEMENT_HANDLING && et(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck) && (be.tagNameCheck = e.CUSTOM_ELEMENT_HANDLING.tagNameCheck), e.CUSTOM_ELEMENT_HANDLING && et(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck) && (be.attributeNameCheck = e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck), e.CUSTOM_ELEMENT_HANDLING && "boolean" == typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (be.allowCustomizedBuiltInElements = e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements), De && (Ce = !1), Le && (ke = !0), Oe && (ue = v({}, [...R]), ge = [], !0 === Oe.html && (v(ue, A), v(ge, P)), !0 === Oe.svg && (v(ue, N), v(ge, x), v(ge, I)), !0 === Oe.svgFilters && (v(ue, k), v(ge, x), v(ge, I)), !0 === Oe.mathMl && (v(ue, S), v(ge, O), v(ge, I))), e.ADD_TAGS && (ue === pe && (ue = D(ue)), v(ue, e.ADD_TAGS, Je)), e.ADD_ATTR && (ge === fe && (ge = D(ge)), v(ge, e.ADD_ATTR, Je)), e.ADD_URI_SAFE_ATTR && v(He, e.ADD_URI_SAFE_ATTR, Je), e.FORBID_CONTENTS && (Ie === Be && (Ie = D(Ie)), v(Ie, e.FORBID_CONTENTS, Je)), Pe && (ue["#text"] = !0), Me && v(ue, ["html", "head", "body"]), ue.table && (v(ue, ["tbody"]), delete ye.tbody), e.TRUSTED_TYPES_POLICY) { 280 | if ("function" != typeof e.TRUSTED_TYPES_POLICY.createHTML) throw T('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'); 281 | if ("function" != typeof e.TRUSTED_TYPES_POLICY.createScriptURL) throw T('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'); 282 | J = e.TRUSTED_TYPES_POLICY, Q = J.createHTML("") 283 | } else void 0 === J && (J = function(e, t) { 284 | if ("object" != typeof e || "function" != typeof e.createPolicy) return null; 285 | let n = null; 286 | const i = "data-tt-policy-suffix"; 287 | t && t.hasAttribute(i) && (n = t.getAttribute(i)); 288 | const a = "dompurify" + (n ? "#" + n : ""); 289 | try { 290 | return e.createPolicy(a, { 291 | createHTML: e => e, 292 | createScriptURL: e => e 293 | }) 294 | } catch (e) { 295 | return console.warn("TrustedTypes policy " + a + " could not be created."), null 296 | } 297 | }(j, o)), null !== J && "string" == typeof Q && (Q = J.createHTML("")); 298 | r && r(e), Qe = e 299 | } 300 | }, 301 | nt = v({}, ["mi", "mo", "mn", "ms", "mtext"]), 302 | it = v({}, ["foreignobject", "desc", "title", "annotation-xml"]), 303 | at = v({}, ["title", "style", "font", "a", "script"]), 304 | rt = v({}, N); 305 | v(rt, k), v(rt, L); 306 | const ot = v({}, S); 307 | v(ot, _); 308 | const lt = function(e) { 309 | m(i.removed, { 310 | element: e 311 | }); 312 | try { 313 | e.parentNode.removeChild(e) 314 | } catch (t) { 315 | e.remove() 316 | } 317 | }, 318 | st = function(e, t) { 319 | try { 320 | m(i.removed, { 321 | attribute: t.getAttributeNode(e), 322 | from: t 323 | }) 324 | } catch (e) { 325 | m(i.removed, { 326 | attribute: null, 327 | from: t 328 | }) 329 | } 330 | if (t.removeAttribute(e), "is" === e && !ge[e]) 331 | if (ke || Le) try { 332 | lt(t) 333 | } catch (e) {} else try { 334 | t.setAttribute(e, "") 335 | } catch (e) {} 336 | }, 337 | ct = function(e) { 338 | let t, n; 339 | if (Ne) e = "" + e; 340 | else { 341 | const t = g(e, /^[\r\n\t ]+/); 342 | n = t && t[0] 343 | } 344 | "application/xhtml+xml" === Ve && $e === Ge && (e = '' + e + ""); 345 | const i = J ? J.createHTML(e) : e; 346 | if ($e === Ge) try { 347 | t = (new H).parseFromString(i, Ve) 348 | } catch (e) {} 349 | if (!t || !t.documentElement) { 350 | t = Z.createDocument($e, "template", null); 351 | try { 352 | t.documentElement.innerHTML = We ? Q : i 353 | } catch (e) {} 354 | } 355 | const a = t.body || t.documentElement; 356 | return e && n && a.insertBefore(l.createTextNode(n), a.childNodes[0] || null), $e === Ge ? ne.call(t, Me ? "html" : "body")[0] : Me ? t.documentElement : a 357 | }, 358 | dt = function(e) { 359 | return ee.call(e.ownerDocument || e, e, B.SHOW_ELEMENT | B.SHOW_COMMENT | B.SHOW_TEXT, null, !1) 360 | }, 361 | ht = function(e) { 362 | return "object" == typeof C ? e instanceof C : e && "object" == typeof e && "number" == typeof e.nodeType && "string" == typeof e.nodeName 363 | }, 364 | mt = function(e, t, n) { 365 | ae[e] && d(ae[e], (e => { 366 | e.call(i, t, n, Qe) 367 | })) 368 | }, 369 | ut = function(e) { 370 | let t; 371 | if (mt("beforeSanitizeElements", e, null), (n = e) instanceof U && ("string" != typeof n.nodeName || "string" != typeof n.textContent || "function" != typeof n.removeChild || !(n.attributes instanceof F) || "function" != typeof n.removeAttribute || "function" != typeof n.setAttribute || "string" != typeof n.namespaceURI || "function" != typeof n.insertBefore || "function" != typeof n.hasChildNodes)) return lt(e), !0; 372 | var n; 373 | const a = Je(e.nodeName); 374 | if (mt("uponSanitizeElement", e, { 375 | tagName: a, 376 | allowedTags: ue 377 | }), e.hasChildNodes() && !ht(e.firstElementChild) && (!ht(e.content) || !ht(e.content.firstElementChild)) && E(/<[/\w]/g, e.innerHTML) && E(/<[/\w]/g, e.textContent)) return lt(e), !0; 378 | if (!ue[a] || ye[a]) { 379 | if (!ye[a] && gt(a)) { 380 | if (be.tagNameCheck instanceof RegExp && E(be.tagNameCheck, a)) return !1; 381 | if (be.tagNameCheck instanceof Function && be.tagNameCheck(a)) return !1 382 | } 383 | if (Pe && !Ie[a]) { 384 | const t = X(e) || e.parentNode, 385 | n = V(e) || e.childNodes; 386 | if (n && t) 387 | for (let i = n.length - 1; i >= 0; --i) t.insertBefore(G(n[i], !0), K(e)) 388 | } 389 | return lt(e), !0 390 | } 391 | return e instanceof w && ! function(e) { 392 | let t = X(e); 393 | t && t.tagName || (t = { 394 | namespaceURI: $e, 395 | tagName: "template" 396 | }); 397 | const n = u(e.tagName), 398 | i = u(t.tagName); 399 | return !!Ye[e.namespaceURI] && (e.namespaceURI === qe ? t.namespaceURI === Ge ? "svg" === n : t.namespaceURI === ze ? "svg" === n && ("annotation-xml" === i || nt[i]) : Boolean(rt[n]) : e.namespaceURI === ze ? t.namespaceURI === Ge ? "math" === n : t.namespaceURI === qe ? "math" === n && it[i] : Boolean(ot[n]) : e.namespaceURI === Ge ? !(t.namespaceURI === qe && !it[i]) && !(t.namespaceURI === ze && !nt[i]) && !ot[n] && (at[n] || !rt[n]) : !("application/xhtml+xml" !== Ve || !Ye[e.namespaceURI])) 400 | }(e) ? (lt(e), !0) : "noscript" !== a && "noembed" !== a || !E(/<\/no(script|embed)/i, e.innerHTML) ? (De && 3 === e.nodeType && (t = e.textContent, t = f(t, re, " "), t = f(t, oe, " "), t = f(t, le, " "), e.textContent !== t && (m(i.removed, { 401 | element: e.cloneNode() 402 | }), e.textContent = t)), mt("afterSanitizeElements", e, null), !1) : (lt(e), !0) 403 | }, 404 | pt = function(e, t, n) { 405 | if (_e && ("id" === t || "name" === t) && (n in l || n in Ze)) return !1; 406 | if (Ce && !Ee[t] && E(se, t)); 407 | else if (Te && E(ce, t)); 408 | else if (!ge[t] || Ee[t]) { 409 | if (!(gt(e) && (be.tagNameCheck instanceof RegExp && E(be.tagNameCheck, e) || be.tagNameCheck instanceof Function && be.tagNameCheck(e)) && (be.attributeNameCheck instanceof RegExp && E(be.attributeNameCheck, t) || be.attributeNameCheck instanceof Function && be.attributeNameCheck(t)) || "is" === t && be.allowCustomizedBuiltInElements && (be.tagNameCheck instanceof RegExp && E(be.tagNameCheck, n) || be.tagNameCheck instanceof Function && be.tagNameCheck(n)))) return !1 410 | } else if (He[t]); 411 | else if (E(me, f(n, he, ""))); 412 | else if ("src" !== t && "xlink:href" !== t && "href" !== t || "script" === e || 0 !== b(n, "data:") || !Fe[e]) 413 | if (we && !E(de, f(n, he, ""))); 414 | else if (n) return !1; 415 | return !0 416 | }, 417 | gt = function(e) { 418 | return e.indexOf("-") > 0 419 | }, 420 | ft = function(e) { 421 | let t, n, a, r; 422 | mt("beforeSanitizeAttributes", e, null); 423 | const { 424 | attributes: o 425 | } = e; 426 | if (!o) return; 427 | const l = { 428 | attrName: "", 429 | attrValue: "", 430 | keepAttr: !0, 431 | allowedAttributes: ge 432 | }; 433 | for (r = o.length; r--;) { 434 | t = o[r]; 435 | const { 436 | name: s, 437 | namespaceURI: c 438 | } = t; 439 | if (n = "value" === s ? t.value : y(t.value), a = Je(s), l.attrName = a, l.attrValue = n, l.keepAttr = !0, l.forceKeepAttr = void 0, mt("uponSanitizeAttribute", e, l), n = l.attrValue, l.forceKeepAttr) continue; 440 | if (st(s, e), !l.keepAttr) continue; 441 | if (!ve && E(/\/>/i, n)) { 442 | st(s, e); 443 | continue 444 | } 445 | De && (n = f(n, re, " "), n = f(n, oe, " "), n = f(n, le, " ")); 446 | const d = Je(e.nodeName); 447 | if (pt(d, a, n)) { 448 | if (!Re || "id" !== a && "name" !== a || (st(s, e), n = "user-content-" + n), J && "object" == typeof j && "function" == typeof j.getAttributeType) 449 | if (c); 450 | else switch (j.getAttributeType(d, a)) { 451 | case "TrustedHTML": 452 | n = J.createHTML(n); 453 | break; 454 | case "TrustedScriptURL": 455 | n = J.createScriptURL(n) 456 | } 457 | try { 458 | c ? e.setAttributeNS(c, s, n) : e.setAttribute(s, n), h(i.removed) 459 | } catch (e) {} 460 | } 461 | } 462 | mt("afterSanitizeAttributes", e, null) 463 | }, 464 | bt = function e(t) { 465 | let n; 466 | const i = dt(t); 467 | for (mt("beforeSanitizeShadowDOM", t, null); n = i.nextNode();) mt("uponSanitizeShadowNode", n, null), ut(n) || (n.content instanceof s && e(n.content), ft(n)); 468 | mt("afterSanitizeShadowDOM", t, null) 469 | }; 470 | return i.sanitize = function(e) { 471 | let t, n, r, o, l = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; 472 | if (We = !e, We && (e = "\x3c!--\x3e"), "string" != typeof e && !ht(e)) { 473 | if ("function" != typeof e.toString) throw T("toString is not a function"); 474 | if ("string" != typeof(e = e.toString())) throw T("dirty is not a string, aborting") 475 | } 476 | if (!i.isSupported) return e; 477 | if (Ae || tt(l), i.removed = [], "string" == typeof e && (xe = !1), xe) { 478 | if (e.nodeName) { 479 | const t = Je(e.nodeName); 480 | if (!ue[t] || ye[t]) throw T("root node is forbidden and cannot be sanitized in-place") 481 | } 482 | } else if (e instanceof C) t = ct("\x3c!----\x3e"), n = t.ownerDocument.importNode(e, !0), 1 === n.nodeType && "BODY" === n.nodeName || "HTML" === n.nodeName ? t = n : t.appendChild(n); 483 | else { 484 | if (!ke && !De && !Me && -1 === e.indexOf("<")) return J && Se ? J.createHTML(e) : e; 485 | if (t = ct(e), !t) return ke ? null : Se ? Q : "" 486 | } 487 | t && Ne && lt(t.firstChild); 488 | const c = dt(xe ? e : t); 489 | for (; r = c.nextNode();) ut(r) || (r.content instanceof s && bt(r.content), ft(r)); 490 | if (xe) return e; 491 | if (ke) { 492 | if (Le) 493 | for (o = te.call(t.ownerDocument); t.firstChild;) o.appendChild(t.firstChild); 494 | else o = t; 495 | return (ge.shadowroot || ge.shadowrootmod) && (o = ie.call(a, o, !0)), o 496 | } 497 | let d = Me ? t.outerHTML : t.innerHTML; 498 | return Me && ue["!doctype"] && t.ownerDocument && t.ownerDocument.doctype && t.ownerDocument.doctype.name && E($, t.ownerDocument.doctype.name) && (d = "\n" + d), De && (d = f(d, re, " "), d = f(d, oe, " "), d = f(d, le, " ")), J && Se ? J.createHTML(d) : d 499 | }, i.setConfig = function(e) { 500 | tt(e), Ae = !0 501 | }, i.clearConfig = function() { 502 | Qe = null, Ae = !1 503 | }, i.isValidAttribute = function(e, t, n) { 504 | Qe || tt({}); 505 | const i = Je(e), 506 | a = Je(t); 507 | return pt(i, a, n) 508 | }, i.addHook = function(e, t) { 509 | "function" == typeof t && (ae[e] = ae[e] || [], m(ae[e], t)) 510 | }, i.removeHook = function(e) { 511 | if (ae[e]) return h(ae[e]) 512 | }, i.removeHooks = function(e) { 513 | ae[e] && (ae[e] = []) 514 | }, i.removeAllHooks = function() { 515 | ae = {} 516 | }, i 517 | }() 518 | }() 519 | } 520 | }, 521 | t = {}; 522 | 523 | function n(i) { 524 | var a = t[i]; 525 | if (void 0 !== a) return a.exports; 526 | var r = t[i] = { 527 | exports: {} 528 | }; 529 | return e[i].call(r.exports, r, r.exports, n), r.exports 530 | }(() => { 531 | "use strict"; 532 | class e { 533 | constructor(e, t, n, i, a, r) { 534 | this.frequency = e, this.id = t, this.difficulty = n, this.problemUrl = i, this.problemName = a, this.acceptance = r 535 | } 536 | } 537 | class t extends e { 538 | constructor(e, t, n, i, a, r, o, l) { 539 | super(e, t, n, i, a, r), this.companyName = o, this.duration = l 540 | } 541 | } 542 | class i { 543 | constructor(e, t, n) { 544 | this.url = n, this.duration = e, this.company = t 545 | } 546 | } 547 | class a { 548 | constructor() { 549 | this.data = {} 550 | } 551 | getKeys() { 552 | return Object.keys(this.data) 553 | } 554 | getList(e) { 555 | if (null == e) throw new Error("Key cannot be undefined"); 556 | return e in this.data ? this.data[e] : [] 557 | } 558 | } 559 | class r extends a { 560 | push(e, t) { 561 | if (null == e || null == t) throw new Error("Key/Value error"); 562 | if (e in this.data) return void this.data[e].push(t); 563 | let n = []; 564 | n.push(t), this.data[e] = n 565 | } 566 | } 567 | class o extends a { 568 | push(e, t) { 569 | if (e in this.data) return void this.data[e].push(t); 570 | let n = new l; 571 | n.push(t), this.data[e] = n 572 | } 573 | } 574 | class l extends Array { 575 | sort(e, t = !1) { 576 | (new e).sort(this, t) 577 | } 578 | } 579 | class s { 580 | static getContainerBackgroundColor() { // ui darkmode 581 | const htmlElement = document.querySelector('html'); 582 | console.log(htmlElement.classList.contains('dark')) 583 | switch (htmlElement.classList.contains('dark')) { 584 | case !0: 585 | return "#151515"; 586 | case !1: 587 | return "#f3f3f3" 588 | } 589 | } 590 | static getComplementaryColor() { 591 | const htmlElement = document.querySelector('html'); 592 | switch (htmlElement.classList.contains('dark')) { 593 | case !0: 594 | return "#282828"; 595 | case !1: 596 | return "#dcdcdc" 597 | } 598 | } 599 | static COLOR_ACCENT = "#62C555"; 600 | static BACKGROUND_CONTAINER_COLOR = "#101010"; 601 | static SUB_BACKGROUND_CONTAINER_COLOR = "#282828"; 602 | static TEXT_COLOR = "#dcdcdc"; 603 | static TEXT_COLOR_SELECTED = "#7d7d7d" 604 | } 605 | class c { 606 | constructor() { 607 | this.elementModifier = [] 608 | } 609 | injectFunctionToTargetElement(e) { 610 | this.elementModifier.push(e) 611 | } 612 | modifyElement() { 613 | this.modifyActiveElement(), this.addObserverToCompaniesSection() 614 | } 615 | isSwiperLoading() { 616 | return null == document.getElementsByClassName("swiper-autoheight")[1] 617 | } 618 | modifyActiveElement() { 619 | if (this.isSwiperLoading()) return void window.setTimeout((() => { 620 | this.modifyActiveElement.bind(this)() 621 | }), 100); 622 | let e = document.getElementsByClassName("swiper-autoheight")[1].getElementsByClassName("swiper-slide-active"), 623 | t = e[e.length - 1].getElementsByTagName("a"); 624 | for (let e = 0; e <= t.length - 1; e++) { 625 | let n = t[e].href.split("/"), 626 | i = n[n.length - 1], 627 | a = t[e]; 628 | null == a.getAttribute("company-name") && a.setAttribute("company-name", i), a.href = "javascript:void(0)"; 629 | for (let e = 0; e <= this.elementModifier.length - 1; e++) this.elementModifier[e](a); 630 | this.onModifyElementSuccess(a) 631 | } 632 | } 633 | onModifyElementSuccess(e) { 634 | let t = e.getElementsByTagName("span"); 635 | t[t.length - 1].style.backgroundColor = s.COLOR_ACCENT 636 | } 637 | addObserverToCompaniesSection() { 638 | if (this.isSwiperLoading()) return void window.setTimeout((() => { 639 | this.addObserverToCompaniesSection.bind(this)() 640 | }), 100); 641 | let e = document.getElementsByClassName("swiper-autoheight")[1].parentNode.parentNode; 642 | const t = new MutationObserver((() => { 643 | this.modifyActiveElement() 644 | })); 645 | e ? t.observe(e, { 646 | childList: !0, 647 | subtree: !0, 648 | attributes: !0, 649 | attributeFilter: ["class"] 650 | }) : window.setTimeout((() => { 651 | this.addObserverToCompaniesSection() 652 | }), 100) 653 | } 654 | } 655 | class d { 656 | getValue(e, t) { 657 | return e[t] 658 | } 659 | doSwap(e, t) { 660 | return e > t 661 | } 662 | swap(e, t, n) { 663 | let i = e[t]; 664 | e[t] = e[n], e[n] = i 665 | } 666 | partition(e, t, n, i) { 667 | let a = this.getValue(e, n), 668 | r = t - 1; 669 | for (let o = t; o <= n - 1; o++) { 670 | let t = this.getValue(e, o), 671 | n = this.doSwap(t, a); 672 | i && (n = !n), n && (r++, this.swap(e, r, o)) 673 | } 674 | return this.swap(e, r + 1, n), r + 1 675 | } 676 | sort(e, t = !1) { 677 | this.quickSort(e, 0, e.length - 1, t) 678 | } 679 | quickSort(e, t, n, i) { 680 | if (null == n && null == t && (n = e.length - 1, t = 0), t < n) { 681 | let a = this.partition(e, t, n, i); 682 | this.quickSort(e, t, a - 1, i), this.quickSort(e, a + 1, n, i) 683 | } 684 | } 685 | } 686 | class h extends d { 687 | getValue(e, t) { 688 | return e[t].acceptance 689 | } 690 | } 691 | class m extends d { 692 | getValue(e, t) { 693 | return e[t].difficulty 694 | } 695 | doSwap(e, t) { 696 | let n = ["Easy", "Medium", "Hard"]; 697 | return n.indexOf(e) < n.indexOf(t) 698 | } 699 | } 700 | class u extends d { 701 | getValue(e, t) { 702 | return parseInt(e[t].id) 703 | } 704 | } 705 | class p extends d { 706 | getValue(e, t) { 707 | return e[t].problemName 708 | } 709 | doSwap(e, t) { 710 | let n = "abcdefghijklmnopqrstuvwxyz0123456789", 711 | i = e[0].toLowerCase(), 712 | a = t[0].toLowerCase(); 713 | return n.indexOf(i) < n.indexOf(a) 714 | } 715 | } 716 | class g extends d { 717 | getValue(e, t) { 718 | return e[t].frequency 719 | } 720 | } 721 | class f { 722 | static generateTextElement(e) { 723 | let t = document.createElement("div"), 724 | n = document.createElement("h3"); 725 | return n.textContent = e, n.style = "\n text-align: center;\n ", t.appendChild(n), t 726 | } 727 | static generateProblemIdElement(e) { 728 | let t = f.generateTextElement(e); 729 | return t.style = "\n width: 5%\n ", t 730 | } 731 | static generateProblemFrequencyElement(e) { 732 | let t = document.createElement("div"); 733 | t.setAttribute("title", String(Math.round(100 * e)) + "%"), t.style = `\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: .75rem;\n background-color: ${s.getComplementaryColor()}; \n border-radius: 0.5rem;\n margin-top: auto;\n margin-bottom: auto;\n width:10%; \n `; 734 | let n = document.createElement("div"); 735 | return n.style = `\n border-radius: 0.5rem;\n height:100%; \n width:${100*e}%; \n display: flex;\n flex-direction: column;\n justify-content: center;\n overflow: hidden;\n color: #fff;\n background-color: ${s.COLOR_ACCENT};\n `, t.appendChild(n), t 736 | } 737 | static generateProblemNameElement(e, t) { 738 | let n = document.createElement("div"), 739 | i = document.createElement("a"); 740 | return i.href = t, i.textContent = e, n.appendChild(i), n.style = "\n width: 50%\n ", n 741 | } 742 | static generateProblemDifficultyElement(e) { 743 | let t = f.generateTextElement(e); 744 | switch (t.style = "\n width: 12%\n ", e) { 745 | case "Hard": 746 | t.children[0].style.color = "red"; 747 | break; 748 | case "Medium": 749 | t.children[0].style.color = "orange"; 750 | break; 751 | case "Easy": 752 | t.children[0].style.color = "green" 753 | } 754 | return t 755 | } 756 | static generateProblemAcceptanceElement(e) { 757 | let t = f.generateTextElement(e); 758 | return t.style = "\n width: 10%\n ", t 759 | } 760 | static generateRowElement() { 761 | let e = document.createElement("div"); 762 | return e.style = `\n display:flex;\n border-top: solid 1px ${s.getComplementaryColor()};\n `, e 763 | } 764 | static generateTableContentElement(e) { 765 | let t = document.createElement("div"); 766 | for (let n = 0; n <= e.length - 1; n++) { 767 | let i = f.generateRowElement(), 768 | a = e[n].frequency, 769 | r = e[n].id, 770 | o = e[n].difficulty, 771 | l = e[n].problemUrl, 772 | s = e[n].problemName, 773 | c = String(Math.round(100 * e[n].acceptance)) + "%"; 774 | i.appendChild(f.generateProblemIdElement(r)), i.appendChild(f.generateProblemNameElement(s, l)), i.appendChild(f.generateProblemAcceptanceElement(c)), i.appendChild(f.generateProblemDifficultyElement(o)), i.appendChild(f.generateProblemFrequencyElement(a)), t.append(i) 775 | } 776 | return t.id = "table-content", t 777 | } 778 | static generateDurationElement(e) { 779 | let t = document.createElement("button"); 780 | return t.innerText = e, t.style = " \n width:auto; \n margin-right: 2%; \n ", t.setAttribute("duration", e), t.addEventListener("select", (() => { 781 | t.classList.add("selected-duration-button"), t.classList.remove("unselected-duration-button") 782 | })), t.addEventListener("unselect", (() => { 783 | t.classList.add("unselected-duration-button"), t.classList.remove("selected-duration-button") 784 | })), t.classList.add("unselected-duration-button"), t 785 | } 786 | static generateTitleElement(e) { 787 | let t = document.createElement("h2"); 788 | return t.innerText = e, t.style = "\n font-size:1.5em;", t 789 | } 790 | } 791 | 792 | function b() { 793 | let e = document.createElement("span"); 794 | e.innerHTML = '

\n please consider leaving a ⭐️ on my repostory here. \n

\n '; 795 | let t = e.getElementsByTagName("a"); 796 | for (let e = 0; e <= t.length - 1; e++) t[e].classList.add("clickable"); 797 | return e 798 | } 799 | class y { 800 | constructor() { 801 | this.tableId = "table-content", this.shownData = [], this.currentlySortedBy = "", this.isReverseSorted = !1, this.parentDiv = document.createElement("div"), this.durationData = {}, this.currentlySelectedDuration = void 0 802 | } 803 | setShownData(e) { 804 | return this.shownData = e, this 805 | } 806 | buildRateUsRow() { 807 | let e = f.generateRowElement(); 808 | return e.style.justifyContent = "center", e.appendChild(b()), this.parentDiv.appendChild(e), this 809 | } 810 | buildTitleRow(e) { 811 | let t = f.generateRowElement(); 812 | return t.style.justifyContent = "center", t.appendChild(f.generateTitleElement(e)), this.parentDiv.appendChild(t), this 813 | } 814 | addDurationData(e, t) { 815 | this.durationData[e] = t 816 | } 817 | buildDurationsRow() { 818 | let e = f.generateRowElement(); 819 | for (let t in this.durationData) { 820 | let n = f.generateDurationElement(t); 821 | if (n.classList.add("clickable"), n.addEventListener("click", this.onDurationButtonClicked), e.appendChild(n), null == this.currentlySelectedDuration) { 822 | this.currentlySelectedDuration = n; 823 | let e = new Event("select"); 824 | this.currentlySelectedDuration.dispatchEvent(e) 825 | } 826 | } 827 | return this.parentDiv.appendChild(e), this 828 | } 829 | buildHeaderRow() { 830 | let e = f.generateRowElement(), 831 | t = f.generateProblemIdElement("#"), 832 | n = f.generateProblemNameElement("Title", "javascript:void(0)"), 833 | i = f.generateProblemAcceptanceElement("Acceptance"), 834 | a = f.generateProblemDifficultyElement("Difficulty"), 835 | r = f.generateProblemAcceptanceElement("Frequency"); 836 | return t.firstChild.classList.add("clickable"), n.firstChild.classList.add("clickable"), i.firstChild.classList.add("clickable"), a.firstChild.classList.add("clickable"), r.firstChild.classList.add("clickable"), t.firstChild.classList.add("default-text-color"), n.firstChild.classList.add("default-text-color"), i.firstChild.classList.add("default-text-color"), a.firstChild.classList.add("default-text-color"), r.firstChild.classList.add("default-text-color"), t.addEventListener("click", this.getOnHeaderClickedFunction(u).bind(this)), n.addEventListener("click", this.getOnHeaderClickedFunction(p).bind(this)), i.addEventListener("click", this.getOnHeaderClickedFunction(h).bind(this)), a.addEventListener("click", this.getOnHeaderClickedFunction(m).bind(this)), r.addEventListener("click", this.getOnHeaderClickedFunction(g).bind(this)), e.appendChild(t), e.appendChild(n), e.appendChild(i), e.appendChild(a), e.appendChild(r), this.parentDiv.appendChild(e), this 837 | } 838 | buildTable(e = p) { 839 | this.shownData.sort(e), this.currentlySortedBy = e.name, this.isReverseSorted = !1; 840 | let t = f.generateTableContentElement(this.shownData); 841 | return this.parentDiv.appendChild(t), this 842 | } 843 | getResult() { 844 | return this.parentDiv 845 | } 846 | onDurationButtonClicked = e => { 847 | let t = new Event("select"); 848 | e.currentTarget.dispatchEvent(t); 849 | let n = new Event("unselect"); 850 | this.currentlySelectedDuration.dispatchEvent(n), this.currentlySelectedDuration = e.currentTarget, this.shownData = this.durationData[e.currentTarget.getAttribute("duration")], this.swapContentTableElement(this.shownData) 851 | }; 852 | getOnHeaderClickedFunction(e) { 853 | return () => { 854 | e.name == this.currentlySortedBy ? (this.shownData.sort(e, !this.isReverseSorted), this.isReverseSorted = !this.isReverseSorted) : (this.shownData.sort(e), this.currentlySortedBy = e.name, this.isReverseSorted = !1), this.swapContentTableElement(this.shownData) 855 | } 856 | } 857 | swapContentTableElement = e => { 858 | null != document.getElementById(this.tableId) && document.getElementById(this.tableId).remove(); 859 | let t = f.generateTableContentElement(e); 860 | this.parentDiv.appendChild(t) 861 | } 862 | } 863 | class E { 864 | constructor() { 865 | if (E._instance) throw new Error("Modal Manager Have been instantiated"); 866 | E._instance = this, this.modal = this.createModal(), this.modalContentBox = this.createModalContentBox(), this.appendToModal(this.modalContentBox), this.appendModal(document.body) 867 | } 868 | createModalContentBox() { 869 | let e = document.createElement("div"); 870 | return e.style = `\n background-color: ${s.getContainerBackgroundColor()};\n margin-top:1%; \n margin-left: auto;\n margin-right: auto;\n padding: 20px;\n max-width: 80%;\n min-width: 60%\n mid-height: 15%; \n border-radius:15px; \n `, e 871 | } 872 | getModalContentBox() { 873 | return this.modalContentBox 874 | } 875 | appendModal(e) { 876 | e.appendChild(this.modal) 877 | } 878 | appendToModal(e) { 879 | this.modal.appendChild(e) 880 | } 881 | appendToContainer(e) { 882 | this.modalContentBox.appendChild(e) 883 | } 884 | showLoadingIcon() { 885 | let e = document.createElement("div"); 886 | e.classList.add("loading-logo"), this.modalContentBox.appendChild(e) 887 | } 888 | createCloseButton() { 889 | let e = document.createElement("span"); 890 | return e.style = " \n float: right;\n font-size: 28px;\n font-weight: bold;\n cursor: pointer;\n ", e.innerText = "x", e.addEventListener("click", resetModal), e 891 | } 892 | createModal() { 893 | let e = document.createElement("div"); 894 | return e.style = " \n display: none; \n position: fixed; \n z-index: 32;\n left: 0;\n top: 0;\n width: 100%; \n height: 100%; \n overflow: auto; \n ", window.addEventListener("click", this.onModalClicked), e.id = "CompanyModal", e 895 | } 896 | openModal() { 897 | this.modal.style.display = "" 898 | } 899 | closeModal() { 900 | this.modal.style.display = "none" 901 | } 902 | clearModalContent() { 903 | for (; null != this.modalContentBox.firstChild;) this.modalContentBox.firstChild.remove() 904 | } 905 | onModalClicked = e => { 906 | e.target == this.modal && this.resetModal() 907 | }; 908 | resetModal = () => { 909 | this.closeModal(), this.clearModalContent() 910 | } 911 | } 912 | let T = new E; 913 | class C { 914 | static API_KEY = "AIzaSyD3pKgr2-5dCitiv8IVgMtrKthD42BfJtM"; 915 | static SHEETS_ID = "14TOiRmqxbhR9WFj3o5HdgmdOQiaePkI7CBW9D55LMf4"; 916 | static TESTING_SHEETS_ID = "1TJUhILyqBYsXWaPSUGwN1EvzBFeRNg1MgXH_SVqjQJo"; 917 | static getUrl(e) { 918 | return `https://sheets.googleapis.com/v4/spreadsheets/${C.SHEETS_ID}/values/${e}?key=${C.API_KEY}` 919 | } 920 | } 921 | class w { 922 | constructor() { 923 | this.cachedData = {} 924 | } 925 | fetchData() { 926 | return this.fetchProblemFrequencyData() 927 | } 928 | async fetchProblemFrequencyData() { 929 | let e = C.getUrl("Problem!A:B"), 930 | t = await fetch(e), 931 | n = await t.json(); 932 | return this.parseProblemFrequencyData(n.values) 933 | } 934 | parseProblemFrequencyData(e) { 935 | let t = {}; 936 | for (let n = 0; n <= e.length - 1; n++) { 937 | let i = e[n][0], 938 | a = e[n][1]; 939 | t[i] = a, this.cachedData[i] = n + 1 940 | } 941 | return t 942 | } 943 | fetchPremiumProblem(e) { 944 | return this.fetchProblemData(e) 945 | } 946 | async fetchProblemData(e) { 947 | if (e in this.cachedData == 0) return new Promise(((e, t) => e("

No data

"))); 948 | let t = "Problem!K" + this.cachedData[e], 949 | n = C.getUrl(t), 950 | i = await fetch(n); 951 | return (await i.json()).values[0] 952 | } 953 | static async fetchProblemDataByRow(e) { 954 | let t = "Problem!K" + e, 955 | n = C.getUrl(t), 956 | i = await fetch(n); 957 | return (await i.json()).values[0][0] 958 | } 959 | } 960 | class v { 961 | constructor() { 962 | this.companyPageTableData = {}, this.cachedData = {}, this.tableDataFetched = !1 963 | } 964 | fetchData(e) { 965 | return 0 == this.tableDataFetched ? this.fetchCompanyPageTable().then((t => this.fetchCompanyProblemData(e))) : this.fetchCompanyProblemData(e) 966 | } 967 | fetchCompanyPageTable() { 968 | let e = C.getUrl("CompaniesProblem_Map!A:C"); 969 | return fetch(e).then((e => e.json())).then((e => { 970 | this.parseCompanyPageTableData(e.values) 971 | })).then(this.tableDataFetched = !0) 972 | } 973 | fetchCompanyProblemData(e) { 974 | if (e in this.cachedData) return new Promise(((t, n) => t(this.cachedData[e]))); 975 | if (e in this.companyPageTableData == 0) return new Promise(((e, t) => e(new o))); 976 | let t = `CompaniesProblem!A${this.companyPageTableData[e][0]}:I${this.companyPageTableData[e][1]}`, 977 | n = C.getUrl(t); 978 | return fetch(n).then((e => e.json())).then((t => this.parseCompanyProblemData(e, t.values))) 979 | } 980 | parseCompanyPageTableData(e) { 981 | for (let t = 1; t <= e.length - 1; t++) { 982 | let n = e[t][0], 983 | i = e[t][1], 984 | a = e[t][2]; 985 | this.companyPageTableData[n] = [i, a] 986 | } 987 | return this.companyPageTableData 988 | } 989 | parseCompanyProblemData(e, n) { 990 | let i = new o; 991 | for (let e = 0; e <= n.length - 1; e++) { 992 | let a = n[e][2], 993 | r = n[e][1], 994 | o = n[e][7], 995 | l = n[e][6], 996 | s = n[e][4], 997 | c = n[e][5], 998 | d = n[e][0], 999 | h = n[e][3], 1000 | m = new t(a, r, o, l, s, c, d, h); 1001 | i.push(h, m) 1002 | } 1003 | return this.cachedData[e] = i, i 1004 | } 1005 | } 1006 | class D { 1007 | fetchData(e) { 1008 | let t = `${e}!A2:F`, 1009 | n = C.getUrl(t); 1010 | return fetch(n).then((e => e.json())).then((e => this.parseTopQuestionData(e.values))) 1011 | } 1012 | parseTopQuestionData(t) { 1013 | let n = new l; 1014 | for (let i = 0; i <= t.length - 1; i++) { 1015 | let a = t[i][0], 1016 | r = t[i][1], 1017 | o = t[i][2], 1018 | l = t[i][3], 1019 | s = t[i][4], 1020 | c = t[i][5], 1021 | d = new e(r, a, c, s, o, l); 1022 | n.push(d) 1023 | } 1024 | return n 1025 | } 1026 | } 1027 | class M { 1028 | constructor() { 1029 | this.map = {}, this.mapFetched = !1 1030 | } 1031 | fetchData(e) { 1032 | return this.mapFetched ? this.fetchProblemTag(e) : this.fetchtProblemTagsMap().then((t => this.fetchProblemTag(e))) 1033 | } 1034 | fetchProblemTag(e) { 1035 | if (!(e in this.map)) return new Promise(((e, t) => e(new r))); 1036 | let t = `ProblemCompaniesTags!A${this.map[e][0]}:C${this.map[e][1]}`, 1037 | n = C.getUrl(t); 1038 | return fetch(n).then((e => e.json())).then((e => this.parseProblemTagData(e.values))) 1039 | } 1040 | parseProblemTagData(e) { 1041 | let t = new r; 1042 | for (let n = 0; n <= e.length - 1; n++) { 1043 | let a = e[n][0], 1044 | r = e[n][1], 1045 | o = e[n][2], 1046 | l = new i; 1047 | l.duration = r, l.company = o, l.url = a, t.push(r, l) 1048 | } 1049 | return this.cachedData = t, t 1050 | } 1051 | fetchtProblemTagsMap() { 1052 | let e = C.getUrl("ProblemCompaniesTags_Map!A:C"); 1053 | return fetch(e).then((e => e.json())).then((e => this.setProblemTagMap(e.values))) 1054 | } 1055 | setProblemTagMap(e) { 1056 | for (let t = 0; t <= e.length - 1; t++) { 1057 | let n = e[t][0], 1058 | i = e[t][1], 1059 | a = e[t][2]; 1060 | this.map[n] = [i, a] 1061 | } 1062 | this.mapFetched = !0 1063 | } 1064 | } 1065 | class A { 1066 | static async fetchEditorialDataByRow(e) { 1067 | let t = "Problem!L" + e, 1068 | n = C.getUrl(t), 1069 | i = await fetch(n), 1070 | a = (await i.json()).values; 1071 | return null == a ? "

No data

" : a[0][0] 1072 | } 1073 | } 1074 | class N { 1075 | constructor() { 1076 | if (N._instance) throw new Error("Firebase Analytics Manager Have been instantiated"); 1077 | this.GA_ENDPOINT = "https://www.google-analytics.com/mp/collect", this.MEASUREMENT_ID = "G-9B493T8583", this.API_SECRET = "Q-G1HeKRTtGyXkBh9aKL7Q", this.SESSION_EXPIRATION_IN_MIN = 30, this.enableAnalytics = !1 1078 | } 1079 | async getOrCreateClientId() { 1080 | if (!this.enableAnalytics) return; 1081 | let e = (await chrome.storage.local.get("clientId")).clientId; 1082 | return e || (e = self.crypto.randomUUID(), await chrome.storage.local.set({ 1083 | clientId: e 1084 | })), e 1085 | } 1086 | async fireModifiedButtonClickedEvent(e, t, n) { 1087 | this.enableAnalytics && fetch(`${this.GA_ENDPOINT}?measurement_id=${this.MEASUREMENT_ID}&api_secret=${this.API_SECRET}`, { 1088 | method: "POST", 1089 | body: JSON.stringify({ 1090 | client_id: await this.getOrCreateClientId(), 1091 | events: [{ 1092 | name: "button_clicked", 1093 | params: { 1094 | id: e, 1095 | type: t, 1096 | name: n 1097 | } 1098 | }] 1099 | }) 1100 | }) 1101 | } 1102 | async fireUnlockedDataEvent(e) { 1103 | this.enableAnalytics && fetch(`${this.GA_ENDPOINT}?measurement_id=${this.MEASUREMENT_ID}&api_secret=${this.API_SECRET}`, { 1104 | method: "POST", 1105 | body: JSON.stringify({ 1106 | client_id: await this.getOrCreateClientId(), 1107 | events: [{ 1108 | name: "data_unlocked", 1109 | params: { 1110 | type: e 1111 | } 1112 | }] 1113 | }) 1114 | }) 1115 | } 1116 | async fireErrorEvent(e, t, n) { 1117 | this.enableAnalytics && fetch(`${this.GA_ENDPOINT}?measurement_id=${this.MEASUREMENT_ID}&api_secret=${this.API_SECRET}`, { 1118 | method: "POST", 1119 | body: JSON.stringify({ 1120 | client_id: await this.getOrCreateClientId(), 1121 | events: [{ 1122 | name: "unlock_error", 1123 | params: { 1124 | error_type: t, 1125 | url: e, 1126 | unlocker_name: n 1127 | } 1128 | }] 1129 | }) 1130 | }) 1131 | } 1132 | } 1133 | let k = new N; 1134 | class L { 1135 | constructor() { 1136 | this.elementModifier = [] 1137 | } 1138 | modifyElement() { 1139 | this.observer = new MutationObserver((() => { 1140 | this.modifyActiveElement() 1141 | })), this.modifyActiveElement(), this.addObserverToProblemTable() 1142 | } 1143 | injectFunctionToTargetElement(e) { 1144 | this.elementModifier.push(e) 1145 | } 1146 | isTableLoading() { 1147 | return "" == document.querySelectorAll('[role="rowgroup"]')[1].querySelectorAll('[role="row"]')[0].querySelectorAll('[role="cell"]')[1].textContent 1148 | } 1149 | modifyActiveElement = () => { 1150 | if (this.isTableLoading()) return void window.setTimeout((() => { 1151 | this.modifyActiveElement.bind(this)() 1152 | }), 100); 1153 | this.disconnectObserverToProblemTable(); 1154 | let e = document.querySelectorAll('[role="rowgroup"]')[1].querySelectorAll('[role="row"]'); 1155 | for (let t = 0; t <= e.length - 1; t++) { 1156 | let n = e[t].querySelectorAll('[role="cell"]')[1].textContent.split(".")[0]; 1157 | e[t].setAttribute("problem-id", String(n)); 1158 | let i = e[t].getElementsByTagName("rect").length > 0; 1159 | e[t].setAttribute("is-premium", i); 1160 | for (let n = 0; n <= this.elementModifier.length - 1; n++) this.elementModifier[n](e[t]) 1161 | } 1162 | this.addObserverToProblemTable() 1163 | }; 1164 | disconnectObserverToProblemTable() { 1165 | this.observer.disconnect() 1166 | } 1167 | addObserverToProblemTable() { 1168 | let e = document.querySelector('[role="table"]'); 1169 | this.observer.observe(e, { 1170 | childList: !0, 1171 | subtree: !0 1172 | }) 1173 | } 1174 | } 1175 | var S = n(856); 1176 | class _ { 1177 | constructor() { 1178 | this.cachedData = {}, this.browser = "undefined" == typeof browser ? chrome : browser 1179 | } 1180 | refreshTableData() { 1181 | let e = { 1182 | FetchDate: Date.now() 1183 | }; 1184 | return (new w).fetchData().then((t => e.data = t)).then((t => this.onDataFetched(e))) 1185 | } 1186 | onDataFetched(e) { 1187 | let t = this.parseRowOffsetData(e.data); 1188 | this.browser.storage.local.set({ 1189 | rowOffset: t 1190 | }), this.browser.storage.local.set({ 1191 | TableFrequencyData: e 1192 | }) 1193 | } 1194 | getRowOffsetData() { 1195 | return this.browser.storage.local.get("rowOffset") 1196 | } 1197 | parseRowOffsetData(e) { 1198 | let t = {}, 1199 | n = 2; 1200 | for (let i in e) t[i] = n, n += 1; 1201 | return t 1202 | } 1203 | getBufferedData(e) { 1204 | return this.browser.storage.local.get(e) 1205 | } 1206 | } 1207 | class R { 1208 | constructor() { 1209 | this.bufferManager = new _, this.dataTTL = 12096e5 1210 | } 1211 | fetchData() { 1212 | return this.bufferManager.getBufferedData("TableFrequencyData").then((e => this.onDataFetched(e))) 1213 | } 1214 | onDataFetched(e) { 1215 | return 0 == Object.keys(e).length || null == e ? this.bufferManager.refreshTableData().then((e => this.bufferManager.getBufferedData("TableFrequencyData"))).then((e => e.TableFrequencyData.data)) : (Date.now() > e.TableFrequencyData.FetchDate + this.dataTTL && this.bufferManager.refreshTableData(), e.TableFrequencyData.data) 1216 | } 1217 | fetchPremiumProblem(e) { 1218 | return this.fetchProblemData(e) 1219 | } 1220 | fetchProblemData(e) { 1221 | return this.bufferManager.getRowOffsetData().then((t => this.onPremiumProblemDataFetched(e, t))) 1222 | } 1223 | onPremiumProblemDataFetched(e, t) { 1224 | if (e in (t = t.rowOffset) == 0) return "

No data

"; 1225 | { 1226 | let n = t[e]; 1227 | return w.fetchProblemDataByRow(n) 1228 | } 1229 | } 1230 | } 1231 | class P { 1232 | fetchData(e) { 1233 | return (new _).getRowOffsetData().then((t => this.onPremiumProblemDataFetched(e, t))) 1234 | } 1235 | onPremiumProblemDataFetched(e, t) { 1236 | if (e in (t = t.rowOffset) == 0) return "

No data

"; 1237 | { 1238 | let n = t[e]; 1239 | return A.fetchEditorialDataByRow(n) 1240 | } 1241 | } 1242 | } 1243 | class x { 1244 | constructor() { 1245 | this.elementModifier = new L, this.dataFetcher = new R, this.containerManager = T, this.isFetching = !1, this.premiumProblemButtonId = 2, this.analyticsManager = k, this.name = "ProblemTableUnlocker" 1246 | } 1247 | onFetchSuccess() { 1248 | this.elementModifier.injectFunctionToTargetElement(x.removeProgressbarUnlockButton), this.elementModifier.injectFunctionToTargetElement(this.insertInnerProgressbar), this.elementModifier.injectFunctionToTargetElement(this.modifyPremiumProblemHref), this.elementModifier.modifyElement() 1249 | } 1250 | modifyPremiumProblemHref = e => { 1251 | if ("true" == e.getAttribute("is-premium")) { 1252 | this.removePremiumIcons(e); 1253 | let t = e.getAttribute("problem-id"), 1254 | n = e.getElementsByTagName("a")[0]; 1255 | n.href = "javascript:void(0)", n.style.color = s.COLOR_ACCENT, n.addEventListener("click", (() => { 1256 | this.onPremiumProblemClicked(t) 1257 | })) 1258 | } 1259 | }; 1260 | unlock() { 1261 | this.dataFetcher.fetchData().then((e => { 1262 | this.problemData = e 1263 | })).then(this.onFetchSuccess.bind(this)).then(this.analyticsManager.fireUnlockedDataEvent(this.name)).catch((e => console.log(this, e))) 1264 | } 1265 | onPremiumProblemClicked = e => { 1266 | this.isFetching || (this.analyticsManager.fireModifiedButtonClickedEvent(this.premiumProblemButtonId, "PremiumProblem", e), this.isFetching = !0, this.containerManager.clearModalContent(), this.containerManager.openModal(), this.containerManager.showLoadingIcon(), this.dataFetcher.fetchPremiumProblem(parseInt(e)).then((e => this.onProblemFetchSuccess(e))).then(this.isFetching = !1)) 1267 | }; 1268 | onProblemFetchSuccess(e) { 1269 | let t = this.containerManager.getModalContentBox(); 1270 | this.containerManager.clearModalContent(); 1271 | let n = String(e).replaceAll("", "
"); 1272 | t.innerHTML = S.sanitize(n); 1273 | let i = t.getElementsByTagName("pre"); 1274 | for (let e = 0; e <= i.length - 1; e++) i[e].style = "\n border-radius: 0.5rem;\n font-family: Menlo,sans-serif;\n font-size: .875rem;\n line-height: 1.25rem;\n margin-bottom: 1rem;\n margin-top: 1rem;\n padding: 1rem;\n " 1275 | } 1276 | removePremiumIcons(e) { 1277 | let t = e.querySelectorAll('[role="cell"]'), 1278 | n = t[0].getElementsByTagName("svg")[0], 1279 | i = t[1].getElementsByTagName("svg")[0]; 1280 | null != n && (n.style.opacity = 0), null != i && (i.style.opacity = 0) 1281 | } 1282 | insertInnerProgressbar = e => { 1283 | let t = e.querySelectorAll('[role="cell"]'), 1284 | n = t[t.length - 1], 1285 | i = e.getAttribute("problem-id"), 1286 | a = this.problemData[i]; 1287 | null == a && (a = 100), a *= 100; 1288 | let r, o = "inner-progressbar", 1289 | l = n.getElementsByClassName(o), 1290 | c = n.getElementsByClassName("rounded-l-lg")[0]; 1291 | l.length > 0 && l[0].remove(), i in this.problemData ? (r = function(e) { 1292 | let t = document.createElement("div"); 1293 | return t.style = `\n background-color: ${s.COLOR_ACCENT};\n width: ${e}%;\n height: 0.5rem;\n border-bottom-right-radius: 0.5rem;\n border-top-right-radius: 0.5rem;\n border-bottom-left-radius: 0.5rem;\n border-top-left-radius: 0.5rem;\n `, t 1294 | }(a), c.setAttribute("title", `${Math.round(a)}%`)) : (r = function(e) { 1295 | let t = document.createElement("div"); 1296 | return t.style = `\n background-color: red;\n width: ${e}%;\n height: 0.5rem;\n border-bottom-right-radius: 0.5rem;\n border-top-right-radius: 0.5rem;\n border-bottom-left-radius: 0.5rem;\n border-top-left-radius: 0.5rem;\n `, t 1297 | }(a), c.setAttribute("title", "No Data")), r.classList.add(o), c.appendChild(r) 1298 | }; 1299 | static removeProgressbarUnlockButton(e) { 1300 | let t = e.querySelectorAll('[role="cell"]'), 1301 | n = t[t.length - 1], 1302 | i = n.getElementsByTagName("svg")[0], 1303 | a = n.getElementsByClassName("rounded-r-lg")[0], 1304 | r = n.getElementsByClassName("rounded-l-lg")[0]; 1305 | null != i && i.remove(), null != a && a.remove(), null != r && (r.style = "\n border-bottom-right-radius: 0.5rem;\n overflow: hidden; \n border-top-right-radius: 0.5rem\n ") 1306 | } 1307 | } 1308 | class O { 1309 | constructor() { 1310 | this.elementModifier = [] 1311 | } 1312 | injectFunctionToTargetElement(e) { 1313 | this.elementModifier.push(e) 1314 | } 1315 | modifyElement() { 1316 | this.isloading() ? window.setTimeout((() => { 1317 | this.modifyElement() 1318 | }), 100) : (this.observer = new MutationObserver((() => { 1319 | this.modifyLockedElement() 1320 | })), this.modifyLockedElement(), this.addObsersverToFoldout()) 1321 | } 1322 | isloading() { 1323 | return null == document.getElementsByClassName("space-y-1.5")[0] 1324 | } 1325 | addObsersverToFoldout() { 1326 | let e = document.getElementsByClassName("space-y-1.5")[0]; 1327 | this.observer.observe(e, { 1328 | childList: !0, 1329 | subtree: !0 1330 | }) 1331 | } 1332 | modifyLockedElement = () => { 1333 | let e = document.getElementsByClassName("space-y-1.5")[0]; 1334 | if (!e) return void window.setTimeout((() => { 1335 | this.modifyLockedElement.bind(this)() 1336 | }), 100); 1337 | let t = e.children; 1338 | for (let e = 0; e <= t.length - 2; e++) { 1339 | let n = t[e].getElementsByTagName("svg"); 1340 | if (n.length > 0) { 1341 | t[e].getElementsByTagName("a")[0].href = "javascript:void(0)"; 1342 | let i = t[e].textContent.replaceAll(" ", ""); 1343 | t[e].setAttribute("item", i), t[e].style.color = s.COLOR_ACCENT, n[0].remove(); 1344 | for (let n = 0; n <= this.elementModifier.length - 1; n++) this.elementModifier[n](t[e]) 1345 | } 1346 | } 1347 | }; 1348 | disconnectObserverToFoldout() { 1349 | this.observer.disconnect() 1350 | } 1351 | } 1352 | class I { 1353 | constructor() { 1354 | this.elementModifier = new O, this.dataFetcher = new D, this.containerManager = T, this.isFetching = !1, this.topProblemButtonId = 4, this.analyticsManager = k, this.name = "TopProblemUnlocker" 1355 | } 1356 | unlock() { 1357 | this.elementModifier.injectFunctionToTargetElement(this.getFunctionToBeInjected()), this.elementModifier.modifyElement(), this.analyticsManager.fireUnlockedDataEvent(this.name) 1358 | } 1359 | onTopProblemClicked = e => { 1360 | if (this.isFetching) return; 1361 | this.isFetching = !0; 1362 | let t = e.currentTarget.getAttribute("item"), 1363 | n = e.currentTarget.getElementsByClassName("font-medium")[0].textContent; 1364 | this.analyticsManager.fireModifiedButtonClickedEvent(this.topProblemButtonId, "TopProblem", n), this.containerManager.clearModalContent(), this.containerManager.openModal(), this.containerManager.showLoadingIcon(), this.dataFetcher.fetchData(t).then((e => this.onFetchSuccess(e, n))).then((e => { 1365 | this.isFetching = !1 1366 | })).catch((e => { 1367 | console.log(this, "Fetch Error" + e), this.isFetching = !1 1368 | })), e.stopImmediatePropagation() 1369 | }; 1370 | onFetchSuccess(e, t) { 1371 | let n = new y; 1372 | n.setShownData(e), n.buildTitleRow(t), n.buildHeaderRow(), n.buildTable(); 1373 | let i = n.getResult(); 1374 | this.containerManager.clearModalContent(), this.containerManager.getModalContentBox().appendChild(i) 1375 | } 1376 | getFunctionToBeInjected() { 1377 | return e => { 1378 | e.addEventListener("click", this.onTopProblemClicked) 1379 | } 1380 | } 1381 | } 1382 | class B { 1383 | constructor() { 1384 | this.tagButtonListener = [] 1385 | } 1386 | getTabMenu() { 1387 | let e = document.getElementsByClassName("gap-8")[1]; 1388 | return null == e && (e = document.getElementsByClassName("flexlayout__tab_button")[0]), e 1389 | } 1390 | modifyElement() { 1391 | // return//not working 1392 | let e = this.getTabMenu(); 1393 | null != e && 0 != e.children[0].children.length ? (this.isDescriptionTabActive() && this.modifyCompaniesTagButton(), this.addObserverToLeftTab()) : window.setTimeout((() => { 1394 | this.modifyElement() 1395 | }), 100) 1396 | } 1397 | addObserverToLeftTab() { 1398 | let e = this.getTabMenu(); 1399 | null != e ? new MutationObserver((() => { 1400 | this.isDescriptionTabActive() && this.modifyCompaniesTagButton() 1401 | })).observe(e, { 1402 | childList: !0, 1403 | subtree: !0, 1404 | attributes: !0, 1405 | attributeFilter: ["class"] 1406 | }) : window.setTimeout((() => { 1407 | this.addObserverToLeftTab() 1408 | }), 100) 1409 | } 1410 | isDescriptionTabActive() { 1411 | return 2 == this.getTabMenu().children[0].children[0].childElementCount 1412 | } 1413 | modifyCompaniesTagButton() { //companies tag 1414 | let e=document.querySelector("#qd-content > div.h-full.flex-col.ssg__qd-splitter-primary-w > div > div > div > div.flex.h-full.w-full.overflow-y-auto.rounded-b > div > div > div.px-5.pt-3 > div > div > div"); 1415 | if(e==null){ 1416 | //dynamic ui 1417 | e=document.querySelector("div > div.flex.w-full.flex-1.flex-col.gap-4.overflow-y-auto.px-4.py-5 > div.flex.gap-1 > div:nth-child(3)") 1418 | } 1419 | if (!e) return void window.setTimeout((() => { 1420 | this.modifyCompaniesTagButton.bind(this)() 1421 | }), 100); 1422 | let t = e.getElementsByTagName("svg")[0]; 1423 | if (null == t) return; 1424 | let n = t.parentElement; 1425 | t.remove(); 1426 | let i = n.cloneNode(!0); 1427 | n.parentElement.replaceChild(i, n), i.style.backgroundColor = s.COLOR_ACCENT, i.style.color = "black"; 1428 | for (let e = 0; e <= this.tagButtonListener.length - 1; e++) i.addEventListener("click", this.tagButtonListener[e]) 1429 | } 1430 | addTagButtonOnClickListener(e) { 1431 | this.tagButtonListener.push(e) 1432 | } 1433 | } 1434 | class F { 1435 | static generateHeader(e) { 1436 | let t = document.createElement("h3"); 1437 | return t.classList.add("default-text-color"), t.textContent = e, t 1438 | } 1439 | static generateTag(e) { 1440 | let t = document.createElement("div"); 1441 | return t.style = "\n min-width:7%;\n margin-right: 3%;\n margin-bottom: 1%;\n max-width:15%; \n text-align:center; \n border-radius: 21px;\n ", t.classList.add("sub-title-text-color"), t.textContent = e, t 1442 | } 1443 | static generateRow() { 1444 | let e = document.createElement("div"); 1445 | return e.style = "\n display:flex;\n flex-wrap: wrap;\n border-top: solid 1px darkgrey;\n\n ", e 1446 | } 1447 | } 1448 | class U { 1449 | constructor() { 1450 | this.parentDiv = document.createElement("div") 1451 | } 1452 | buildHeader(e) { 1453 | let t = F.generateRow(); 1454 | return t.style.justifyContent = "center", t.appendChild(F.generateHeader(e)), this.parentDiv.appendChild(t), this 1455 | } 1456 | getResult() { 1457 | return this.parentDiv 1458 | } 1459 | buildTagsBox(e) { 1460 | let t = F.generateRow(); 1461 | for (let n = 0; n <= e.length - 1; n++) { 1462 | let i = F.generateTag(e[n].company); 1463 | t.appendChild(i) 1464 | } 1465 | return this.parentDiv.appendChild(t), this 1466 | } 1467 | buildRateUsText() { 1468 | let e = F.generateRow(); 1469 | return e.style.justifyContent = "center", e.appendChild(b()), this.parentDiv.appendChild(e), this 1470 | } 1471 | } 1472 | class H { 1473 | constructor() { 1474 | this.elementModifier = [], this.checkCount = 0 1475 | } 1476 | injectFunctionToTargetElement(e) { 1477 | this.elementModifier.push(e) 1478 | } 1479 | getTabMenu() { 1480 | let e = document.getElementsByClassName("gap-8")[1]; 1481 | return null == e && (e = document.getElementsByClassName("flexlayout__tab_button")[0]), e 1482 | } 1483 | modifyElement() { 1484 | // return // not working 1485 | null != this.getTabMenu() ? this.addEventListenerToEditorialButton() : window.setTimeout((() => { 1486 | this.modifyElement() 1487 | }), 100) 1488 | } 1489 | addEventListenerToEditorialButton() { 1490 | let e = this.getEditorialButton(); 1491 | null != e ? (e.parentElement.parentElement.addEventListener("click", (t => { 1492 | for (let t = 0; t <= this.elementModifier.length - 1; t++) this.elementModifier[t](e); 1493 | null != e.getAttribute("problem-id") && t.stopImmediatePropagation() 1494 | })), this.disableEditorialRedirect(), this.removeEditorialLockLogo()) : window.setTimeout((() => { 1495 | this.addEventListenerToEditorialButton() 1496 | }), 50) 1497 | } 1498 | 1499 | findElementByTagNameAndInnerHTML(tagName, html, rootNode = document) { 1500 | const elements = rootNode.getElementsByTagName(tagName); 1501 | 1502 | for (let i = 0; i < elements.length; i++) { 1503 | if (elements[i].innerHTML.includes(html)) { 1504 | return elements[i]; 1505 | } 1506 | } 1507 | 1508 | return null; 1509 | } 1510 | getEditorialButton() { // the div inside a of editorial 1511 | let sv = this.findElementByTagNameAndInnerHTML("span","Editorial") 1512 | 1513 | if (sv == null){ 1514 | const style = 'background-color: darkblue; color: white; font-style: italic; border: 5px solid hotpink; font-size: 2em;' 1515 | console.log("%cTurn off dynamic ui, for editorial to work, https://github.com/31b4/Leetcode-Premium-Bypass/issues/14", style); 1516 | //dynamic ui 1517 | return document.querySelector("div > div.flex.w-full.flex-1.flex-col.gap-4.overflow-y-auto.px-4.py-5 > div.flex.gap-1 > div:nth-child(4)") 1518 | return sv.parentNode 1519 | } 1520 | return this.findElementByTagNameAndInnerHTML("span","Editorial").parentNode; 1521 | } 1522 | disableEditorialRedirect() { // a tag of editorial 1523 | this.findElementByTagNameAndInnerHTML("span","Editorial").parentNode.parentNode.href = "javascript:void(0)" 1524 | } 1525 | removeEditorialLockLogo() { 1526 | let e = this.getEditorialButton(); 1527 | 1528 | if (null == e) return void window.setTimeout((() => { 1529 | this.removeEditorialLockLogo() 1530 | }), 50); 1531 | // free editorial 1532 | let t= document.querySelector("#qd-content > div.h-full.flex-col.ssg__qd-splitter-primary-w > div > div > div > div:nth-child(1) > div > div > a:nth-child(2) > div > span > div > div:nth-child(1) > div > svg") 1533 | 1534 | // All screen sizes 1535 | // let t=this.findElementByTagNameAndInnerHTML("span","Editorial").getElementsByTagName('svg')[0] 1536 | if (null == t) { 1537 | if (this.checkCount > 5) return; 1538 | return window.setTimeout((() => { 1539 | this.removeEditorialLockLogo() 1540 | }), 150), void(this.checkCount += 1) 1541 | } 1542 | t.style.opacity = 0, e.setAttribute("problem-name", document.URL.split("/")[4]), e.setAttribute("problem-id", this.getProblemId()), this.addUnlockedIndicator() 1543 | } 1544 | addUnlockedIndicator() { 1545 | this.getTabMenu().children[1].getElementsByTagName("span")[0].style.color = s.COLOR_ACCENT 1546 | } 1547 | getProblemId() { 1548 | return document.getElementsByClassName("text-lg")[0].textContent.split(".")[0] 1549 | } 1550 | } 1551 | class j { 1552 | constructor() { 1553 | this.parentDiv = document.createElement("div") 1554 | } 1555 | buildContent(e) { 1556 | let t = document.createElement("div"); 1557 | return t.style.justifyContent = "center", t.innerHTML = e, this.parentDiv.appendChild(t), this 1558 | } 1559 | getResult() { 1560 | return this.parentDiv 1561 | } 1562 | buildRateUsText() { 1563 | let e = F.generateRow(); 1564 | return e.style.justifyContent = "center", e.appendChild(b()), this.parentDiv.appendChild(e), this 1565 | } 1566 | }! function(e) { 1567 | for (const t in e) 1568 | if (window.location.href.includes(t)) { 1569 | let n = e[t]; 1570 | for (let e = 0; e <= n.length - 1; e++) { 1571 | let i = new n[e]; 1572 | try { 1573 | i.unlock() 1574 | } catch (e) { 1575 | k.fireErrorEvent(t, e.message, i.name), console.log(i.name + " Error " + e) 1576 | } 1577 | } 1578 | break 1579 | } 1580 | }({ 1581 | "https://leetcode.com/problemset": [x, class { 1582 | constructor() { 1583 | this.elementModifier = new c, this.dataFetcher = new v, this.containerManager = T, this.isFetching = !1, this.analyticsManager = k, this.companyProblemButtonId = 1, this.name = "CompaniesProblemUnlocker" 1584 | } 1585 | unlock() { 1586 | this.elementModifier.injectFunctionToTargetElement(this.getFunctionToBeInjected()), this.elementModifier.modifyElement(), this.analyticsManager.fireUnlockedDataEvent(this.name) 1587 | } 1588 | getFunctionToBeInjected() { 1589 | return e => { 1590 | e.addEventListener("click", this.onCompanyButtonClick) 1591 | } 1592 | } 1593 | onCompanyButtonClick = e => { 1594 | if (this.isFetching) return; 1595 | this.isFetching = !0; 1596 | let t = e.currentTarget.getAttribute("company-name"); 1597 | this.analyticsManager.fireModifiedButtonClickedEvent(this.companyProblemButtonId, "CompanyButton", t); 1598 | let n = e.currentTarget.getElementsByClassName("text-label-2")[0].textContent; 1599 | this.containerManager.clearModalContent(), this.containerManager.openModal(), this.containerManager.showLoadingIcon(), this.dataFetcher.fetchData(t).then((e => this.onFetchSuccess(e, n))).then((e => { 1600 | this.isFetching = !1 1601 | })).catch((e => { 1602 | this.isFetching = !1 1603 | })) 1604 | }; 1605 | onFetchSuccess(e, t) { 1606 | let n = this.containerManager.getModalContentBox(), 1607 | i = new y(e), 1608 | a = e.getKeys(); 1609 | for (let t = 0; t <= a.length - 1; t++) i.addDurationData(a[t], e.getList(a[t])); 1610 | i.buildRateUsRow(), i.buildTitleRow(t), i.buildDurationsRow(), i.setShownData(e.getList(a[0])), i.buildHeaderRow(), i.buildTable(), this.containerManager.clearModalContent(), n.appendChild(i.getResult()) 1611 | } 1612 | }, I], 1613 | "https://leetcode.com/problem-list": [x, I], 1614 | "https://leetcode.com/problems": [class { 1615 | constructor() { 1616 | this.elementModifier = new B, this.dataFetcher = new M, this.containerManager = T, this.isFetching = !1, this.problemTagButtonId = 3, this.analyticsManager = k, this.name = "ProblemTagsUnlocker" 1617 | } 1618 | onTagButtonClicked = () => { 1619 | if (this.isFetching) return; 1620 | this.isFetching = !0; 1621 | let e = document.URL.split("/")[4]; 1622 | this.analyticsManager.fireModifiedButtonClickedEvent(this.problemTagButtonId, "ProblemTagButton", e), this.containerManager.clearModalContent(), this.containerManager.openModal(), this.containerManager.showLoadingIcon(), this.dataFetcher.fetchData(e).then((e => this.onFetchSucces(e))).then((e => { 1623 | this.isFetching = !1 1624 | })).catch((e => { 1625 | console.log(this, e), this.isFetching = !1 1626 | })) 1627 | }; 1628 | unlock() { 1629 | this.elementModifier.addTagButtonOnClickListener(this.onTagButtonClicked), this.elementModifier.modifyElement(), this.analyticsManager.fireUnlockedDataEvent(this.name) 1630 | } 1631 | onFetchSucces = e => { 1632 | let t = e.getKeys(), 1633 | n = new U; 1634 | for (let i = 0; i <= t.length - 1; i++) n.buildHeader(t[i]), n.buildTagsBox(e.getList(t[i])); 1635 | n.buildRateUsText(); 1636 | let i = this.containerManager.getModalContentBox(); 1637 | this.containerManager.clearModalContent(), i.appendChild(n.getResult()) 1638 | } 1639 | }, class { 1640 | constructor() { 1641 | this.name = "EditorialUnlocker", this.elementModifier = new H, this.dataFetcher = new P, this.containerManager = T 1642 | } 1643 | unlock() { 1644 | this.elementModifier.injectFunctionToTargetElement(this.onEditorialTabClicked), this.elementModifier.modifyElement() 1645 | } 1646 | onEditorialTabClicked = e => { 1647 | let t = e.getAttribute("problem-id"); 1648 | null != t && (this.containerManager.clearModalContent(), this.containerManager.openModal(), this.containerManager.showLoadingIcon(), this.dataFetcher.fetchData(t).then((e => this.onDataFetched(e)))) 1649 | }; 1650 | onDataFetched(e) { 1651 | let t = new j; 1652 | t.buildRateUsText(), t.buildContent(e); 1653 | let n = this.containerManager.getModalContentBox(); 1654 | this.containerManager.clearModalContent(), n.appendChild(t.getResult()) 1655 | } 1656 | }], 1657 | "https://leetcode.com/study-plan": [I] 1658 | }) 1659 | })() 1660 | })(); 1661 | -------------------------------------------------------------------------------- /dist/main.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! @license DOMPurify 3.0.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.3/LICENSE */ 2 | -------------------------------------------------------------------------------- /dist/modules/AnalyticsManager.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | class FirebaseAnalyticsManager{ 4 | constructor( ){ 5 | if(FirebaseAnalyticsManager._instance) { 6 | throw new Error("Firebase Analytics Manager Have been instantiated") 7 | } 8 | this.GA_ENDPOINT = 'https://www.google-analytics.com/mp/collect'; 9 | this.MEASUREMENT_ID = `G-9B493T8583`; 10 | this.API_SECRET = `Q-G1HeKRTtGyXkBh9aKL7Q`; 11 | this.SESSION_EXPIRATION_IN_MIN = 30; 12 | this.enableAnalytics = false; 13 | } 14 | 15 | async getOrCreateClientId() { 16 | if(!this.enableAnalytics) return; 17 | const result = await chrome.storage.local.get('clientId'); 18 | let clientId = result.clientId; 19 | if (!clientId) { 20 | clientId = self.crypto.randomUUID(); 21 | await chrome.storage.local.set({clientId}); 22 | } 23 | return clientId; 24 | } 25 | 26 | async fireModifiedButtonClickedEvent(id, buttonType, buttonName) { 27 | if(!this.enableAnalytics) return; 28 | fetch( 29 | `${this.GA_ENDPOINT}?measurement_id=${this.MEASUREMENT_ID}&api_secret=${this.API_SECRET}`, 30 | { 31 | method: 'POST', 32 | body: JSON.stringify({ 33 | client_id: await this.getOrCreateClientId(), 34 | events: [ 35 | { 36 | name: 'button_clicked', 37 | params: { 38 | id: id, 39 | type:buttonType, 40 | name: buttonName 41 | }, 42 | }, 43 | ], 44 | }), 45 | } 46 | ); 47 | } 48 | 49 | 50 | async fireUnlockedDataEvent(dataType) { 51 | if(!this.enableAnalytics) return; 52 | fetch( 53 | `${this.GA_ENDPOINT}?measurement_id=${this.MEASUREMENT_ID}&api_secret=${this.API_SECRET}`, 54 | { 55 | method: 'POST', 56 | body: JSON.stringify({ 57 | client_id: await this.getOrCreateClientId(), 58 | events: [ 59 | { 60 | name: 'data_unlocked', 61 | params: { 62 | type: dataType 63 | }, 64 | }, 65 | ], 66 | }), 67 | } 68 | ); 69 | } 70 | 71 | async fireErrorEvent(url, error, unlocker){ 72 | if(!this.enableAnalytics) return; 73 | fetch( 74 | `${this.GA_ENDPOINT}?measurement_id=${this.MEASUREMENT_ID}&api_secret=${this.API_SECRET}`, 75 | { 76 | method: 'POST', 77 | body: JSON.stringify({ 78 | client_id: await this.getOrCreateClientId(), 79 | events: [ 80 | { 81 | name: 'unlock_error', 82 | params: { 83 | error_type: error, 84 | url: url, 85 | unlocker_name : unlocker 86 | }, 87 | }, 88 | ], 89 | }), 90 | } 91 | ); 92 | } 93 | } 94 | 95 | 96 | 97 | 98 | 99 | 100 | let analyticsManager = new FirebaseAnalyticsManager() 101 | export{ analyticsManager} -------------------------------------------------------------------------------- /dist/modules/BufferManager/GoogleSheetsBufferManager.js: -------------------------------------------------------------------------------- 1 | import { GoogleSheetsAPIManager, GoogleSheetsProblemTableDataFetcher } from "../DataFetcher/GoogleSheetsDataFetcher"; 2 | 3 | 4 | function getBrowser() { 5 | if (typeof browser === 'undefined') return chrome 6 | return browser 7 | } 8 | 9 | class GoogleSheetBufferManager { 10 | constructor() { 11 | this.cachedData = {} 12 | this.browser = getBrowser(); 13 | } 14 | 15 | refreshTableData() { 16 | let savedData = {"FetchDate": Date.now()} 17 | let dataFetcher = new GoogleSheetsProblemTableDataFetcher(); 18 | return dataFetcher.fetchData() 19 | .then(data=> savedData["data"] = data) 20 | .then(data => this.onDataFetched(savedData)) 21 | } 22 | 23 | onDataFetched(savedData) { 24 | let rowOffsetData = this.parseRowOffsetData(savedData["data"]) 25 | this.browser.storage.local.set({"rowOffset":rowOffsetData}) 26 | this.browser.storage.local.set({"TableFrequencyData":savedData}) 27 | } 28 | 29 | getRowOffsetData() { 30 | return this.browser.storage.local.get("rowOffset") 31 | } 32 | 33 | parseRowOffsetData(data) { 34 | let rowOffsetData = {} 35 | let i = 2 36 | // {ProblemID: Row} 37 | for(let key in data) { 38 | rowOffsetData[key] = i 39 | i +=1 40 | } 41 | return rowOffsetData; 42 | } 43 | 44 | getBufferedData(key) { 45 | return this.browser.storage.local.get(key) 46 | } 47 | } 48 | 49 | export { GoogleSheetBufferManager} -------------------------------------------------------------------------------- /dist/modules/ContainerManager.js: -------------------------------------------------------------------------------- 1 | import { CSSStyler } from "./Objects" 2 | 3 | 4 | class ModalManager{ 5 | constructor() { 6 | if(ModalManager._instance) { 7 | throw new Error("Modal Manager Have been instantiated") 8 | } 9 | ModalManager._instance = this 10 | this.modal = this.createModal() 11 | this.modalContentBox = this.createModalContentBox() 12 | this.appendToModal(this.modalContentBox) 13 | this.appendModal(document.body) 14 | } 15 | 16 | createModalContentBox() { 17 | let modalContentBox = document.createElement('div') 18 | modalContentBox.style = ` 19 | background-color: ${CSSStyler.getContainerBackgroundColor()}; 20 | margin-top:1%; 21 | margin-left: auto; 22 | margin-right: auto; 23 | padding: 20px; 24 | max-width: 80%; 25 | min-width: 60% 26 | mid-height: 15%; 27 | border-radius:15px; 28 | ` 29 | return modalContentBox 30 | } 31 | 32 | getModalContentBox() { 33 | return this.modalContentBox 34 | } 35 | 36 | appendModal(targetParent) { 37 | targetParent.appendChild(this.modal) 38 | } 39 | 40 | appendToModal(targetElement) { 41 | this.modal.appendChild(targetElement) 42 | } 43 | 44 | appendToContainer(targetElement) { 45 | this.modalContentBox.appendChild(targetElement) 46 | } 47 | 48 | showLoadingIcon() { 49 | let loadingDiv = document.createElement('div') 50 | loadingDiv.classList.add('loading-logo') 51 | this.modalContentBox.appendChild(loadingDiv) 52 | } 53 | 54 | createCloseButton() { 55 | let closeButton = document.createElement('span') 56 | closeButton.style = ` 57 | float: right; 58 | font-size: 28px; 59 | font-weight: bold; 60 | cursor: pointer; 61 | ` 62 | 63 | closeButton.innerText = "x" 64 | closeButton.addEventListener('click', resetModal) 65 | return closeButton 66 | } 67 | 68 | createModal() { 69 | let modal = document.createElement('div') 70 | modal.style = ` 71 | display: none; 72 | position: fixed; 73 | z-index: 32; 74 | left: 0; 75 | top: 0; 76 | width: 100%; 77 | height: 100%; 78 | overflow: auto; 79 | ` 80 | window.addEventListener('click', this.onModalClicked) 81 | modal.id = "CompanyModal" 82 | return modal 83 | } 84 | 85 | openModal() { 86 | this.modal.style.display = "" 87 | } 88 | 89 | closeModal() { 90 | this.modal.style.display = "none" 91 | } 92 | 93 | clearModalContent() { 94 | while(this.modalContentBox.firstChild != undefined) { 95 | this.modalContentBox.firstChild.remove() 96 | } 97 | } 98 | 99 | onModalClicked = (event) => { 100 | if (event.target == this.modal) { 101 | this.resetModal() 102 | } 103 | } 104 | 105 | resetModal = () => { 106 | this.closeModal() 107 | this.clearModalContent() 108 | } 109 | } 110 | 111 | let modalManager = new ModalManager() 112 | 113 | export {modalManager} -------------------------------------------------------------------------------- /dist/modules/ContentBuilder/EditorialContentBuilder.js: -------------------------------------------------------------------------------- 1 | import { getRateUsElement } from "../ElementGenerator/ElementHelperClass" 2 | import { TagsElementGenerator } from "../ElementGenerator/TagContentElementGenerator" 3 | 4 | 5 | class EditorialContentBuilder{ 6 | constructor() { 7 | this.parentDiv = document.createElement('div') 8 | } 9 | 10 | buildContent(innerHTML) { 11 | let row = document.createElement('div') 12 | row.style.justifyContent = 'center' 13 | row.innerHTML = innerHTML 14 | this.parentDiv.appendChild(row) 15 | return this 16 | } 17 | 18 | getResult() { 19 | return this.parentDiv 20 | } 21 | 22 | buildRateUsText() { 23 | let row = TagsElementGenerator.generateRow() 24 | row.style.justifyContent = 'center' 25 | row.appendChild(getRateUsElement()) 26 | this.parentDiv.appendChild(row) 27 | return this 28 | } 29 | } 30 | 31 | 32 | 33 | export {EditorialContentBuilder} -------------------------------------------------------------------------------- /dist/modules/ContentBuilder/TableContentBuilder.js: -------------------------------------------------------------------------------- 1 | 2 | import {AcceptanceSorter, DifficultySorter, NameSorter, IDSorter, FrequencySorter} from "../ProblemSorter" 3 | import { TableElementGenerator } from "../ElementGenerator/TableContentElementGenerator" 4 | import { getRateUsElement } from "../ElementGenerator/ElementHelperClass" 5 | 6 | class TableContentBuilder{ 7 | constructor() { 8 | this.tableId = "table-content" 9 | this.shownData = [] 10 | this.currentlySortedBy = "" 11 | this.isReverseSorted = false 12 | this.parentDiv = document.createElement('div') 13 | this.durationData = {} 14 | this.currentlySelectedDuration = undefined 15 | } 16 | 17 | setShownData(data) { 18 | this.shownData = data 19 | return this 20 | } 21 | 22 | buildRateUsRow() { 23 | let row = TableElementGenerator.generateRowElement() 24 | row.style.justifyContent = "center"; 25 | row.appendChild(getRateUsElement()) 26 | this.parentDiv.appendChild(row) 27 | return this 28 | } 29 | 30 | buildTitleRow(title) { 31 | let row = TableElementGenerator.generateRowElement() 32 | row.style.justifyContent = "center"; 33 | row.appendChild(TableElementGenerator.generateTitleElement(title)) 34 | this.parentDiv.appendChild(row) 35 | return this 36 | } 37 | 38 | addDurationData(duration, data) { 39 | this.durationData[duration] = data 40 | } 41 | 42 | buildDurationsRow() { 43 | let row = TableElementGenerator.generateRowElement() 44 | for(let duration in this.durationData) { 45 | let element = TableElementGenerator.generateDurationElement(duration) 46 | element.classList.add("clickable") 47 | element.addEventListener('click', this.onDurationButtonClicked) 48 | row.appendChild(element) 49 | if(this.currentlySelectedDuration == undefined) { 50 | this.currentlySelectedDuration = element 51 | let selectEvent = new Event('select'); 52 | this.currentlySelectedDuration.dispatchEvent(selectEvent) 53 | } 54 | } 55 | this.parentDiv.appendChild(row) 56 | return this 57 | } 58 | 59 | buildHeaderRow() { 60 | let row = TableElementGenerator.generateRowElement() 61 | let idHeaderCell = TableElementGenerator.generateProblemIdElement("#") 62 | let titleHeaderCell = TableElementGenerator.generateProblemNameElement("Title", "javascript:void(0)") 63 | let acceptanceHeaderCell= TableElementGenerator.generateProblemAcceptanceElement("Acceptance") 64 | let difficultyHeaderCell= TableElementGenerator.generateProblemDifficultyElement("Difficulty") 65 | let frequencyHeaderCell= TableElementGenerator.generateProblemAcceptanceElement("Frequency") 66 | 67 | idHeaderCell.firstChild.classList.add("clickable") 68 | titleHeaderCell.firstChild.classList.add("clickable") 69 | acceptanceHeaderCell.firstChild.classList.add("clickable") 70 | difficultyHeaderCell.firstChild.classList.add("clickable") 71 | frequencyHeaderCell.firstChild.classList.add("clickable") 72 | 73 | idHeaderCell.firstChild.classList.add("default-text-color") 74 | titleHeaderCell.firstChild.classList.add("default-text-color") 75 | acceptanceHeaderCell.firstChild.classList.add("default-text-color") 76 | difficultyHeaderCell.firstChild.classList.add("default-text-color") 77 | frequencyHeaderCell.firstChild.classList.add("default-text-color") 78 | 79 | idHeaderCell.addEventListener('click', this.getOnHeaderClickedFunction(IDSorter).bind(this)) 80 | titleHeaderCell.addEventListener('click', this.getOnHeaderClickedFunction(NameSorter).bind(this)) 81 | acceptanceHeaderCell.addEventListener('click', this.getOnHeaderClickedFunction(AcceptanceSorter).bind(this)) 82 | difficultyHeaderCell.addEventListener('click', this.getOnHeaderClickedFunction(DifficultySorter).bind(this)) 83 | frequencyHeaderCell.addEventListener('click', this.getOnHeaderClickedFunction(FrequencySorter).bind(this)) 84 | 85 | row.appendChild(idHeaderCell) 86 | row.appendChild(titleHeaderCell) 87 | row.appendChild(acceptanceHeaderCell) 88 | row.appendChild(difficultyHeaderCell) 89 | row.appendChild(frequencyHeaderCell) 90 | this.parentDiv.appendChild(row) 91 | return this 92 | } 93 | 94 | buildTable(Sortby = NameSorter) { 95 | this.shownData.sort(Sortby) 96 | this.currentlySortedBy = Sortby.name 97 | this.isReverseSorted = false 98 | let table = TableElementGenerator.generateTableContentElement(this.shownData) 99 | this.parentDiv.appendChild(table) 100 | return this 101 | } 102 | 103 | getResult() { 104 | return this.parentDiv 105 | } 106 | 107 | 108 | onDurationButtonClicked = (event) => { 109 | let selectEvent = new Event('select'); 110 | event.currentTarget.dispatchEvent(selectEvent) 111 | let unselectEvent = new Event('unselect'); 112 | this.currentlySelectedDuration.dispatchEvent(unselectEvent) 113 | this.currentlySelectedDuration = event.currentTarget 114 | 115 | this.shownData = this.durationData[event.currentTarget.getAttribute("duration")] 116 | this.swapContentTableElement(this.shownData) 117 | } 118 | 119 | getOnHeaderClickedFunction(Sorter) { 120 | return () => { 121 | if(Sorter.name == this.currentlySortedBy) { 122 | this.shownData.sort(Sorter, !this.isReverseSorted) 123 | this.isReverseSorted = !this.isReverseSorted 124 | } else { 125 | this.shownData.sort(Sorter) 126 | this.currentlySortedBy = Sorter.name 127 | this.isReverseSorted = false 128 | } 129 | this.swapContentTableElement(this.shownData) 130 | } 131 | } 132 | 133 | swapContentTableElement = (swapTo) => { 134 | if(document.getElementById(this.tableId) != undefined) document.getElementById(this.tableId).remove() 135 | let table = TableElementGenerator.generateTableContentElement(swapTo) 136 | this.parentDiv.appendChild(table) 137 | } 138 | } 139 | 140 | export {TableContentBuilder} -------------------------------------------------------------------------------- /dist/modules/ContentBuilder/TagsContentBuilder.js: -------------------------------------------------------------------------------- 1 | 2 | import { getRateUsElement } from "../ElementGenerator/ElementHelperClass" 3 | import { TagsElementGenerator } from "../ElementGenerator/TagContentElementGenerator" 4 | 5 | 6 | class TagsContentBuilder{ 7 | constructor() { 8 | this.parentDiv = document.createElement('div') 9 | } 10 | 11 | buildHeader(headerName) { 12 | let row = TagsElementGenerator.generateRow() 13 | row.style.justifyContent = 'center' 14 | row.appendChild(TagsElementGenerator.generateHeader(headerName)) 15 | this.parentDiv.appendChild(row) 16 | return this 17 | } 18 | 19 | getResult() { 20 | return this.parentDiv 21 | } 22 | 23 | buildTagsBox(tags) { 24 | let row = TagsElementGenerator.generateRow() 25 | for(let i =0; i <= tags.length -1; i ++) { 26 | let tag = TagsElementGenerator.generateTag(tags[i].company) 27 | row.appendChild(tag) 28 | } 29 | this.parentDiv.appendChild(row) 30 | return this 31 | } 32 | 33 | buildRateUsText() { 34 | let row = TagsElementGenerator.generateRow() 35 | row.style.justifyContent = 'center' 36 | row.appendChild(getRateUsElement()) 37 | this.parentDiv.appendChild(row) 38 | return this 39 | } 40 | } 41 | 42 | 43 | export {TagsContentBuilder} -------------------------------------------------------------------------------- /dist/modules/DataFetcher/GoogleSheetsDataFetcher.js: -------------------------------------------------------------------------------- 1 | import {ProblemInfo,ProblemInfoList, CompanyProblemInfo, CompanyProblemInfoList, ProblemArray, ProblemT, ProblemTag} from "../Objects" 2 | 3 | class GoogleSheetsAPIManager{ 4 | static API_KEY = "AIzaSyD3pKgr2-5dCitiv8IVgMtrKthD42BfJtM" 5 | static SHEETS_ID = "14TOiRmqxbhR9WFj3o5HdgmdOQiaePkI7CBW9D55LMf4" 6 | static TESTING_SHEETS_ID = "1TJUhILyqBYsXWaPSUGwN1EvzBFeRNg1MgXH_SVqjQJo" 7 | 8 | static getUrl (range) { 9 | return `https://sheets.googleapis.com/v4/spreadsheets/${GoogleSheetsAPIManager.SHEETS_ID}/values/${range}?key=${GoogleSheetsAPIManager.API_KEY}` 10 | } 11 | } 12 | 13 | class GoogleSheetsProblemTableDataFetcher { 14 | constructor() { 15 | this.cachedData = {}; 16 | } 17 | 18 | fetchData() { 19 | return this.fetchProblemFrequencyData() 20 | } 21 | 22 | async fetchProblemFrequencyData() { 23 | let range = "Problem!A:B" 24 | let url = GoogleSheetsAPIManager.getUrl(range) 25 | let response = await fetch(url) 26 | let data = await response.json(); 27 | let parsedData = this.parseProblemFrequencyData(data["values"]) 28 | return parsedData 29 | } 30 | 31 | parseProblemFrequencyData(data) { 32 | let returnData = {} 33 | for(let i =0; i<=data.length -1; i ++) { 34 | let id = data[i][0] 35 | let frequency = data[i][1] 36 | returnData[id] = frequency 37 | this.cachedData[id] = i + 1 38 | } 39 | return returnData 40 | } 41 | 42 | fetchPremiumProblem(problemId) { 43 | return this.fetchProblemData(problemId) 44 | } 45 | 46 | async fetchProblemData(problemId) { 47 | if(problemId in this.cachedData == false) return new Promise((resolve, reject) => resolve("

No data

")) 48 | let row = this.cachedData[problemId] 49 | let range = "Problem!K" + row 50 | let url = GoogleSheetsAPIManager.getUrl(range) 51 | let response = await fetch(url) 52 | let data = await response.json() 53 | return data["values"][0] 54 | } 55 | 56 | static async fetchProblemDataByRow(row) { 57 | let range = "Problem!K" + row 58 | let url = GoogleSheetsAPIManager.getUrl(range) 59 | let response = await fetch(url) 60 | let data = await response.json() 61 | return data["values"][0][0] 62 | } 63 | } 64 | 65 | 66 | class GoogleSheetsCompanyProblemDataFetcher { 67 | constructor() { 68 | this.companyPageTableData = {} 69 | this.cachedData = {} 70 | this.tableDataFetched = false 71 | // this.fetchCompanyPageTable() //cache company map data 72 | } 73 | 74 | fetchData(companyName) { 75 | if(this.tableDataFetched == false) { 76 | return this.fetchCompanyPageTable() 77 | .then(data => this.fetchCompanyProblemData(companyName)) 78 | } 79 | return this.fetchCompanyProblemData(companyName) 80 | } 81 | 82 | fetchCompanyPageTable() { 83 | let range = "CompaniesProblem_Map!A:C" 84 | let url = GoogleSheetsAPIManager.getUrl(range) 85 | return fetch(url) 86 | .then(data => data.json()) 87 | .then(data => {this.parseCompanyPageTableData(data["values"])}) 88 | .then(this.tableDataFetched = true) 89 | } 90 | 91 | fetchCompanyProblemData(companyName){ 92 | if(companyName in this.cachedData) { return new Promise((resolve, reject) => resolve(this.cachedData[companyName]))} 93 | if(companyName in this.companyPageTableData == false) { return new Promise((resolve, reject) => resolve(new CompanyProblemInfoList()))} 94 | let startRow = this.companyPageTableData[companyName][0] 95 | let endRow = this.companyPageTableData[companyName][1] 96 | let companyDataSheetName = "CompaniesProblem" 97 | let range = `${companyDataSheetName}!A${startRow}:I${endRow}` 98 | let url = GoogleSheetsAPIManager.getUrl(range) 99 | return fetch(url) 100 | .then(data => data.json()) 101 | .then(data =>this.parseCompanyProblemData(companyName, data["values"])) 102 | } 103 | 104 | parseCompanyPageTableData(data) { 105 | for(let i =1; i <= data.length-1; i ++) { 106 | let companyName = data[i][0] 107 | let starRow = data[i][1] 108 | let endRow = data[i][2] 109 | this.companyPageTableData[companyName] = [starRow, endRow] 110 | } 111 | return this.companyPageTableData 112 | } 113 | 114 | parseCompanyProblemData(companyName, data) { 115 | let companyProblemInfoList = new CompanyProblemInfoList() 116 | for(let i =0; i <= data.length - 1; i ++ ){ 117 | let frequency = data[i][2] 118 | let id = data[i][1] 119 | let difficulty = data[i][7] 120 | let problemUrl = data[i][6] 121 | let problemName = data[i][4] 122 | let acceptance = data[i][5] 123 | let companyName = data[i][0] 124 | let duration = data[i][3] 125 | let problemInfo = new CompanyProblemInfo(frequency,id,difficulty,problemUrl,problemName,acceptance,companyName,duration) 126 | companyProblemInfoList.push(duration, problemInfo) 127 | } 128 | this.cachedData[companyName] = companyProblemInfoList 129 | return companyProblemInfoList 130 | } 131 | } 132 | 133 | class GoogleSheetsTopProblemDataFetcher { 134 | fetchData(itemName) { 135 | let range = `${itemName}!A2:F` 136 | let url = GoogleSheetsAPIManager.getUrl(range) 137 | return fetch(url) 138 | .then(data => data.json()) 139 | .then(data => this.parseTopQuestionData(data["values"])) 140 | } 141 | 142 | parseTopQuestionData(data){ 143 | let problemInfoList = new ProblemArray() 144 | for(let i =0; i <= data.length -1; i ++) { 145 | let id = data[i][0] 146 | let freq = data[i][1] 147 | let name = data[i][2] 148 | let acceptance = data[i][3] 149 | let url = data[i][4] 150 | let difficulty = data[i][5] 151 | let problemInfo = new ProblemInfo(freq, id, difficulty, url, name, acceptance) 152 | problemInfoList.push(problemInfo) 153 | } 154 | return problemInfoList 155 | } 156 | } 157 | 158 | class GoogleSheetsProblemTagsDataFetcher { 159 | constructor() { 160 | this.map = {} 161 | this.mapFetched = false 162 | // this.fetchtProblemTagsMap() 163 | } 164 | 165 | fetchData(url) { 166 | if(this.mapFetched) return this.fetchProblemTag(url) 167 | return this.fetchtProblemTagsMap().then(data => this.fetchProblemTag(url)) 168 | } 169 | 170 | fetchProblemTag(url) { 171 | if(!(url in this.map)) { return new Promise((resolve, reject) => resolve(new ProblemInfoList()))} 172 | let startRow = this.map[url][0] 173 | let endRow = this.map[url][1] 174 | let range = `ProblemCompaniesTags!A${startRow}:C${endRow}` 175 | let fetchUrl = GoogleSheetsAPIManager.getUrl(range) 176 | return fetch(fetchUrl).then(data=> data.json()).then(data => this.parseProblemTagData(data["values"])) 177 | } 178 | 179 | parseProblemTagData(data) { 180 | let tagList = new ProblemInfoList() 181 | for(let i =0; i <= data.length -1; i ++) { 182 | let link = data[i][0] 183 | let duration = data[i][1] 184 | let company = data[i][2] 185 | let problemTagObject = new ProblemTag() 186 | problemTagObject.duration = duration 187 | problemTagObject.company = company 188 | problemTagObject.url = link 189 | tagList.push(duration, problemTagObject) 190 | } 191 | this.cachedData = tagList 192 | return tagList 193 | } 194 | 195 | fetchtProblemTagsMap(){ 196 | let range = `ProblemCompaniesTags_Map!A:C` 197 | let url = GoogleSheetsAPIManager.getUrl(range) 198 | return fetch(url) 199 | .then(data => data.json()) 200 | .then(data => this.setProblemTagMap(data["values"])) 201 | } 202 | 203 | setProblemTagMap(data) { 204 | for(let i =0; i <= data.length-1; i ++){ 205 | let url = data[i][0] 206 | let startRow = data[i][1] 207 | let endRow = data[i][2] 208 | this.map[url] = [startRow, endRow] 209 | } 210 | this.mapFetched = true 211 | } 212 | } 213 | 214 | 215 | 216 | class GoogleSheetsEditorialDataFetcher{ 217 | static async fetchEditorialDataByRow(row) { 218 | let range = "Problem!L" + row 219 | let url = GoogleSheetsAPIManager.getUrl(range) 220 | let response = await fetch(url) 221 | let data = await response.json() 222 | let values = data["values"] 223 | if (values == undefined) return "

No data

" 224 | return values[0][0] 225 | } 226 | } 227 | 228 | export { 229 | GoogleSheetsAPIManager, 230 | GoogleSheetsProblemTableDataFetcher, 231 | GoogleSheetsCompanyProblemDataFetcher, 232 | GoogleSheetsTopProblemDataFetcher, 233 | GoogleSheetsProblemTagsDataFetcher, 234 | GoogleSheetsEditorialDataFetcher 235 | } 236 | 237 | -------------------------------------------------------------------------------- /dist/modules/DataFetcher/GoogleSheetsDataGrabber.js: -------------------------------------------------------------------------------- 1 | import { GoogleSheetsAPIManager } from "./GoogleSheetsDataFetcher"; 2 | 3 | 4 | 5 | 6 | class SheetsColumnIncrementor { 7 | constructor(sheetName) { 8 | this.sheetName = sheetName; 9 | this.columns = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('') 10 | this.currentIndex = 0 11 | this.currentColumn = this.columns[this.currentIndex] 12 | } 13 | 14 | getNextColumnUrl() { 15 | let range = `${this.sheetName}!${this.currentColumn}:${this.currentColumn}` 16 | let url = GoogleSheetsAPIManager.getUrl(range) 17 | if(this.currentIndex > this.columns.length) return url 18 | this.currentIndex += 1 19 | this.currentColumn = this.columns[this.currentIndex] 20 | return url 21 | } 22 | } 23 | 24 | 25 | class GoogleSheetsDataGrabber { 26 | constructor() { 27 | this.incrementor = new SheetsColumnIncrementor("Problem") 28 | this.data = [] 29 | this.promiseResolve; 30 | this.promiseReject; 31 | this.promise = new Promise((resolve, reject) => { 32 | this.promiseResolve = resolve; 33 | this.promiseReject = reject; 34 | }) 35 | } 36 | 37 | fetchData() { 38 | this.fetchTableData() 39 | return this.promise 40 | } 41 | 42 | async fetchTableData() { 43 | let url = this.incrementor.getNextColumnUrl() 44 | let response = await fetch(url) 45 | if(response.status == 400) { 46 | console.log(this.data) 47 | this.promiseResolve(this.data) 48 | return 49 | } 50 | let data = await response.json(); 51 | this.data.push(data["values"]) 52 | this.fetchTableData() 53 | } 54 | 55 | 56 | } 57 | 58 | 59 | class CompanyProblemDataGrabber { 60 | 61 | 62 | 63 | 64 | 65 | 66 | } 67 | 68 | class TopProblemDataGrabber { 69 | 70 | 71 | 72 | 73 | 74 | } 75 | 76 | class ProblemCompanyTagsDataGrabber { 77 | fetchData() { 78 | return this.fetchCompanyProblemData() 79 | } 80 | 81 | async fetchCompanyProblemData() { 82 | let range = "CompaniesProblem!A:H" 83 | let url = GoogleSheetsAPIManager.getUrl(range) 84 | let response = await fetch(url) 85 | let data = await response.json(); 86 | return data["values"] 87 | } 88 | } 89 | 90 | class ProblemCompaniesTagsDataGrabber { 91 | fetchData() { 92 | return this.fetchProblemCompaniesTagData() 93 | } 94 | 95 | async fetchProblemCompaniesTagData() { 96 | let range = "ProblemCompaniesTags!A:C" 97 | let url = GoogleSheetsAPIManager.getUrl(range) 98 | let response = await fetch(url) 99 | let data = await response.json(); 100 | return data["values"] 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | 108 | export { 109 | ProblemDataGrabber, 110 | CompanyProblemDataGrabber 111 | 112 | } -------------------------------------------------------------------------------- /dist/modules/DataFetcher/LocalStorageDataFetcher.js: -------------------------------------------------------------------------------- 1 | import { GoogleSheetBufferManager } from "../BufferManager/GoogleSheetsBufferManager" 2 | import { GoogleSheetsEditorialDataFetcher, GoogleSheetsProblemTableDataFetcher } from "./GoogleSheetsDataFetcher"; 3 | 4 | 5 | 6 | class LocalStorageFrequencyDataFetcher{ 7 | constructor() { 8 | this.bufferManager = new GoogleSheetBufferManager() 9 | this.dataTTL = 1209600000; //two weeks in milliseconds 10 | } 11 | 12 | fetchData() { 13 | return this.bufferManager.getBufferedData("TableFrequencyData") 14 | .then(data => this.onDataFetched(data)) 15 | }; 16 | 17 | onDataFetched(data) { 18 | if(Object.keys(data).length == 0 || data == undefined){ 19 | return this.bufferManager.refreshTableData() 20 | .then(data => this.bufferManager.getBufferedData("TableFrequencyData")) 21 | .then(data => data["TableFrequencyData"]["data"]) 22 | } 23 | //refresh data if over ttl duration. The cost refreshing the whole data is the same as checking if there is new update or not 24 | if(Date.now() > data["TableFrequencyData"]["FetchDate"] + this.dataTTL) { 25 | this.bufferManager.refreshTableData() 26 | } 27 | return data["TableFrequencyData"]["data"] 28 | } 29 | 30 | fetchPremiumProblem(problemId) { 31 | return this.fetchProblemData(problemId) 32 | } 33 | 34 | fetchProblemData(problemId) { 35 | return this.bufferManager.getRowOffsetData() 36 | .then(data => this.onPremiumProblemDataFetched(problemId, data)) 37 | } 38 | 39 | onPremiumProblemDataFetched(problemId, data) { 40 | data = data["rowOffset"] 41 | if(problemId in data == false){ 42 | return "

No data

"; 43 | }else { 44 | let row = data[problemId] 45 | return GoogleSheetsProblemTableDataFetcher.fetchProblemDataByRow(row) 46 | } 47 | } 48 | } 49 | 50 | 51 | class LocalStorageEditorialDataFetcher { 52 | fetchData(problemId) { 53 | return new GoogleSheetBufferManager().getRowOffsetData() 54 | .then(data => this.onPremiumProblemDataFetched(problemId, data)) 55 | } 56 | 57 | onPremiumProblemDataFetched(problemId, data) { 58 | data = data["rowOffset"] 59 | if(problemId in data == false){ 60 | return "

No data

"; 61 | }else { 62 | let row = data[problemId] 63 | return GoogleSheetsEditorialDataFetcher.fetchEditorialDataByRow(row) 64 | } 65 | } 66 | } 67 | 68 | 69 | 70 | 71 | 72 | export {LocalStorageFrequencyDataFetcher, 73 | LocalStorageEditorialDataFetcher 74 | } -------------------------------------------------------------------------------- /dist/modules/ElementGenerator/ElementHelperClass.js: -------------------------------------------------------------------------------- 1 | import { CSSStyler } from "../Objects" 2 | 3 | function generateInnerProgressbar(width) { 4 | let progress = document.createElement('div') 5 | progress.style = ` 6 | background-color: ${CSSStyler.COLOR_ACCENT}; 7 | width: ${width}%; 8 | height: 0.5rem; 9 | border-bottom-right-radius: 0.5rem; 10 | border-top-right-radius: 0.5rem; 11 | border-bottom-left-radius: 0.5rem; 12 | border-top-left-radius: 0.5rem; 13 | ` 14 | return progress 15 | } 16 | 17 | function generateRedInnerProgressBar(width) { 18 | let progress = document.createElement('div') 19 | progress.style = ` 20 | background-color: red; 21 | width: ${width}%; 22 | height: 0.5rem; 23 | border-bottom-right-radius: 0.5rem; 24 | border-top-right-radius: 0.5rem; 25 | border-bottom-left-radius: 0.5rem; 26 | border-top-left-radius: 0.5rem; 27 | ` 28 | return progress 29 | } 30 | 31 | function getRateUsElement() { 32 | let span = document.createElement('span') 33 | let innerHtml = `

34 | if you enjoy our app rate us here. 35 |

36 | ` 37 | span.innerHTML = innerHtml; 38 | let urls = span.getElementsByTagName('a') 39 | for(let i =0; i <= urls.length -1; i ++) { 40 | urls[i].classList.add('clickable') 41 | } 42 | return span 43 | } 44 | 45 | 46 | export { 47 | generateInnerProgressbar, 48 | generateRedInnerProgressBar, 49 | getRateUsElement 50 | } -------------------------------------------------------------------------------- /dist/modules/ElementGenerator/TableContentElementGenerator.js: -------------------------------------------------------------------------------- 1 | 2 | import { CSSStyler } from "../Objects" 3 | class TableElementGenerator{ 4 | static generateTextElement(text) { 5 | let div = document.createElement('div') 6 | let h3 = document.createElement('h3') 7 | h3.textContent = text 8 | h3.style = ` 9 | text-align: center; 10 | ` 11 | div.appendChild(h3) 12 | return div 13 | } 14 | 15 | static generateProblemIdElement(text) { 16 | let div = TableElementGenerator.generateTextElement(text) 17 | div.style = ` 18 | width: 5% 19 | ` 20 | return div 21 | } 22 | 23 | static generateProblemFrequencyElement(percentage){ 24 | let progressBar = document.createElement('div') 25 | progressBar.setAttribute("title", String(Math.round(percentage*100)) + "%") 26 | progressBar.style = ` 27 | display: flex; 28 | height: 1rem; 29 | overflow: hidden; 30 | font-size: .75rem; 31 | background-color: ${CSSStyler.getComplementaryColor()}; 32 | border-radius: 0.5rem; 33 | margin-top: auto; 34 | margin-bottom: auto; 35 | width:10%; 36 | ` 37 | 38 | let progress = document.createElement('div') 39 | progress.style = ` 40 | border-radius: 0.5rem; 41 | height:100%; 42 | width:${percentage*100}%; 43 | display: flex; 44 | flex-direction: column; 45 | justify-content: center; 46 | overflow: hidden; 47 | color: #fff; 48 | background-color: ${CSSStyler.COLOR_ACCENT}; 49 | ` 50 | progressBar.appendChild(progress) 51 | return progressBar 52 | } 53 | 54 | static generateProblemNameElement(problem_name, problem_url) { 55 | let problemCell = document.createElement('div') 56 | let a = document.createElement('a') 57 | a.href = problem_url 58 | a.textContent = problem_name 59 | problemCell.appendChild(a) 60 | problemCell.style = ` 61 | width: 50% 62 | ` 63 | return problemCell 64 | } 65 | 66 | static generateProblemDifficultyElement(text) { 67 | let div = TableElementGenerator.generateTextElement(text) 68 | div.style = ` 69 | width: 12% 70 | ` 71 | switch(text) { 72 | case "Hard": 73 | div.children[0].style.color = "red" 74 | break 75 | case "Medium": 76 | div.children[0].style.color = "orange" 77 | break ; 78 | case "Easy": 79 | div.children[0].style.color = "green" 80 | break ; 81 | } 82 | return div 83 | } 84 | 85 | static generateProblemAcceptanceElement(percentage) { 86 | let div = TableElementGenerator.generateTextElement(percentage) 87 | div.style = ` 88 | width: 10% 89 | ` 90 | return div 91 | } 92 | 93 | static generateRowElement(){ 94 | let row = document.createElement('div') 95 | row.style = ` 96 | display:flex; 97 | border-top: solid 1px ${CSSStyler.getComplementaryColor()}; 98 | ` 99 | return row 100 | } 101 | 102 | static generateTableContentElement(data) { 103 | let parentDiv = document.createElement('div') 104 | for(let i = 0; i <= data.length-1; i ++) { 105 | let row = TableElementGenerator.generateRowElement() 106 | 107 | let frequency = data[i].frequency 108 | let id = data[i].id 109 | let difficulty = data[i].difficulty 110 | let problemUrl = data[i].problemUrl 111 | let problemName = data[i].problemName 112 | let acceptance = String(Math.round(data[i].acceptance * 100)) + "%" 113 | 114 | row.appendChild(TableElementGenerator.generateProblemIdElement(id)) 115 | row.appendChild(TableElementGenerator.generateProblemNameElement(problemName, problemUrl)) 116 | row.appendChild(TableElementGenerator.generateProblemAcceptanceElement(acceptance)) 117 | row.appendChild(TableElementGenerator.generateProblemDifficultyElement(difficulty)) 118 | row.appendChild(TableElementGenerator.generateProblemFrequencyElement(frequency)) 119 | 120 | parentDiv.append(row) 121 | } 122 | parentDiv.id = "table-content" 123 | return parentDiv 124 | } 125 | 126 | static generateDurationElement(data) { 127 | let button = document.createElement('button') 128 | button.innerText =data 129 | button.style = ` 130 | width:auto; 131 | margin-right: 2%; 132 | ` 133 | button.setAttribute("duration", data) 134 | 135 | button.addEventListener('select', () => { 136 | button.classList.add("selected-duration-button") 137 | button.classList.remove("unselected-duration-button") 138 | }) 139 | 140 | button.addEventListener('unselect', () => { 141 | button.classList.add("unselected-duration-button") 142 | button.classList.remove("selected-duration-button") 143 | }) 144 | button.classList.add("unselected-duration-button") 145 | return button 146 | } 147 | 148 | static generateTitleElement(title) { 149 | let h2 = document.createElement('h2') 150 | h2.innerText = title 151 | h2.style = ` 152 | font-size:1.5em;` 153 | return h2 154 | } 155 | 156 | } 157 | 158 | 159 | 160 | 161 | export { 162 | TableElementGenerator, 163 | } -------------------------------------------------------------------------------- /dist/modules/ElementGenerator/TagContentElementGenerator.js: -------------------------------------------------------------------------------- 1 | import { CSSStyler } from "../Objects" 2 | 3 | class TagsElementGenerator{ 4 | static generateHeader(text) { 5 | let h3 = document.createElement('h3') 6 | h3.classList.add("default-text-color") 7 | h3.textContent = text 8 | return h3 9 | } 10 | 11 | static generateTag(text) { 12 | let div = document.createElement('div') 13 | div.style = ` 14 | min-width:7%; 15 | margin-right: 3%; 16 | margin-bottom: 1%; 17 | max-width:15%; 18 | text-align:center; 19 | border-radius: 21px; 20 | ` 21 | div.classList.add("sub-title-text-color") 22 | div.textContent = text; 23 | return div 24 | 25 | } 26 | 27 | static generateRow() { 28 | let row = document.createElement('div') 29 | row.style = ` 30 | display:flex; 31 | flex-wrap: wrap; 32 | border-top: solid 1px darkgrey; 33 | 34 | ` 35 | return row 36 | 37 | } 38 | } 39 | 40 | export {TagsElementGenerator} -------------------------------------------------------------------------------- /dist/modules/ElementModifier/CompanySwipperElementModifier.js: -------------------------------------------------------------------------------- 1 | import { CSSStyler } from "../Objects" 2 | 3 | 4 | class CompanySwipperElementModifier { 5 | 6 | constructor() { 7 | this.elementModifier = [] 8 | } 9 | 10 | injectFunctionToTargetElement(func) { 11 | this.elementModifier.push(func) 12 | } 13 | 14 | modifyElement() { 15 | this.modifyActiveElement() 16 | this.addObserverToCompaniesSection() 17 | } 18 | 19 | isSwiperLoading() { 20 | return document.getElementsByClassName("swiper-autoheight")[1] == undefined 21 | } 22 | 23 | modifyActiveElement() { 24 | if(this.isSwiperLoading()) { 25 | window.setTimeout(() => {this.modifyActiveElement.bind(this)()} ,100); 26 | return 27 | } 28 | let parentSwipper = document.getElementsByClassName("swiper-autoheight")[1] 29 | let swipers = parentSwipper.getElementsByClassName('swiper-slide-active') 30 | let swiper = swipers[swipers.length-1] 31 | let links = swiper.getElementsByTagName('a') 32 | for(let ii = 0; ii <= links.length-1; ii ++) { 33 | let href = links[ii].href.split("/") 34 | let companyName = href[href.length-1] 35 | let companyButton = links[ii] 36 | if (companyButton.getAttribute("company-name") == null) { 37 | companyButton.setAttribute("company-name", companyName) 38 | } 39 | companyButton.href = "javascript:void(0)" 40 | for(let iii = 0; iii <= this.elementModifier.length -1; iii++) { 41 | this.elementModifier[iii](companyButton) 42 | } 43 | this.onModifyElementSuccess(companyButton) 44 | } 45 | } 46 | 47 | onModifyElementSuccess(element) { 48 | //generate some kind of indication if element have been modified 49 | let spans = element.getElementsByTagName('span') 50 | let companyButtonNumbeLabel = spans[spans.length-1] 51 | companyButtonNumbeLabel.style.backgroundColor = CSSStyler.COLOR_ACCENT 52 | 53 | } 54 | 55 | addObserverToCompaniesSection() { 56 | if(this.isSwiperLoading()) { 57 | window.setTimeout(() => {this.addObserverToCompaniesSection.bind(this)()} ,100); 58 | return 59 | } 60 | let parentSwipper = document.getElementsByClassName("swiper-autoheight")[1] 61 | var swipper = parentSwipper.parentNode.parentNode 62 | const observer = new MutationObserver(() => { 63 | this.modifyActiveElement() 64 | }); 65 | 66 | if(!swipper) { 67 | window.setTimeout(() => {this.addObserverToCompaniesSection()} ,100); 68 | return; 69 | } 70 | var config = {childList: true, subtree: true, attributes: true, attributeFilter: ['class']}; 71 | observer.observe(swipper,config); 72 | } 73 | } 74 | 75 | export {CompanySwipperElementModifier} -------------------------------------------------------------------------------- /dist/modules/ElementModifier/EditorialPageElementModifier.js: -------------------------------------------------------------------------------- 1 | import { CSSStyler } from "../Objects" 2 | 3 | 4 | 5 | 6 | class EditorialPageElementModifier { 7 | constructor() { 8 | this.elementModifier = [] 9 | this.checkCount = 0 10 | } 11 | 12 | injectFunctionToTargetElement(func) { 13 | this.elementModifier.push(func) 14 | } 15 | 16 | getTabMenu() { 17 | let tabMenu = document.getElementsByClassName('gap-8')[0] 18 | if (tabMenu == undefined) tabMenu = document.getElementsByClassName('gap-6')[0] 19 | return tabMenu 20 | } 21 | 22 | modifyElement() { 23 | let tabs = this.getTabMenu() 24 | if(tabs == undefined) { 25 | window.setTimeout(() => {this.modifyElement()} ,100); 26 | return; 27 | } 28 | this.addEventListenerToEditorialButton(); 29 | } 30 | 31 | addEventListenerToEditorialButton() { 32 | let button = this.getEditorialButton() 33 | if (button == undefined) { 34 | window.setTimeout(() => {this.addEventListenerToEditorialButton()}, 50) 35 | return 36 | } 37 | button.parentElement.parentElement.addEventListener('click', (event) => { 38 | for(let iii = 0; iii <= this.elementModifier.length -1; iii++) { 39 | this.elementModifier[iii](button) 40 | } 41 | if(button.getAttribute("problem-id") != undefined) event.stopImmediatePropagation() 42 | }) 43 | this.disableEditorialRedirect() 44 | this.removeEditorialLockLogo(); 45 | } 46 | 47 | getEditorialButton() { 48 | return document.getElementsByClassName('gap-8')[0].children[1] 49 | .getElementsByClassName("flex")[1] 50 | } 51 | 52 | disableEditorialRedirect() { 53 | document.getElementsByClassName('gap-8')[0].children[1].href = "javascript:void(0)" 54 | } 55 | 56 | removeEditorialLockLogo() { 57 | let editorialButton = this.getEditorialButton() 58 | if (editorialButton == undefined) { 59 | window.setTimeout(() => {this.removeEditorialLockLogo()} ,50); 60 | return; 61 | } 62 | let lockLogo = document.getElementsByClassName('gap-8')[0].children[1] 63 | .getElementsByClassName("flex")[1] 64 | .children[0].getElementsByTagName('svg')[0] 65 | if(lockLogo == undefined) { 66 | if(this.checkCount > 5) return 67 | window.setTimeout(() => {this.removeEditorialLockLogo()} ,150); 68 | this.checkCount += 1 69 | return 70 | } 71 | lockLogo.style.opacity = 0; 72 | editorialButton.setAttribute("problem-name", document.URL.split("/")[4]) 73 | editorialButton.setAttribute("problem-id", this.getProblemId()) 74 | this.addUnlockedIndicator() 75 | } 76 | 77 | addUnlockedIndicator(){ 78 | let editorialTabElement = this.getTabMenu().children[1]; 79 | editorialTabElement.getElementsByTagName('span')[0].style.color = CSSStyler.COLOR_ACCENT 80 | } 81 | 82 | getProblemId() { 83 | //Todo: Dont rely on problem id, if description page is not opened problem id wont be fetched 84 | return document.getElementsByClassName("text-lg")[0].textContent.split(".")[0] 85 | } 86 | } 87 | 88 | 89 | export {EditorialPageElementModifier} -------------------------------------------------------------------------------- /dist/modules/ElementModifier/ProblemTableElementModifier.js: -------------------------------------------------------------------------------- 1 | 2 | class ProblemTableElementModifier{ 3 | constructor() { 4 | this.elementModifier = [] 5 | } 6 | 7 | modifyElement() { 8 | this.observer = new MutationObserver(() => { 9 | this.modifyActiveElement() 10 | }); 11 | this.modifyActiveElement() 12 | this.addObserverToProblemTable() 13 | } 14 | 15 | injectFunctionToTargetElement(func){ 16 | this.elementModifier.push(func) 17 | } 18 | 19 | isTableLoading() { 20 | let table = document.querySelectorAll('[role="rowgroup"]')[1] 21 | let problemsets = table.querySelectorAll('[role="row"]') 22 | return problemsets[0].querySelectorAll('[role="cell"]')[1].textContent == '' 23 | } 24 | 25 | modifyActiveElement = () => { 26 | if(this.isTableLoading()) { 27 | window.setTimeout(() => {this.modifyActiveElement.bind(this)()} ,100); 28 | return; 29 | } 30 | this.disconnectObserverToProblemTable() 31 | let table = document.querySelectorAll('[role="rowgroup"]')[1] 32 | let problemsets = table.querySelectorAll('[role="row"]') 33 | for(let i =0; i <= problemsets.length -1 ; i ++) { 34 | let cells = problemsets[i].querySelectorAll('[role="cell"]') 35 | let problemName = cells[1].textContent 36 | let id = problemName.split(".")[0] 37 | problemsets[i].setAttribute("problem-id", String(id)) 38 | let isPremium = problemsets[i].getElementsByTagName("rect").length > 0 39 | problemsets[i].setAttribute("is-premium", isPremium) 40 | for(let ii = 0; ii <= this.elementModifier.length -1; ii ++) { 41 | this.elementModifier[ii](problemsets[i]) 42 | } 43 | } 44 | this.addObserverToProblemTable() 45 | } 46 | 47 | disconnectObserverToProblemTable() { 48 | this.observer.disconnect() 49 | } 50 | 51 | addObserverToProblemTable() { 52 | let table = document.querySelector('[role="table"]') 53 | var config = {childList: true, subtree: true}; 54 | this.observer.observe(table,config); 55 | } 56 | } 57 | 58 | export { ProblemTableElementModifier} -------------------------------------------------------------------------------- /dist/modules/ElementModifier/ProblemTagsElementModifier.js: -------------------------------------------------------------------------------- 1 | import { CSSStyler } from "../Objects"; 2 | 3 | 4 | class ProblemTagsElementModifier { 5 | constructor() { 6 | this.tagButtonListener = [] 7 | } 8 | 9 | getTabMenu() { 10 | let tabMenu = document.getElementsByClassName('gap-8')[0] 11 | if (tabMenu == undefined) tabMenu = document.getElementsByClassName('gap-6')[0] 12 | return tabMenu 13 | } 14 | 15 | modifyElement() { 16 | let tabMenu = this.getTabMenu() 17 | if(tabMenu == undefined) { 18 | window.setTimeout(() => {this.modifyElement()} ,100); 19 | return; 20 | } 21 | let tabs = tabMenu.children[0].children 22 | if(tabs.length == 0) { 23 | window.setTimeout(() => {this.modifyElement()} ,100); 24 | return; 25 | } 26 | if(this.isDescriptionTabActive()) this.modifyCompaniesTagButton() 27 | this.addObserverToLeftTab() 28 | } 29 | 30 | addObserverToLeftTab() { 31 | let tabElement = this.getTabMenu() 32 | if(tabElement == undefined) { 33 | window.setTimeout(() => {this.addObserverToLeftTab()} ,100); 34 | return; 35 | } 36 | let config = {childList: true, subtree: true, attributes: true, attributeFilter: ['class']}; 37 | let observer = new MutationObserver(() => { 38 | if(this.isDescriptionTabActive()) this.modifyCompaniesTagButton() 39 | }) 40 | observer.observe(tabElement, config) 41 | } 42 | 43 | isDescriptionTabActive() { 44 | return this.getTabMenu().children[0].children[0].childElementCount == 2 45 | } 46 | 47 | modifyCompaniesTagButton() { 48 | let tagButton = document.getElementsByClassName('pt-3')[0] 49 | if(!tagButton) { 50 | window.setTimeout(() => {this.modifyCompaniesTagButton.bind(this)()} ,100); 51 | return; 52 | } 53 | let lockicon = tagButton.getElementsByTagName('svg')[0] 54 | if(lockicon == undefined) return 55 | let tagDiv = lockicon.parentElement 56 | lockicon.remove() 57 | let newNode = tagDiv.cloneNode(true) 58 | tagDiv.parentElement.replaceChild(newNode, tagDiv) 59 | newNode.style.backgroundColor = CSSStyler.COLOR_ACCENT 60 | newNode.style.color = 'black' 61 | for(let i =0; i <= this.tagButtonListener.length -1; i ++) { 62 | newNode.addEventListener('click', this.tagButtonListener[i]) 63 | } 64 | } 65 | 66 | addTagButtonOnClickListener(func){ 67 | this.tagButtonListener.push(func) 68 | } 69 | } 70 | 71 | 72 | 73 | export {ProblemTagsElementModifier} -------------------------------------------------------------------------------- /dist/modules/ElementModifier/TagPageProblemTableElementModifier.js: -------------------------------------------------------------------------------- 1 | class TagPageProblemTableElementModifier { 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | } 10 | 11 | export {TagPageProblemTableElementModifier} -------------------------------------------------------------------------------- /dist/modules/ElementModifier/TopProblemFoldoutElementModifier.js: -------------------------------------------------------------------------------- 1 | 2 | import { CSSStyler } from "../Objects"; 3 | 4 | class TopProblemFoldoutElementModifier{ 5 | constructor() { 6 | this.elementModifier = [] 7 | } 8 | 9 | injectFunctionToTargetElement(func) { 10 | this.elementModifier.push(func) 11 | } 12 | 13 | modifyElement() { 14 | if(this.isloading()) { 15 | window.setTimeout(() => {this.modifyElement()} ,100); 16 | return; 17 | } 18 | this.observer = new MutationObserver(() => { 19 | this.modifyLockedElement() 20 | }); 21 | this.modifyLockedElement() 22 | this.addObsersverToFoldout() 23 | } 24 | 25 | isloading() { 26 | return document.getElementsByClassName("space-y-1.5")[0] == undefined 27 | } 28 | 29 | addObsersverToFoldout() { 30 | let foldout = document.getElementsByClassName("space-y-1.5")[0] 31 | var config = {childList: true, subtree: true}; 32 | this.observer.observe(foldout,config); 33 | } 34 | 35 | modifyLockedElement = () => { 36 | let foldout = document.getElementsByClassName("space-y-1.5")[0] 37 | if(!foldout) { 38 | window.setTimeout(() => {this.modifyLockedElement.bind(this)()} ,100); 39 | return; 40 | } 41 | let foldoutItem = foldout.children 42 | for(let i =0; i <= foldoutItem.length -2; i ++){ 43 | let lockLogo = foldoutItem[i].getElementsByTagName('svg') 44 | if (lockLogo.length > 0) { 45 | foldoutItem[i].getElementsByTagName('a')[0].href = "javascript:void(0)" 46 | let itemName = foldoutItem[i].textContent.replaceAll(" ", "") 47 | foldoutItem[i].setAttribute("item", itemName) 48 | foldoutItem[i].style.color = CSSStyler.COLOR_ACCENT 49 | lockLogo[0].remove() 50 | for(let iii = 0; iii <= this.elementModifier.length -1; iii++) { 51 | this.elementModifier[iii](foldoutItem[i]) 52 | } 53 | } 54 | } 55 | } 56 | 57 | disconnectObserverToFoldout() { 58 | this.observer.disconnect() 59 | } 60 | } 61 | 62 | export {TopProblemFoldoutElementModifier} -------------------------------------------------------------------------------- /dist/modules/Objects.js: -------------------------------------------------------------------------------- 1 | 2 | class ProblemInfo{ 3 | constructor(frequency, id, difficulty, problemUrl, problemName, acceptance) { 4 | this.frequency = frequency 5 | this.id = id 6 | this.difficulty = difficulty 7 | this.problemUrl = problemUrl 8 | this.problemName = problemName 9 | this.acceptance = acceptance 10 | } 11 | } 12 | 13 | class CompanyProblemInfo extends ProblemInfo { 14 | constructor(frequency, id, difficulty, problemUrl, problemName, acceptance, companyName, duration) { 15 | super(frequency, id, difficulty, problemUrl, problemName, acceptance) 16 | this.companyName = companyName 17 | this.duration = duration 18 | } 19 | } 20 | 21 | class ProblemTag{ 22 | constructor(duration, company, url) { 23 | this.url = url 24 | this.duration = duration 25 | this.company = company 26 | } 27 | } 28 | 29 | class InfoList{ 30 | constructor() { 31 | this.data = {} 32 | } 33 | 34 | getKeys() { 35 | return Object.keys(this.data) 36 | } 37 | 38 | getList(key) { 39 | if (key == undefined) throw new Error("Key cannot be undefined") 40 | if (key in this.data) return this.data[key] 41 | return [] 42 | } 43 | } 44 | 45 | 46 | class ProblemInfoList extends InfoList{ 47 | push(key, value) { 48 | if(key == undefined || value == undefined ) throw new Error("Key/Value error") 49 | if (key in this.data) { 50 | this.data[key].push(value) 51 | return; 52 | } 53 | let arr = [] 54 | arr.push(value) 55 | this.data[key] = arr 56 | } 57 | } 58 | 59 | class CompanyProblemInfoList extends InfoList{ 60 | push(key, value) { 61 | if (key in this.data) { 62 | this.data[key].push(value) 63 | return; 64 | } 65 | let arr = new ProblemArray() 66 | arr.push(value) 67 | this.data[key] = arr 68 | } 69 | } 70 | 71 | class CompanyProblemDurations { 72 | static SIXMONTHS = "6 months" 73 | static TWOYEARS = "2 years" 74 | static ALLTIME = "All time" 75 | static ONEYEAR = "1 year" 76 | 77 | static DURATION_LIST = [ 78 | CompanyProblemDurations.SIXMONTHS, 79 | CompanyProblemDurations.ONEYEAR, 80 | CompanyProblemDurations.TWOYEARS, 81 | CompanyProblemDurations.ALLTIME, 82 | ] 83 | } 84 | 85 | class ProblemArray extends Array { 86 | sort(by, reverse = false) { 87 | let sorter = new by() 88 | sorter.sort(this, reverse) 89 | } 90 | } 91 | 92 | 93 | class CSSStyler { 94 | 95 | static getContainerBackgroundColor() { 96 | let isDarkMode = document.body.classList.contains("chakra-ui-dark") 97 | switch (isDarkMode){ 98 | case true: 99 | return "#151515" 100 | case false: 101 | return "#f3f3f3" 102 | } 103 | } 104 | 105 | static getComplementaryColor() { 106 | let isDarkMode = document.body.classList.contains("chakra-ui-dark") 107 | switch (isDarkMode){ 108 | case true: 109 | return "#282828" 110 | case false: 111 | return "#dcdcdc" 112 | } 113 | } 114 | 115 | 116 | static COLOR_ACCENT = "#62C555" 117 | static BACKGROUND_CONTAINER_COLOR = '#101010' 118 | static SUB_BACKGROUND_CONTAINER_COLOR = '#282828' 119 | static TEXT_COLOR = '#dcdcdc' 120 | static TEXT_COLOR_SELECTED = '#7d7d7d' 121 | } 122 | 123 | export {ProblemInfo 124 | , CompanyProblemInfo 125 | , CompanyProblemInfoList 126 | , CompanyProblemDurations 127 | , ProblemArray 128 | , CSSStyler 129 | , ProblemTag 130 | , ProblemInfoList 131 | , InfoList 132 | } -------------------------------------------------------------------------------- /dist/modules/ProblemSorter.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | class Sorter{ 5 | getValue(list, index){ 6 | return list[index] 7 | } 8 | 9 | doSwap(left, right) { 10 | return left > right 11 | } 12 | 13 | swap(arr, i, j) { 14 | let temp = arr[i]; 15 | arr[i] = arr[j]; 16 | arr[j] = temp; 17 | } 18 | 19 | partition(arr, low, high, reverse) { 20 | let pivot = this.getValue(arr, high); 21 | let i = (low - 1); 22 | 23 | for (let j = low; j <= high - 1; j++) { 24 | let low = this.getValue(arr, j) 25 | let swap = this.doSwap(low, pivot) 26 | if(reverse) { 27 | swap = !swap 28 | } 29 | if (swap) { 30 | i++; 31 | this.swap(arr, i, j); 32 | } 33 | } 34 | this.swap(arr, i + 1, high); 35 | return (i + 1); 36 | } 37 | 38 | 39 | sort(arr, reverse = false){ 40 | this.quickSort(arr, 0, arr.length-1, reverse) 41 | } 42 | 43 | quickSort(arr, low, high, reverse) { 44 | if(high == undefined && low == undefined) { 45 | high = arr.length -1; 46 | low = 0 47 | } 48 | if (low < high) { 49 | let pi = this.partition(arr, low, high, reverse); 50 | this.quickSort(arr, low, pi - 1, reverse); 51 | this.quickSort(arr, pi + 1, high, reverse); 52 | } 53 | } 54 | } 55 | 56 | class AcceptanceSorter extends Sorter { 57 | getValue(list, index){ 58 | return list[index].acceptance 59 | } 60 | } 61 | 62 | class DifficultySorter extends Sorter{ 63 | getValue(list, index){ 64 | return list[index].difficulty 65 | } 66 | 67 | doSwap(left, right) { 68 | let difficulties = ["Easy", "Medium", "Hard"] 69 | return difficulties.indexOf(left) < difficulties.indexOf(right) 70 | } 71 | 72 | } 73 | 74 | class IDSorter extends Sorter { 75 | getValue(list, index){ 76 | return parseInt(list[index].id) 77 | } 78 | } 79 | 80 | class NameSorter extends Sorter { 81 | getValue(list, index){ 82 | return list[index].problemName 83 | } 84 | 85 | doSwap(left, right) { 86 | let alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" 87 | let firstWordLeft = left[0].toLowerCase() 88 | let firstWordRight = right[0].toLowerCase() 89 | return alphabet.indexOf(firstWordLeft) < alphabet.indexOf(firstWordRight) 90 | } 91 | } 92 | 93 | class FrequencySorter extends Sorter { 94 | getValue(list, index){ 95 | return list[index].frequency 96 | } 97 | } 98 | 99 | 100 | export {AcceptanceSorter, DifficultySorter, NameSorter, IDSorter, FrequencySorter} 101 | 102 | -------------------------------------------------------------------------------- /dist/modules/Unlocker/CompaniesProblemUnlocker.js: -------------------------------------------------------------------------------- 1 | 2 | import { CompanySwipperElementModifier } from "../ElementModifier/CompanySwipperElementModifier" 3 | import { TableContentBuilder } from "../ContentBuilder/TableContentBuilder" 4 | import {modalManager} from "../ContainerManager" 5 | import { GoogleSheetsCompanyProblemDataFetcher } from "../DataFetcher/GoogleSheetsDataFetcher" 6 | import { analyticsManager } from "../AnalyticsManager" 7 | 8 | 9 | class CompaniesProblemUnlocker { 10 | constructor() { 11 | this.elementModifier = new CompanySwipperElementModifier() 12 | this.dataFetcher = new GoogleSheetsCompanyProblemDataFetcher() 13 | this.containerManager = modalManager 14 | this.isFetching = false 15 | this.analyticsManager = analyticsManager 16 | this.companyProblemButtonId = 1 17 | this.name = "CompaniesProblemUnlocker" 18 | } 19 | 20 | unlock() { 21 | this.elementModifier.injectFunctionToTargetElement(this.getFunctionToBeInjected()) 22 | this.elementModifier.modifyElement() 23 | this.analyticsManager.fireUnlockedDataEvent(this.name) 24 | } 25 | 26 | getFunctionToBeInjected() { 27 | return (element) => { 28 | element.addEventListener("click", this.onCompanyButtonClick); 29 | } 30 | } 31 | 32 | onCompanyButtonClick = (event) => { 33 | if(this.isFetching) return; 34 | this.isFetching = true 35 | let companyName = event.currentTarget.getAttribute("company-name") 36 | this.analyticsManager.fireModifiedButtonClickedEvent(this.companyProblemButtonId, "CompanyButton", companyName) 37 | let title = event.currentTarget.getElementsByClassName("text-label-2")[0].textContent 38 | this.containerManager.clearModalContent() 39 | this.containerManager.openModal() 40 | this.containerManager.showLoadingIcon() 41 | this.dataFetcher.fetchData(companyName) 42 | .then(data => this.onFetchSuccess(data, title)).then(data => {this.isFetching = false}) 43 | .catch(e =>{this.isFetching=false} ) 44 | } 45 | 46 | onFetchSuccess(data, companyName) { 47 | let targetParent = this.containerManager.getModalContentBox() 48 | let tableBuilder = new TableContentBuilder(data) 49 | let durations = data.getKeys() 50 | for(let i =0; i <= durations.length -1; i ++) { 51 | tableBuilder.addDurationData(durations[i], data.getList(durations[i])) 52 | } 53 | tableBuilder.buildRateUsRow() 54 | tableBuilder.buildTitleRow(companyName) 55 | tableBuilder.buildDurationsRow() 56 | tableBuilder.setShownData(data.getList(durations[0])) 57 | tableBuilder.buildHeaderRow() 58 | tableBuilder.buildTable() 59 | this.containerManager.clearModalContent() 60 | targetParent.appendChild(tableBuilder.getResult()) 61 | } 62 | } 63 | 64 | 65 | export { 66 | CompaniesProblemUnlocker 67 | } -------------------------------------------------------------------------------- /dist/modules/Unlocker/EditorialUnlocker.js: -------------------------------------------------------------------------------- 1 | import { LocalStorageEditorialDataFetcher } from "../DataFetcher/LocalStorageDataFetcher" 2 | import { EditorialPageElementModifier } from "../ElementModifier/EditorialPageElementModifier" 3 | import { modalManager } from "../ContainerManager" 4 | import { EditorialContentBuilder } from "../ContentBuilder/EditorialContentBuilder" 5 | 6 | 7 | class EditorialUnlocker{ 8 | constructor() { 9 | this.name = "EditorialUnlocker" 10 | this.elementModifier = new EditorialPageElementModifier() 11 | this.dataFetcher = new LocalStorageEditorialDataFetcher() 12 | this.containerManager = modalManager; 13 | 14 | } 15 | 16 | unlock(){ 17 | this.elementModifier.injectFunctionToTargetElement(this.onEditorialTabClicked) 18 | this.elementModifier.modifyElement() 19 | } 20 | 21 | onEditorialTabClicked = (button) => { 22 | let problemId = button.getAttribute("problem-id") 23 | if (problemId == undefined) return; 24 | this.containerManager.clearModalContent() 25 | this.containerManager.openModal() 26 | this.containerManager.showLoadingIcon() 27 | this.dataFetcher.fetchData(problemId) 28 | .then(data => this.onDataFetched(data)) 29 | } 30 | 31 | onDataFetched(innerHtml) { 32 | let builder = new EditorialContentBuilder() 33 | builder.buildRateUsText() 34 | builder.buildContent(innerHtml) 35 | let targetParent = this.containerManager.getModalContentBox() 36 | this.containerManager.clearModalContent() 37 | targetParent.appendChild(builder.getResult()) 38 | } 39 | } 40 | 41 | 42 | export {EditorialUnlocker} -------------------------------------------------------------------------------- /dist/modules/Unlocker/ProblemTableUnlocker.js: -------------------------------------------------------------------------------- 1 | import { ProblemTableElementModifier } from "../ElementModifier/ProblemTableElementModifier"; 2 | import { generateInnerProgressbar, generateRedInnerProgressBar } from "../ElementGenerator/ElementHelperClass"; 3 | import { GoogleSheetsProblemTableDataFetcher } from "../DataFetcher/GoogleSheetsDataFetcher"; 4 | import { CSSStyler } from "../Objects"; 5 | import { modalManager } from "../ContainerManager"; 6 | import * as DOMPurify from 'dompurify'; 7 | import { LocalStorageFrequencyDataFetcher } from "../DataFetcher/LocalStorageDataFetcher"; 8 | import { analyticsManager } from "../AnalyticsManager"; 9 | 10 | class ProblemTableUnlocker{ 11 | constructor() { 12 | this.elementModifier = new ProblemTableElementModifier() 13 | this.dataFetcher = new LocalStorageFrequencyDataFetcher() 14 | this.containerManager = modalManager 15 | this.isFetching = false; 16 | this.premiumProblemButtonId = 2; 17 | this.analyticsManager = analyticsManager; 18 | this.name = "ProblemTableUnlocker" 19 | } 20 | 21 | onFetchSuccess() { 22 | this.elementModifier.injectFunctionToTargetElement(ProblemTableUnlocker.removeProgressbarUnlockButton) 23 | this.elementModifier.injectFunctionToTargetElement(this.insertInnerProgressbar) 24 | this.elementModifier.injectFunctionToTargetElement(this.modifyPremiumProblemHref) 25 | this.elementModifier.modifyElement() 26 | } 27 | 28 | modifyPremiumProblemHref = (row) => { 29 | let isPremium = row.getAttribute("is-premium") == "true" 30 | if(isPremium){ 31 | this.removePremiumIcons(row) 32 | let problemId = row.getAttribute("problem-id") 33 | let problemUrl = row.getElementsByTagName("a")[0] 34 | problemUrl.href = "javascript:void(0)" 35 | problemUrl.style.color = CSSStyler.COLOR_ACCENT; 36 | problemUrl.addEventListener('click', () => { 37 | this.onPremiumProblemClicked(problemId) 38 | } 39 | ) 40 | } 41 | } 42 | 43 | unlock() { 44 | this.dataFetcher.fetchData() 45 | .then(data => {this.problemData = data}) 46 | .then(this.onFetchSuccess.bind(this)) 47 | .then(this.analyticsManager.fireUnlockedDataEvent(this.name)) 48 | .catch(e => (console.log(this, e))) 49 | } 50 | 51 | onPremiumProblemClicked = (problemId) => { 52 | if(this.isFetching) return 53 | this.analyticsManager.fireModifiedButtonClickedEvent(this.premiumProblemButtonId, "PremiumProblem", problemId) 54 | this.isFetching = true; 55 | this.containerManager.clearModalContent() 56 | this.containerManager.openModal() 57 | this.containerManager.showLoadingIcon() 58 | this.dataFetcher.fetchPremiumProblem(parseInt(problemId)) 59 | .then(data => this.onProblemFetchSuccess(data)) 60 | .then(this.isFetching = false) 61 | } 62 | 63 | onProblemFetchSuccess(data){ 64 | let targetParent = this.containerManager.getModalContentBox() 65 | this.containerManager.clearModalContent() 66 | let htmlString = String(data).replaceAll("", "
") 67 | targetParent.innerHTML= DOMPurify.sanitize(htmlString); 68 | let pres = targetParent.getElementsByTagName("pre") 69 | for(let i =0; i <= pres.length-1; i ++) { 70 | pres[i].style = ` 71 | border-radius: 0.5rem; 72 | font-family: Menlo,sans-serif; 73 | font-size: .875rem; 74 | line-height: 1.25rem; 75 | margin-bottom: 1rem; 76 | margin-top: 1rem; 77 | padding: 1rem; 78 | ` 79 | } 80 | } 81 | 82 | removePremiumIcons(row) { 83 | let cells = row.querySelectorAll('[role="cell"]') 84 | let lockLogo = cells[0].getElementsByTagName("svg")[0] 85 | let premiumLogo = cells[1].getElementsByTagName("svg")[0] 86 | if(lockLogo != undefined) lockLogo.style.opacity = 0; 87 | if(premiumLogo != undefined) premiumLogo.style.opacity = 0; 88 | } 89 | 90 | insertInnerProgressbar = (row) => { 91 | let cells = row.querySelectorAll('[role="cell"]') 92 | let progressBar = cells[cells.length -1] 93 | 94 | let id = row.getAttribute("problem-id") 95 | let width = this.problemData[id] 96 | if(width == undefined) width = 100 97 | width *= 100 98 | let innerProgressbarClassName = "inner-progressbar" 99 | let innerProgressbar = progressBar.getElementsByClassName(innerProgressbarClassName) 100 | let outerProgressbar = progressBar.getElementsByClassName('rounded-l-lg')[0] 101 | if(innerProgressbar.length > 0) { innerProgressbar[0].remove()} 102 | let progress; 103 | if(id in this.problemData) { 104 | progress = generateInnerProgressbar(width) 105 | outerProgressbar.setAttribute("title", `${Math.round(width)}%`) 106 | } else { 107 | progress = generateRedInnerProgressBar(width) 108 | outerProgressbar.setAttribute("title", `No Data`) 109 | } 110 | progress.classList.add(innerProgressbarClassName) 111 | outerProgressbar.appendChild(progress) 112 | } 113 | 114 | static removeProgressbarUnlockButton(row) { 115 | let cells = row.querySelectorAll('[role="cell"]') 116 | let progressbar = cells[cells.length -1] 117 | 118 | let lockLogo = progressbar.getElementsByTagName("svg")[0] 119 | let leftBar = progressbar.getElementsByClassName('rounded-r-lg')[0] 120 | let rightBar = progressbar.getElementsByClassName('rounded-l-lg')[0] 121 | if (lockLogo!= undefined) lockLogo.remove(); 122 | if (leftBar!= undefined) leftBar.remove() 123 | if (rightBar != undefined){ 124 | rightBar.style = ` 125 | border-bottom-right-radius: 0.5rem; 126 | overflow: hidden; 127 | border-top-right-radius: 0.5rem 128 | ` 129 | } 130 | } 131 | 132 | } 133 | 134 | export {ProblemTableUnlocker} -------------------------------------------------------------------------------- /dist/modules/Unlocker/ProblemTagsUnlocker.js: -------------------------------------------------------------------------------- 1 | 2 | import { GoogleSheetsProblemTagsDataFetcher } from "../DataFetcher/GoogleSheetsDataFetcher" 3 | import { ProblemTagsElementModifier } from "../ElementModifier/ProblemTagsElementModifier" 4 | import { TagsContentBuilder } from "../ContentBuilder/TagsContentBuilder" 5 | import { modalManager } from "../ContainerManager" 6 | import { analyticsManager } from "../AnalyticsManager" 7 | 8 | class ProblemTagsUnlocker{ 9 | constructor() { 10 | this.elementModifier = new ProblemTagsElementModifier() 11 | this.dataFetcher = new GoogleSheetsProblemTagsDataFetcher() 12 | this.containerManager = modalManager 13 | this.isFetching = false 14 | this.problemTagButtonId = 3; 15 | this.analyticsManager = analyticsManager; 16 | this.name = "ProblemTagsUnlocker" 17 | } 18 | 19 | onTagButtonClicked = () => { 20 | if(this.isFetching) return 21 | this.isFetching=true 22 | let problemName = document.URL.split('/')[4] 23 | this.analyticsManager.fireModifiedButtonClickedEvent(this.problemTagButtonId, "ProblemTagButton", problemName) 24 | this.containerManager.clearModalContent() 25 | this.containerManager.openModal() 26 | this.containerManager.showLoadingIcon() 27 | this.dataFetcher.fetchData(problemName) 28 | .then(data => this.onFetchSucces(data)) 29 | .then(data=>{this.isFetching=false}) 30 | .catch( e => {console.log(this, e); 31 | this.isFetching=false}) 32 | } 33 | 34 | unlock() { 35 | this.elementModifier.modifyElement() 36 | this.elementModifier.addTagButtonOnClickListener(this.onTagButtonClicked) 37 | this.analyticsManager.fireUnlockedDataEvent(this.name) 38 | } 39 | 40 | onFetchSucces = (data) => { 41 | let keys = data.getKeys() 42 | let builder = new TagsContentBuilder() 43 | for(let i =0; i<= keys.length -1; i ++) { 44 | builder.buildHeader(keys[i]) 45 | builder.buildTagsBox(data.getList(keys[i])) 46 | } 47 | builder.buildRateUsText() 48 | let targetParent = this.containerManager.getModalContentBox() 49 | this.containerManager.clearModalContent() 50 | targetParent.appendChild(builder.getResult()) 51 | } 52 | } 53 | 54 | 55 | export {ProblemTagsUnlocker} -------------------------------------------------------------------------------- /dist/modules/Unlocker/TagPageProblemTableUnlocker.js: -------------------------------------------------------------------------------- 1 | 2 | class TagPageProbleTableUnlocker{ 3 | constructor() { 4 | } 5 | 6 | 7 | unlock() { 8 | } 9 | } 10 | 11 | 12 | export {TagPageProbleTableUnlocker} -------------------------------------------------------------------------------- /dist/modules/Unlocker/TopProblemUnlocker.js: -------------------------------------------------------------------------------- 1 | 2 | import { modalManager } from "../ContainerManager" 3 | import { GoogleSheetsTopProblemDataFetcher } from "../DataFetcher/GoogleSheetsDataFetcher" 4 | import { TopProblemFoldoutElementModifier } from "../ElementModifier/TopProblemFoldoutElementModifier" 5 | import { TableContentBuilder } from "../ContentBuilder/TableContentBuilder" 6 | import { analyticsManager } from "../AnalyticsManager" 7 | 8 | class TopProblemUnlocker { 9 | constructor() { 10 | this.elementModifier = new TopProblemFoldoutElementModifier() 11 | this.dataFetcher = new GoogleSheetsTopProblemDataFetcher() 12 | this.containerManager = modalManager 13 | this.isFetching = false 14 | this.topProblemButtonId = 4; 15 | this.analyticsManager = analyticsManager; 16 | this.name = "TopProblemUnlocker" 17 | } 18 | 19 | unlock( ){ 20 | this.elementModifier.injectFunctionToTargetElement(this.getFunctionToBeInjected()) 21 | this.elementModifier.modifyElement() 22 | this.analyticsManager.fireUnlockedDataEvent(this.name) 23 | } 24 | 25 | onTopProblemClicked = (event) => { 26 | if(this.isFetching)return 27 | this.isFetching=true 28 | let itemName = event.currentTarget.getAttribute("item") 29 | let title = event.currentTarget.getElementsByClassName("font-medium")[0].textContent 30 | this.analyticsManager.fireModifiedButtonClickedEvent(this.topProblemButtonId, "TopProblem", title) 31 | this.containerManager.clearModalContent() 32 | this.containerManager.openModal() 33 | this.containerManager.showLoadingIcon() 34 | this.dataFetcher.fetchData(itemName) 35 | .then(data => this.onFetchSuccess(data, title)) 36 | .then(data =>{this.isFetching=false}) 37 | .catch(e => { 38 | console.log(this, "Fetch Error" + e); 39 | this.isFetching=false 40 | }) 41 | event.stopImmediatePropagation(); 42 | } 43 | 44 | onFetchSuccess(data, itemName){ 45 | let tableBulder = new TableContentBuilder() 46 | tableBulder.setShownData(data) 47 | tableBulder.buildTitleRow(itemName) 48 | tableBulder.buildHeaderRow() 49 | tableBulder.buildTable() 50 | let table = tableBulder.getResult() 51 | this.containerManager.clearModalContent() 52 | this.containerManager.getModalContentBox().appendChild(table) 53 | } 54 | 55 | getFunctionToBeInjected() { 56 | return (element) => { 57 | element.addEventListener("click", this.onTopProblemClicked); 58 | } 59 | } 60 | } 61 | 62 | export{ TopProblemUnlocker} -------------------------------------------------------------------------------- /dist/style.css: -------------------------------------------------------------------------------- 1 | 2 | :root { 3 | --accent-color: #62C555; 4 | --container-background: #101010; 5 | --container-sub-background: #282828; 6 | --default-text-color:rgb(247, 247, 247); 7 | --selected-text-color:rgb(134, 134, 134); 8 | } 9 | 10 | .clickable{ 11 | text-decoration: underline; 12 | cursor: pointer; 13 | font-weight: bold; 14 | } 15 | 16 | .green-text{ 17 | color:var(--accent-color) 18 | } 19 | 20 | .green-background{ 21 | background-color: var(--accent-color); 22 | } 23 | 24 | .black-background { 25 | background-color:var(--container-background) 26 | } 27 | 28 | .dark-grey-background{ 29 | color:var(--container-sub-background) 30 | } 31 | 32 | .selected-duration-button{ 33 | color:var(--selected-text-color); 34 | } 35 | 36 | .loading-logo { 37 | border: 8px solid #f3f3f3; 38 | border-top: 8px solid var(--accent-color); 39 | border-radius: 50%; 40 | width: 60px; 41 | height: 60px; 42 | animation: spin 2s linear infinite; 43 | margin: auto; 44 | } 45 | 46 | @keyframes spin { 47 | 0% { transform: rotate(0deg); } 48 | 100% { transform: rotate(360deg); } 49 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Leetcode Premium Bypasser", 3 | "version": "1.1.2", 4 | "description": "Enables leetcode premium features", 5 | "permissions": ["storage", "unlimitedStorage","tabs"], 6 | "action": { 7 | "default_popup": "dist/index.html" 8 | }, 9 | "manifest_version": 3, 10 | "content_scripts": [ 11 | { 12 | "css": ["dist/style.css"], 13 | "js": ["/dist/main.js"], 14 | "matches": [ 15 | "https://leetcode.com/*" 16 | ] 17 | } 18 | ], 19 | "icons": { 20 | "96": "/assets/logo.png" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leetcodepremiumbypass", 3 | "version": "1.1.2", 4 | "description": "

Only aviable locally

", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha tests/* --require @babel/register --recursive --timeout 300000", 8 | "build": "webpack" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "dompurify": "^3.0.3" 15 | }, 16 | "devDependencies": { 17 | "@babel/core": "^7.21.8", 18 | "@babel/preset-env": "^7.21.5", 19 | "@babel/register": "^7.21.0", 20 | "mocha": "^10.2.0", 21 | "selenium-webdriver": "^4.10.0", 22 | "webpack": "^5.89.0", 23 | "webpack-cli": "^5.1.4" 24 | } 25 | } 26 | --------------------------------------------------------------------------------