Skip to content

Commit 3a43235

Browse files
committedJun 10, 2024
first commit
0 parents  commit 3a43235

36 files changed

+5168
-0
lines changed
 

‎.eslintrc.cjs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
root: true,
3+
env: { browser: true, es2020: true },
4+
extends: [
5+
"eslint:recommended",
6+
"plugin:@typescript-eslint/recommended",
7+
"plugin:react-hooks/recommended",
8+
"plugin:react/recommended",
9+
],
10+
ignorePatterns: ["dist", ".eslintrc.cjs"],
11+
parser: "@typescript-eslint/parser",
12+
plugins: ["react-refresh"],
13+
rules: {
14+
"react-refresh/only-export-components": [
15+
"warn",
16+
{ allowConstantExport: true },
17+
],
18+
"@typescript-eslint/no-explicit-any": "off",
19+
},
20+
};

‎.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
# dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

‎README.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Quizlet Getter
2+
3+
- Simple chrome extension for getting flashcards from Quizlet
4+
5+
## Demo
6+
7+
![](images/demo.png)
8+
9+
## Installation
10+
11+
- Download repository
12+
- Unzip the packed file
13+
- Go to chrome extension manager
14+
- Turn on "Developer mode"
15+
- Click "Load unpacked" button
16+
- Choose "dist" folder in the extracted folder
17+
18+
## How to use
19+
20+
- Go to your Quizlet
21+
- Load all the question in your page
22+
- Scroll to the top of website and click "Get all Flashcards"
23+
24+
![](images/button.png)
25+
26+
## Caution
27+
28+
### Only work if your target page have the question format like below:
29+
30+
- Must be a multichoices question
31+
- Question in the left box
32+
- Answers belong with question in the left box
33+
- Answers follow the format: a. Answer 1, b. Answer 2, ...
34+
- Keys in the right box
35+
36+
### Examples
37+
38+
![](images/format.png)
39+
![](images/format2.png)

‎dist/image1.png

655 Bytes
Loading

‎dist/image2.png

1.67 KB
Loading

‎dist/image3.png

1.02 KB
Loading

‎dist/index.css

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React + TS</title>
8+
<script type="module" crossorigin src="/index.js"></script>
9+
<link rel="stylesheet" crossorigin href="/index.css">
10+
</head>
11+
<body>
12+
<div id="root"></div>
13+
</body>
14+
</html>

‎dist/index.js

+48
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/manifest.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "Quizlet Getter",
4+
"description": "Base Level Extension",
5+
"version": "1.0",
6+
"content_scripts": [
7+
{
8+
"js": ["index.js"],
9+
"css": ["index.css"],
10+
"matches": ["https://quizlet.com/*"]
11+
}
12+
],
13+
"action": {
14+
"default_icon": "image3.png"
15+
},
16+
"icons": {
17+
"16": "image3.png",
18+
"32": "image3.png",
19+
"48": "image3.png",
20+
"128": "image3.png"
21+
}
22+
}

‎dist/vite.svg

+1
Loading

‎images/button.png

7.82 KB
Loading

‎images/demo.png

59.1 KB
Loading

‎images/format.png

6.08 KB
Loading

‎images/format2.png

5.82 KB
Loading

‎index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React + TS</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

‎package-lock.json

+4,551
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "quizlet-getter",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc && vite build",
9+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"cheerio": "^1.0.0-rc.12",
14+
"react": "^18.2.0",
15+
"react-dom": "^18.2.0"
16+
},
17+
"devDependencies": {
18+
"@types/node": "^20.12.7",
19+
"@types/react": "^18.2.66",
20+
"@types/react-dom": "^18.2.22",
21+
"@typescript-eslint/eslint-plugin": "^7.2.0",
22+
"@typescript-eslint/parser": "^7.2.0",
23+
"@vitejs/plugin-react": "^4.2.1",
24+
"autoprefixer": "^10.4.19",
25+
"eslint": "^8.57.0",
26+
"eslint-plugin-react-hooks": "^4.6.0",
27+
"eslint-plugin-react-refresh": "^0.4.6",
28+
"postcss": "^8.4.38",
29+
"postcss-loader": "^8.1.1",
30+
"tailwindcss": "^3.4.3",
31+
"typescript": "^5.2.2",
32+
"vite": "^5.2.0"
33+
}
34+
}

‎postcss.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

‎public/image1.png

655 Bytes
Loading

‎public/image2.png

1.67 KB
Loading

‎public/image3.png

1.02 KB
Loading

