├── README.md
├── background.js
├── leetcode-pdf.js
├── logo.png
├── manifest.json
├── popup.html
├── popup.js
├── privacy-policy.html
└── vendors
├── buttons.js
└── jquery-3.3.1.min.js
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Leet Book
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## LeetCode Submission Saver
24 | [Github](https://github.com/TheShubham99/leetcode-to-pdf)
25 |
26 | LeetCode Submission Saver is a tool that allows users to save all their accepted submissions from LeetCode to a PDF file for easy reference and offline access.
27 |
28 | ## How to Use
29 |
30 | Click to open following youtube video
31 | [](https://www.youtube.com/watch?v=lLwm5kSVyFk)
32 |
33 | (https://www.youtube.com/watch?v=lLwm5kSVyFk) // Video Link
34 |
35 | 1. **Install the extension**: Install the Leet Book Extension on chrome. [Link To Extension](https://chromewebstore.google.com/detail/emdfjmejkmhehmpaggklnmhgloijgnam?hl=en-GB&utm_source=ext_sidebar.)
36 |
37 | 2. **Log in to LeetCode**: Visit the LeetCode website (https://leetcode.com) and log in to your account.
38 |
39 | 3. **Download PDF**: Click on download PDF to save the PDF to your device.
40 |
41 | 4. **Observe the Console**: After pasting the code, wait for a few moments and observe the console for any errors or messages.
42 |
43 | 5. **View the Output**: Once the process is complete, the generated PDF will be available for download.
44 |
45 | ## Credits
46 |
47 | Inspired by [Leetcode Downloader for Submissions](https://github.com/world177/Leetcode-Downloader-for-Submissions) by world177.
48 |
49 | ## Contributing
50 |
51 | Contributions are welcome! If you have any ideas for improvements or feature requests, feel free to open an issue or submit a pull request.
52 |
53 | ## License
54 |
55 | This project is licensed under the [MIT License](LICENSE).
56 |
57 | ---
58 |
59 | **Disclaimer**: This project is not affiliated with LeetCode. Use it responsibly and in compliance with LeetCode's terms of service.
60 |
--------------------------------------------------------------------------------
/background.js:
--------------------------------------------------------------------------------
1 | let process_status = "";
2 | let button = `Download PDF ⤋`;
3 | let processing = false;
4 |
5 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
6 | if (message.type === "getUpdates") {
7 | if (!processing) {
8 | process_status = "";
9 | button = `Download PDF ⤋`;
10 | }
11 | // Process the message and prepare the data to send back
12 | const responseData = { button, process_status, processing };
13 |
14 | // Send the response back to the content script or popup script
15 | sendResponse(responseData);
16 | }
17 |
18 | if (message.type === "setUpdates") {
19 | if (message.button) {
20 | button = message.button;
21 | }
22 |
23 | if (message.process_status) {
24 | process_status = message.process_status;
25 | }
26 |
27 | if (message.processing == false) {
28 | processing = false;
29 | } else {
30 | processing = true;
31 | }
32 | }
33 | });
34 |
--------------------------------------------------------------------------------
/leetcode-pdf.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | // Credits :
4 | Inspired from - https://github.com/world177/Leetcode-Downloader-for-Submissions
5 | User - https://github.com/world177
6 | */
7 |
8 | LeetCodeSubmissionDownloader.LEETCODE_SUBMISSIONS_API =
9 | "https://leetcode.com/api/submissions";
10 | LeetCodeSubmissionDownloader.BASE_PROBLEM_ADDRESS =
11 | "https://leetcode.com/problems/";
12 |
13 | LeetCodeSubmissionDownloader.INCREASE_LAST_BY = 20;
14 | LeetCodeSubmissionDownloader.WAIT_BETWEEN_REQUESTS = 2500; // milliseconds
15 | LeetCodeSubmissionDownloader.INCREASE_WAIT_BY_ON_ERROR = 2;
16 |
17 | LeetCodeSubmissionDownloader.questionMap = {};
18 | LeetCodeSubmissionDownloader.last = 0;
19 | LeetCodeSubmissionDownloader.processed = 0;
20 | LeetCodeSubmissionDownloader.waitUsedBetweenRequests =
21 | LeetCodeSubmissionDownloader.WAIT_BETWEEN_REQUESTS;
22 | LeetCodeSubmissionDownloader.MAX_ATTEMPT = 5;
23 |
24 | function sleep(ms) {
25 | return new Promise((resolve) => setTimeout(resolve, ms));
26 | }
27 | // Function to send a message to the popup script
28 | function sendMessageToPopup(message) {
29 | chrome.runtime.sendMessage({ type: "setUpdates", ...message });
30 | }
31 |
32 | function fetchUsername() {
33 | return new Promise((resolve, reject) => {
34 | const query = `
35 | query globalData {
36 | userStatus {
37 | username
38 | }
39 | }
40 | `;
41 |
42 | const body = JSON.stringify({
43 | query: query,
44 | variables: {},
45 | operationName: "globalData",
46 | });
47 |
48 | const xhr = new XMLHttpRequest();
49 | xhr.open("POST", "https://leetcode.com/graphql/");
50 | xhr.setRequestHeader("Content-Type", "application/json");
51 | xhr.onload = function () {
52 | if (xhr.status >= 200 && xhr.status < 300) {
53 | const data = JSON.parse(xhr.responseText);
54 | if (data && data.data && data.data.userStatus) {
55 | const username = data.data.userStatus.username;
56 | resolve(username);
57 | } else {
58 | console.error("Failed to fetch username");
59 | reject("Failed to fetch username");
60 | }
61 | } else {
62 | console.error("Failed to fetch username");
63 | reject("Failed to fetch username");
64 | }
65 | };
66 | xhr.onerror = function () {
67 | console.error("Request failed");
68 | reject("Request failed");
69 | };
70 | xhr.send(body);
71 | });
72 | }
73 |
74 | function fetchQuestionContent(titleSlug) {
75 | return new Promise((resolve, reject) => {
76 | setTimeout(() => {
77 | const query = `
78 | query questionContent($titleSlug: String!) {
79 | question(titleSlug: $titleSlug) {
80 | content
81 | }
82 | }
83 | `;
84 |
85 | const variables = {
86 | titleSlug: titleSlug,
87 | };
88 |
89 | const body = JSON.stringify({
90 | query: query,
91 | variables: variables,
92 | operationName: "questionContent",
93 | });
94 |
95 | const xhr = new XMLHttpRequest();
96 | xhr.open("POST", "https://leetcode.com/graphql/");
97 | xhr.setRequestHeader("Content-Type", "application/json");
98 | xhr.onload = function () {
99 | if (xhr.status >= 200 && xhr.status < 300) {
100 | const data = JSON.parse(xhr.responseText);
101 | resolve(data.data.question.content);
102 | } else {
103 | console.error("Failed to fetch question content");
104 | reject("Failed to fetch question content");
105 | }
106 | };
107 | xhr.onerror = function () {
108 | console.error("Request failed");
109 | reject("Request failed");
110 | };
111 | xhr.send(body);
112 | }, 50); // 50 milliseconds delay to avoid rate limitting
113 | });
114 | }
115 |
116 | function highlightCode(language, code) {
117 | let highlightedCode = "";
118 | const lines = code.split("\n");
119 |
120 | switch (language) {
121 | case "python":
122 | case "python3":
123 | for (const line of lines) {
124 | const isComment = line.trim().startsWith("#");
125 | if (isComment) {
126 | highlightedCode += `\n`;
127 | } else {
128 | let highlightedLine = line.replace(
129 | /\b(if|else|elif|while|for|in|def|class|pass|return)\b/g,
130 | "$1"
131 | );
132 | highlightedCode += `${highlightedLine}\n`;
133 | }
134 | }
135 | break;
136 | case "cpp":
137 | for (const line of lines) {
138 | // Define patterns to highlight C++ keywords and comments
139 | const isComment =
140 | line.trim().startsWith("//") ||
141 | line.trim().startsWith("/*") ||
142 | line.trim().endsWith("*/");
143 |
144 | if (isComment) {
145 | highlightedCode += `\n`;
146 | } else {
147 | let highlightedLine = line.replace(
148 | /\b(if|else|while|for|return|break|continue|class|int|double|string|void|float|long|bool|vector|string|char|map|set)\b/g,
149 | "$1"
150 | );
151 | highlightedCode += `${highlightedLine}\n`;
152 | }
153 | }
154 | break;
155 | default:
156 | // If language is not recognized, simply return the original code
157 | highlightedCode = code;
158 | break;
159 | }
160 |
161 | return highlightedCode;
162 | }
163 |
164 | function generateHTMLContent(username = "") {
165 | let htmlContent = `
166 |
167 |
168 |
169 |
170 |
171 | LeetCode Submissions
172 |
173 |
253 |
254 |
255 |
266 | `;
267 |
268 | // Iterate over each question in the questionMap and add it to the HTML content
269 | for (const [
270 | title,
271 | { heading, language, description, solution },
272 | ] of Object.entries(LeetCodeSubmissionDownloader.questionMap)) {
273 | let highlightedSolution = highlightCode(
274 | language,
275 | solution
276 | .replace(/&/g, "&")
277 | .replace(//g, ">")
279 | .replace(/"/g, """)
280 | .replace(/'/g, "'")
281 | );
282 |
283 | htmlContent += `
284 |
285 |
288 |
289 |
Description
290 | ${description}
291 |
292 |
(scroll down for solution)
293 |
294 |
295 |
Solution
296 |
Language: ${language}
297 |
Status: Accepted
298 |
${highlightedSolution}
299 |
300 |
301 | `;
302 | }
303 |
304 | htmlContent += `
305 |
306 |
307 | `;
308 |
309 | return htmlContent;
310 | }
311 |
312 | // Function to open print dialog for specific HTML content
313 | function printHTML(htmlContent) {
314 | sendMessageToPopup({
315 | button: `Download PDF ⤋`,
316 | processing: false,
317 | });
318 | // Open the HTML content in a new window
319 | const newWindow = window.open();
320 | newWindow.document.write(htmlContent);
321 | // Close the document to complete the loading process
322 | newWindow.document.close();
323 | }
324 |
325 | LeetCodeSubmissionDownloader.pullFromLeetCodeAPI = function () {
326 | if (LeetCodeSubmissionDownloader.MAX_ATTEMPT <= 0) {
327 | sendMessageToPopup({
328 | process_status: "Error: Something Went Wrong",
329 | button: "Please wait...",
330 | processing: false,
331 | });
332 | return;
333 | }
334 | try {
335 | let address = new URL(
336 | LeetCodeSubmissionDownloader.LEETCODE_SUBMISSIONS_API +
337 | "?offset=" +
338 | LeetCodeSubmissionDownloader.last +
339 | "&limit=" +
340 | LeetCodeSubmissionDownloader.INCREASE_LAST_BY
341 | );
342 |
343 | let xmlHttp = new XMLHttpRequest();
344 |
345 | xmlHttp.onreadystatechange = function () {
346 | if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
347 | LeetCodeSubmissionDownloader.processAPIResults(xmlHttp.responseText);
348 | } else if (xmlHttp.readyState == 4) {
349 | LeetCodeSubmissionDownloader.handleError();
350 | }
351 | };
352 |
353 | xmlHttp.addEventListener("error", function () {
354 | LeetCodeSubmissionDownloader.handleError();
355 | });
356 |
357 | xmlHttp.open("GET", address, true);
358 | xmlHttp.send(null);
359 | } catch (e) {
360 | LeetCodeSubmissionDownloader.handleError();
361 | }
362 | };
363 |
364 | LeetCodeSubmissionDownloader.handleError = async function () {
365 | LeetCodeSubmissionDownloader.waitUsedBetweenRequests *=
366 | LeetCodeSubmissionDownloader.INCREASE_WAIT_BY_ON_ERROR;
367 |
368 | sendMessageToPopup({
369 | process_status: "Error: Failed to pull API data",
370 | processing: true,
371 | });
372 | console.log(
373 | "Error: Failed to pull API data from " +
374 | LeetCodeSubmissionDownloader.LEETCODE_SUBMISSIONS_API +
375 | ". Doubling the wait time between requests to " +
376 | LeetCodeSubmissionDownloader.waitUsedBetweenRequests +
377 | " milliseconds."
378 | );
379 |
380 | LeetCodeSubmissionDownloader.MAX_ATTEMPT -= 1;
381 | await sleep(LeetCodeSubmissionDownloader.waitUsedBetweenRequests);
382 |
383 | LeetCodeSubmissionDownloader.pullFromLeetCodeAPI();
384 | };
385 |
386 | // example question in questionMap
387 | // const question = {
388 | // title: title,
389 | // heading:titleHeading,
390 | // language: submissionsFound[i].lang,
391 | // description: questionContent,
392 | // solution: String(submissionsFound[i].code),
393 | // question_id: submissionsFound[i].question_id,
394 | // };
395 |
396 | LeetCodeSubmissionDownloader.processAPIResults = async function (
397 | lastWebResult
398 | ) {
399 | let data = JSON.parse(lastWebResult);
400 |
401 | let submissionsFound = data.submissions_dump;
402 |
403 | LeetCodeSubmissionDownloader.last += submissionsFound.length;
404 | LeetCodeSubmissionDownloader.processed += 0;
405 |
406 | for (let i = 0; i < submissionsFound.length; i++) {
407 | const title = submissionsFound[i].title_slug;
408 | const titleHeading =
409 | submissionsFound[i].question_id + " " + submissionsFound[i].title;
410 |
411 | if (
412 | title in LeetCodeSubmissionDownloader.questionMap ||
413 | submissionsFound[i]?.status_display?.toLowerCase() !== "accepted"
414 | ) {
415 | // if question is already in map
416 | // or
417 | // status is not accepted then continue
418 | continue;
419 | }
420 |
421 | LeetCodeSubmissionDownloader.processed += 1;
422 |
423 | await fetchQuestionContent(title)
424 | .then((questionContent) => {
425 | // Log question content
426 | const question = {
427 | title: title,
428 | heading: titleHeading,
429 | language: submissionsFound[i].lang,
430 | description: questionContent,
431 | solution: String(submissionsFound[i].code),
432 | question_id: submissionsFound[i].question_id,
433 | };
434 |
435 | LeetCodeSubmissionDownloader.questionMap[title] = question;
436 | })
437 | .catch((error) => {
438 | console.error("Failed to fetch question content:", error);
439 | });
440 | }
441 |
442 | if (!data.has_next) {
443 | sendMessageToPopup({
444 | process_status:
445 | ` Total submissions processed: ` +
446 | LeetCodeSubmissionDownloader.processed +
447 | "",
448 | processing: false,
449 | });
450 | console.log(
451 | "Total Questions processed: " + LeetCodeSubmissionDownloader.processed
452 | );
453 | fetchUsername()
454 | .then((username) => {
455 | const htmlContent = generateHTMLContent(username);
456 | printHTML(htmlContent);
457 | })
458 | .catch((error) => {
459 | console.error("Failed to get username Error:", error);
460 | const htmlContent = generateHTMLContent("");
461 | printHTML(htmlContent);
462 | });
463 | } else {
464 | // Send messages to the popup script
465 | sendMessageToPopup({ button: "Loading...", processing: true });
466 | sendMessageToPopup({
467 | process_status:
468 | `
469 |
472 | Please wait..
473 | Questions saved so far: ` +
474 | LeetCodeSubmissionDownloader.processed +
475 | `
`,
476 | processing: true,
477 | });
478 | console.log(
479 | "Questions saved so far: " + LeetCodeSubmissionDownloader.processed
480 | );
481 | console.log("Batch Complete! Hold on, next batch in progress.");
482 |
483 | await sleep(LeetCodeSubmissionDownloader.waitUsedBetweenRequests);
484 |
485 | LeetCodeSubmissionDownloader.pullFromLeetCodeAPI();
486 | }
487 | };
488 |
489 | function LeetCodeSubmissionDownloader() {
490 | return this;
491 | }
492 |
493 | LeetCodeSubmissionDownloader.pullFromLeetCodeAPI();
494 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheShubham99/leetcode-to-pdf/edad806919f0f9b26a1e28d09bcd538a7ad0b168/logo.png
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "LeetCode PDF Saver",
4 | "author": "Prathamesh Sahasrabhojane",
5 | "version": "1.1",
6 | "description": "Save all accepted submissions from LeetCode to a PDF.",
7 | "permissions": ["scripting"],
8 | "host_permissions": [
9 | "https://leetcode.com/*"
10 | ],
11 | "action": {
12 | "default_popup": "popup.html",
13 | "default_icon": {
14 | "16": "logo.png",
15 | "48": "logo.png",
16 | "128": "logo.png"
17 | }
18 | },
19 | "background": {
20 | "service_worker": "background.js"
21 | },
22 | "content_security_policy": {
23 | "extension_pages": "script-src 'self'; object-src 'self';"
24 | }
25 | }
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | LeetBook
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
LeetBook
23 |
24 |
1. Make sure you are logged in on LeetCode.com
25 |
2. Click on the following button to view and download your accepted
26 | submissions.
27 |
28 |
Do not close the tab.
29 |
Downloading may take upto 10 minutes.
30 |
31 |
33 |
Star
36 | on GitHub
37 |
38 |
39 |
42 |
43 |
44 |
51 |
Report a Bug
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/popup.js:
--------------------------------------------------------------------------------
1 | let button;
2 | let processStatus;
3 |
4 | async function getUpdates(button, processStatus) {
5 | await chrome.runtime.sendMessage({ type: "getUpdates" }, (response) => {
6 | if (!response.processing) {
7 | button.innerHTML = `Download PDF ⤋`;
8 | processStatus.innerHTML = "";
9 | button.disabled = false;
10 | return;
11 | }
12 | if (response?.process_status) {
13 | processStatus.innerHTML = response.process_status;
14 | }
15 | if (response?.button) {
16 | button.innerHTML = response.button;
17 | if (String(response?.button)?.toLowerCase()?.includes("loading")) {
18 | button.disabled = true;
19 | } else {
20 | button.disabled = false;
21 | }
22 | }
23 | });
24 | }
25 |
26 | document.addEventListener("DOMContentLoaded", async () => {
27 | button = document.getElementById("download-submissions");
28 | processStatus = document.getElementById("process-status");
29 |
30 | await getUpdates(button, processStatus);
31 |
32 | if (button) {
33 | button.addEventListener("click", async () => {
34 | const int = setInterval(async () => {
35 | await getUpdates(button, processStatus);
36 | }, 4000);
37 | processStatus.innerText = "Please wait...";
38 | button.innerHTML = "Loading...";
39 | button.disabled = true;
40 | const [tab] = await chrome.tabs.query({
41 | active: true,
42 | currentWindow: true,
43 | });
44 |
45 | button.disabled = true;
46 | originalContent = button.innerHTML;
47 | button.innerHTML = "Loading...";
48 |
49 | if (tab.url.includes("leetcode.com")) {
50 | chrome.scripting.executeScript({
51 | target: { tabId: tab.id },
52 | files: ["leetcode-pdf.js"],
53 | });
54 | } else {
55 | button.disabled = false;
56 | button.innerHTML = originalContent;
57 | processStatus.innerHTML = "This extension works only on leetcode.com";
58 | alert("This extension works only on leetcode.com");
59 | }
60 | return int;
61 | });
62 | }
63 | });
64 |
--------------------------------------------------------------------------------
/privacy-policy.html:
--------------------------------------------------------------------------------
1 | Privacy Policy for LeetCode Submission Saver
2 | Introduction
3 | Your privacy is important to us. This Privacy Policy explains how "LeetCode Submission Saver" (the "Extension")
4 | collects, uses, and protects your information when you use our service.
5 |
6 | Information We Collect
7 | The Extension collects the following information:
8 |
9 | User Submissions: When you use the Extension to download your accepted LeetCode submissions, we temporarily access your
10 | LeetCode account data to fetch the submissions. This data includes your username and accepted solutions.
11 | How We Use the Information
12 | The information we collect is used solely for the purpose of generating a PDF file containing your accepted LeetCode
13 | submissions. Specifically:
14 |
15 | Generate PDF: The data is used to compile your accepted submissions into a PDF file for download.
16 | User Authentication: The Extension verifies that you are logged in to LeetCode to access your submissions.
17 | Data Storage and Security
18 | We prioritize your data security:
19 |
20 | No Data Storage: We do not store any of your data. All processing is done locally on your device, and no data is sent to
21 | external servers.
22 | Secure Access: The Extension accesses LeetCode data through secure API requests directly from your browser.
23 | Permissions
24 | The Extension requires certain permissions to function:
25 |
26 | Active Tab Permission: To interact with the active LeetCode tab where the user is logged in and downloading submissions.
27 | Host Permission: To fetch data from https://leetcode.com/*.
28 | Scripting Permission: To execute scripts necessary for fetching and processing your submissions.
29 | Third-Party Services
30 | The Extension does not use third-party services to process or store your data.
31 |
32 | Changes to This Privacy Policy
33 | We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy
34 | on this page. You are advised to review this Privacy Policy periodically for any changes.
35 |
36 | Contact Us
37 | If you have any questions or concerns about this Privacy Policy, please contact us at:
38 |
39 | Email: ssahasrabhojane@gmail.com
40 |
41 | Consent
42 | By using the Extension, you consent to this Privacy Policy.
--------------------------------------------------------------------------------
/vendors/buttons.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * github-buttons v2.28.0
3 | * (c) 2024 なつき
4 | * @license BSD-2-Clause
5 | */
6 | !(function () {
7 | "use strict";
8 | var e = window.document,
9 | o = e.location,
10 | t = window.Math,
11 | r = window.HTMLElement,
12 | a = window.XMLHttpRequest,
13 | n = "github-button",
14 | i = "https://buttons.github.io/buttons.html",
15 | c = "github.com",
16 | l = "https://api." + c,
17 | d = a && "prototype" in a && "withCredentials" in a.prototype,
18 | s =
19 | d &&
20 | r &&
21 | "attachShadow" in r.prototype &&
22 | !("prototype" in r.prototype.attachShadow),
23 | u = function (e, o) {
24 | for (var t = 0, r = e.length; t < r; t++) o(e[t]);
25 | },
26 | h = function (e) {
27 | return function (o, t, r) {
28 | var a = e.createElement(o);
29 | if (null != t)
30 | for (var n in t) {
31 | var i = t[n];
32 | null != i && (null != a[n] ? (a[n] = i) : a.setAttribute(n, i));
33 | }
34 | return (
35 | null != r &&
36 | u(r, function (o) {
37 | a.appendChild("string" == typeof o ? e.createTextNode(o) : o);
38 | }),
39 | a
40 | );
41 | };
42 | },
43 | f = h(e),
44 | g = function (e) {
45 | var o;
46 | return function () {
47 | o || ((o = 1), e.apply(this, arguments));
48 | };
49 | },
50 | p = function (e, o) {
51 | return {}.hasOwnProperty.call(e, o);
52 | },
53 | b = function (e) {
54 | return ("" + e).toLowerCase();
55 | },
56 | v = function (e, o, t, r) {
57 | null == o && (o = "&"),
58 | null == t && (t = "="),
59 | null == r && (r = window.decodeURIComponent);
60 | var a = {};
61 | return (
62 | u(e.split(o), function (e) {
63 | if ("" !== e) {
64 | var o = e.split(t);
65 | a[r(o[0])] = null != o[1] ? r(o.slice(1).join(t)) : void 0;
66 | }
67 | }),
68 | a
69 | );
70 | },
71 | m = function (e, o, t) {
72 | e.addEventListener
73 | ? e.addEventListener(o, t, !1)
74 | : e.attachEvent("on" + o, t);
75 | },
76 | w = function (e, o, t) {
77 | e.removeEventListener
78 | ? e.removeEventListener(o, t, !1)
79 | : e.detachEvent("on" + o, t);
80 | },
81 | k = function (e, o, t) {
82 | var r = function () {
83 | return w(e, o, r), t.apply(this, arguments);
84 | };
85 | m(e, o, r);
86 | },
87 | x = function (e, o, t) {
88 | if (null != e.readyState) {
89 | var r = "readystatechange",
90 | a = function () {
91 | if (o.test(e.readyState))
92 | return w(e, r, a), t.apply(this, arguments);
93 | };
94 | m(e, r, a);
95 | }
96 | },
97 | y = {
98 | light:
99 | ".btn:focus-visible,.social-count:focus-visible{outline:2px solid #0969da;outline-offset:-2px}.btn{color:#24292f;background-color:#ebf0f4;border-color:#d0d7de;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f6f8fa'/%3e%3cstop offset='90%25' stop-color='%23ebf0f4'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e\");background-image:-moz-linear-gradient(top, #f6f8fa, #ebf0f4 90%);background-image:linear-gradient(180deg, #f6f8fa, #ebf0f4 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF6F8FA', endColorstr='#FFEAEFF3')}:root .btn{filter:none}.btn:hover,.btn:focus{background-color:#e4e9ed;background-position:0 -0.5em;border-color:#d0d7de;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23eef1f4'/%3e%3cstop offset='90%25' stop-color='%23e4e9ed'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e\");background-image:-moz-linear-gradient(top, #eef1f4, #e4e9ed 90%);background-image:linear-gradient(180deg, #eef1f4, #e4e9ed 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFEEF1F4', endColorstr='#FFE3E8EC')}:root .btn:hover,:root .btn:focus{filter:none}.btn:active{background-color:#e7ebef;border-color:#d0d7de;background-image:none;filter:none}.social-count{color:#24292f;background-color:#fff;border-color:#d0d7de}.social-count:hover,.social-count:focus{color:#0969da}.octicon-heart{color:#bf3989}",
100 | light_high_contrast:
101 | ".btn:focus-visible,.social-count:focus-visible{outline:2px solid #0349b4;outline-offset:-2px}.btn{color:#0e1116;background-color:#e7ecf0;border-color:#20252c;background-image:none;filter:none}.btn:hover,.btn:focus{background-color:#d1d9df;background-position:0 -0.5em;border-color:#20252c;background-image:none;filter:none}.btn:active{background-color:#cfd6dd;border-color:#20252c}.social-count{color:#0e1116;background-color:#fff;border-color:#20252c}.social-count:hover,.social-count:focus{color:#023b95}.octicon-heart{color:#971368}",
102 | dark: ".btn:focus-visible,.social-count:focus-visible{outline:2px solid #1f6feb;outline-offset:-2px}.btn{color:#c9d1d9;background-color:#1a1e23;border-color:#30363d;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%2321262d'/%3e%3cstop offset='90%25' stop-color='%231a1e23'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e\");background-image:-moz-linear-gradient(top, #21262d, #1a1e23 90%);background-image:linear-gradient(180deg, #21262d, #1a1e23 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF21262D', endColorstr='#FF191D22')}:root .btn{filter:none}.btn:hover,.btn:focus{background-color:#22262c;background-position:0 -0.5em;border-color:#30363d;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23292e36'/%3e%3cstop offset='90%25' stop-color='%2322262c'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e\");background-image:-moz-linear-gradient(top, #292e36, #22262c 90%);background-image:linear-gradient(180deg, #292e36, #22262c 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF292E36', endColorstr='#FF21252B')}:root .btn:hover,:root .btn:focus{filter:none}.btn:active{background-color:#31363e;border-color:#30363d;background-image:none;filter:none}.social-count{color:#c9d1d9;background-color:#0d1117;border-color:#30363d}.social-count:hover,.social-count:focus{color:#388bfd}.octicon-heart{color:#db61a2}",
103 | dark_dimmed:
104 | ".btn:focus-visible,.social-count:focus-visible{outline:2px solid #316dca;outline-offset:-2px}.btn{color:#adbac7;background-color:#30363d;border-color:#444c56;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23373e47'/%3e%3cstop offset='90%25' stop-color='%2330363d'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e\");background-image:-moz-linear-gradient(top, #373e47, #30363d 90%);background-image:linear-gradient(180deg, #373e47, #30363d 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF373E47', endColorstr='#FF2F353C')}:root .btn{filter:none}.btn:hover,.btn:focus{background-color:#363c44;background-position:0 -0.5em;border-color:#444c56;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%233d444e'/%3e%3cstop offset='90%25' stop-color='%23363c44'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e\");background-image:-moz-linear-gradient(top, #3d444e, #363c44 90%);background-image:linear-gradient(180deg, #3d444e, #363c44 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF3D444E', endColorstr='#FF353B43')}:root .btn:hover,:root .btn:focus{filter:none}.btn:active{background-color:#434a54;border-color:#444c56;background-image:none;filter:none}.social-count{color:#adbac7;background-color:#22272e;border-color:#444c56}.social-count:hover,.social-count:focus{color:#4184e4}.octicon-heart{color:#c96198}",
105 | dark_high_contrast:
106 | ".btn:focus-visible,.social-count:focus-visible{outline:2px solid #409eff;outline-offset:-2px}.btn{color:#f0f3f6;background-color:#3d424d;border-color:#7a828e;background-image:none;filter:none}.btn:hover,.btn:focus{background-color:#414751;background-position:0 -0.5em;border-color:#7a828e;background-image:none;filter:none}.btn:active{background-color:#555c67;border-color:#7a828e}.social-count{color:#f0f3f6;background-color:#0a0c10;border-color:#7a828e}.social-count:hover,.social-count:focus{color:#5cacff}.octicon-heart{color:#f87cbd}",
107 | },
108 | C = function (e, o) {
109 | return (
110 | "@media(prefers-color-scheme:" + e + "){" + y[p(y, o) ? o : e] + "}"
111 | );
112 | },
113 | M = {
114 | "comment-discussion": {
115 | heights: {
116 | 16: {
117 | width: 16,
118 | path: '',
119 | },
120 | },
121 | },
122 | download: {
123 | heights: {
124 | 16: {
125 | width: 16,
126 | path: '',
127 | },
128 | },
129 | },
130 | eye: {
131 | heights: {
132 | 16: {
133 | width: 16,
134 | path: '',
135 | },
136 | },
137 | },
138 | heart: {
139 | heights: {
140 | 16: {
141 | width: 16,
142 | path: '',
143 | },
144 | },
145 | },
146 | "issue-opened": {
147 | heights: {
148 | 16: {
149 | width: 16,
150 | path: '',
151 | },
152 | },
153 | },
154 | "mark-github": {
155 | heights: {
156 | 16: {
157 | width: 16,
158 | path: '',
159 | },
160 | },
161 | },
162 | package: {
163 | heights: {
164 | 16: {
165 | width: 16,
166 | path: '',
167 | },
168 | },
169 | },
170 | play: {
171 | heights: {
172 | 16: {
173 | width: 16,
174 | path: '',
175 | },
176 | },
177 | },
178 | "repo-forked": {
179 | heights: {
180 | 16: {
181 | width: 16,
182 | path: '',
183 | },
184 | },
185 | },
186 | "repo-template": {
187 | heights: {
188 | 16: {
189 | width: 16,
190 | path: '',
191 | },
192 | },
193 | },
194 | star: {
195 | heights: {
196 | 16: {
197 | width: 16,
198 | path: '',
199 | },
200 | },
201 | },
202 | },
203 | Z = function (e, o) {
204 | (e = b(e).replace(/^octicon-/, "")), p(M, e) || (e = "mark-github");
205 | var t = o >= 24 && 24 in M[e].heights ? 24 : 16,
206 | r = M[e].heights[t];
207 | return (
208 | '"
221 | );
222 | },
223 | A = {},
224 | F = function (e, o) {
225 | var t = A[e] || (A[e] = []);
226 | if (!(t.push(o) > 1)) {
227 | var r = g(function () {
228 | for (delete A[e]; (o = t.shift()); ) o.apply(null, arguments);
229 | });
230 | if (d) {
231 | var n = new a();
232 | m(n, "abort", r),
233 | m(n, "error", r),
234 | m(n, "load", function () {
235 | var e;
236 | try {
237 | e = JSON.parse(this.responseText);
238 | } catch (e) {
239 | return void r(e);
240 | }
241 | r(200 !== this.status, e);
242 | }),
243 | n.open("GET", e),
244 | n.send();
245 | } else {
246 | var i = this || window;
247 | i._ = function (e) {
248 | (i._ = null), r(200 !== e.meta.status, e.data);
249 | };
250 | var c = h(i.document)("script", {
251 | async: !0,
252 | src: e + (-1 !== e.indexOf("?") ? "&" : "?") + "callback=_",
253 | }),
254 | l = function () {
255 | i._ && i._({ meta: {} });
256 | };
257 | m(c, "load", l),
258 | m(c, "error", l),
259 | x(c, /de|m/, l),
260 | i.document.getElementsByTagName("head")[0].appendChild(c);
261 | }
262 | }
263 | },
264 | E = function (e, o, t) {
265 | var r = h(e.ownerDocument),
266 | a = e.appendChild(r("style", { type: "text/css" })),
267 | n =
268 | "body{margin:0}a{text-decoration:none;outline:0}.widget{display:inline-block;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif;font-size:0;line-height:0;white-space:nowrap}.btn,.social-count{position:relative;display:inline-block;display:inline-flex;height:14px;padding:2px 5px;font-size:11px;font-weight:600;line-height:14px;vertical-align:bottom;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-repeat:repeat-x;background-position:-1px -1px;background-size:110% 110%;border:1px solid}.btn{border-radius:.25em}.btn:not(:last-child){border-radius:.25em 0 0 .25em}.social-count{border-left:0;border-radius:0 .25em .25em 0}.widget-lg .btn,.widget-lg .social-count{height:16px;padding:5px 10px;font-size:12px;line-height:16px}.octicon{display:inline-block;vertical-align:text-top;fill:currentColor;overflow:visible}" +
269 | (function (e) {
270 | if (null == e) return y.light;
271 | if (p(y, e)) return y[e];
272 | var o = v(e, ";", ":", function (e) {
273 | return e.replace(/^[ \t\n\f\r]+|[ \t\n\f\r]+$/g, "");
274 | });
275 | return (
276 | y[p(y, o["no-preference"]) ? o["no-preference"] : "light"] +
277 | C("light", o.light) +
278 | C("dark", o.dark)
279 | );
280 | })(o["data-color-scheme"]);
281 | a.styleSheet
282 | ? (a.styleSheet.cssText = n)
283 | : a.appendChild(e.ownerDocument.createTextNode(n));
284 | var i = "large" === b(o["data-size"]),
285 | d = r(
286 | "a",
287 | {
288 | className: "btn",
289 | href: o.href,
290 | rel: "noopener",
291 | target: "_blank",
292 | title: o.title || void 0,
293 | "aria-label": o["aria-label"] || void 0,
294 | innerHTML: Z(o["data-icon"], i ? 16 : 14) + " ",
295 | },
296 | [r("span", {}, [o["data-text"] || ""])]
297 | ),
298 | s = e.appendChild(
299 | r("div", { className: "widget" + (i ? " widget-lg" : "") }, [d])
300 | ),
301 | u = d.hostname.replace(/\.$/, "");
302 | if (("." + u).substring(u.length - 10) !== "." + c)
303 | return d.removeAttribute("href"), void t(s);
304 | var f = (" /" + d.pathname).split(/\/+/);
305 | if (
306 | ((((u === c || u === "gist." + c) && "archive" === f[3]) ||
307 | (u === c &&
308 | "releases" === f[3] &&
309 | ("download" === f[4] ||
310 | ("latest" === f[4] && "download" === f[5]))) ||
311 | u === "codeload." + c) &&
312 | (d.target = "_top"),
313 | "true" === b(o["data-show-count"]) &&
314 | u === c &&
315 | "marketplace" !== f[1] &&
316 | "sponsors" !== f[1] &&
317 | "orgs" !== f[1] &&
318 | "users" !== f[1] &&
319 | "-" !== f[1])
320 | ) {
321 | var g, m;
322 | if (!f[2] && f[1]) (m = "followers"), (g = "?tab=followers");
323 | else if (!f[3] && f[2]) (m = "stargazers_count"), (g = "/stargazers");
324 | else if (f[4] || "subscription" !== f[3])
325 | if (f[4] || "fork" !== f[3]) {
326 | if ("issues" !== f[3]) return void t(s);
327 | (m = "open_issues_count"), (g = "/issues");
328 | } else (m = "forks_count"), (g = "/forks");
329 | else (m = "subscribers_count"), (g = "/watchers");
330 | var w = f[2] ? "/repos/" + f[1] + "/" + f[2] : "/users/" + f[1];
331 | F.call(this, l + w, function (e, o) {
332 | if (!e) {
333 | var a = o[m];
334 | s.appendChild(
335 | r(
336 | "a",
337 | {
338 | className: "social-count",
339 | href: o.html_url + g,
340 | rel: "noopener",
341 | target: "_blank",
342 | "aria-label":
343 | a +
344 | " " +
345 | m
346 | .replace(/_count$/, "")
347 | .replace("_", " ")
348 | .slice(0, a < 2 ? -1 : void 0) +
349 | " on GitHub",
350 | },
351 | [("" + a).replace(/\B(?=(\d{3})+(?!\d))/g, ",")]
352 | )
353 | );
354 | }
355 | t(s);
356 | });
357 | } else t(s);
358 | },
359 | L = window.devicePixelRatio || 1,
360 | _ = function (e) {
361 | return (L > 1 ? t.ceil((t.round(e * L) / L) * 2) / 2 : t.ceil(e)) || 0;
362 | },
363 | G = function (e, o) {
364 | (e.style.width = o[0] + "px"), (e.style.height = o[1] + "px");
365 | },
366 | T = function (o, r) {
367 | if (null != o && null != r)
368 | if (
369 | (o.getAttribute &&
370 | (o = (function (e) {
371 | var o = {
372 | href: e.href,
373 | title: e.title,
374 | "aria-label": e.getAttribute("aria-label"),
375 | };
376 | return (
377 | u(
378 | ["icon", "color-scheme", "text", "size", "show-count"],
379 | function (t) {
380 | var r = "data-" + t;
381 | o[r] = e.getAttribute(r);
382 | }
383 | ),
384 | null == o["data-text"] &&
385 | (o["data-text"] = e.textContent || e.innerText),
386 | o
387 | );
388 | })(o)),
389 | s)
390 | ) {
391 | var a = f("span");
392 | E(a.attachShadow({ mode: "closed" }), o, function () {
393 | r(a);
394 | });
395 | } else {
396 | var n = f("iframe", {
397 | src: "javascript:0",
398 | title: o.title || void 0,
399 | allowtransparency: !0,
400 | scrolling: "no",
401 | frameBorder: 0,
402 | });
403 | G(n, [0, 0]), (n.style.border = "none");
404 | var c = function () {
405 | var a,
406 | l = n.contentWindow;
407 | try {
408 | a = l.document.body;
409 | } catch (o) {
410 | return void e.body.appendChild(n.parentNode.removeChild(n));
411 | }
412 | w(n, "load", c),
413 | E.call(l, a, o, function (e) {
414 | var a = (function (e) {
415 | var o = e.offsetWidth,
416 | r = e.offsetHeight;
417 | if (e.getBoundingClientRect) {
418 | var a = e.getBoundingClientRect();
419 | (o = t.max(o, _(a.width))), (r = t.max(r, _(a.height)));
420 | }
421 | return [o, r];
422 | })(e);
423 | n.parentNode.removeChild(n),
424 | k(n, "load", function () {
425 | G(n, a);
426 | }),
427 | (n.src =
428 | i +
429 | "#" +
430 | (n.name = (function (e, o, t, r) {
431 | null == o && (o = "&"),
432 | null == t && (t = "="),
433 | null == r && (r = window.encodeURIComponent);
434 | var a = [];
435 | for (var n in e) {
436 | var i = e[n];
437 | null != i && a.push(r(n) + t + r(i));
438 | }
439 | return a.join(o);
440 | })(o))),
441 | r(n);
442 | });
443 | };
444 | m(n, "load", c), e.body.appendChild(n);
445 | }
446 | };
447 | o.protocol + "//" + o.host + o.pathname === i
448 | ? E(e.body, v(window.name || o.hash.replace(/^#/, "")), function () {})
449 | : (function (o) {
450 | if (
451 | "complete" === e.readyState ||
452 | ("loading" !== e.readyState && !e.documentElement.doScroll)
453 | )
454 | setTimeout(o);
455 | else if (e.addEventListener) {
456 | var t = g(o);
457 | k(e, "DOMContentLoaded", t), k(window, "load", t);
458 | } else x(e, /m/, o);
459 | })(function () {
460 | var o,
461 | t = e.querySelectorAll
462 | ? e.querySelectorAll("a." + n)
463 | : ((o = []),
464 | u(e.getElementsByTagName("a"), function (e) {
465 | -1 !==
466 | (" " + e.className + " ")
467 | .replace(/[ \t\n\f\r]+/g, " ")
468 | .indexOf(" " + n + " ") && o.push(e);
469 | }),
470 | o);
471 | u(t, function (e) {
472 | T(e, function (o) {
473 | e.parentNode.replaceChild(o, e);
474 | });
475 | });
476 | });
477 | })();
478 |
--------------------------------------------------------------------------------