├── .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 | 
42 |
43 | ### Step 3: Load the Extension
44 |
45 | 1. Click on 'Load unpacked.'
46 |
47 | 
48 |
49 | 2. Select the unzipped folder from Step 1.
50 |
51 | 
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 | 
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 |
--------------------------------------------------------------------------------