Sorryops

Collect and reuse ORIOKS test answers

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

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