‎public/manifest.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "Quizlet Getter",
4+
"description": "Base Level Extension",
5+
"version": "1.0",
6+
"content_scripts": [
7+
{
8+
"js": ["index.js"],
9+
"css": ["index.css"],
10+
"matches": ["https://quizlet.com/*"]
11+
}
12+
],
13+
"action": {
14+
"default_icon": "image3.png"
15+
},
16+
"icons": {
17+
"16": "image3.png",
18+
"32": "image3.png",
19+
"48": "image3.png",
20+
"128": "image3.png"
21+
}
22+
}

‎public/vite.svg

+1
Loading

‎src/App.tsx

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as cheerio from "cheerio";
2+
export default function App() {
3+
const link = document.createElement("a");
4+
5+
const handleClick = () => {
6+
const data: string =
7+
document.querySelector(".SetPageTerms-termsList")?.innerHTML || "";
8+
const $ = cheerio.load(data);
9+
10+
// File content
11+
const outputLines: any[] = [];
12+
13+
$(".SetPageTerms-term .sebjgj3").each((_i, e) => {
14+
console.log(_i);
15+
let questionText = $(e).find("div:nth-child(1)").html()?.trim();
16+
17+
// Split the text by <br> tags
18+
const questionLines = questionText
19+
?.split("<br>")
20+
.map((line) => line.trim());
21+
22+
const answers: any[] = [];
23+
questionLines?.forEach((line, _index) => {
24+
if (line.replace(/(<([^>]+)>)/gi, "").charAt(1) == ".") {
25+
answers.push(line.replace(/(<([^>]+)>)/gi, ""));
26+
questionText?.replace(line, "");
27+
}
28+
});
29+
questionText = questionText?.replace(/(<([^>]+)>)/gi, "");
30+
31+
// console.log("question");
32+
// console.log(questionText);
33+
// console.log("answers:");
34+
// console.log(answers);
35+
let key: any = $(e).find("div:nth-child(2)").text().trim();
36+
key = key?.split("").map((e: any) => {
37+
return answers.find(
38+
(ans: any) => ans.charAt(0).toLowerCase() === e.toLowerCase()
39+
);
40+
});
41+
// console.log("key");
42+
// console.log(key);
43+
44+
const outputLine = `${questionText} | ${key.join(", ")}`;
45+
outputLines.push(outputLine);
46+
// const value = $(e).attr("value");
47+
});
48+
const file = new Blob([outputLines.join("\n")], { type: "text/plain" });
49+
50+
link.href = URL.createObjectURL(file);
51+
52+
link.download = "questions_and_keys.txt";
53+
link.click();
54+
URL.revokeObjectURL(link.href);
55+
};
56+
return (
57+
<div
58+
className="w-[300px] py-4 px-8 rounded-lg border-2 border-black bg-indigo-400 text-xl hover:bg-white hover:text-black"
59+
onClick={handleClick}
60+
>
61+
Get all Flashcards
62+
</div>
63+
);
64+
}

‎src/constants/classData.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
const weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
2+
const slots = ["1", "2", "3", "4", "5", "6", "7", "8"];
3+
4+
const classData = new Map<string, Map<string, string[]>>([
5+
[
6+
"Mon",
7+
new Map<string, string[]>([
8+
["1", []],
9+
["2", []],
10+
["3", []],
11+
["4", []],
12+
["5", []],
13+
["6", []],
14+
["7", []],
15+
["8", []],
16+
]),
17+
],
18+
[
19+
"Tue",
20+
new Map<string, string[]>([
21+
["1", []],
22+
["2", []],
23+
["3", []],
24+
["4", []],
25+
["5", []],
26+
["6", []],
27+
["7", []],
28+
["8", []],
29+
]),
30+
],
31+
[
32+
"Wed",
33+
new Map<string, string[]>([
34+
["1", []],
35+
["2", []],
36+
["3", []],
37+
["4", []],
38+
["5", []],
39+
["6", []],
40+
["7", []],
41+
["8", []],
42+
]),
43+
],
44+
[
45+
"Thu",
46+
new Map<string, string[]>([
47+
["1", []],
48+
["2", []],
49+
["3", []],
50+
["4", []],
51+
["5", []],
52+
["6", []],
53+
["7", []],
54+
["8", []],
55+
]),
56+
],
57+
[
58+
"Fri",
59+
new Map<string, string[]>([
60+
["1", []],
61+
["2", []],
62+
["3", []],
63+
["4", []],
64+
["5", []],
65+
["6", []],
66+
["7", []],
67+
["8", []],
68+
]),
69+
],
70+
[
71+
"Sat",
72+
new Map<string, string[]>([
73+
["1", []],
74+
["2", []],
75+
["3", []],
76+
["4", []],
77+
["5", []],
78+
["6", []],
79+
["7", []],
80+
["8", []],
81+
]),
82+
],
83+
[
84+
"Sun",
85+
new Map<string, string[]>([
86+
["1", []],
87+
["2", []],
88+
["3", []],
89+
["4", []],
90+
["5", []],
91+
["6", []],
92+
["7", []],
93+
["8", []],
94+
]),
95+
],
96+
]);
97+
98+
export { classData, weekdays, slots };

