Sorryops

Collect and reuse ORIOKS test answers

当前为 2024-05-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Sorryops
  3. // @name:ru Сориупс
  4. // @namespace https://git.disroot.org/electromagneticcyclone/sorryops
  5. // @version 20240502.2
  6. // @description Collect and reuse ORIOKS test answers
  7. // @description:ru Скрипт для сбора и переиспользования ответов на тесты ОРИОКС
  8. // @icon https://orioks.miet.ru/favicon.ico
  9. // @author electromagneticcyclone & angelbeautifull
  10. // @license GPL-3.0-or-later
  11. // @supportURL https://git.disroot.org/electromagneticcyclone/sorryops
  12. // @match https://orioks.miet.ru/student/student/test*
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_addStyle
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_setClipboard
  18. // @grant GM_xmlhttpRequest
  19. // @grant GM_openInTab
  20. // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
  21. // @connect sorryops.ru
  22. // @run-at document-start
  23. // ==/UserScript==
  24.  
  25. /* Version */
  26. const VERSION = "20240502.2";
  27. /* End Version */
  28.  
  29. /* Charset */
  30. const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  31. /* End Charset */
  32.  
  33. /* Labels */
  34.  
  35. const all_labels = {
  36. en: {
  37. l: "English",
  38. settings_title: "Settings",
  39. script_language: "Language",
  40. show_user_id: "Show user ID",
  41. user_id: "User ID (keep private)",
  42. server: "Sync answers with server (leave blank to disable)",
  43. wait_server_response: "Wait server response",
  44. auto_answer: "Auto answer",
  45. auto_answer_no: "No",
  46. auto_answer_first: "First",
  47. auto_answer_random: "Random",
  48. auto_answer_not_greedy: "Choose a known answer if there are more wrong answers than that number",
  49. display_values: "Answers variant",
  50. display_values_ori: "ORIOKS",
  51. display_values_sorry: "Sorry",
  52. display_values_both: "Both",
  53. display_answer: "Display answer near variant",
  54. stop_timer: "Freeze and hide timer",
  55. register_keyboard_keys: "Register hotkeys",
  56. copy_answers: "Copy results to the clipboard",
  57. append_question_number: "Show question numbers in the final report",
  58. accumulator_enable: "Accumulate test results in one field",
  59. auto_continue: "Auto continue (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable)",
  60. auto_restart: "Auto restart (DANGEROUS!!! Will be disabled after an hour. Press `d` to disable. Make sure you have infinite attempts)",
  61. },
  62. ru: {
  63. l: "Русский",
  64. settings_title: "Настройки",
  65. script_language: "Язык",
  66. show_user_id: "Показать индетификатор пользователя",
  67. user_id: "Индетификатор (держать в секрете)",
  68. server: "Синхронизировать ответы с сервером (оставить пустым для отключения)",
  69. wait_server_response: "Ждать ответа сервера",
  70. auto_answer: "Автовыбор ответа",
  71. auto_answer_no: "Нет",
  72. auto_answer_first: "Первый",
  73. auto_answer_random: "Случайный",
  74. auto_answer_not_greedy: "Выбирать известный ответ, если неправильных больше этого числа",
  75. display_values: "Вариант отображения ответов",
  76. display_values_ori: "ОРИОКС",
  77. display_values_sorry: "Сори",
  78. display_values_both: "Оба",
  79. display_answer: "Отображать ответ рядом с вариантом",
  80. stop_timer: "Заморозить и скрыть таймер",
  81. register_keyboard_keys: "Горячие клавиши",
  82. copy_answers: "Копировать результаты в буфер обмена",
  83. append_question_number: "Отображать номер вопроса в финальном отчёте",
  84. accumulator_enable: "Собирать отчёты в одно поле",
  85. auto_continue: "Автопродолжение (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить)",
  86. auto_restart: "Автоперезапуск (ОПАСНО!!! Отключается через час. Нажмите `d`, чтобы остановить. Убедитесь, что количество попыток неограничено)",
  87. },
  88. };
  89.  
  90. var labels = all_labels[(() => {
  91. var lang = GM_getValue('language', "-");
  92. if (!lang || (lang == "-")) {
  93. lang = navigator.language || navigator.userLanguage;
  94. }
  95. for (var l in all_labels) {
  96. if (lang.includes(l)) {
  97. return l;
  98. }
  99. }
  100. })()];
  101. if (labels == undefined) {
  102. labels = all_labels.ru;
  103. }
  104.  
  105. /* End Labels */
  106.  
  107. /* Config */
  108.  
  109. var config = new GM_config({
  110. id: 'config',
  111. title: labels.settings_title,
  112. fields: {
  113. script_language: {
  114. label: labels.script_language,
  115. type: 'select',
  116. options: [
  117. '-',
  118. all_labels.en.l,
  119. all_labels.ru.l,
  120. ],
  121. default: '-',
  122. },
  123. show_user_id: {
  124. label: labels.show_user_id,
  125. type: 'checkbox',
  126. default: false,
  127. },
  128. user_id: {
  129. label: labels.user_id + (GM_getValue("show_user_id", false) ? "" : "<input readonly value='******'>"),
  130. type: GM_getValue("show_user_id", false) ? 'text' : 'hidden',
  131. save: false,
  132. default: '',
  133. },
  134. valid_user_id: {
  135. type: 'hidden',
  136. default: '',
  137. },
  138. server: {
  139. label: labels.server,
  140. type: 'text',
  141. default: '',
  142. },
  143. wait_server_response: {
  144. label: labels.wait_server_response,
  145. type: 'checkbox',
  146. default: true,
  147. },
  148. auto_answer: {
  149. label: labels.auto_answer,
  150. type: 'select',
  151. options: [
  152. labels.auto_answer_no,
  153. labels.auto_answer_first,
  154. labels.auto_answer_random,
  155. ],
  156. default: labels.auto_answer_no,
  157. },
  158. auto_answer_not_greedy: {
  159. label: labels.auto_answer_not_greedy,
  160. type: 'number',
  161. default: -1,
  162. },
  163. display_values: {
  164. label: labels.display_values,
  165. type: 'select',
  166. options: [
  167. labels.display_values_ori,
  168. labels.display_values_sorry,
  169. labels.display_values_both,
  170. ],
  171. default: labels.display_values_ori,
  172. },
  173. display_answer: {
  174. label: labels.display_answer,
  175. type: 'checkbox',
  176. default: true,
  177. },
  178. stop_timer: {
  179. label: labels.stop_timer,
  180. type: 'checkbox',
  181. default: false,
  182. },
  183. register_keyboard_keys: {
  184. label: labels.register_keyboard_keys,
  185. type: 'checkbox',
  186. default: true,
  187. },
  188. copy_answers: {
  189. label: labels.copy_answers,
  190. type: 'checkbox',
  191. default: false,
  192. },
  193. append_question_number: {
  194. label: labels.append_question_number,
  195. type: 'checkbox',
  196. default: true,
  197. },
  198. accumulator_enable: {
  199. label: labels.accumulator_enable,
  200. type: 'checkbox',
  201. default: false,
  202. },
  203. auto_continue: {
  204. label: labels.auto_continue,
  205. type: 'checkbox',
  206. default: false,
  207. },
  208. auto_continue_time: {
  209. type: 'hidden',
  210. default: 0,
  211. },
  212. auto_restart: {
  213. label: labels.auto_restart,
  214. type: 'checkbox',
  215. default: false,
  216. },
  217. auto_restart_time: {
  218. type: 'hidden',
  219. default: 0,
  220. },
  221. },
  222. events: {
  223. init: function() {
  224. var valid_user_id = this.get('valid_user_id');
  225. if (!validate_user_id(valid_user_id)) {
  226. valid_user_id = generate_user_id();
  227. }
  228. this.set('user_id', valid_user_id);
  229. this.set('valid_user_id', valid_user_id);
  230. GM_setValue('show_user_id', this.get('show_user_id'));
  231. GM_setValue('stop_timer', this.get('stop_timer'));
  232. if (this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  233. this.set('auto_continue', false);
  234. }
  235. if (this.get('accumulator_enable') == false) {
  236. GM_setValue('accumulated_answers', "");
  237. }
  238. switch (this.get('script_language')) {
  239. case all_labels.en.l:
  240. GM_setValue('language', "en");
  241. break;
  242. case all_labels.ru.l:
  243. GM_setValue('language', "ru");
  244. break;
  245. default:
  246. GM_setValue('language', "-");
  247. break;
  248. }
  249. },
  250. save: function(forgotten) {
  251. this.set('auto_continue_time', Date.now());
  252. this.set('auto_restart_time', Date.now());
  253. if (this.isOpen && this.get('auto_continue') && (this.get('auto_answer') == "No")) {
  254. this.set('auto_continue', false);
  255. alert("Can't automatically continue without answer.");
  256. }
  257. if (!validate_user_id(forgotten['user_id'])) {
  258. this.set('user_id', this.get('valid_user_id'))
  259. alert('User ID is invalid!');
  260. } else {
  261. this.set('valid_user_id', forgotten['user_id'])
  262. }
  263. this.init();
  264. },
  265. },
  266. });
  267.  
  268. GM_registerMenuCommand(labels.settings_title, () => {
  269. config.open();
  270. });
  271.  
  272. /* End Config */
  273.  
  274. /* Server */
  275.  
  276. function send_to_server(results) {
  277. var server = config.get('server');
  278. if (server != '') {
  279. GM_xmlhttpRequest({
  280. method: 'POST',
  281. url: 'https://' + server,
  282. headers: {
  283. 'Content-Type': 'application/json',
  284. },
  285. data: JSON.stringify(results),
  286. });
  287. }
  288. }
  289.  
  290. function fetch_from_server(path, func) {
  291. var server = config.get('server');
  292. var fetched_data = GM_getValue('fetched_data');
  293. if (fetched_data == undefined) {
  294. fetched_data = {};
  295. }
  296. if ((server != '') && (Object.keys(fetched_data).length == 0)) {
  297. GM_xmlhttpRequest({
  298. method: 'GET',
  299. url: 'https://' + server + '/' + path + '?uid=' + config.get('user_id'),
  300. timeout: 1000,
  301. onload: function (response) {
  302. var text = response.responseText;
  303. if (response.status == 404) {
  304. fetched_data = {version: VERSION};
  305. GM_setValue('fetched_data', fetched_data);
  306. func(fetched_data);
  307. } else if (!text.includes("{")) {
  308. func({});
  309. } else {
  310. fetched_data = JSON.parse(text);
  311. GM_setValue('fetched_data', fetched_data);
  312. func(fetched_data);
  313. }
  314. },
  315. onerror: function (e) {
  316. console.log(e);
  317. func({});
  318. },
  319. onabort: function (e) {
  320. func({});
  321. },
  322. ontimeout: function (e) {
  323. func({});
  324. }
  325. });
  326. } else {
  327. func(fetched_data);
  328. }
  329. }
  330.  
  331. /* End Server */
  332.  
  333. /* Stop timer */
  334.  
  335. if (GM_getValue('stop_timer', true)) {
  336. var i, pbox;
  337. var pboxes = document.getElementsByTagName('p');
  338. for (i = 0; i < pboxes.length; i++) {
  339. pbox = pboxes[i];
  340. if (pbox.textContent.includes("Осталось:")) {
  341. pbox.parentNode.remove();
  342. document.getElementsByTagName('hr')[0].remove();
  343. var injectFakeTimer = function(window) {
  344. window.setInterval = (f, t, its_me = false) => {
  345. return window.setInterval(f, its_me ? t : 10^999);
  346. };
  347. }
  348. var scriptFakeTimer = document.createElement('script');
  349. scriptFakeTimer.setAttribute("type", "application/javascript");
  350. scriptFakeTimer.textContent = '(' + injectFakeTimer + ')(window);';
  351. document.body.appendChild(scriptFakeTimer);
  352. break;
  353. }
  354. }
  355. }
  356.  
  357. /* End Stop timer */
  358.  
  359. /* Events */
  360.  
  361. window.addEventListener('load', main);
  362. window.onkeydown = (e) => {
  363. if ((e.key == "Enter") && config.get('register_keyboard_keys')) {
  364. press_continue_btn();
  365. }
  366. if (e.key == "d") {
  367. config.set('auto_continue', false);
  368. config.set('auto_restart', false);
  369. config.save();
  370. }
  371. };
  372.  
  373. /* End Events */
  374.  
  375. /* Page properties */
  376.  
  377. // const success = -1487162948;
  378. var answers = [];
  379. var sorted_objects_value = [];
  380. var variant, hash, type, correct, incorrect, version;
  381. var prev_new_answer_f = false;
  382. var new_answer_f = false;
  383. var testID = (() => {
  384. var url = document.URL;
  385. url = url.slice(url.indexOf("idKM=") + 5);
  386. url = url.slice(0, url.indexOf("&"));
  387. return url;
  388. })();
  389.  
  390. /* End properties */
  391.  
  392. /* Functions */
  393.  
  394. function comb(s, blacklist = []) {
  395. var result = [];
  396. for (var i = 1; i < (1 << s.length); i++) {
  397. var temp = '';
  398. for (var j = 0; j < s.length; j++) {
  399. if (i & Math.pow(2, j)) {
  400. temp += s[j];
  401. }
  402. }
  403. temp = "{" + temp + "}";
  404. if (!blacklist.includes(temp)) {
  405. result.push(temp);
  406. }
  407. }
  408. return result;
  409. }
  410.  
  411. // https://github.com/ajayyy/maze-utils/blob/036086403f675b8fea0e22065f26ba534e351562/src/setup.ts
  412. function generate_user_id(length = 36) {
  413. var i;
  414. var result = "";
  415. const cryptoFuncs = typeof window === "undefined" ? crypto : window.crypto;
  416. if (cryptoFuncs && cryptoFuncs.getRandomValues) {
  417. const values = new Uint32Array(length);
  418. cryptoFuncs.getRandomValues(values);
  419. for (i = 0; i < length; i++) {
  420. result += charset[values[i] % charset.length];
  421. }
  422. } else {
  423. for (i = 0; i < length; i++) {
  424. result += charset[Math.floor(Math.random() * charset.length)];
  425. }
  426. }
  427. return result;
  428. }
  429.  
  430. function validate_user_id(uid, length = 36) {
  431. var i;
  432. if (uid.length != length) {
  433. return false;
  434. }
  435. for (i = 0; i < length; i++) {
  436. if (!charset.includes(uid[i])) {
  437. return false;
  438. }
  439. }
  440. return true;
  441. }
  442.  
  443. // https://stackoverflow.com/a/15710692
  444. function hashCode(s, return_num = false) {
  445. var result = "";
  446. var h = s.split("").reduce(function(a, b) {
  447. a = ((a << 5) - a) + b.charCodeAt(0);
  448. return a & a;
  449. }, 0);
  450. if (return_num) {
  451. return h;
  452. }
  453. while (h != 0) {
  454. result += charset[((h % charset.length) + charset.length) % charset.length];
  455. h = Math.floor(Math.abs(h) / charset.length) * (h / Math.abs(h));
  456. }
  457. return result;
  458. }
  459.  
  460. function set_to_clear(id, exec_if_not_cleared) {
  461. var clear = GM_getValue('clear_tests', new Object());
  462. if (!clear[id]) {
  463. exec_if_not_cleared();
  464. }
  465. clear[id] = true;
  466. GM_setValue('clear_tests', clear);
  467. }
  468.  
  469. function DB_cleaner() {
  470. var clear = GM_getValue('clear_tests', new Object());
  471. var tests = GM_getValue('tests', new Object());
  472. for (var test in clear) {
  473. delete tests[test];
  474. }
  475. GM_setValue('tests', tests);
  476. GM_setValue('clear_tests', new Object());
  477. }
  478.  
  479. function press_continue_btn() {
  480. var i;
  481. var buttons = document.getElementsByTagName('button');
  482. var button = undefined;
  483. for (i = 0; i < buttons.length; i++) {
  484. var btn = buttons[i];
  485. if (btn.textContent.includes("Пройти") || btn.textContent.includes("Продолжить")) {
  486. button = btn;
  487. break;
  488. }
  489. }
  490. if (button === undefined) {
  491. return;
  492. }
  493. if (button.textContent.includes("Пройти")) {
  494. window.location.replace(button.parentNode.href);
  495. } else if (button.textContent.includes("Продолжить")) {
  496. button.click();
  497. }
  498. }
  499.  
  500. function calculate_variant_hash() {
  501. variant = document.getElementById('w0').parentNode.textContent;
  502. variant = variant.slice(variant.indexOf("Вопрос:"));
  503. hash = hashCode(variant);
  504. }
  505.  
  506. function update_variant() {
  507. var i, pbox;
  508. var status = "Неизвестен";
  509. var chosen_answer = "";
  510. switch (type) {
  511. case 'checkbox':
  512. case 'radio': {
  513. for (i = 0; i < answers.length; i++) {
  514. chosen_answer += answers[i].checked ? answers[i].sorry_value : "";
  515. }
  516. chosen_answer = chosen_answer.split('').sort().join('');
  517. if (type == 'checkbox') {
  518. chosen_answer = "{" + chosen_answer + "}";
  519. }
  520. } break;
  521. case 'text': {
  522. for (i = 0; i < answers.length; i++) {
  523. chosen_answer += "[" + answers[i].value + "]";
  524. }
  525. }
  526. }
  527. new_answer_f = true;
  528. if ((version != VERSION) && (version !== undefined)) {
  529. status = "<span style='color: red;'>Скрипт устарел</span>";
  530. GM_setValue('fetched_data', {});
  531. } else if (version === undefined) {
  532. if (config.get('wait_server_response')) {
  533. status = "<span style='color: blue;'>Ожидание ответа</span>";
  534. } else {
  535. status = "<span style='color: red;'>Нет соединения</span>";
  536. }
  537. } else if (chosen_answer == correct) {
  538. status = "<span style='color: green;'>Верно</span>";
  539. new_answer_f = false;
  540. } else if (incorrect.includes(chosen_answer)) {
  541. status = "<span style='color: red;'>Неверно</span>";
  542. new_answer_f = false;
  543. }
  544. GM_setValue('new_answer_f', prev_new_answer_f || new_answer_f);
  545. var pboxes = document.getElementsByTagName('p');
  546. const display_answer = config.get('display_answer');
  547. for (i = 0; i < pboxes.length; i++) {
  548. pbox = pboxes[i];
  549. if (pbox.textContent.includes("Вопрос:")) {
  550. pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i>";
  551. if (config.get('server') != '') {
  552. pbox.innerHTML += "<br>Статус: " + status;
  553. }
  554. pbox.innerHTML += "<br>Вопрос:";
  555. break;
  556. }
  557. }
  558. var question_num = undefined;
  559. for (i = 0; i < pboxes.length; i++) {
  560. pbox = pboxes[i];
  561. if (pbox.textContent.includes("Текущий вопрос: ")) {
  562. question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16).trim();
  563. break;
  564. }
  565. }
  566. if (hash !== undefined) {
  567. var tests = GM_getValue('tests', new Object());
  568. if (tests[testID] === undefined) {
  569. tests[testID] = new Object();
  570. }
  571. tests[testID][hash] = [question_num, chosen_answer, answers.length];
  572. GM_setValue('tests', tests);
  573. }
  574. }
  575.  
  576. function color_answers() {
  577. var answer, correct_element, incorrect_element, sorry_val;
  578. switch (type) {
  579. case 'radio': {
  580. for (answer in answers) {
  581. if (answers[answer].sorry_value == correct) {
  582. if (!answers[answer].sorry_colored && (version !== undefined)) {
  583. correct_element = answers[answer].parentNode;
  584. sorry_val = answers[answer].sorry_value;
  585. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  586. answers[answer] = correct_element.getElementsByTagName('input')[0];
  587. answers[answer].sorry_value = sorry_val;
  588. answers[answer].sorry_colored = true;
  589. }
  590. }
  591. if (incorrect.includes(answers[answer].sorry_value)) {
  592. if (!answers[answer].sorry_colored && (version !== undefined)) {
  593. incorrect_element = answers[answer].parentNode;
  594. sorry_val = answers[answer].sorry_value;
  595. incorrect_element.innerHTML = "<span style='color: red;'>" + incorrect_element.innerHTML + "</span>";
  596. answers[answer] = incorrect_element.getElementsByTagName('input')[0];
  597. answers[answer].sorry_value = sorry_val;
  598. answers[answer].sorry_colored = true;
  599. }
  600. }
  601. }
  602. } break;
  603. case 'checkbox': {
  604. for (answer in answers) {
  605. if (correct != undefined) {
  606. if (correct.includes(answers[answer].sorry_value)) {
  607. if (!answers[answer].sorry_colored && (version !== undefined)) {
  608. correct_element = answers[answer].parentNode;
  609. sorry_val = answers[answer].sorry_value;
  610. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  611. answers[answer] = correct_element.getElementsByTagName('input')[0];
  612. answers[answer].sorry_value = sorry_val;
  613. answers[answer].sorry_colored = true;
  614. }
  615. }
  616. }
  617. }
  618. } break;
  619. }
  620. }
  621.  
  622. function auto_answer() {
  623. var answer, chosen_answer, sorry_val, correct_element, incorrect_element;
  624. const auto_answer = config.get('auto_answer');
  625. function pick_answer(picked) {
  626. switch (type) {
  627. case 'radio': {
  628. for (answer in answers) {
  629. if (answers[answer].sorry_value == picked) {
  630. answers[answer].click();
  631. }
  632. }
  633. } break;
  634. case 'checkbox': {
  635. for (answer in answers) {
  636. if (picked.includes(answers[answer].sorry_value)) {
  637. answers[answer].click();
  638. }
  639. }
  640. } break;
  641. case 'text': {
  642. var corr = picked.slice(1, picked.length - 1).split('][');
  643. for (i = 0; (i < answers.leght) && (i < corr.length); i++) {
  644. answers[i].placeholder = corr[i];
  645. }
  646. } break;
  647. }
  648. }
  649. if (correct != undefined) {
  650. pick_answer(correct);
  651. } else if ((incorrect.length >= config.get('auto_answer_not_greedy')) && (config.get('auto_answer_not_greedy') > 0)) {
  652. chosen_answer = Math.floor(Math.random() * incorrect.length);
  653. pick_answer(incorrect[chosen_answer]);
  654. } else if (auto_answer == labels.auto_answer_random) {
  655. switch (type) {
  656. case 'radio': {
  657. var possible_answers = [];
  658. for (answer in answers) {
  659. if (incorrect.includes(answers[answer].sorry_value) == false) {
  660. possible_answers.push(answer);
  661. }
  662. }
  663. chosen_answer = Math.floor(Math.random() * possible_answers.length);
  664. answers[possible_answers[chosen_answer]].click();
  665. } break;
  666. case 'checkbox': {
  667. var combs = comb(charset.slice(0, answers.length), incorrect);
  668. var pick = combs[Math.floor(Math.random() * combs.length)];
  669. for (i = 0; i < answers.length; i++) {
  670. if(pick.includes(answers[i].sorry_value)) {
  671. answers[i].click();
  672. }
  673. }
  674. } break;
  675. }
  676. } else if (auto_answer == labels.auto_answer_first) {
  677. Object.values(sorted_objects_value)[0].click();
  678. }
  679. }
  680.  
  681. function parse_server_data(server_data) {
  682. version = server_data.version;
  683. if ((version !== VERSION) && (version !== undefined)) {
  684. console.warn("Sorryops is outdated");
  685. server_data = {};
  686. GM_openInTab("https://greasyfork.org/en/scripts/481036-sorryops");
  687. config.set('auto_continue', false);
  688. }
  689. if (server_data.hasOwnProperty('correct')) {
  690. correct = server_data.correct;
  691. if (correct === undefined) {
  692. correct = undefined;
  693. } else if (correct.hasOwnProperty(hash)) {
  694. correct = correct[hash];
  695. } else {
  696. correct = undefined;
  697. }
  698. } else {
  699. correct = undefined;
  700. }
  701. if (server_data.hasOwnProperty('incorrect')) {
  702. incorrect = server_data.incorrect;
  703. if (incorrect === undefined) {
  704. incorrect = [];
  705. } else if (incorrect.hasOwnProperty(hash)) {
  706. incorrect = incorrect[hash];
  707. } else {
  708. incorrect = [];
  709. }
  710. } else {
  711. incorrect = [];
  712. }
  713. }
  714.  
  715. function auto_continue() {
  716. var old_time, cur_time;
  717. if (config.get('auto_continue')) {
  718. old_time = config.get('auto_continue_time');
  719. cur_time = Date.now();
  720. if (cur_time - old_time > 60 * 60 * 1000) {
  721. config.set('auto_continue', false);
  722. } else {
  723. press_continue_btn();
  724. }
  725. }
  726. }
  727.  
  728. function auto_restart() {
  729. var old_time, cur_time;
  730. if (config.get('auto_restart')) {
  731. old_time = config.get('auto_restart_time');
  732. cur_time = Date.now();
  733. if (cur_time - old_time > 60 * 60 * 1000) {
  734. config.set('auto_restart', false);
  735. } else {
  736. press_continue_btn();
  737. }
  738. }
  739. }
  740.  
  741. /* End Functions */
  742.  
  743. /* Handlers */
  744.  
  745. function test_form_handler(server_response) {
  746. var i, key, answer, sorry_val;
  747. var complicated_hash_f = false;
  748. var boxes = [];
  749. var sorted_objects;
  750. var objects_hash = new Object();
  751. var objects_value = new Object();
  752. var form = document.getElementById('testform-answer');
  753. var manual_form = document.getElementById('testform-answer-0');
  754. if (form != null) {
  755. boxes = form.getElementsByTagName('input');
  756. } else if (manual_form != null) {
  757. i = 1;
  758. while (manual_form != null) {
  759. boxes.push(manual_form);
  760. manual_form = document.getElementById('testform-answer-' + i++);
  761. }
  762. }
  763. type = boxes[0].type;
  764. for (i = 0; i < boxes.length; i++) {
  765. if (boxes[i].parentNode.innerHTML.includes("<img")) {
  766. complicated_hash_f = true;
  767. break;
  768. }
  769. }
  770. switch (type) {
  771. case 'checkbox':
  772. case 'radio': {
  773. for (i = 0; i < boxes.length; i++) {
  774. boxes[i].hash = hashCode(complicated_hash_f ? boxes[i].parentNode.innerHTML : boxes[i].parentNode.innerText, true);
  775. objects_hash[boxes[i].hash] = boxes[i];
  776. objects_value[boxes[i].value] = boxes[i];
  777. }
  778. const sorted_objects_hash = Object.keys(objects_hash).sort().reduce(
  779. (obj, key) => {
  780. obj[key] = objects_hash[key];
  781. return obj;
  782. }, {}
  783. );
  784. sorted_objects_value = Object.keys(objects_value).sort().reduce(
  785. (obj, key) => {
  786. obj[key] = objects_value[key];
  787. return obj;
  788. }, {}
  789. );
  790. i = 0;
  791. sorted_objects = sorted_objects_hash;
  792. for (key in sorted_objects) {
  793. sorted_objects[key].parentNode.remove();
  794. form.appendChild(sorted_objects[key].parentNode);
  795. }
  796. calculate_variant_hash();
  797. parse_server_data(server_response);
  798. for (key in sorted_objects) {
  799. sorted_objects[key].sorry_value = charset[i++];
  800. var span = document.createElement('span');
  801. var disp_val;
  802. switch (config.get('display_values')) {
  803. case labels.display_values_ori:
  804. disp_val = sorted_objects[key].value;
  805. break;
  806. case labels.display_values_sorry:
  807. disp_val = sorted_objects[key].sorry_value;
  808. break;
  809. case labels.display_values_both:
  810. disp_val = sorted_objects[key].value + ":" + sorted_objects[key].sorry_value;
  811. break;
  812. }
  813. span.innerHTML = disp_val + ") ";
  814. sorted_objects[key].parentNode.insertBefore(span, sorted_objects[key]);
  815. answers.push(sorted_objects[key]);
  816. }
  817. if (config.get('display_values') == labels.display_values_ori) {
  818. for (key in sorted_objects_value) {
  819. sorted_objects_value[key].parentNode.remove();
  820. form.appendChild(sorted_objects_value[key].parentNode);
  821. }
  822. }
  823. } break;
  824. case 'text': {
  825. answers = boxes;
  826. calculate_variant_hash();
  827. } break;
  828. }
  829. color_answers();
  830. auto_answer();
  831. update_variant();
  832. for (i = 0; i < answers.length; i++) {
  833. answers[i].addEventListener('change', update_variant);
  834. }
  835. }
  836.  
  837. function result_page_handler() {
  838. var i;
  839. var correct_num = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
  840. var all_num = variant.slice(variant.indexOf("Число неверных ответов: ") + 24);
  841. correct_num = correct_num.slice(0, correct_num.indexOf("\n")).trim();
  842. all_num = all_num.slice(0, all_num.indexOf("\n")).trim();
  843. all_num = (parseInt(all_num) + parseInt(correct_num)).toString();
  844. var test = GM_getValue('tests', new Object())[testID];
  845. if (test === undefined) {
  846. return;
  847. }
  848. var printer = "";
  849. var sorted_test = [];
  850. for (var hash in test) {
  851. sorted_test.push([hash].concat(test[hash]));
  852. }
  853. sorted_test.sort((a, b) => {return a[1] - b[1]});
  854. for (i = 0; i < sorted_test.length; i++) {
  855. printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
  856. }
  857. printer += correct_num;
  858. if (config.get('copy_answers')) {
  859. GM_setClipboard(printer);
  860. }
  861. if (config.get('accumulator_enable')) {
  862. var acc = GM_getValue('accumulated_answers', "");
  863. if (acc != "") {
  864. acc += "\n\n";
  865. }
  866. var prefix = testID;
  867. if (prefix != "") {
  868. acc += prefix + "\n";
  869. }
  870. acc += printer;
  871. GM_setValue('accumulated_answers', acc);
  872. printer = acc;
  873. }
  874. printer = "<textarea readonly style='resize:none; width:fit-content; height:fit-content' rows='" + String(Object.keys(test).length + 1) + "' cols='50' onfocus='this.select();' id='answers'>" + printer + "</textarea>";
  875. var pboxes = document.getElementsByTagName('p');
  876. for (i = 0; i < pboxes.length; i++) {
  877. var pbox = pboxes[i];
  878. if (pbox.textContent.includes("Попытка ")) {
  879. pbox.outerHTML += printer;
  880. break;
  881. }
  882. }
  883. set_to_clear(testID, () => {
  884. if (GM_getValue('new_answer_f')) {
  885. send_to_server({
  886. type: "test_results",
  887. uid: config.get('user_id'),
  888. id: testID,
  889. answers: sorted_test,
  890. correct: correct_num,
  891. all: all_num,
  892. });
  893. }
  894. GM_setValue('fetched_data', {});
  895. GM_setValue('new_answer_f', false);
  896. });
  897. }
  898.  
  899. /* End Handlers */
  900.  
  901. function main() {
  902. variant = document.getElementById('w0').parentNode.textContent;
  903. prev_new_answer_f = !!GM_getValue('new_answer_f');
  904. if (variant.includes("Вопрос:")) {
  905. fetch_from_server(testID, (server_response) => {
  906. DB_cleaner();
  907. test_form_handler(server_response);
  908. if (config.get('wait_server_response') && (version === undefined)) {
  909. window.setInterval(() => {fetch_from_server(testID, (server_response) => {
  910. if (version === undefined) {
  911. parse_server_data(server_response);
  912. color_answers();
  913. if (config.get('auto_continue')) {
  914. auto_answer();
  915. }
  916. update_variant();
  917. if (version === VERSION) {
  918. auto_continue();
  919. }
  920. }
  921. })}, 1000, true);
  922. } else {
  923. auto_continue();
  924. }
  925. });
  926. } else if (variant.includes("Результат прохождения теста:")) {
  927. result_page_handler();
  928. auto_restart();
  929. }
  930. }