AnkiWeb Quiz

Shows quiz on ankiweb.

  1. // ==UserScript==
  2. // @name AnkiWeb Quiz
  3. // @namespace https://greasyfork.org/users/102866
  4. // @description Shows quiz on ankiweb.
  5. // @match https://ankiuser.net/*
  6. // @match https://ankiweb.net/*
  7. // @author TiLied
  8. // @version 2.0.04
  9. // @grant GM_listValues
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @grant GM_deleteValue
  13. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
  14. // @grant GM.listValues
  15. // @grant GM.getValue
  16. // @grant GM.setValue
  17. // @grant GM.deleteValue
  18. // ==/UserScript==
  19.  
  20. class AnkiWebQuiz
  21. {
  22. _Options = new Object();
  23. _Decks = new Object();
  24. _GlobalId = 0;
  25.  
  26. _DeckId;
  27.  
  28. constructor()
  29. {
  30. //GM.deleteValue("awqDecks");
  31. //GM.deleteValue("awqGlobalId");
  32. console.log("AnkiWeb Quiz v" + GM.info.script.version + " initialization");
  33.  
  34. this._LoadOptionsAndDecks();
  35. this._SetCSS();
  36.  
  37. globalThis.window.setTimeout(() =>
  38. {
  39. this._OptionsUI();
  40. }, 750);
  41. }
  42.  
  43. _SetCSS()
  44. {
  45. globalThis.window.document.head.append("<!--Start of AnkiWeb Quiz v" + GM.info.script.version + " CSS-->");
  46. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_quizGrid" +
  47. "{" +
  48. "display: grid;" +
  49. "grid-template-columns: repeat(4,auto);" +
  50. "grid-template-rows: auto;" +
  51. "grid-gap: 5px;" +
  52. "}</style>");
  53.  
  54. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_quizButton" +
  55. "{" +
  56. "color: #fff !important;" +
  57. "background-color: #0275d8;" +
  58. "border-color: #0275d8;" +
  59. "font-size: 1rem;" +
  60. "border-radius: .3rem;" +
  61. "border: 1px solid transparent;" +
  62. "max-width:350px;" +
  63. "cursor: pointer;" +
  64. "max-height: 300px;" +
  65. "overflow: auto;" +
  66. "padding: 5px;" +
  67. "}</ style >");
  68. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_quizButton:hover" +
  69. "{" +
  70. "background-color: #025aa5;" +
  71. "}</ style >");
  72.  
  73. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_true" +
  74. "{" +
  75. "background-color: #75d802;" +
  76. "}</style>");
  77. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_true:hover" +
  78. "{" +
  79. "background-color: #5aa502;" +
  80. "}</style>");
  81. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>.awq_trueBorder" +
  82. "{" +
  83. "border-color: #75d802;" +
  84. "border-width: 3px;" +
  85. "}</style>");
  86. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_false" +
  87. "{" +
  88. "background-color: #d80275;" +
  89. "}</style>");
  90. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>div.awq_false:hover" +
  91. "{" +
  92. "background-color: #a5025a;" +
  93. "}</style>");
  94. globalThis.window.document.head.insertAdjacentHTML("beforeend", "<style type='text/css'>.awq_falseBorder" +
  95. "{" +
  96. "border-color: #d80275;" +
  97. "border-width: 3px;" +
  98. "}</style>");
  99.  
  100. globalThis.window.document.head.append("<!--End of AnkiWeb Quiz v" + GM.info.script.version + " CSS-->");
  101.  
  102. }
  103. async _LoadOptionsAndDecks()
  104. {
  105. this._Options = await GM.getValue("awqOptions");
  106. this._Decks = await GM.getValue("awqDecks");
  107. this._GlobalId = await GM.getValue("awqGlobalId");
  108.  
  109. if (this._Options == null)
  110. {
  111. this._Options = new Object();
  112. this._Options["Buttons"] = 8;
  113. }
  114. if (this._Decks == null)
  115. this._Decks = new Object();
  116. if (this._GlobalId == null)
  117. this._GlobalId = 0;
  118.  
  119. //Console log prefs with value
  120. console.log("*prefs:");
  121. console.log("*-----*");
  122.  
  123. let vals = await GM.listValues();
  124.  
  125. for (let i = 0; i < vals.length; i++)
  126. {
  127. console.log("*" + vals[i] + ":" + await GM.getValue(vals[i]));
  128. }
  129. console.log("*-----*");
  130. }
  131. _OptionsUI()
  132. {
  133.  
  134. let nav = globalThis.window.document.body.querySelector(".navbar-nav");
  135.  
  136. let num = `<label for=\"awqButtons\">Number of Buttons (4-12):</label>\r\n\r\n<input type=\"number\" id=\"awqButtons\" name=\"tentacles\" min=\"4\" max=\"12\" value=\"${this._Options["Buttons"]}\" />`;
  137. nav.insertAdjacentHTML("beforeend", num);
  138.  
  139. let inputt = globalThis.window.document.querySelector("#awqButtons");
  140.  
  141. inputt.addEventListener("change", (e) =>
  142. {
  143.  
  144. let value = e.target["value"];
  145. console.log(value);
  146. this._Options["Buttons"] = value;
  147. GM.setValue("awqOptions", this._Options);
  148.  
  149. }, false);
  150.  
  151. }
  152.  
  153. async Main()
  154. {
  155. if (globalThis.window.document.location.pathname.startsWith("/decks"))
  156. {
  157. let strs = globalThis.window.document.querySelectorAll("button.btn-link");
  158. for (let i = 0; i < strs.length; i++)
  159. {
  160. let _node = strs[i];
  161. let _text = _node.textContent.trim().replaceAll(" ", "");
  162.  
  163. if(this._Decks[_text] == null)
  164. this._Decks[_text] = new Object();
  165.  
  166. _node.addEventListener("click", () =>
  167. {
  168. GM.setValue("awqDeckId", _text);
  169. }, true);
  170.  
  171. }
  172. GM.setValue("awqDecks", this._Decks);
  173. }
  174. if (globalThis.window.document.location.pathname.startsWith("/study"))
  175. {
  176. this._DeckId = await GM.getValue("awqDeckId");
  177. if (this._DeckId == null)
  178. {
  179. console.log("Deck id is null");
  180. return;
  181. }
  182.  
  183. let _awq = globalThis.window.document.body.querySelector("#qa");
  184. console.log(_awq);
  185.  
  186. if (_awq == null)
  187. {
  188. globalThis.window.setTimeout(() =>
  189. {
  190. this.Main();
  191. }, 1000);
  192. return;
  193. }
  194. let _inner = _awq.innerHTML;
  195. let _id = "";
  196. let keys = Object.keys(this._Decks[this._DeckId]);
  197. for (let i = 0; i < keys.length; i++)
  198. {
  199. if (_inner == this._Decks[this._DeckId][keys[i]]["question"])
  200. {
  201. _id = this._Decks[this._DeckId][keys[i]]["cardId"];
  202. break;
  203. }
  204. }
  205.  
  206. if (_id == "")
  207. {
  208. _id += this.GetId();
  209.  
  210. this._Decks[this._DeckId][_id] = new Object();
  211. this._Decks[this._DeckId][_id]["cardId"] = _id;
  212. this._Decks[this._DeckId][_id]["question"] = _inner;
  213. this._Decks[this._DeckId][_id]["answer"] = "Use this deck more!";
  214. }
  215.  
  216. this.Qiuz(_id);
  217.  
  218. GM.setValue("awqDecks", this._Decks);
  219. }
  220. }
  221.  
  222. Qiuz(id)
  223. {
  224. this.PlayAudio();
  225.  
  226. let cardsId = new Array();
  227.  
  228. cardsId.push(id);
  229.  
  230. let keys = Object.keys(this._Decks[this._DeckId]);
  231.  
  232. let len = this._Options["Buttons"] - 1;
  233. if(len >= keys.length)
  234. {
  235. len = keys.length- 1;
  236. }
  237.  
  238. for(let i = 0; i < len; i++)
  239. {
  240. let _randomInt = this.GetRandomInt(keys.length);
  241. let _id = keys[_randomInt];
  242. let _continue = false;
  243.  
  244. for (let j = 0; j < cardsId.length; j++)
  245. {
  246. if (_id == cardsId[j])
  247. {
  248. i--;
  249. _continue = true;
  250. break;
  251. }
  252. }
  253. if (_continue)
  254. continue;
  255. else
  256. cardsId.push(_id);
  257. }
  258. //Console.WriteLine(cardsId);
  259.  
  260. cardsId = this.Shuffle(cardsId);
  261.  
  262. let before = globalThis.window.document.querySelector("#qa_box");
  263. let divGrid = globalThis.window.document.createElement("div");
  264. divGrid.classList.add("awq_quizGrid");
  265.  
  266. before.parentNode.insertBefore(divGrid, before);
  267.  
  268. let answer = globalThis.window.document.querySelector("button.btn-primary");
  269. if (!answer.classList.contains("awqEvent"))
  270. {
  271. answer.classList.add("awqEvent");
  272. answer.addEventListener("click", () =>
  273. {
  274. let eases = globalThis.window.document.querySelectorAll("button.m-1");
  275.  
  276. //Console.WriteLine(eases);
  277. for (let i = 0; i < eases.length; i++)
  278. {
  279. if (eases[i].classList.contains("awqEvent"))
  280. continue;
  281.  
  282. eases[i].classList.add("awqEvent");
  283. eases[i].addEventListener("click", () =>
  284. {
  285. this.AddEventsForEases();
  286. }, false);
  287. }
  288. }, false);
  289. }
  290.  
  291. for (let i = 0; i < cardsId.length; i++)
  292. {
  293. let div = globalThis.window.document.createElement("div");
  294. div.classList.add("awq_quizButton");
  295. div.id = cardsId[i];
  296.  
  297. div.addEventListener("click", (e) =>
  298. {
  299. let _id = e.currentTarget.id;
  300. console.log(_id);
  301.  
  302. let _button = globalThis.window.document.querySelector("button.btn-primary");
  303. _button.click();
  304.  
  305. let _awq = globalThis.window.document.body.querySelector("#qa");
  306. this._Decks[this._DeckId][id]["answer"] = _awq.innerHTML;
  307. this.PlayAudio();
  308.  
  309. globalThis.window.setTimeout(() =>
  310. {
  311. let _eases = globalThis.window.document.querySelectorAll("button.m-1");
  312.  
  313. //Console.WriteLine(_eases);
  314. for (let i = 0; i < _eases.length; i++)
  315. {
  316. if (_eases[i].classList.contains("awqEvent"))
  317. continue;
  318.  
  319. _eases[i].classList.add("awqEvent");
  320. _eases[i].addEventListener("click", () =>
  321. {
  322. this.AddEventsForEases();
  323. }, false);
  324. }
  325.  
  326. if (_id == id)
  327. {
  328. div.classList.add("awq_true");
  329. div.classList.add("awq_trueBorder");
  330.  
  331. _eases[1].classList.add("awq_trueBorder");
  332. }
  333. else
  334. {
  335. div.classList.add("awq_false");
  336. div.classList.add("awq_falseBorder");
  337.  
  338. _eases[0].classList.add("awq_falseBorder");
  339. }
  340. }, 500);
  341. },false);
  342.  
  343. let q = this._Decks[this._DeckId][cardsId[i]]["question"].replace("</awq>", "");
  344. let regex2 = new RegExp("style=", "g");
  345. let html = "<awq>" + this._Decks[this._DeckId][cardsId[i]]["answer"].replace(q, "").replace("\n\n<hr id=\"answer\">\n\n", "").replace("<img", "<img width=\"100%\"").replace(regex2, "data-style=");
  346.  
  347. div.insertAdjacentHTML("beforeend", html);
  348.  
  349. divGrid.append(div);
  350. }
  351. }
  352.  
  353. AddEventsForEases()
  354. {
  355. let grid = globalThis.window.document.querySelector(".awq_quizGrid");
  356. grid.remove();
  357. this.Main();
  358. }
  359.  
  360. GetId()
  361. {
  362. let id = this._GlobalId++;
  363. GM.setValue("awqGlobalId", id);
  364. return id;
  365. }
  366.  
  367. PlayAudio()
  368. {
  369. let box = globalThis.window.document.body.querySelector("#qa_box");
  370. if (box != null)
  371. {
  372. let audio = box.querySelector("audio");
  373. if(audio != null)
  374. audio.play();
  375. }
  376. }
  377.  
  378. GetRandomInt(max)
  379. {
  380. return Math.floor(Math.random() * max);
  381. }
  382.  
  383. Shuffle(array)
  384. {
  385. for (let i = array.length- 1; i > 0; i--)
  386. {
  387. let _i = i + 1;
  388. let j = Math.floor(Math.random() * _i);
  389. let temp = array[i];
  390. array[i] = array[j];
  391. array[j] = temp;
  392. }
  393.  
  394. return array;
  395. }
  396. }
  397.  
  398.  
  399. let awq;
  400.  
  401. window.onload = function ()
  402. {
  403. awq = new AnkiWebQuiz();
  404.  
  405. setTimeout(() =>
  406. {
  407. awq.Main();
  408. console.log(awq);
  409. }, 1000);
  410. };