‎src/constants/colorGenerator.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

‎src/constants/formData.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as cheerio from "cheerio";
2+
3+
const url = "https://fap.fpt.edu.vn/FrontOffice/MoveSubject.aspx";
4+
5+
export const formGetter = (id: string) => {
6+
// const __EVENTTARGET = document
7+
// .getElementById("__EVENTTARGET")
8+
// ?.getAttribute("value");
9+
const __EVENTARGUMENT = document
10+
.getElementById("__EVENTARGUMENT")
11+
?.getAttribute("value");
12+
const __LASTFOCUS = document
13+
.getElementById("__LASTFOCUS")
14+
?.getAttribute("value");
15+
const __VIEWSTATEGENERATOR = document
16+
.getElementById("__VIEWSTATEGENERATOR")
17+
?.getAttribute("value");
18+
const __VIEWSTATE = document
19+
.getElementById("__VIEWSTATE")
20+
?.getAttribute("value");
21+
const __EVENTVALIDATION = document
22+
.getElementById("__EVENTVALIDATION")
23+
?.getAttribute("value");
24+
const formData = new FormData();
25+
formData.append("__EVENTTARGET", "ctl00$mainContent$dllCourse");
26+
formData.append("__EVENTARGUMENT", __EVENTARGUMENT || "");
27+
formData.append("__LASTFOCUS", __LASTFOCUS || "");
28+
formData.append("__EVENTVALIDATION", __EVENTVALIDATION || "");
29+
formData.append("__VIEWSTATE", __VIEWSTATE || "");
30+
formData.append("__VIEWSTATEGENERATOR", __VIEWSTATEGENERATOR || "");
31+
formData.append("ctl00$mainContent$dllCourse", id + "");
32+
formData.append("ctl00$mainContent$hdException", "");
33+
return formData;
34+
};
35+
36+
export const secondFormGetter = async (secondId: string, id: string) => {
37+
const page = await (
38+
await fetch(url + `?id=${secondId}`, {
39+
method: "GET",
40+
})
41+
).text();
42+
const $ = cheerio.load(page);
43+
// const __EVENTTARGET = $("#__EVENTTARGET").attr("value");
44+
const __EVENTARGUMENT = $("#__EVENTARGUMENT").attr("value");
45+
const __LASTFOCUS = $("#__LASTFOCUS").attr("value");
46+
const __VIEWSTATEGENERATOR = $("#__VIEWSTATEGENERATOR").attr("value");
47+
const __VIEWSTATE = $("#__VIEWSTATE").attr("value");
48+
const __EVENTVALIDATION = $("#__EVENTVALIDATION").attr("value");
49+
const formData = new FormData();
50+
formData.append("__EVENTTARGET", "ctl00$mainContent$dllCourse");
51+
formData.append("__EVENTARGUMENT", __EVENTARGUMENT || "");
52+
formData.append("__LASTFOCUS", __LASTFOCUS || "");
53+
formData.append("__EVENTVALIDATION", __EVENTVALIDATION || "");
54+
formData.append("__VIEWSTATE", __VIEWSTATE || "");
55+
formData.append("__VIEWSTATEGENERATOR", __VIEWSTATEGENERATOR || "");
56+
formData.append("ctl00$mainContent$dllCourse", id);
57+
formData.append("ctl00$mainContent$hdException", "");
58+
return formData;
59+
};

‎src/index.css

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@import "tailwindcss/base";
2+
@import "tailwindcss/components";
3+
@import "tailwindcss/utilities";
4+
5+
progress {
6+
border-radius: 8px;
7+
width: 80%;
8+
height: 12px;
9+
/* margin-left: -11.5%; */
10+
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2);
11+
}
12+
progress::-webkit-progress-bar {
13+
background-color: rgb(255, 255, 255);
14+
border-radius: 7px;
15+
}
16+
progress::-webkit-progress-value {
17+
background-color: rgb(21, 255, 0);
18+
border-radius: 7px;
19+
/* box-shadow: 1px 1px 5px 3px rgba(255, 0, 0, 0.8); */
20+
}
21+
progress::-moz-progress-bar {
22+
/* style rules */
23+
}

‎src/main.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ReactDOM from "react-dom/client";
2+
import App from "./App.tsx";
3+
import "./index.css";
4+
5+
const rootPath = ".biky2b7";
6+
7+
document.querySelector(rootPath)?.prepend(document.createElement("div"));
8+
ReactDOM.createRoot(document.querySelector(`${rootPath} > div`)!).render(
9+
<App />
10+
);

‎src/utils.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export function mapToObject(map: Map<string, Map<string, string[]>>) {
2+
const obj: any = {};
3+
map.forEach((value, key: any) => {
4+
obj[key] = {};
5+
value.forEach((innerValue, innerKey) => {
6+
obj[key][innerKey] = innerValue;
7+
});
8+
});
9+
return obj;
10+
}
11+
12+
export function objectToMap(obj: any): Map<string, Map<string, string[]>> {
13+
const map = new Map<string, Map<string, string[]>>();
14+
for (const [key, value] of Object.entries(obj as { [s: string]: unknown })) {
15+
const innerMap = new Map<string, string[]>();
16+
for (const [innerKey, innerValue] of Object.entries(
17+
value as { [s: string]: string[] }
18+
)) {
19+
innerMap.set(innerKey, innerValue);
20+
}
21+
map.set(key, innerMap);
22+
}
23+
return map;
24+
}
25+
26+
export const textToColor = (text: string): string => {
27+
const intensity = 120;
28+
let hash = 0;
29+
for (let i = 0; i < text.length; i++) {
30+
hash = text.charCodeAt(i) + ((hash << 5) - hash);
31+
hash = hash & hash;
32+
}
33+
let color = "#";
34+
for (let i = 0; i < 3; i++) {
35+
let value = (hash >> (i * 8)) & 255;
36+
// Increase each RGB component to make the color lighter
37+
value = Math.min(value + intensity, 255); // Add 100 to make it lighter
38+
color += ("00" + value.toString(16)).slice(-2);
39+
}
40+
return color;
41+
};

‎src/vite-env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />

‎tailwind.config.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/** @type {import('tailwindcss').Config} */
2+
export default {
3+
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
4+
theme: {
5+
extend: {},
6+
},
7+
plugins: [],
8+
};

‎tsconfig.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"useDefineForClassFields": true,
5+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
6+
"module": "ESNext",
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"resolveJsonModule": true,
13+
"isolatedModules": true,
14+
"noEmit": true,
15+
"jsx": "react-jsx",
16+
17+
/* Linting */
18+
"strict": true,
19+
"noUnusedLocals": false,
20+
// "noUnusedLocals": true,
21+
"noUnusedParameters": true,
22+
"noFallthroughCasesInSwitch": true
23+
},
24+
"include": ["src"],
25+
"references": [{ "path": "./tsconfig.node.json" }]
26+
}

‎tsconfig.node.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"compilerOptions": {
3+
"composite": true,
4+
"skipLibCheck": true,
5+
"module": "ESNext",
6+
"moduleResolution": "bundler",
7+
"allowSyntheticDefaultImports": true,
8+
"strict": true
9+
},
10+
"include": ["vite.config.ts"]
11+
}

‎vite.config.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { defineConfig } from "vite";
2+
import react from "@vitejs/plugin-react";
3+
import path from "path";
4+
const isDevelopment = process.env.NODE_ENV !== "production";
5+
const fileExtensions = ["js", "jsx", "ts", "tsx", "css"];
6+
7+
const alias = {
8+
"@": path.resolve(__dirname, "src"),
9+
};
10+
11+
// https://vitejs.dev/config/
12+
export default defineConfig({
13+
plugins: [react()],
14+
mode: isDevelopment ? "development" : "production",
15+
resolve: {
16+
alias,
17+
extensions: fileExtensions.map((extension) => `.${extension}`),
18+
},
19+
build: {
20+
rollupOptions: {
21+
output: {
22+
dir: "dist",
23+
entryFileNames: "index.js",
24+
assetFileNames: "index.css",
25+
chunkFileNames: "chunk.js",
26+
manualChunks: undefined,
27+
},
28+
},
29+
},
30+
});

0 commit comments

Comments
 (0)
Please sign in to comment.